@theokit/sdk 4.24.0 → 4.26.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 (382) hide show
  1. package/dist/a2a/index.cjs +10 -171
  2. package/dist/a2a/index.cjs.map +1 -1
  3. package/dist/a2a/index.js +3 -170
  4. package/dist/a2a/index.js.map +1 -1
  5. package/dist/agent-factory-registry-UU46BUMU.cjs +16 -0
  6. package/dist/agent-factory-registry-UU46BUMU.cjs.map +1 -0
  7. package/dist/agent-factory-registry-ZVTABLEY.js +3 -0
  8. package/dist/agent-factory-registry-ZVTABLEY.js.map +1 -0
  9. package/dist/auth/index.cjs +63 -367
  10. package/dist/auth/index.cjs.map +1 -1
  11. package/dist/auth/index.js +6 -342
  12. package/dist/auth/index.js.map +1 -1
  13. package/dist/batch-2FSD2UNL.cjs +244 -0
  14. package/dist/batch-2FSD2UNL.cjs.map +1 -0
  15. package/dist/batch-MR2HKYMS.js +241 -0
  16. package/dist/batch-MR2HKYMS.js.map +1 -0
  17. package/dist/chunk-2HRYBRD7.cjs +31 -0
  18. package/dist/chunk-2HRYBRD7.cjs.map +1 -0
  19. package/dist/chunk-2MF3IE2D.cjs +2863 -0
  20. package/dist/chunk-2MF3IE2D.cjs.map +1 -0
  21. package/dist/chunk-2ONFGMBV.js +6 -0
  22. package/dist/chunk-2ONFGMBV.js.map +1 -0
  23. package/dist/chunk-2SFBB54R.js +23 -0
  24. package/dist/chunk-2SFBB54R.js.map +1 -0
  25. package/dist/chunk-2XLKLVVR.js +18 -0
  26. package/dist/chunk-2XLKLVVR.js.map +1 -0
  27. package/dist/chunk-4SXHEZIV.cjs +240 -0
  28. package/dist/chunk-4SXHEZIV.cjs.map +1 -0
  29. package/dist/chunk-4VPXM6UU.js +10 -0
  30. package/dist/chunk-4VPXM6UU.js.map +1 -0
  31. package/dist/chunk-4WL52CXU.cjs +148 -0
  32. package/dist/chunk-4WL52CXU.cjs.map +1 -0
  33. package/dist/chunk-5NBUH3NO.js +39 -0
  34. package/dist/chunk-5NBUH3NO.js.map +1 -0
  35. package/dist/chunk-5PZMMF4H.js +43 -0
  36. package/dist/chunk-5PZMMF4H.js.map +1 -0
  37. package/dist/chunk-6CLRANZA.js +289 -0
  38. package/dist/chunk-6CLRANZA.js.map +1 -0
  39. package/dist/chunk-6JSIEDKL.cjs +121 -0
  40. package/dist/chunk-6JSIEDKL.cjs.map +1 -0
  41. package/dist/chunk-6M4ODB2V.cjs +145 -0
  42. package/dist/chunk-6M4ODB2V.cjs.map +1 -0
  43. package/dist/chunk-6X6ID4MO.js +20 -0
  44. package/dist/chunk-6X6ID4MO.js.map +1 -0
  45. package/dist/chunk-77FOZCOT.js +165 -0
  46. package/dist/chunk-77FOZCOT.js.map +1 -0
  47. package/dist/chunk-7AD3ZTZL.js +25 -0
  48. package/dist/chunk-7AD3ZTZL.js.map +1 -0
  49. package/dist/chunk-7GFX45DD.js +57 -0
  50. package/dist/chunk-7GFX45DD.js.map +1 -0
  51. package/dist/chunk-7S7KKUO6.cjs +45 -0
  52. package/dist/chunk-7S7KKUO6.cjs.map +1 -0
  53. package/dist/chunk-7W7ZWMLQ.js +99 -0
  54. package/dist/chunk-7W7ZWMLQ.js.map +1 -0
  55. package/dist/chunk-A44COQJJ.cjs +353 -0
  56. package/dist/chunk-A44COQJJ.cjs.map +1 -0
  57. package/dist/chunk-AKSBP7OO.js +185 -0
  58. package/dist/chunk-AKSBP7OO.js.map +1 -0
  59. package/dist/chunk-ANIZEWTU.cjs +196 -0
  60. package/dist/chunk-ANIZEWTU.cjs.map +1 -0
  61. package/dist/chunk-BEURCLDV.cjs +139 -0
  62. package/dist/chunk-BEURCLDV.cjs.map +1 -0
  63. package/dist/chunk-BFWPZMG7.js +379 -0
  64. package/dist/chunk-BFWPZMG7.js.map +1 -0
  65. package/dist/chunk-BUUUWQMB.cjs +34 -0
  66. package/dist/chunk-BUUUWQMB.cjs.map +1 -0
  67. package/dist/chunk-BV2MWEMV.cjs +26 -0
  68. package/dist/chunk-BV2MWEMV.cjs.map +1 -0
  69. package/dist/chunk-BWT67BI4.cjs +268 -0
  70. package/dist/chunk-BWT67BI4.cjs.map +1 -0
  71. package/dist/chunk-BXV2QUIC.js +191 -0
  72. package/dist/chunk-BXV2QUIC.js.map +1 -0
  73. package/dist/chunk-C6Y6CWYD.cjs +44 -0
  74. package/dist/chunk-C6Y6CWYD.cjs.map +1 -0
  75. package/dist/chunk-C7PICHRF.cjs +68 -0
  76. package/dist/chunk-C7PICHRF.cjs.map +1 -0
  77. package/dist/chunk-CAH3G4IS.js +13 -0
  78. package/dist/chunk-CAH3G4IS.js.map +1 -0
  79. package/dist/chunk-D42XHI4O.cjs +565 -0
  80. package/dist/chunk-D42XHI4O.cjs.map +1 -0
  81. package/dist/chunk-DGRZNMTU.js +104 -0
  82. package/dist/chunk-DGRZNMTU.js.map +1 -0
  83. package/dist/chunk-DUK5WMKJ.js +71 -0
  84. package/dist/chunk-DUK5WMKJ.js.map +1 -0
  85. package/dist/chunk-EEZ7CFMM.js +14 -0
  86. package/dist/chunk-EEZ7CFMM.js.map +1 -0
  87. package/dist/chunk-EPYZOCOL.cjs +82 -0
  88. package/dist/chunk-EPYZOCOL.cjs.map +1 -0
  89. package/dist/chunk-EVADEGM7.cjs +168 -0
  90. package/dist/chunk-EVADEGM7.cjs.map +1 -0
  91. package/dist/chunk-FD2UT76F.js +3 -0
  92. package/dist/chunk-FD2UT76F.js.map +1 -0
  93. package/dist/chunk-FV3YEMQ7.js +179 -0
  94. package/dist/chunk-FV3YEMQ7.js.map +1 -0
  95. package/dist/chunk-G2F67XUR.cjs +184 -0
  96. package/dist/chunk-G2F67XUR.cjs.map +1 -0
  97. package/dist/chunk-G6GO6BXD.js +994 -0
  98. package/dist/chunk-G6GO6BXD.js.map +1 -0
  99. package/dist/chunk-GIW4L6HY.cjs +27 -0
  100. package/dist/chunk-GIW4L6HY.cjs.map +1 -0
  101. package/dist/chunk-GJAGKFTR.js +74 -0
  102. package/dist/chunk-GJAGKFTR.js.map +1 -0
  103. package/dist/chunk-HFPGOTVF.cjs +324 -0
  104. package/dist/chunk-HFPGOTVF.cjs.map +1 -0
  105. package/dist/chunk-HRA3CPGG.js +486 -0
  106. package/dist/chunk-HRA3CPGG.js.map +1 -0
  107. package/dist/chunk-HZQEDNF6.js +66 -0
  108. package/dist/chunk-HZQEDNF6.js.map +1 -0
  109. package/dist/chunk-IT3PWBW4.cjs +19 -0
  110. package/dist/chunk-IT3PWBW4.cjs.map +1 -0
  111. package/dist/chunk-JD74LN7R.cjs +171 -0
  112. package/dist/chunk-JD74LN7R.cjs.map +1 -0
  113. package/dist/chunk-JTFYYKDT.js +135 -0
  114. package/dist/chunk-JTFYYKDT.js.map +1 -0
  115. package/dist/chunk-K2BQQ445.js +30 -0
  116. package/dist/chunk-K2BQQ445.js.map +1 -0
  117. package/dist/chunk-KAP4GJHO.js +546 -0
  118. package/dist/chunk-KAP4GJHO.js.map +1 -0
  119. package/dist/chunk-KDNBTPY2.js +458 -0
  120. package/dist/chunk-KDNBTPY2.js.map +1 -0
  121. package/dist/chunk-KIO7QFIV.cjs +223 -0
  122. package/dist/chunk-KIO7QFIV.cjs.map +1 -0
  123. package/dist/chunk-KSKU6JY6.cjs +71 -0
  124. package/dist/chunk-KSKU6JY6.cjs.map +1 -0
  125. package/dist/chunk-LSLPF6OI.cjs +192 -0
  126. package/dist/chunk-LSLPF6OI.cjs.map +1 -0
  127. package/dist/chunk-LU7VALW5.js +312 -0
  128. package/dist/chunk-LU7VALW5.js.map +1 -0
  129. package/dist/chunk-LUSXDZPV.cjs +296 -0
  130. package/dist/chunk-LUSXDZPV.cjs.map +1 -0
  131. package/dist/chunk-M72RJVPC.js +69 -0
  132. package/dist/chunk-M72RJVPC.js.map +1 -0
  133. package/dist/chunk-MQLCZ4HJ.cjs +1010 -0
  134. package/dist/chunk-MQLCZ4HJ.cjs.map +1 -0
  135. package/dist/chunk-MUUQ2WFJ.cjs +4 -0
  136. package/dist/chunk-MUUQ2WFJ.cjs.map +1 -0
  137. package/dist/chunk-MZ5ZRORA.cjs +496 -0
  138. package/dist/chunk-MZ5ZRORA.cjs.map +1 -0
  139. package/dist/chunk-N24XBYNX.cjs +140 -0
  140. package/dist/chunk-N24XBYNX.cjs.map +1 -0
  141. package/dist/chunk-NINIJCHU.cjs +23 -0
  142. package/dist/chunk-NINIJCHU.cjs.map +1 -0
  143. package/dist/chunk-NRSH7RJY.js +2841 -0
  144. package/dist/chunk-NRSH7RJY.js.map +1 -0
  145. package/dist/chunk-OIYQBUW4.cjs +111 -0
  146. package/dist/chunk-OIYQBUW4.cjs.map +1 -0
  147. package/dist/chunk-PNVDQL5Y.cjs +15 -0
  148. package/dist/chunk-PNVDQL5Y.cjs.map +1 -0
  149. package/dist/chunk-POQT2OIF.js +214 -0
  150. package/dist/chunk-POQT2OIF.js.map +1 -0
  151. package/dist/chunk-PSJN532Q.js +10573 -0
  152. package/dist/chunk-PSJN532Q.js.map +1 -0
  153. package/dist/chunk-QUTUJ6P6.cjs +76 -0
  154. package/dist/chunk-QUTUJ6P6.cjs.map +1 -0
  155. package/dist/chunk-R43CNCES.cjs +460 -0
  156. package/dist/chunk-R43CNCES.cjs.map +1 -0
  157. package/dist/chunk-S2BBRVQX.js +68 -0
  158. package/dist/chunk-S2BBRVQX.js.map +1 -0
  159. package/dist/chunk-S5RCBTSF.js +145 -0
  160. package/dist/chunk-S5RCBTSF.js.map +1 -0
  161. package/dist/chunk-S7OEXQMI.cjs +136 -0
  162. package/dist/chunk-S7OEXQMI.cjs.map +1 -0
  163. package/dist/chunk-SKWVAQAY.js +129 -0
  164. package/dist/chunk-SKWVAQAY.js.map +1 -0
  165. package/dist/chunk-SQLZ7ONU.js +152 -0
  166. package/dist/chunk-SQLZ7ONU.js.map +1 -0
  167. package/dist/chunk-TIZ4TS5V.cjs +72 -0
  168. package/dist/chunk-TIZ4TS5V.cjs.map +1 -0
  169. package/dist/chunk-TQKCU7CL.js +142 -0
  170. package/dist/chunk-TQKCU7CL.js.map +1 -0
  171. package/dist/chunk-U54XCYG3.js +116 -0
  172. package/dist/chunk-U54XCYG3.js.map +1 -0
  173. package/dist/chunk-U6QGZHPN.js +29 -0
  174. package/dist/chunk-U6QGZHPN.js.map +1 -0
  175. package/dist/chunk-UCFONJBG.js +79 -0
  176. package/dist/chunk-UCFONJBG.js.map +1 -0
  177. package/dist/chunk-UJ7IQJAG.js +141 -0
  178. package/dist/chunk-UJ7IQJAG.js.map +1 -0
  179. package/dist/chunk-UVNG67DG.js +253 -0
  180. package/dist/chunk-UVNG67DG.js.map +1 -0
  181. package/dist/chunk-V4XRLPEF.js +25 -0
  182. package/dist/chunk-V4XRLPEF.js.map +1 -0
  183. package/dist/chunk-VJ26EKGJ.cjs +28 -0
  184. package/dist/chunk-VJ26EKGJ.cjs.map +1 -0
  185. package/dist/chunk-VK7MAV65.cjs +90 -0
  186. package/dist/chunk-VK7MAV65.cjs.map +1 -0
  187. package/dist/chunk-VYCKUKPA.cjs +106 -0
  188. package/dist/chunk-VYCKUKPA.cjs.map +1 -0
  189. package/dist/chunk-VYHJZVL5.cjs +29 -0
  190. package/dist/chunk-VYHJZVL5.cjs.map +1 -0
  191. package/dist/chunk-WA3FO4R7.js +155 -0
  192. package/dist/chunk-WA3FO4R7.js.map +1 -0
  193. package/dist/chunk-WJVHMTKB.cjs +12 -0
  194. package/dist/chunk-WJVHMTKB.cjs.map +1 -0
  195. package/dist/chunk-WLKFWQSJ.js +333 -0
  196. package/dist/chunk-WLKFWQSJ.js.map +1 -0
  197. package/dist/chunk-WPZTYOUX.cjs +59 -0
  198. package/dist/chunk-WPZTYOUX.cjs.map +1 -0
  199. package/dist/chunk-X2FR4OIT.js +27 -0
  200. package/dist/chunk-X2FR4OIT.js.map +1 -0
  201. package/dist/chunk-XIG2IE3L.js +134 -0
  202. package/dist/chunk-XIG2IE3L.js.map +1 -0
  203. package/dist/chunk-YLQQX5W2.cjs +20 -0
  204. package/dist/chunk-YLQQX5W2.cjs.map +1 -0
  205. package/dist/chunk-YPEPCZHF.cjs +147 -0
  206. package/dist/chunk-YPEPCZHF.cjs.map +1 -0
  207. package/dist/chunk-YPQF26X7.cjs +8 -0
  208. package/dist/chunk-YPQF26X7.cjs.map +1 -0
  209. package/dist/chunk-YU6KON2I.cjs +160 -0
  210. package/dist/chunk-YU6KON2I.cjs.map +1 -0
  211. package/dist/chunk-Z2OC3UIW.cjs +388 -0
  212. package/dist/chunk-Z2OC3UIW.cjs.map +1 -0
  213. package/dist/chunk-ZUBIEQJF.cjs +10590 -0
  214. package/dist/chunk-ZUBIEQJF.cjs.map +1 -0
  215. package/dist/chunk-ZZWTY3RX.js +230 -0
  216. package/dist/chunk-ZZWTY3RX.js.map +1 -0
  217. package/dist/compact-session-OO2GD76G.js +17 -0
  218. package/dist/compact-session-OO2GD76G.js.map +1 -0
  219. package/dist/compact-session-OO7NVWPB.cjs +54 -0
  220. package/dist/compact-session-OO7NVWPB.cjs.map +1 -0
  221. package/dist/compaction.cjs +54 -303
  222. package/dist/compaction.cjs.map +1 -1
  223. package/dist/compaction.js +3 -292
  224. package/dist/compaction.js.map +1 -1
  225. package/dist/concurrency.cjs +9 -81
  226. package/dist/concurrency.cjs.map +1 -1
  227. package/dist/concurrency.js +5 -80
  228. package/dist/concurrency.js.map +1 -1
  229. package/dist/context-GWANQLX4.js +5 -0
  230. package/dist/context-GWANQLX4.js.map +1 -0
  231. package/dist/context-KNWVTZSY.cjs +22 -0
  232. package/dist/context-KNWVTZSY.cjs.map +1 -0
  233. package/dist/{cron-x3muPNgg.d.cts → cron-D0JKR-yX.d.cts} +2 -1
  234. package/dist/{cron-B_NkE_VM.d.ts → cron-DivRaEmN.d.ts} +2 -1
  235. package/dist/cron.cjs +44 -20676
  236. package/dist/cron.cjs.map +1 -1
  237. package/dist/cron.d.cts +1 -1
  238. package/dist/cron.d.ts +1 -1
  239. package/dist/cron.js +38 -20676
  240. package/dist/cron.js.map +1 -1
  241. package/dist/errors.cjs +78 -470
  242. package/dist/errors.cjs.map +1 -1
  243. package/dist/errors.d.ts +3 -424
  244. package/dist/errors.js +2 -455
  245. package/dist/errors.js.map +1 -1
  246. package/dist/eval.cjs +72 -20411
  247. package/dist/eval.cjs.map +1 -1
  248. package/dist/eval.js +59 -20402
  249. package/dist/eval.js.map +1 -1
  250. package/dist/executor-ORJS5GV2.js +981 -0
  251. package/dist/executor-ORJS5GV2.js.map +1 -0
  252. package/dist/executor-Y4MKPIUD.cjs +985 -0
  253. package/dist/executor-Y4MKPIUD.cjs.map +1 -0
  254. package/dist/filesystem/index.cjs +7 -112
  255. package/dist/filesystem/index.cjs.map +1 -1
  256. package/dist/filesystem/index.js +4 -109
  257. package/dist/filesystem/index.js.map +1 -1
  258. package/dist/fork-agent-RSV4E3EA.js +61 -0
  259. package/dist/fork-agent-RSV4E3EA.js.map +1 -0
  260. package/dist/fork-agent-Y2IK2RYU.cjs +64 -0
  261. package/dist/fork-agent-Y2IK2RYU.cjs.map +1 -0
  262. package/dist/fs-session-store-KAUQYSGI.cjs +17 -0
  263. package/dist/fs-session-store-KAUQYSGI.cjs.map +1 -0
  264. package/dist/fs-session-store-ZKB5NN37.js +8 -0
  265. package/dist/fs-session-store-ZKB5NN37.js.map +1 -0
  266. package/dist/generate-object-H3RNAM6S.cjs +20 -0
  267. package/dist/generate-object-H3RNAM6S.cjs.map +1 -0
  268. package/dist/generate-object-YJPDV5BS.js +7 -0
  269. package/dist/generate-object-YJPDV5BS.js.map +1 -0
  270. package/dist/index-manager-CB6I7XFQ.cjs +19 -0
  271. package/dist/index-manager-CB6I7XFQ.cjs.map +1 -0
  272. package/dist/index-manager-L27Q3QAC.js +10 -0
  273. package/dist/index-manager-L27Q3QAC.js.map +1 -0
  274. package/dist/index.cjs +371 -22888
  275. package/dist/index.cjs.map +1 -1
  276. package/dist/index.d.cts +16 -21
  277. package/dist/index.d.ts +16 -21
  278. package/dist/index.js +92 -22713
  279. package/dist/index.js.map +1 -1
  280. package/dist/inject-session-6MJUSGZP.cjs +27 -0
  281. package/dist/inject-session-6MJUSGZP.cjs.map +1 -0
  282. package/dist/inject-session-KOFHS5K3.js +25 -0
  283. package/dist/inject-session-KOFHS5K3.js.map +1 -0
  284. package/dist/internal/judge/judge-call.d.ts +32 -19
  285. package/dist/internal/persistence/index.cjs +93 -508
  286. package/dist/internal/persistence/index.cjs.map +1 -1
  287. package/dist/internal/persistence/index.js +11 -489
  288. package/dist/internal/persistence/index.js.map +1 -1
  289. package/dist/internal/security/index.cjs +44 -361
  290. package/dist/internal/security/index.cjs.map +1 -1
  291. package/dist/internal/security/index.js +4 -355
  292. package/dist/internal/security/index.js.map +1 -1
  293. package/dist/judge-call-CEQ3FYYD.js +126 -0
  294. package/dist/judge-call-CEQ3FYYD.js.map +1 -0
  295. package/dist/judge-call-QG4WZ7F3.cjs +130 -0
  296. package/dist/judge-call-QG4WZ7F3.cjs.map +1 -0
  297. package/dist/models.cjs +24 -919
  298. package/dist/models.cjs.map +1 -1
  299. package/dist/models.js +11 -907
  300. package/dist/models.js.map +1 -1
  301. package/dist/oauth-transaction-store-7CKHPQRN.cjs +32 -0
  302. package/dist/oauth-transaction-store-7CKHPQRN.cjs.map +1 -0
  303. package/dist/oauth-transaction-store-W74I6EFD.js +3 -0
  304. package/dist/oauth-transaction-store-W74I6EFD.js.map +1 -0
  305. package/dist/path-safety.cjs +31 -200
  306. package/dist/path-safety.cjs.map +1 -1
  307. package/dist/path-safety.js +3 -197
  308. package/dist/path-safety.js.map +1 -1
  309. package/dist/persistence.cjs +73 -393
  310. package/dist/persistence.cjs.map +1 -1
  311. package/dist/persistence.js +9 -381
  312. package/dist/persistence.js.map +1 -1
  313. package/dist/project.cjs +8 -112
  314. package/dist/project.cjs.map +1 -1
  315. package/dist/project.js +6 -110
  316. package/dist/project.js.map +1 -1
  317. package/dist/registry-34IWWDQQ.js +9 -0
  318. package/dist/registry-34IWWDQQ.js.map +1 -0
  319. package/dist/registry-APYDISL6.cjs +46 -0
  320. package/dist/registry-APYDISL6.cjs.map +1 -0
  321. package/dist/retry.cjs +7 -87
  322. package/dist/retry.cjs.map +1 -1
  323. package/dist/retry.js +3 -90
  324. package/dist/retry.js.map +1 -1
  325. package/dist/run-to-completion-J73I6IY4.cjs +48 -0
  326. package/dist/run-to-completion-J73I6IY4.cjs.map +1 -0
  327. package/dist/run-to-completion-JDPIUKSH.js +3 -0
  328. package/dist/run-to-completion-JDPIUKSH.js.map +1 -0
  329. package/dist/run-until-PNO3QQLI.cjs +180 -0
  330. package/dist/run-until-PNO3QQLI.cjs.map +1 -0
  331. package/dist/run-until-XLHEHRQM.js +177 -0
  332. package/dist/run-until-XLHEHRQM.js.map +1 -0
  333. package/dist/sandbox/index.cjs +35 -337
  334. package/dist/sandbox/index.cjs.map +1 -1
  335. package/dist/sandbox/index.js +7 -323
  336. package/dist/sandbox/index.js.map +1 -1
  337. package/dist/sanitize/index.cjs +5 -112
  338. package/dist/sanitize/index.cjs.map +1 -1
  339. package/dist/sanitize/index.js +1 -114
  340. package/dist/sanitize/index.js.map +1 -1
  341. package/dist/server/auth/index.cjs +35 -175
  342. package/dist/server/auth/index.cjs.map +1 -1
  343. package/dist/server/auth/index.js +7 -161
  344. package/dist/server/auth/index.js.map +1 -1
  345. package/dist/server/errors-envelope.cjs +17 -328
  346. package/dist/server/errors-envelope.cjs.map +1 -1
  347. package/dist/server/errors-envelope.js +4 -317
  348. package/dist/server/errors-envelope.js.map +1 -1
  349. package/dist/session-transcript-LPNZR3YS.cjs +46 -0
  350. package/dist/session-transcript-LPNZR3YS.cjs.map +1 -0
  351. package/dist/session-transcript-T7LGJUGW.js +5 -0
  352. package/dist/session-transcript-T7LGJUGW.js.map +1 -0
  353. package/dist/skills.cjs +12 -280
  354. package/dist/skills.cjs.map +1 -1
  355. package/dist/skills.js +4 -282
  356. package/dist/skills.js.map +1 -1
  357. package/dist/stream-object-QEQJJDTI.cjs +20 -0
  358. package/dist/stream-object-QEQJJDTI.cjs.map +1 -0
  359. package/dist/stream-object-WUQSSRY2.js +7 -0
  360. package/dist/stream-object-WUQSSRY2.js.map +1 -0
  361. package/dist/stream-to-completion-DFJ5T3BK.js +30 -0
  362. package/dist/stream-to-completion-DFJ5T3BK.js.map +1 -0
  363. package/dist/stream-to-completion-H2MHISSK.cjs +32 -0
  364. package/dist/stream-to-completion-H2MHISSK.cjs.map +1 -0
  365. package/dist/subagents.cjs +2 -8
  366. package/dist/subagents.cjs.map +1 -1
  367. package/dist/subagents.js +1 -7
  368. package/dist/subagents.js.map +1 -1
  369. package/dist/subscription/index.cjs +4 -17
  370. package/dist/subscription/index.cjs.map +1 -1
  371. package/dist/subscription/index.js +3 -16
  372. package/dist/subscription/index.js.map +1 -1
  373. package/dist/task-store.cjs +16 -258
  374. package/dist/task-store.cjs.map +1 -1
  375. package/dist/task-store.js +4 -259
  376. package/dist/task-store.js.map +1 -1
  377. package/dist/types/goal-events.d.ts +2 -1
  378. package/dist/workflow.cjs +71 -2797
  379. package/dist/workflow.cjs.map +1 -1
  380. package/dist/workflow.js +8 -2812
  381. package/dist/workflow.js.map +1 -1
  382. package/package.json +1 -1
@@ -0,0 +1,2841 @@
1
+ import { currentCredentialPool, computeBackoffMs, sleepWithAbort, newPooledCredential, CredentialPool } from './chunk-6CLRANZA.js';
2
+ import { invalidateSessionCache } from './chunk-EEZ7CFMM.js';
3
+ import { tryJson, isPlainObject, sanitizeToolInput } from './chunk-U54XCYG3.js';
4
+ import { reconstructMessages, SessionTranscript } from './chunk-ZZWTY3RX.js';
5
+ import { estimateTokens } from './chunk-WA3FO4R7.js';
6
+ import { globalSingleton, registerProvider, registerBuiltins, getProviderProfile, inferModelDialect, inferRegionFromModelId, stripBedrockPrefix, resolveBedrockBaseUrl, resolveVertexBaseUrl, stripVertexPrefix } from './chunk-KAP4GJHO.js';
7
+ import { NetworkError, AuthenticationError, RateLimitError, ConfigurationError, UnknownAgentError, CredentialPoolExhaustedError } from './chunk-WLKFWQSJ.js';
8
+ import { redactSecrets } from './chunk-JTFYYKDT.js';
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { readdir } from 'fs/promises';
11
+ import { homedir } from 'os';
12
+ import { join } from 'path';
13
+ import { pathToFileURL } from 'url';
14
+ import { createRequire } from 'module';
15
+
16
+ var discoveryState = globalSingleton("theokit-sdk.providers.discovered", () => ({
17
+ done: false
18
+ }));
19
+ function pluginsRoot() {
20
+ return join(homedir(), ".theokit", "plugins", "model-providers");
21
+ }
22
+ function trustFilePath() {
23
+ return join(homedir(), ".theokit", "plugins", "trusted-providers.json");
24
+ }
25
+ function loadTrustedNames() {
26
+ const trusted = /* @__PURE__ */ new Set();
27
+ const path = trustFilePath();
28
+ if (existsSync(path)) {
29
+ try {
30
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
31
+ if (Array.isArray(parsed)) {
32
+ let discarded = 0;
33
+ for (const name of parsed) {
34
+ if (typeof name === "string" && name.length > 0) trusted.add(name);
35
+ else discarded += 1;
36
+ }
37
+ if (discarded > 0) {
38
+ process.stderr.write(
39
+ `[theokit-sdk] WARN: ${path} contains ${discarded} non-string entr${discarded === 1 ? "y" : "ies"} \u2014 discarded (entries must be plugin-name strings)
40
+ `
41
+ );
42
+ }
43
+ } else {
44
+ process.stderr.write(
45
+ `[theokit-sdk] WARN: ${path} is not a JSON array \u2014 trusting NO provider plugins (fail-closed)
46
+ `
47
+ );
48
+ }
49
+ } catch {
50
+ process.stderr.write(
51
+ `[theokit-sdk] WARN: ${path} is not valid JSON \u2014 trusting NO provider plugins (fail-closed)
52
+ `
53
+ );
54
+ }
55
+ }
56
+ for (const name of (process.env.THEOKIT_TRUSTED_PROVIDERS ?? "").split(",")) {
57
+ const trimmed = name.trim();
58
+ if (trimmed.length > 0) trusted.add(trimmed);
59
+ }
60
+ return trusted;
61
+ }
62
+ async function discoverProviderPlugins() {
63
+ if (discoveryState.done) return;
64
+ discoveryState.done = true;
65
+ const root = pluginsRoot();
66
+ if (!existsSync(root)) return;
67
+ let entries;
68
+ try {
69
+ entries = await readdir(root);
70
+ } catch {
71
+ return;
72
+ }
73
+ const trusted = loadTrustedNames();
74
+ for (const entry of entries) {
75
+ if (!trusted.has(entry)) {
76
+ process.stderr.write(
77
+ `[theokit-sdk] WARN: provider plugin "${entry}" is present but NOT trusted \u2014 skipped. To trust it, add "${entry}" to ${trustFilePath()} (JSON array of names) or THEOKIT_TRUSTED_PROVIDERS (comma-separated), then restart the process.
78
+ `
79
+ );
80
+ continue;
81
+ }
82
+ await loadOne(join(root, entry), entry);
83
+ }
84
+ }
85
+ async function loadOne(dir, entryName) {
86
+ for (const ext of ["index.mjs", "index.js"]) {
87
+ const indexPath = join(dir, ext);
88
+ if (!existsSync(indexPath)) continue;
89
+ try {
90
+ const mod = await import(pathToFileURL(indexPath).href);
91
+ const plugin = mod.default ?? mod[entryName] ?? mod;
92
+ if (plugin !== null && typeof plugin === "object" && plugin.kind === "model-provider") {
93
+ const profile = plugin.profile;
94
+ if (profile !== void 0 && typeof profile === "object") {
95
+ registerProvider(profile);
96
+ }
97
+ }
98
+ return;
99
+ } catch (err) {
100
+ const msg = err instanceof Error ? err.message : String(err);
101
+ process.stderr.write(`[theokit-sdk] failed to load provider plugin "${entryName}": ${msg}
102
+ `);
103
+ return;
104
+ }
105
+ }
106
+ }
107
+
108
+ // src/internal/error-mappers/shared.ts
109
+ var RAW_MAX_BYTES = 2048;
110
+ function parseRetryAfter(headers) {
111
+ if (headers === void 0) return void 0;
112
+ const raw = headers.get("retry-after");
113
+ if (raw === null) return void 0;
114
+ const n = Number(raw);
115
+ if (Number.isFinite(n) && n >= 0) return Math.ceil(n);
116
+ const dateMs = Date.parse(raw);
117
+ if (Number.isFinite(dateMs)) return Math.max(0, Math.ceil((dateMs - Date.now()) / 1e3));
118
+ return void 0;
119
+ }
120
+ function truncateRaw(body) {
121
+ if (body === null || body === void 0) return void 0;
122
+ const s = typeof body === "string" ? body : JSON.stringify(body);
123
+ const truncated = s.length <= RAW_MAX_BYTES ? s : `${s.slice(0, RAW_MAX_BYTES)}\u2026`;
124
+ return redactSecrets(truncated);
125
+ }
126
+ function buildErrorMetadata(args) {
127
+ const retryAfter = parseRetryAfter(args.headers);
128
+ const raw = truncateRaw(args.body);
129
+ return {
130
+ provider: args.provider,
131
+ endpoint: args.endpoint,
132
+ code: args.code,
133
+ statusCode: args.status,
134
+ ...retryAfter !== void 0 ? { retryAfter } : {},
135
+ ...raw !== void 0 ? { raw } : {}
136
+ };
137
+ }
138
+
139
+ // src/internal/error-mappers/anthropic.ts
140
+ function mapAnthropicError(args) {
141
+ const { status, body, headers, endpoint } = args;
142
+ const code = mapAnthropicStatusToCode(status, body);
143
+ const message = formatMessage(status, code);
144
+ const metadata = buildErrorMetadata({
145
+ provider: "anthropic",
146
+ endpoint,
147
+ code,
148
+ status,
149
+ headers,
150
+ body
151
+ });
152
+ if (status === 401 || status === 403) {
153
+ return new AuthenticationError(message, { code: "anthropic_auth_failed", metadata });
154
+ }
155
+ if (status === 429) {
156
+ return new RateLimitError(message, { code: "anthropic_rate_limit", metadata });
157
+ }
158
+ if (status === 400) {
159
+ return new ConfigurationError(message, {
160
+ code: `anthropic_${code}`,
161
+ metadata
162
+ });
163
+ }
164
+ if (status === 408) {
165
+ return new NetworkError(message, { code: "anthropic_timeout", metadata });
166
+ }
167
+ if (status >= 500 && status < 600) {
168
+ return new NetworkError(message, { code: "anthropic_server_error", metadata });
169
+ }
170
+ return new UnknownAgentError(message, { code: "anthropic_unknown", metadata });
171
+ }
172
+ function mapAnthropicStatusToCode(status, body) {
173
+ if (status === 401 || status === 403) return "auth_failed";
174
+ if (status === 429) return "rate_limit";
175
+ if (status === 408) return "timeout";
176
+ if (status === 400) {
177
+ const text = JSON.stringify(body ?? "").toLowerCase();
178
+ if (text.includes("context") && (text.includes("too long") || text.includes("too_long") || text.includes("length"))) {
179
+ return "context_too_long";
180
+ }
181
+ if (text.includes("filtered") || text.includes("content policy") || text.includes("content_policy")) {
182
+ return "content_filtered";
183
+ }
184
+ return "invalid_request";
185
+ }
186
+ if (status >= 500 && status < 600) return "server_error";
187
+ return "unknown";
188
+ }
189
+ function formatMessage(status, code) {
190
+ return `Anthropic API error: ${code} (HTTP ${status})`;
191
+ }
192
+
193
+ // src/internal/llm/finish.ts
194
+ function collapseSystemText(system) {
195
+ if (system === void 0) return "";
196
+ if (typeof system === "string") return system;
197
+ return system.map((b) => b.text).join("\n\n");
198
+ }
199
+ function parseToolArguments(buffered) {
200
+ if (buffered === void 0 || buffered.length === 0) return {};
201
+ try {
202
+ return JSON.parse(buffered);
203
+ } catch {
204
+ const repaired = tryJson(buffered, true);
205
+ if (isPlainObject(repaired)) return repaired;
206
+ return { raw: buffered };
207
+ }
208
+ }
209
+ function mapOpenAIFinish(reason) {
210
+ switch (reason) {
211
+ case "tool_calls":
212
+ return "tool_use";
213
+ case "length":
214
+ return "max_tokens";
215
+ default:
216
+ return "end_turn";
217
+ }
218
+ }
219
+ function makeLlmFinish(state) {
220
+ const finish = {
221
+ stopReason: state.stopReason,
222
+ text: state.text,
223
+ toolCalls: state.toolCalls
224
+ };
225
+ if (state.inputTokens !== void 0) finish.inputTokens = state.inputTokens;
226
+ if (state.outputTokens !== void 0) finish.outputTokens = state.outputTokens;
227
+ if (state.cacheReadTokens !== void 0) finish.cacheReadTokens = state.cacheReadTokens;
228
+ if (state.cacheWriteTokens !== void 0) finish.cacheWriteTokens = state.cacheWriteTokens;
229
+ if (state.reasoningTokens !== void 0) finish.reasoningTokens = state.reasoningTokens;
230
+ return finish;
231
+ }
232
+
233
+ // src/internal/llm/tool-result-content.ts
234
+ function toBlockToolResultContent(content) {
235
+ return content;
236
+ }
237
+ function toStringToolResultContent(content, providerName) {
238
+ if (typeof content === "string") return content;
239
+ if (content.some((block) => block.type === "image")) {
240
+ throw new ConfigurationError(
241
+ `provider "${providerName}" does not support image content in tool results`
242
+ );
243
+ }
244
+ return content.filter(
245
+ (block) => block.type === "text"
246
+ ).map((block) => block.text).join("\n");
247
+ }
248
+ function renderToolResultContentText(content, fn) {
249
+ if (typeof content === "string") return fn(content);
250
+ return content.map(
251
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
252
+ );
253
+ }
254
+
255
+ // src/internal/llm/anthropic-shared.ts
256
+ function extractAnthropicText(response) {
257
+ return (response.content ?? []).filter((c) => c.type === "text").map((c) => c.text).join("");
258
+ }
259
+ function extractAnthropicToolCalls(response) {
260
+ return (response.content ?? []).filter(
261
+ (c) => c.type === "tool_use"
262
+ ).map((c) => ({
263
+ type: "tool_use",
264
+ id: c.id,
265
+ name: c.name,
266
+ input: parseToolArguments(JSON.stringify(c.input))
267
+ }));
268
+ }
269
+ function mapAnthropicStopReason(reason) {
270
+ switch (reason) {
271
+ case "tool_use":
272
+ return "tool_use";
273
+ case "max_tokens":
274
+ return "max_tokens";
275
+ case "stop_sequence":
276
+ return "stop_sequence";
277
+ case "end_turn":
278
+ case null:
279
+ return "end_turn";
280
+ default:
281
+ return "end_turn";
282
+ }
283
+ }
284
+ function toAnthropicWireMessage(message) {
285
+ const role = message.role === "system" ? "user" : message.role;
286
+ const content = message.content.map((part) => {
287
+ if (part.type === "text") return { type: "text", text: part.text };
288
+ if (part.type === "tool_use") {
289
+ return { type: "tool_use", id: part.id, name: part.name, input: part.input };
290
+ }
291
+ if (part.type === "image") {
292
+ return { type: "image", source: part.source };
293
+ }
294
+ return {
295
+ type: "tool_result",
296
+ tool_use_id: part.toolUseId,
297
+ // SE7 — this wire is block-capable: strings pass through, structured
298
+ // text/image blocks are forwarded natively.
299
+ content: toBlockToolResultContent(part.content),
300
+ ...part.isError === true ? { is_error: true } : {}
301
+ };
302
+ });
303
+ return { role, content };
304
+ }
305
+ function postAnthropicRequest(args) {
306
+ return args.fetchImpl(args.url, {
307
+ method: "POST",
308
+ signal: args.signal,
309
+ headers: {
310
+ "content-type": "application/json",
311
+ accept: "application/json",
312
+ authorization: `Bearer ${args.token}`
313
+ },
314
+ body: JSON.stringify(args.body)
315
+ });
316
+ }
317
+ async function handleAnthropicResponse(args) {
318
+ if (!args.response.ok) {
319
+ throw args.errorMapper({
320
+ status: args.response.status,
321
+ body: await parseHttpErrorBody(args.response),
322
+ headers: args.response.headers,
323
+ endpoint: args.endpoint
324
+ });
325
+ }
326
+ return consumeAnthropicResponse(args.response);
327
+ }
328
+ async function consumeAnthropicResponse(response) {
329
+ const data = await response.json();
330
+ return {
331
+ text: extractAnthropicText(data),
332
+ toolCalls: extractAnthropicToolCalls(data),
333
+ stopReason: mapAnthropicStopReason(data.stop_reason ?? null),
334
+ inputTokens: data.usage?.input_tokens,
335
+ outputTokens: data.usage?.output_tokens
336
+ };
337
+ }
338
+ async function parseHttpErrorBody(response) {
339
+ const text = await response.text().catch(() => "");
340
+ try {
341
+ return JSON.parse(text);
342
+ } catch {
343
+ return text;
344
+ }
345
+ }
346
+ function buildAnthropicCommonBody(request) {
347
+ const body = {
348
+ max_tokens: request.maxTokens ?? 4096,
349
+ messages: request.messages.map(toAnthropicWireMessage)
350
+ };
351
+ const wireSystem = encodeAnthropicSystem(request.system);
352
+ if (wireSystem !== void 0) body.system = wireSystem;
353
+ if (request.temperature !== void 0) body.temperature = request.temperature;
354
+ if (request.tools !== void 0 && request.tools.length > 0) {
355
+ body.tools = request.tools.map((tool) => ({
356
+ name: tool.name,
357
+ description: tool.description,
358
+ input_schema: tool.inputSchema
359
+ }));
360
+ }
361
+ return body;
362
+ }
363
+ function encodeAnthropicSystem(system) {
364
+ if (system === void 0) return void 0;
365
+ if (typeof system === "string") return system;
366
+ if (system.length === 0) return void 0;
367
+ return system.map((block) => {
368
+ const wire = { type: "text", text: block.text };
369
+ if (block.cacheable === true) wire.cache_control = { type: "ephemeral" };
370
+ return wire;
371
+ });
372
+ }
373
+
374
+ // src/internal/llm/sse.ts
375
+ var DEFAULT_SSE_IDLE_MS = 6e4;
376
+ async function* parseSseStream(body, signal, idleTimeoutMs = DEFAULT_SSE_IDLE_MS) {
377
+ if (body === null) return;
378
+ const reader = body.getReader();
379
+ const decoder = new TextDecoder("utf-8");
380
+ const state = { buffer: "", event: "message", data: "" };
381
+ try {
382
+ for await (const chunk of readChunks(reader, signal, idleTimeoutMs)) {
383
+ state.buffer += decoder.decode(chunk, { stream: true });
384
+ for (const record of drainCompleteRecords(state)) yield record;
385
+ }
386
+ if (state.data.length > 0) yield { event: state.event, data: state.data };
387
+ } finally {
388
+ await cancelReaderQuietly(reader);
389
+ releaseReader(reader);
390
+ }
391
+ }
392
+ async function* readChunks(reader, signal, idleTimeoutMs) {
393
+ while (true) {
394
+ if (signal.aborted) return;
395
+ const { value, done } = await readWithIdleTimeout(reader, idleTimeoutMs);
396
+ if (done) return;
397
+ if (value !== void 0) yield value;
398
+ }
399
+ }
400
+ function readWithIdleTimeout(reader, idleTimeoutMs) {
401
+ if (idleTimeoutMs <= 0) return reader.read();
402
+ return new Promise(
403
+ (resolve, reject) => {
404
+ const timer = setTimeout(() => {
405
+ reject(
406
+ new NetworkError(`SSE stream idle for ${idleTimeoutMs}ms \u2014 upstream stalled`, {
407
+ code: "stream_idle_timeout"
408
+ })
409
+ );
410
+ }, idleTimeoutMs);
411
+ reader.read().then(
412
+ (result) => {
413
+ clearTimeout(timer);
414
+ resolve(result);
415
+ },
416
+ (err) => {
417
+ clearTimeout(timer);
418
+ reject(err);
419
+ }
420
+ );
421
+ }
422
+ );
423
+ }
424
+ async function cancelReaderQuietly(reader) {
425
+ try {
426
+ await reader.cancel();
427
+ } catch {
428
+ }
429
+ }
430
+ function drainCompleteRecords(state) {
431
+ const out = [];
432
+ let newlineIndex = state.buffer.indexOf("\n");
433
+ while (newlineIndex !== -1) {
434
+ const line = state.buffer.slice(0, newlineIndex).replace(/\r$/, "");
435
+ state.buffer = state.buffer.slice(newlineIndex + 1);
436
+ const finished = applyLine(state, line);
437
+ if (finished !== void 0) out.push(finished);
438
+ newlineIndex = state.buffer.indexOf("\n");
439
+ }
440
+ return out;
441
+ }
442
+ function applyLine(state, line) {
443
+ if (line === "") {
444
+ if (state.data.length === 0) {
445
+ state.event = "message";
446
+ return void 0;
447
+ }
448
+ const record = { event: state.event, data: state.data };
449
+ state.event = "message";
450
+ state.data = "";
451
+ return record;
452
+ }
453
+ if (line.startsWith(":")) return void 0;
454
+ if (line.startsWith("event:")) {
455
+ state.event = stripOneLeadingSpace(line.slice(6));
456
+ return void 0;
457
+ }
458
+ if (line.startsWith("data:")) {
459
+ const piece = stripOneLeadingSpace(line.slice(5));
460
+ state.data = state.data.length === 0 ? piece : `${state.data}
461
+ ${piece}`;
462
+ }
463
+ return void 0;
464
+ }
465
+ function stripOneLeadingSpace(value) {
466
+ return value.startsWith(" ") ? value.slice(1) : value;
467
+ }
468
+ function releaseReader(reader) {
469
+ try {
470
+ reader.releaseLock();
471
+ } catch {
472
+ }
473
+ }
474
+
475
+ // src/internal/llm/anthropic.ts
476
+ var AnthropicClient = class {
477
+ constructor(options) {
478
+ this.options = options;
479
+ this.baseUrl = options.baseUrl ?? "https://api.anthropic.com";
480
+ this.fetchImpl = options.fetch ?? fetch;
481
+ }
482
+ options;
483
+ name = "anthropic";
484
+ baseUrl;
485
+ fetchImpl;
486
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: HTTP+SSE handshake + accumulator is intentionally one block
487
+ async *stream(request, signal) {
488
+ const body = buildAnthropicBody(request);
489
+ const headers = {
490
+ "content-type": "application/json",
491
+ "x-api-key": this.options.apiKey,
492
+ "anthropic-version": this.options.version ?? "2023-06-01"
493
+ };
494
+ if (this.options.extraHeaders !== void 0) Object.assign(headers, this.options.extraHeaders);
495
+ const response = await this.fetchImpl(`${this.baseUrl}/v1/messages`, {
496
+ method: "POST",
497
+ signal,
498
+ headers,
499
+ body: JSON.stringify(body)
500
+ });
501
+ if (!response.ok) {
502
+ const text = await response.text().catch(() => "");
503
+ let body2 = text;
504
+ try {
505
+ body2 = JSON.parse(text);
506
+ } catch {
507
+ }
508
+ throw mapAnthropicError({
509
+ status: response.status,
510
+ body: body2,
511
+ headers: response.headers,
512
+ endpoint: "/v1/messages"
513
+ });
514
+ }
515
+ const accumulator = new AnthropicStreamAccumulator();
516
+ for await (const record of parseSseStream(response.body, signal)) {
517
+ if (record.event === "ping" || record.event === "message_start") continue;
518
+ let parsed;
519
+ try {
520
+ parsed = JSON.parse(record.data);
521
+ } catch {
522
+ continue;
523
+ }
524
+ const events = accumulator.consume(parsed);
525
+ for (const event of events) yield event;
526
+ }
527
+ if (!accumulator.finishReasonSeen) {
528
+ throw new NetworkError("Anthropic SSE stream truncated (no stop_reason)", {
529
+ code: "stream_truncated"
530
+ });
531
+ }
532
+ return accumulator.finish();
533
+ }
534
+ };
535
+ var AnthropicStreamAccumulator = class {
536
+ text = "";
537
+ stopReason = "end_turn";
538
+ /**
539
+ * M2 #61 — whether a `message_delta` carrying a real `stop_reason` was seen.
540
+ * A stream that closes before it is a truncation (server FIN / proxy hiccup),
541
+ * not a clean `end_turn` — the caller throws `stream_truncated` on `false`.
542
+ */
543
+ sawStopReason = false;
544
+ inputTokens;
545
+ outputTokens;
546
+ cacheReadTokens;
547
+ cacheWriteTokens;
548
+ toolCalls = /* @__PURE__ */ new Map();
549
+ toolBuffers = /* @__PURE__ */ new Map();
550
+ consume(event) {
551
+ if (event.type === "content_block_start") {
552
+ this.handleToolStart(event);
553
+ return [];
554
+ }
555
+ if (event.type === "content_block_delta") {
556
+ return this.handleContentDelta(event);
557
+ }
558
+ if (event.type === "message_delta") {
559
+ this.handleMessageDelta(event);
560
+ }
561
+ return [];
562
+ }
563
+ handleToolStart(start) {
564
+ if (start.content_block.type !== "tool_use") return;
565
+ this.toolCalls.set(start.index, {
566
+ type: "tool_use",
567
+ id: start.content_block.id,
568
+ name: start.content_block.name,
569
+ input: { ...start.content_block.input }
570
+ });
571
+ this.toolBuffers.set(start.index, "");
572
+ }
573
+ handleContentDelta(delta) {
574
+ if (delta.delta.type === "text_delta") {
575
+ const text = delta.delta.text;
576
+ this.text += text;
577
+ return [{ type: "text_delta", text }];
578
+ }
579
+ const idx = delta.index;
580
+ const existing = this.toolBuffers.get(idx) ?? "";
581
+ this.toolBuffers.set(idx, existing + delta.delta.partial_json);
582
+ return [];
583
+ }
584
+ /**
585
+ * T3.8 — extract token counters from Anthropic's `message_delta` usage.
586
+ * Public on the class so the test seam can drive it directly without
587
+ * spinning the SSE parser.
588
+ */
589
+ handleMessageDelta(md) {
590
+ if (md.delta.stop_reason !== void 0 && md.delta.stop_reason !== null) {
591
+ this.sawStopReason = true;
592
+ }
593
+ this.stopReason = mapAnthropicStopReason(md.delta.stop_reason);
594
+ if (md.usage?.input_tokens !== void 0) this.inputTokens = md.usage.input_tokens;
595
+ if (md.usage?.output_tokens !== void 0) this.outputTokens = md.usage.output_tokens;
596
+ if (md.usage?.cache_creation_input_tokens !== void 0 && md.usage.cache_creation_input_tokens > 0) {
597
+ this.cacheWriteTokens = md.usage.cache_creation_input_tokens;
598
+ }
599
+ if (md.usage?.cache_read_input_tokens !== void 0 && md.usage.cache_read_input_tokens > 0) {
600
+ this.cacheReadTokens = md.usage.cache_read_input_tokens;
601
+ }
602
+ }
603
+ /** M2 #61 — whether the terminal `message_delta` (stop_reason) was seen. */
604
+ get finishReasonSeen() {
605
+ return this.sawStopReason;
606
+ }
607
+ finish() {
608
+ const toolCalls = [];
609
+ for (const [index, tool] of this.toolCalls.entries()) {
610
+ const buffered = this.toolBuffers.get(index);
611
+ const parsed = parseToolArguments(buffered);
612
+ if (Object.keys(parsed).length > 0) tool.input = parsed;
613
+ toolCalls.push(tool);
614
+ }
615
+ return makeLlmFinish({
616
+ stopReason: this.stopReason,
617
+ text: this.text,
618
+ toolCalls,
619
+ inputTokens: this.inputTokens,
620
+ outputTokens: this.outputTokens,
621
+ // T3.8 — surface Anthropic native cache-token counters when the
622
+ // request used `cache_control: {type:"ephemeral"}` on system blocks.
623
+ cacheReadTokens: this.cacheReadTokens,
624
+ cacheWriteTokens: this.cacheWriteTokens
625
+ });
626
+ }
627
+ };
628
+ function buildAnthropicBody(request) {
629
+ return {
630
+ model: request.model,
631
+ stream: true,
632
+ ...buildAnthropicCommonBody(request)
633
+ };
634
+ }
635
+
636
+ // src/internal/error-mappers/bedrock.ts
637
+ function classifyBedrockError(args, awsType, message) {
638
+ if (args.status === 429 || awsType.includes("Throttling") || awsType.includes("TooManyRequests")) {
639
+ return "rate_limit";
640
+ }
641
+ if (args.status === 401 || args.status === 403 || awsType.includes("AccessDenied") || awsType.includes("UnauthorizedOperation")) {
642
+ return "auth_failed";
643
+ }
644
+ if (args.status === 400 || args.status === 404 || awsType.includes("ValidationException") || awsType.includes("ResourceNotFound") || message.includes("use case details")) {
645
+ return "invalid_request";
646
+ }
647
+ if (args.status === 408 || awsType.includes("Timeout")) return "timeout";
648
+ if (args.status >= 500) return "server_error";
649
+ return "unknown";
650
+ }
651
+ function buildBedrockMetadata(args, code) {
652
+ return buildErrorMetadata({
653
+ provider: "bedrock",
654
+ endpoint: args.endpoint,
655
+ code,
656
+ status: args.status,
657
+ headers: args.headers,
658
+ body: args.body
659
+ });
660
+ }
661
+ function mapBedrockError(args) {
662
+ const parsed = parseBody(args.body);
663
+ const awsType = parsed.__type ?? "";
664
+ const message = parsed.message ?? parsed.Message ?? "Bedrock request failed";
665
+ const code = classifyBedrockError(args, awsType, message);
666
+ const metadata = buildBedrockMetadata(args, code);
667
+ switch (code) {
668
+ case "rate_limit":
669
+ return new RateLimitError(`Bedrock throttled: ${message}`, { metadata });
670
+ case "auth_failed":
671
+ return new AuthenticationError(`Bedrock auth: ${message}`, { metadata });
672
+ case "invalid_request": {
673
+ const friendly = message.includes("use case details") ? `Bedrock account setup required: ${message} (AWS Console \u2192 Bedrock \u2192 Model access \u2192 Anthropic \u2192 Submit use case form, then retry in 15 min.)` : `Bedrock validation: ${message}`;
674
+ return new ConfigurationError(friendly, { metadata });
675
+ }
676
+ case "timeout":
677
+ return new NetworkError(`Bedrock timeout: ${message}`, { metadata });
678
+ case "server_error":
679
+ return new NetworkError(`Bedrock server error: ${message}`, { metadata });
680
+ default:
681
+ return new UnknownAgentError(`Bedrock unknown: ${message}`, { metadata });
682
+ }
683
+ }
684
+ function parseBody(body) {
685
+ if (body !== null && typeof body === "object") return body;
686
+ if (typeof body === "string") {
687
+ try {
688
+ return JSON.parse(body);
689
+ } catch {
690
+ return {};
691
+ }
692
+ }
693
+ return {};
694
+ }
695
+ var cachedToken = null;
696
+ var CACHE_TTL_MS = 90 * 60 * 1e3;
697
+ async function resolveBedrockToken(region) {
698
+ const env = process.env.AWS_BEARER_TOKEN_BEDROCK;
699
+ if (env !== void 0 && env.length > 0) return env;
700
+ const now = Date.now();
701
+ if (cachedToken !== null && cachedToken.expiresAt > now) {
702
+ return cachedToken.value;
703
+ }
704
+ try {
705
+ const r = createRequire(import.meta.url);
706
+ const gen = r("@aws/bedrock-token-generator");
707
+ if (typeof gen.getToken !== "function") return void 0;
708
+ const providers = r("@aws-sdk/credential-providers");
709
+ if (typeof providers.fromNodeProviderChain !== "function") return void 0;
710
+ const credentialProvider = providers.fromNodeProviderChain();
711
+ const token = await gen.getToken({ credentials: credentialProvider, region });
712
+ cachedToken = { value: token, expiresAt: now + CACHE_TTL_MS };
713
+ return token;
714
+ } catch {
715
+ return void 0;
716
+ }
717
+ }
718
+
719
+ // src/internal/llm/bedrock-anthropic.ts
720
+ var BedrockAnthropicClient = class {
721
+ constructor(options) {
722
+ this.options = options;
723
+ this.fetchImpl = options.fetch ?? fetch;
724
+ }
725
+ options;
726
+ name = "bedrock";
727
+ fetchImpl;
728
+ async resolveTokenOrThrow(region) {
729
+ const fromOpts = this.options.apiKey;
730
+ if (fromOpts !== void 0 && fromOpts.length > 0) return fromOpts;
731
+ const fromEnv = await resolveBedrockToken(region);
732
+ if (fromEnv !== void 0 && fromEnv.length > 0) return fromEnv;
733
+ throw new ConfigurationError(
734
+ "Bedrock requires AWS_BEARER_TOKEN_BEDROCK env var, or install `@aws/bedrock-token-generator` for auto-refresh, or pass apiKey explicitly.",
735
+ { code: "auth_failed" }
736
+ );
737
+ }
738
+ async *stream(request, signal) {
739
+ const region = inferRegionFromModelId(request.model) ?? process.env.AWS_REGION ?? "us-east-1";
740
+ const token = await this.resolveTokenOrThrow(region);
741
+ const bareModel = stripBedrockPrefix(request.model);
742
+ const baseUrl = this.options.baseUrl ?? resolveBedrockBaseUrl(request.model);
743
+ const endpoint = `/model/${encodeURIComponent(bareModel)}/invoke`;
744
+ const url = `${baseUrl}${endpoint}`;
745
+ const response = await postAnthropicRequest({
746
+ fetchImpl: this.fetchImpl,
747
+ url,
748
+ token,
749
+ body: buildBedrockBody(request),
750
+ signal
751
+ });
752
+ const consumed = await handleAnthropicResponse({
753
+ response,
754
+ endpoint,
755
+ errorMapper: mapBedrockError
756
+ });
757
+ if (consumed.text.length > 0) yield { type: "text_delta", text: consumed.text };
758
+ return makeLlmFinish(consumed);
759
+ }
760
+ };
761
+ function buildBedrockBody(request) {
762
+ return {
763
+ anthropic_version: "bedrock-2023-05-31",
764
+ ...buildAnthropicCommonBody(request)
765
+ };
766
+ }
767
+
768
+ // src/internal/error-mappers/openai-compatible.ts
769
+ function mapOpenAICompatibleError(args) {
770
+ const { providerId, status, body, headers, endpoint } = args;
771
+ const code = mapOpenAiStatusToCode(status, body);
772
+ const message = formatMessage2(providerId, status, code);
773
+ const metadata = buildErrorMetadata({
774
+ provider: providerId,
775
+ endpoint,
776
+ code,
777
+ status,
778
+ headers,
779
+ body
780
+ });
781
+ if (status === 401 || status === 403) {
782
+ return new AuthenticationError(message, { code: `${providerId}_auth_failed`, metadata });
783
+ }
784
+ if (status === 429) {
785
+ return new RateLimitError(message, { code: `${providerId}_rate_limit`, metadata });
786
+ }
787
+ if (status === 400) {
788
+ return new ConfigurationError(message, { code: `${providerId}_${code}`, metadata });
789
+ }
790
+ if (status === 408) {
791
+ return new NetworkError(message, { code: `${providerId}_timeout`, metadata });
792
+ }
793
+ if (status >= 500 && status < 600) {
794
+ return new NetworkError(message, { code: `${providerId}_server_error`, metadata });
795
+ }
796
+ return new UnknownAgentError(message, { code: `${providerId}_unknown`, metadata });
797
+ }
798
+ function extractOpenAiErrorCode(body) {
799
+ if (body === null || typeof body !== "object") return void 0;
800
+ const err = body.error;
801
+ if (err === void 0 || err === null) return void 0;
802
+ if (typeof err.code === "string") return err.code;
803
+ if (typeof err.type === "string") return err.type;
804
+ return void 0;
805
+ }
806
+ function mapOpenAiStatusToCode(status, body) {
807
+ const rawCode = extractOpenAiErrorCode(body)?.toLowerCase() ?? "";
808
+ if (rawCode.includes("context_length") || rawCode.includes("context_window") || rawCode.includes("too_many_tokens") || rawCode.includes("max_tokens")) {
809
+ return "context_too_long";
810
+ }
811
+ if (rawCode.includes("content_filter") || rawCode.includes("content_policy") || rawCode.includes("safety")) {
812
+ return "content_filtered";
813
+ }
814
+ if (rawCode.includes("model_not_found") || rawCode.includes("model_unavailable") || rawCode.includes("invalid_model")) {
815
+ return "model_unavailable";
816
+ }
817
+ if (status === 402 || rawCode.includes("insufficient_quota") || rawCode.includes("quota_exceeded")) {
818
+ return "quota_exceeded";
819
+ }
820
+ if (status === 401 || status === 403) return "auth_failed";
821
+ if (status === 429) return "rate_limit";
822
+ if (status === 408) return "timeout";
823
+ if (status === 400) return "invalid_request";
824
+ if (status >= 500 && status < 600) return "server_error";
825
+ return "unknown";
826
+ }
827
+ function formatMessage2(providerId, status, code) {
828
+ return `${providerId} API error: ${code} (HTTP ${status})`;
829
+ }
830
+
831
+ // src/internal/llm/fault-injection.ts
832
+ var ENV_GATE_KEY = "NODE_ENV";
833
+ var ENV_GATE_VALUE = "test";
834
+ var ENV_OVERRIDE_KEY = "THEOKIT_TEST_RESPONSE_OVERRIDE";
835
+ function maybeWrapWithFaultInjection(client) {
836
+ if (!isGateOn()) return client;
837
+ return new FaultInjectingLlmClient(client);
838
+ }
839
+ var FaultInjectingLlmClient = class {
840
+ name;
841
+ /**
842
+ * Inner (real) client. Exposed so layered transport assertions (router
843
+ * pool-wiring tests, telemetry inspectors) can walk one level deep —
844
+ * same pattern PoolAwareLlmClient uses for its inner transport.
845
+ */
846
+ inner;
847
+ constructor(inner) {
848
+ this.inner = inner;
849
+ this.name = inner.name;
850
+ }
851
+ async *stream(request, signal) {
852
+ if (!isGateOn()) {
853
+ return yield* this.inner.stream(request, signal);
854
+ }
855
+ const parsed = parseOverrideOrWarnOnce();
856
+ if (parsed === void 0) {
857
+ return yield* this.inner.stream(request, signal);
858
+ }
859
+ if (parsed.status >= 400) {
860
+ throw mapOpenAICompatibleError({
861
+ providerId: this.name,
862
+ status: parsed.status,
863
+ body: parsed.body,
864
+ headers: void 0,
865
+ endpoint: "/v1/chat/completions"
866
+ });
867
+ }
868
+ const text = extractText(parsed.body);
869
+ yield { type: "text_delta", text };
870
+ yield { type: "stop", reason: "end_turn" };
871
+ return {
872
+ stopReason: "end_turn",
873
+ text,
874
+ toolCalls: [],
875
+ inputTokens: 0,
876
+ outputTokens: 0
877
+ };
878
+ }
879
+ };
880
+ function isGateOn() {
881
+ return process.env[ENV_GATE_KEY] === ENV_GATE_VALUE;
882
+ }
883
+ var warnedInvalidJson = false;
884
+ function parseOverrideOrWarnOnce() {
885
+ const raw = process.env[ENV_OVERRIDE_KEY];
886
+ if (raw === void 0 || raw.length === 0) return void 0;
887
+ let candidate;
888
+ try {
889
+ candidate = JSON.parse(raw);
890
+ } catch (_err) {
891
+ warnInvalidJsonOnce();
892
+ return void 0;
893
+ }
894
+ if (candidate === null || typeof candidate !== "object") {
895
+ warnInvalidJsonOnce();
896
+ return void 0;
897
+ }
898
+ const obj = candidate;
899
+ if (typeof obj.status !== "number" || !Number.isFinite(obj.status)) {
900
+ warnInvalidJsonOnce();
901
+ return void 0;
902
+ }
903
+ return { status: obj.status, body: obj.body };
904
+ }
905
+ function warnInvalidJsonOnce(_raw) {
906
+ if (warnedInvalidJson) return;
907
+ warnedInvalidJson = true;
908
+ process.stderr.write(
909
+ `[theokit-sdk] ${ENV_OVERRIDE_KEY} is set but value is not valid JSON of shape \`{"status": number, "body": object | string}\`. Falling back to real LLM call. This warning is shown once per process.
910
+ `
911
+ );
912
+ }
913
+ function extractText(body) {
914
+ if (typeof body === "string") return body;
915
+ if (body === null || typeof body !== "object") return "";
916
+ const fromOpenAi = readOpenAiContent(body);
917
+ if (fromOpenAi !== void 0) return fromOpenAi;
918
+ const obj = body;
919
+ if (typeof obj.text === "string") return obj.text;
920
+ return "";
921
+ }
922
+ function readOpenAiContent(body) {
923
+ const obj = body;
924
+ if (!Array.isArray(obj.choices) || obj.choices.length === 0) return void 0;
925
+ const first = obj.choices[0];
926
+ const content = first?.message?.content;
927
+ if (typeof content === "string") return content;
928
+ return void 0;
929
+ }
930
+
931
+ // src/internal/error-mappers/ollama.ts
932
+ function mapOllamaTransportError(args) {
933
+ if (args.providerId !== "ollama") return void 0;
934
+ const causeCode = extractCauseCode(args.cause);
935
+ if (causeCode !== "ECONNREFUSED" && causeCode !== "ENOTFOUND") return void 0;
936
+ const metadata = buildErrorMetadata({
937
+ provider: "ollama",
938
+ endpoint: args.endpoint,
939
+ code: "network",
940
+ status: 0,
941
+ headers: void 0,
942
+ body: { cause: causeCode }
943
+ });
944
+ return new ConfigurationError(
945
+ "Ollama is not reachable. Run `ollama serve` to start the local runtime, or set OLLAMA_HOST to point at a remote Ollama server.",
946
+ { code: "ollama_unreachable", metadata }
947
+ );
948
+ }
949
+ function mapOllamaHttpError(args) {
950
+ if (args.providerId !== "ollama") return void 0;
951
+ const errString = extractErrorString(args.body).toLowerCase();
952
+ if (args.status === 404 && errString.includes("not found") && errString.includes("pull")) {
953
+ const modelHint = extractModelName(errString);
954
+ const metadata = buildErrorMetadata({
955
+ provider: "ollama",
956
+ endpoint: args.endpoint,
957
+ code: "model_unavailable",
958
+ status: args.status,
959
+ headers: args.headers,
960
+ body: args.body
961
+ });
962
+ const pullCmd = modelHint !== void 0 ? `ollama pull ${modelHint}` : "ollama pull <model>";
963
+ return new ConfigurationError(
964
+ `Ollama model is not pulled. Run \`${pullCmd}\` to download it first.`,
965
+ { code: "ollama_model_not_pulled", metadata }
966
+ );
967
+ }
968
+ if (args.status === 503 && errString.includes("model is loading")) {
969
+ const metadata = buildErrorMetadata({
970
+ provider: "ollama",
971
+ endpoint: args.endpoint,
972
+ code: "server_error",
973
+ status: args.status,
974
+ headers: args.headers,
975
+ body: args.body
976
+ });
977
+ return new NetworkError(
978
+ "Ollama model is loading. Retry in a few seconds \u2014 Ollama is warming up the model into memory.",
979
+ { code: "ollama_model_loading", metadata }
980
+ );
981
+ }
982
+ return void 0;
983
+ }
984
+ function extractCauseCode(cause) {
985
+ if (cause === null || typeof cause !== "object") return void 0;
986
+ const directCode = cause.code;
987
+ if (typeof directCode === "string") return directCode;
988
+ const nested = cause.cause;
989
+ if (nested !== void 0) return extractCauseCode(nested);
990
+ return void 0;
991
+ }
992
+ function extractErrorString(body) {
993
+ if (typeof body === "string") return body;
994
+ if (body === null || typeof body !== "object") return "";
995
+ const err = body.error;
996
+ if (typeof err === "string") return err;
997
+ return "";
998
+ }
999
+ function extractModelName(errString) {
1000
+ const match = errString.match(/'([^']+)'/);
1001
+ return match?.[1];
1002
+ }
1003
+
1004
+ // src/internal/llm/ollama-native.ts
1005
+ var OllamaNativeClient = class {
1006
+ constructor(options) {
1007
+ this.options = options;
1008
+ const raw = (options.baseUrl ?? "http://localhost:11434").replace(/\/+$/, "");
1009
+ this.baseUrl = raw.replace(/\/v1$/i, "");
1010
+ this.fetchImpl = options.fetch ?? fetch;
1011
+ }
1012
+ options;
1013
+ name = "ollama";
1014
+ baseUrl;
1015
+ fetchImpl;
1016
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: HTTP+NDJSON handshake + accumulator is intentionally one block (mirrors openai.ts)
1017
+ async *stream(request, signal) {
1018
+ const headers = { "content-type": "application/json" };
1019
+ if (this.options.apiKey !== void 0 && this.options.apiKey.length > 0) {
1020
+ headers.authorization = `Bearer ${this.options.apiKey}`;
1021
+ }
1022
+ const body = buildOllamaChatBody(request);
1023
+ const url = `${this.baseUrl}/api/chat`;
1024
+ let response;
1025
+ try {
1026
+ response = await this.fetchImpl(url, {
1027
+ method: "POST",
1028
+ signal,
1029
+ headers,
1030
+ body: JSON.stringify(body)
1031
+ });
1032
+ } catch (fetchErr) {
1033
+ const mapped = mapOllamaTransportError({
1034
+ providerId: "ollama",
1035
+ cause: fetchErr,
1036
+ endpoint: "/api/chat"
1037
+ });
1038
+ if (mapped !== void 0) throw mapped;
1039
+ throw fetchErr;
1040
+ }
1041
+ if (!response.ok) {
1042
+ const text = await response.text().catch(() => "");
1043
+ let bodyJson = text;
1044
+ try {
1045
+ bodyJson = JSON.parse(text);
1046
+ } catch {
1047
+ }
1048
+ const mapped = mapOllamaHttpError({
1049
+ providerId: "ollama",
1050
+ status: response.status,
1051
+ body: bodyJson,
1052
+ headers: response.headers,
1053
+ endpoint: "/api/chat"
1054
+ });
1055
+ if (mapped !== void 0) throw mapped;
1056
+ throw new Error(`Ollama /api/chat HTTP ${response.status}: ${text.slice(0, 200)}`);
1057
+ }
1058
+ const accumulator = new OllamaStreamAccumulator();
1059
+ if (response.body === null) {
1060
+ return accumulator.finish();
1061
+ }
1062
+ for await (const chunk of parseNdjsonStream(response.body, signal)) {
1063
+ const events = accumulator.consume(chunk);
1064
+ for (const event of events) yield event;
1065
+ if (chunk.done === true) break;
1066
+ }
1067
+ return accumulator.finish();
1068
+ }
1069
+ };
1070
+ var OllamaStreamAccumulator = class {
1071
+ text = "";
1072
+ stopReason = "end_turn";
1073
+ inputTokens;
1074
+ outputTokens;
1075
+ toolCalls = [];
1076
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: NDJSON chunk dispatch — text delta + tool_call append + finish-reason mapping is one cohesive accumulator (mirrors openai.ts OpenAIStreamAccumulator.consume).
1077
+ consume(chunk) {
1078
+ const events = [];
1079
+ if (chunk.prompt_eval_count !== void 0) this.inputTokens = chunk.prompt_eval_count;
1080
+ if (chunk.eval_count !== void 0) this.outputTokens = chunk.eval_count;
1081
+ const msg = chunk.message;
1082
+ if (msg?.content !== void 0 && msg.content.length > 0) {
1083
+ this.text += msg.content;
1084
+ events.push({ type: "text_delta", text: msg.content });
1085
+ }
1086
+ if (msg?.tool_calls !== void 0) {
1087
+ for (const call of msg.tool_calls) {
1088
+ const input = typeof call.function.arguments === "string" ? parseOllamaToolArguments(call.function.arguments) : call.function.arguments;
1089
+ this.toolCalls.push({
1090
+ type: "tool_use",
1091
+ id: `ollama_call_${this.toolCalls.length + 1}`,
1092
+ name: call.function.name,
1093
+ input
1094
+ });
1095
+ }
1096
+ }
1097
+ if (chunk.done === true && chunk.done_reason !== void 0) {
1098
+ this.stopReason = mapOllamaDoneReason(chunk.done_reason, this.toolCalls.length > 0);
1099
+ }
1100
+ return events;
1101
+ }
1102
+ finish() {
1103
+ if (this.toolCalls.length > 0 && this.stopReason === "end_turn") {
1104
+ this.stopReason = "tool_use";
1105
+ }
1106
+ return makeLlmFinish({
1107
+ stopReason: this.stopReason,
1108
+ text: this.text,
1109
+ toolCalls: this.toolCalls,
1110
+ inputTokens: this.inputTokens,
1111
+ outputTokens: this.outputTokens
1112
+ });
1113
+ }
1114
+ };
1115
+ function parseOllamaToolArguments(raw) {
1116
+ try {
1117
+ const parsed = JSON.parse(raw);
1118
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
1119
+ } catch {
1120
+ return {};
1121
+ }
1122
+ }
1123
+ function mapOllamaDoneReason(reason, hasToolCalls) {
1124
+ if (hasToolCalls) return "tool_use";
1125
+ if (reason === "length") return "max_tokens";
1126
+ if (reason === "stop") return "end_turn";
1127
+ return "end_turn";
1128
+ }
1129
+ async function* parseNdjsonStream(body, signal) {
1130
+ const reader = body.getReader();
1131
+ const decoder = new TextDecoder("utf-8");
1132
+ let buffer = "";
1133
+ try {
1134
+ while (true) {
1135
+ if (signal.aborted) return;
1136
+ const { done, value } = await reader.read();
1137
+ if (done) break;
1138
+ buffer += decoder.decode(value, { stream: true });
1139
+ const lines = buffer.split("\n");
1140
+ buffer = lines.pop() ?? "";
1141
+ for (const line of lines) {
1142
+ const trimmed = line.trim();
1143
+ if (trimmed.length === 0) continue;
1144
+ try {
1145
+ yield JSON.parse(trimmed);
1146
+ } catch {
1147
+ }
1148
+ }
1149
+ }
1150
+ const tail = buffer.trim();
1151
+ if (tail.length > 0) {
1152
+ try {
1153
+ yield JSON.parse(tail);
1154
+ } catch {
1155
+ }
1156
+ }
1157
+ } finally {
1158
+ try {
1159
+ await reader.cancel();
1160
+ } catch {
1161
+ }
1162
+ reader.releaseLock();
1163
+ }
1164
+ }
1165
+ var ollamaSystemText = collapseSystemText;
1166
+ function buildOllamaChatBody(request) {
1167
+ const messages = [];
1168
+ const systemText = ollamaSystemText(request.system);
1169
+ if (systemText.length > 0) {
1170
+ messages.push({ role: "system", content: systemText });
1171
+ }
1172
+ for (const message of request.messages) {
1173
+ for (const out of toOllamaMessages(message)) messages.push(out);
1174
+ }
1175
+ const tools = toOllamaTools(request.tools);
1176
+ const body = {
1177
+ model: request.model,
1178
+ stream: true,
1179
+ messages,
1180
+ // ADR D192: 24h keep_alive prevents Ollama from evicting the chat model
1181
+ // when other models (embeddings, judges) are loaded between turns.
1182
+ // Cuts cold-load latency from 20-30s to single-digit ms per turn.
1183
+ keep_alive: process.env.OLLAMA_KEEP_ALIVE ?? "24h"
1184
+ };
1185
+ if (tools.length > 0) body.tools = tools;
1186
+ const opts = {};
1187
+ if (request.maxTokens !== void 0) opts.num_predict = request.maxTokens;
1188
+ if (request.temperature !== void 0) opts.temperature = request.temperature;
1189
+ if (Object.keys(opts).length > 0) body.options = opts;
1190
+ return body;
1191
+ }
1192
+ function toOllamaMessages(message) {
1193
+ if (message.role === "system") {
1194
+ return [{ role: "system", content: joinTextParts(message) }];
1195
+ }
1196
+ if (message.role === "user") {
1197
+ if (message.content.some((p) => p.type === "image")) {
1198
+ throw new ConfigurationError(
1199
+ "image input is not supported on the ollama-native provider; use an OpenAI/OpenRouter model for multimodal turns",
1200
+ { code: "ollama_image_unsupported" }
1201
+ );
1202
+ }
1203
+ const out = [];
1204
+ for (const part of message.content) {
1205
+ if (part.type === "tool_result") {
1206
+ out.push({
1207
+ role: "tool",
1208
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
1209
+ content: toStringToolResultContent(part.content, "ollama"),
1210
+ tool_name: part.toolUseId
1211
+ });
1212
+ }
1213
+ }
1214
+ const userText = joinTextParts(message);
1215
+ if (userText.length > 0) out.push({ role: "user", content: userText });
1216
+ return out;
1217
+ }
1218
+ const text = joinTextParts(message);
1219
+ const toolCalls = [];
1220
+ for (const part of message.content) {
1221
+ if (part.type === "tool_use") {
1222
+ toolCalls.push({
1223
+ function: { name: part.name, arguments: part.input }
1224
+ });
1225
+ }
1226
+ }
1227
+ const msg = { role: "assistant", content: text };
1228
+ if (toolCalls.length > 0) msg.tool_calls = toolCalls;
1229
+ return [msg];
1230
+ }
1231
+ function joinTextParts(message) {
1232
+ return message.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
1233
+ }
1234
+ function toOllamaTools(tools) {
1235
+ if (tools === void 0 || tools.length === 0) return [];
1236
+ return tools.map((tool) => ({
1237
+ type: "function",
1238
+ function: {
1239
+ name: tool.name,
1240
+ description: tool.description,
1241
+ parameters: tool.inputSchema
1242
+ }
1243
+ }));
1244
+ }
1245
+
1246
+ // src/internal/llm/hermes-tool-extract.ts
1247
+ var HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
1248
+ var HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
1249
+ function extractHermesToolCalls(content, makeId, allowedToolNames) {
1250
+ const isPromoted = (name) => name.length > 0 && (allowedToolNames === void 0 || allowedToolNames.has(name));
1251
+ const toolCalls = [];
1252
+ const droppedNames = [];
1253
+ for (const block of content.matchAll(HERMES_BLOCK)) {
1254
+ const name = (block[1] ?? "").trim();
1255
+ if (!isPromoted(name)) {
1256
+ if (name.length > 0 && allowedToolNames !== void 0) droppedNames.push(name);
1257
+ continue;
1258
+ }
1259
+ toolCalls.push({
1260
+ type: "tool_use",
1261
+ id: makeId(),
1262
+ name,
1263
+ input: parseHermesParams(block[2] ?? "")
1264
+ });
1265
+ }
1266
+ const residualText = toolCalls.length === 0 ? content : content.replace(
1267
+ HERMES_BLOCK,
1268
+ (full, rawName) => isPromoted((rawName ?? "").trim()) ? "" : full
1269
+ ).trim();
1270
+ return { toolCalls, residualText, droppedNames };
1271
+ }
1272
+ function parseHermesParams(inner) {
1273
+ const input = {};
1274
+ for (const param of inner.matchAll(HERMES_PARAM)) {
1275
+ const key = param[1];
1276
+ const value = param[2];
1277
+ if (key === void 0 || value === void 0) continue;
1278
+ input[key.trim()] = value;
1279
+ }
1280
+ return sanitizeToolInput(input, { trim: true }).value;
1281
+ }
1282
+ var STREAM_MARKER = "<function=";
1283
+ var DEFAULT_STREAM_BUFFER_CAP = 8192;
1284
+ var isStreamWs = (c) => c === " " || c === " " || c === "\n" || c === "\r";
1285
+ function streamToolCallBufferState(held, allowedToolNames, cap = DEFAULT_STREAM_BUFFER_CAP) {
1286
+ if (allowedToolNames.size === 0) return "impossible";
1287
+ const t = held.trimStart();
1288
+ if (t.length < STREAM_MARKER.length) {
1289
+ return STREAM_MARKER.startsWith(t) ? "possible" : "impossible";
1290
+ }
1291
+ if (!t.startsWith(STREAM_MARKER)) return "impossible";
1292
+ const parsed = parseStreamMarkerName(t);
1293
+ if (parsed === "building") return "possible";
1294
+ if (parsed === "invalid") return "impossible";
1295
+ const nameOk = parsed.complete ? allowedToolNames.has(parsed.name) : someToolNameStartsWith(allowedToolNames, parsed.name);
1296
+ if (!nameOk) return "impossible";
1297
+ return held.length > cap ? "impossible" : "possible";
1298
+ }
1299
+ function parseStreamMarkerName(t) {
1300
+ let cursor = STREAM_MARKER.length;
1301
+ while (cursor < t.length && isStreamWs(t[cursor])) cursor += 1;
1302
+ const nameStart = cursor;
1303
+ while (cursor < t.length && t[cursor] !== ">" && !isStreamWs(t[cursor])) cursor += 1;
1304
+ const name = t.slice(nameStart, cursor);
1305
+ if (name.length === 0) return cursor >= t.length ? "building" : "invalid";
1306
+ return { name, complete: cursor < t.length && t[cursor] === ">" };
1307
+ }
1308
+ function someToolNameStartsWith(allowedToolNames, prefix) {
1309
+ for (const name of allowedToolNames) {
1310
+ if (name.startsWith(prefix)) return true;
1311
+ }
1312
+ return false;
1313
+ }
1314
+ function firstPossibleMarkerStart(held, allowedToolNames) {
1315
+ for (let i = held.indexOf("<"); i !== -1; i = held.indexOf("<", i + 1)) {
1316
+ if (streamToolCallBufferState(held.slice(i), allowedToolNames) === "possible") return i;
1317
+ }
1318
+ return -1;
1319
+ }
1320
+ var StreamSuppressionBuffer = class {
1321
+ constructor(allowedToolNames) {
1322
+ this.allowedToolNames = allowedToolNames;
1323
+ }
1324
+ allowedToolNames;
1325
+ #held = "";
1326
+ /** Feed a content delta; returns the text to emit as a `text_delta` now, or `undefined` to hold. */
1327
+ push(content) {
1328
+ this.#held += content;
1329
+ if (streamToolCallBufferState(this.#held, this.allowedToolNames) === "possible")
1330
+ return void 0;
1331
+ const holdStart = firstPossibleMarkerStart(this.#held, this.allowedToolNames);
1332
+ if (holdStart > 0) {
1333
+ const flush2 = this.#held.slice(0, holdStart);
1334
+ this.#held = this.#held.slice(holdStart);
1335
+ return flush2;
1336
+ }
1337
+ const flush = this.#held;
1338
+ this.#held = "";
1339
+ return flush;
1340
+ }
1341
+ /** Drain the held buffer at stream end. `hasNativeCalls` mirrors `finish()`'s size-guard: when
1342
+ * native `tool_calls` exist, `finish()` won't strip the leaked block, so stream the held text WHOLE
1343
+ * (keeping `accumulatedText == finish.text`); otherwise strip the recoverable blocks. Idempotent. */
1344
+ drain(hasNativeCalls) {
1345
+ if (this.#held.length === 0) return void 0;
1346
+ const held = this.#held;
1347
+ this.#held = "";
1348
+ if (hasNativeCalls) return held;
1349
+ const residual = extractHermesToolCalls(held, () => "held", this.allowedToolNames).residualText;
1350
+ return residual.length > 0 ? residual : void 0;
1351
+ }
1352
+ };
1353
+
1354
+ // src/internal/llm/openai-messages.ts
1355
+ function toOpenAIMessages(message) {
1356
+ if (message.role === "system") return [systemMessage(message)];
1357
+ if (message.role === "user") return userOrToolMessages(message);
1358
+ return [assistantMessage(message)];
1359
+ }
1360
+ function systemMessage(message) {
1361
+ return { role: "system", content: joinTextParts2(message) };
1362
+ }
1363
+ function joinTextParts2(message) {
1364
+ return message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
1365
+ }
1366
+ function userOrToolMessages(message) {
1367
+ const out = [];
1368
+ for (const part of message.content) {
1369
+ if (part.type === "tool_result") {
1370
+ out.push({
1371
+ role: "tool",
1372
+ tool_call_id: part.toolUseId,
1373
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
1374
+ // image block fails fast (ConfigurationError).
1375
+ content: toStringToolResultContent(part.content, "openai")
1376
+ });
1377
+ }
1378
+ }
1379
+ const userText = joinTextParts2(message);
1380
+ const imageParts = message.content.filter(
1381
+ (p) => p.type === "image"
1382
+ );
1383
+ if (imageParts.length > 0) {
1384
+ const content = [];
1385
+ if (userText.length > 0) content.push({ type: "text", text: userText });
1386
+ for (const img of imageParts) {
1387
+ const url = img.source.type === "base64" ? `data:${img.source.media_type};base64,${img.source.data}` : img.source.url;
1388
+ content.push({ type: "image_url", image_url: { url } });
1389
+ }
1390
+ out.push({ role: "user", content });
1391
+ } else if (userText.length > 0) {
1392
+ out.push({ role: "user", content: userText });
1393
+ }
1394
+ return out;
1395
+ }
1396
+ function assistantMessage(message) {
1397
+ const text = joinTextParts2(message);
1398
+ const toolCalls = message.content.filter((part) => part.type === "tool_use").map((part) => {
1399
+ const tc = part;
1400
+ return {
1401
+ id: tc.id,
1402
+ type: "function",
1403
+ function: { name: tc.name, arguments: JSON.stringify(tc.input) }
1404
+ };
1405
+ });
1406
+ const result = { role: "assistant", content: text };
1407
+ if (toolCalls.length > 0) result.tool_calls = toolCalls;
1408
+ return result;
1409
+ }
1410
+
1411
+ // src/internal/llm/openai.ts
1412
+ function deriveChatPath(baseUrl) {
1413
+ try {
1414
+ return /\/v\d+[a-z]*(\/|$)/.test(new URL(baseUrl).pathname) ? "/chat/completions" : "/v1/chat/completions";
1415
+ } catch {
1416
+ return "/v1/chat/completions";
1417
+ }
1418
+ }
1419
+ var OpenAIClient = class {
1420
+ constructor(options) {
1421
+ this.options = options;
1422
+ this.baseUrl = (options.baseUrl ?? "https://api.openai.com").replace(/\/+$/, "");
1423
+ this.chatPath = options.chatCompletionsPath ?? deriveChatPath(this.baseUrl);
1424
+ this.fetchImpl = options.fetch ?? fetch;
1425
+ }
1426
+ options;
1427
+ name = "openai";
1428
+ baseUrl;
1429
+ chatPath;
1430
+ fetchImpl;
1431
+ /**
1432
+ * Whether this client recovers leaked Hermes tool-call dialect from assistant
1433
+ * text (theokit#58 follow-up). Observability for the route→client wiring of
1434
+ * `extractToolCallsFromContent`. @internal
1435
+ */
1436
+ get recoversLeakedToolCalls() {
1437
+ return this.options.extractToolCallsFromContent ?? false;
1438
+ }
1439
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: HTTP+SSE handshake + accumulator is intentionally one block
1440
+ async *stream(request, signal) {
1441
+ const headers = {
1442
+ "content-type": "application/json",
1443
+ authorization: `Bearer ${this.options.apiKey}`
1444
+ };
1445
+ if (this.options.organization !== void 0) {
1446
+ headers["openai-organization"] = this.options.organization;
1447
+ }
1448
+ if (this.options.extraHeaders !== void 0) Object.assign(headers, this.options.extraHeaders);
1449
+ const providerId = this.options.providerName ?? this.name;
1450
+ let response;
1451
+ try {
1452
+ response = await this.fetchImpl(`${this.baseUrl}${this.chatPath}`, {
1453
+ method: "POST",
1454
+ signal,
1455
+ headers,
1456
+ body: JSON.stringify(buildOpenAIBody(request, providerId))
1457
+ });
1458
+ } catch (fetchErr) {
1459
+ const mapped = mapOllamaTransportError({
1460
+ providerId,
1461
+ cause: fetchErr,
1462
+ endpoint: "/v1/chat/completions"
1463
+ });
1464
+ if (mapped !== void 0) throw mapped;
1465
+ throw fetchErr;
1466
+ }
1467
+ if (!response.ok) {
1468
+ const text = await response.text().catch(() => "");
1469
+ let body = text;
1470
+ try {
1471
+ body = JSON.parse(text);
1472
+ } catch {
1473
+ }
1474
+ const ollamaMapped = mapOllamaHttpError({
1475
+ providerId,
1476
+ status: response.status,
1477
+ body,
1478
+ headers: response.headers,
1479
+ endpoint: "/v1/chat/completions"
1480
+ });
1481
+ if (ollamaMapped !== void 0) throw ollamaMapped;
1482
+ throw mapOpenAICompatibleError({
1483
+ providerId,
1484
+ status: response.status,
1485
+ body,
1486
+ headers: response.headers,
1487
+ endpoint: "/v1/chat/completions"
1488
+ });
1489
+ }
1490
+ const accumulator = new OpenAIStreamAccumulator(
1491
+ this.options.extractToolCallsFromContent ?? false,
1492
+ providerId,
1493
+ // R5: request-scoped allowlist — leaked recovery only promotes a block whose name is a tool the
1494
+ // model was actually given. Empty set (no tools) recovers nothing.
1495
+ new Set(request.tools?.map((tool) => tool.name) ?? [])
1496
+ );
1497
+ let sawDone = false;
1498
+ for await (const record of parseSseStream(response.body, signal)) {
1499
+ if (record.data === "[DONE]") {
1500
+ sawDone = true;
1501
+ break;
1502
+ }
1503
+ let chunk;
1504
+ try {
1505
+ chunk = JSON.parse(record.data);
1506
+ } catch {
1507
+ continue;
1508
+ }
1509
+ if (chunk.error !== void 0 && chunk.error !== null) {
1510
+ const status = typeof chunk.error.code === "number" ? chunk.error.code : 502;
1511
+ throw mapOpenAICompatibleError({
1512
+ providerId,
1513
+ status,
1514
+ body: chunk,
1515
+ headers: response.headers,
1516
+ endpoint: "/v1/chat/completions"
1517
+ });
1518
+ }
1519
+ const events = accumulator.consume(chunk);
1520
+ for (const event of events) yield event;
1521
+ }
1522
+ if (!sawDone && !accumulator.finishReasonSeen) {
1523
+ throw new NetworkError("SSE stream truncated (no finish_reason / [DONE])", {
1524
+ code: "stream_truncated"
1525
+ });
1526
+ }
1527
+ const drainEvent = accumulator.finalizeHeldText();
1528
+ if (drainEvent !== void 0) yield drainEvent;
1529
+ return accumulator.finish();
1530
+ }
1531
+ };
1532
+ var OpenAIStreamAccumulator = class {
1533
+ /**
1534
+ * @param extractFromContent opt-in leaked-dialect safe-parse (theokit#58). Default false.
1535
+ * @param providerName provider id, used only to label the recovery log line.
1536
+ * @param allowedToolNames R5 request-scoped allowlist — built from `request.tools` at `stream()`;
1537
+ * leaked recovery in `finish()` only promotes a block whose name is in this set. `undefined`
1538
+ * (direct construction) recovers all (back-compat); an empty set recovers nothing.
1539
+ */
1540
+ constructor(extractFromContent = false, providerName = "openai", allowedToolNames) {
1541
+ this.extractFromContent = extractFromContent;
1542
+ this.providerName = providerName;
1543
+ this.allowedToolNames = allowedToolNames;
1544
+ this.suppress = extractFromContent && allowedToolNames !== void 0 && allowedToolNames.size > 0 ? new StreamSuppressionBuffer(allowedToolNames) : void 0;
1545
+ }
1546
+ extractFromContent;
1547
+ providerName;
1548
+ allowedToolNames;
1549
+ text = "";
1550
+ stopReason = "end_turn";
1551
+ inputTokens;
1552
+ outputTokens;
1553
+ cacheReadTokens;
1554
+ cacheWriteTokens;
1555
+ reasoningTokens;
1556
+ toolCalls = /* @__PURE__ */ new Map();
1557
+ /** R7: present only when recovery is enabled AND the request declares tools — holds suspected
1558
+ * leaked-dialect content back from the `text_delta` stream. `undefined` ⇒ stream immediately. */
1559
+ suppress;
1560
+ consume(chunk) {
1561
+ const events = [];
1562
+ this.applyUsage(chunk.usage);
1563
+ for (const choice of chunk.choices ?? []) {
1564
+ events.push(...this.applyChoice(choice));
1565
+ }
1566
+ return events;
1567
+ }
1568
+ applyChoice(choice) {
1569
+ const events = [];
1570
+ const reasoningEvent = this.applyReasoningDelta(
1571
+ choice.delta?.reasoning ?? choice.delta?.reasoning_content
1572
+ );
1573
+ if (reasoningEvent !== void 0) events.push(reasoningEvent);
1574
+ const textEvent = this.applyContentDelta(choice.delta?.content);
1575
+ if (textEvent !== void 0) events.push(textEvent);
1576
+ this.mergeToolCallDeltas(choice.delta?.tool_calls);
1577
+ this.applyFinishReason(choice.finish_reason);
1578
+ if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
1579
+ const flushEvent = this.finalizeHeldText();
1580
+ if (flushEvent !== void 0) events.push(flushEvent);
1581
+ }
1582
+ return events;
1583
+ }
1584
+ applyReasoningDelta(reasoning) {
1585
+ if (typeof reasoning !== "string" || reasoning.length === 0) return void 0;
1586
+ return { type: "reasoning_delta", text: reasoning };
1587
+ }
1588
+ applyUsage(usage) {
1589
+ if (usage?.prompt_tokens !== void 0) this.inputTokens = usage.prompt_tokens;
1590
+ if (usage?.completion_tokens !== void 0) this.outputTokens = usage.completion_tokens;
1591
+ const cachedDetail = usage?.prompt_tokens_details?.cached_tokens;
1592
+ if (cachedDetail !== void 0) this.cacheReadTokens = cachedDetail;
1593
+ const reasoningDetail = usage?.completion_tokens_details?.reasoning_tokens;
1594
+ if (reasoningDetail !== void 0) this.reasoningTokens = reasoningDetail;
1595
+ if (usage?.cache_read_input_tokens !== void 0) {
1596
+ this.cacheReadTokens = usage.cache_read_input_tokens;
1597
+ }
1598
+ if (usage?.cache_creation_input_tokens !== void 0) {
1599
+ this.cacheWriteTokens = usage.cache_creation_input_tokens;
1600
+ }
1601
+ }
1602
+ applyContentDelta(content) {
1603
+ if (typeof content !== "string" || content.length === 0) return void 0;
1604
+ this.text += content;
1605
+ if (this.suppress === void 0) return { type: "text_delta", text: content };
1606
+ const emit = this.suppress.push(content);
1607
+ return emit !== void 0 ? { type: "text_delta", text: emit } : void 0;
1608
+ }
1609
+ /** R7 held-buffer finalizer, called at the `finish_reason` chunk (in `applyChoice`) AND after the
1610
+ * SSE loop in `stream()` — so a stream that omits a `finish_reason` terminal never silently drops
1611
+ * held text. `toolCalls.size > 0` (native calls present) makes `finish()` skip recovery, so the
1612
+ * buffer streams the held text whole. Idempotent once drained. */
1613
+ finalizeHeldText() {
1614
+ const emit = this.suppress?.drain(this.toolCalls.size > 0);
1615
+ return emit !== void 0 ? { type: "text_delta", text: emit } : void 0;
1616
+ }
1617
+ mergeToolCallDeltas(deltas) {
1618
+ for (const call of deltas ?? []) {
1619
+ const existing = this.toolCalls.get(call.index) ?? { id: "", name: "", args: "" };
1620
+ if (call.id !== void 0) existing.id = call.id;
1621
+ if (call.function?.name !== void 0) existing.name = call.function.name;
1622
+ if (call.function?.arguments !== void 0) existing.args += call.function.arguments;
1623
+ this.toolCalls.set(call.index, existing);
1624
+ }
1625
+ }
1626
+ /** M2 #61 — true once any chunk carried a non-null `finish_reason` (else a
1627
+ * stream ending without `[DONE]` is a truncation, not a clean end). */
1628
+ sawFinishReason = false;
1629
+ get finishReasonSeen() {
1630
+ return this.sawFinishReason;
1631
+ }
1632
+ applyFinishReason(reason) {
1633
+ if (reason === void 0 || reason === null) return;
1634
+ this.sawFinishReason = true;
1635
+ this.stopReason = mapOpenAIFinish(reason);
1636
+ }
1637
+ finish() {
1638
+ const toolCalls = [];
1639
+ for (const call of this.toolCalls.values()) {
1640
+ const input = parseToolArguments(call.args);
1641
+ toolCalls.push({ type: "tool_use", id: call.id, name: call.name, input });
1642
+ }
1643
+ let text = this.text;
1644
+ let stopReason = this.stopReason;
1645
+ if (this.extractFromContent && toolCalls.length === 0) {
1646
+ const recovered = extractHermesToolCalls(
1647
+ this.text,
1648
+ () => `hermes-${globalThis.crypto.randomUUID()}`,
1649
+ this.allowedToolNames
1650
+ );
1651
+ if (recovered.toolCalls.length > 0) {
1652
+ toolCalls.push(...recovered.toolCalls);
1653
+ text = recovered.residualText;
1654
+ stopReason = "tool_use";
1655
+ process.stderr.write(
1656
+ `[theokit-sdk] recovered ${recovered.toolCalls.length} leaked tool call(s) from assistant content (provider="${this.providerName}", names=${recovered.toolCalls.map((c) => c.name).join(",")})
1657
+ `
1658
+ );
1659
+ }
1660
+ if (recovered.droppedNames.length > 0) {
1661
+ process.stderr.write(
1662
+ `[theokit-sdk] dropped ${recovered.droppedNames.length} leaked block(s) whose name is not a tool in the request (provider="${this.providerName}", names=${recovered.droppedNames.join(",")})
1663
+ `
1664
+ );
1665
+ }
1666
+ }
1667
+ return makeLlmFinish({
1668
+ stopReason,
1669
+ text,
1670
+ toolCalls,
1671
+ inputTokens: this.inputTokens,
1672
+ outputTokens: this.outputTokens,
1673
+ cacheReadTokens: this.cacheReadTokens,
1674
+ cacheWriteTokens: this.cacheWriteTokens,
1675
+ reasoningTokens: this.reasoningTokens
1676
+ });
1677
+ }
1678
+ };
1679
+ function applyReasoningRequest(body, effort, providerName) {
1680
+ if (providerName === "openai") {
1681
+ body.reasoning_effort = effort;
1682
+ return;
1683
+ }
1684
+ body.reasoning = { effort };
1685
+ }
1686
+ function applyToolsRequest(body, request) {
1687
+ if (request.tools === void 0 || request.tools.length === 0) return;
1688
+ body.tools = request.tools.map((tool) => ({
1689
+ type: "function",
1690
+ function: { name: tool.name, description: tool.description, parameters: tool.inputSchema }
1691
+ }));
1692
+ if (request.toolChoice !== void 0) body.tool_choice = request.toolChoice;
1693
+ }
1694
+ function buildOpenAIBody(request, providerName) {
1695
+ const messages = [];
1696
+ const systemText = openAISystemText(request.system);
1697
+ if (systemText.length > 0) {
1698
+ messages.push({ role: "system", content: systemText });
1699
+ }
1700
+ for (const message of request.messages) {
1701
+ for (const out of toOpenAIMessages(message)) messages.push(out);
1702
+ }
1703
+ const body = {
1704
+ model: request.model,
1705
+ stream: true,
1706
+ // D376: opt-in to OpenAI's final-chunk usage record (no-op for providers
1707
+ // that don't honor it; OpenRouter / OpenAI both respect it).
1708
+ stream_options: { include_usage: true },
1709
+ messages
1710
+ };
1711
+ if (request.maxTokens !== void 0) body.max_tokens = request.maxTokens;
1712
+ if (request.temperature !== void 0) body.temperature = request.temperature;
1713
+ if (request.reasoning?.effort !== void 0)
1714
+ applyReasoningRequest(body, request.reasoning.effort, providerName);
1715
+ applyToolsRequest(body, request);
1716
+ const responseFormat = encodeOpenAIResponseFormat(request.responseFormat);
1717
+ if (responseFormat !== void 0) body.response_format = responseFormat;
1718
+ return body;
1719
+ }
1720
+ var openAISystemText = collapseSystemText;
1721
+ function encodeOpenAIResponseFormat(rf) {
1722
+ if (rf === void 0) return void 0;
1723
+ if (rf.type === "json_object") return { type: "json_object" };
1724
+ return {
1725
+ type: "json_schema",
1726
+ json_schema: {
1727
+ name: rf.jsonSchema.name,
1728
+ schema: rf.jsonSchema.schema,
1729
+ strict: rf.jsonSchema.strict ?? true
1730
+ }
1731
+ };
1732
+ }
1733
+
1734
+ // src/internal/resilience/circuit-breaker.ts
1735
+ var DEFAULT_MAX_TIMEOUTS = 3;
1736
+ var DEFAULT_COOLDOWN_MS = 6e4;
1737
+ var CircuitBreaker = class {
1738
+ constructor(opts = {}) {
1739
+ this.opts = opts;
1740
+ }
1741
+ opts;
1742
+ states = /* @__PURE__ */ new Map();
1743
+ /** @returns true when the breaker is open and the call should be skipped. */
1744
+ shouldSkip(key) {
1745
+ const state = this.states.get(key);
1746
+ if (state === void 0) return false;
1747
+ if (state.cooldownUntilMs === 0) return false;
1748
+ if (this.now() < state.cooldownUntilMs) return true;
1749
+ state.cooldownUntilMs = 0;
1750
+ state.consecutiveTimeouts = 0;
1751
+ return false;
1752
+ }
1753
+ recordSuccess(key) {
1754
+ const state = this.states.get(key);
1755
+ if (state === void 0) return;
1756
+ state.consecutiveTimeouts = 0;
1757
+ state.cooldownUntilMs = 0;
1758
+ }
1759
+ recordTimeout(key) {
1760
+ const state = this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
1761
+ state.consecutiveTimeouts += 1;
1762
+ if (state.consecutiveTimeouts >= (this.opts.maxTimeouts ?? DEFAULT_MAX_TIMEOUTS)) {
1763
+ state.cooldownUntilMs = this.now() + (this.opts.cooldownMs ?? DEFAULT_COOLDOWN_MS);
1764
+ }
1765
+ this.states.set(key, state);
1766
+ }
1767
+ /** @internal — tests inspect counter state. */
1768
+ inspect(key) {
1769
+ return this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
1770
+ }
1771
+ now() {
1772
+ return this.opts.now?.() ?? Date.now();
1773
+ }
1774
+ };
1775
+
1776
+ // src/internal/llm/stream-relay.ts
1777
+ async function tryFirstEvent(client, request, signal, logFallback = false) {
1778
+ const generator = client.stream(request, signal);
1779
+ try {
1780
+ const firstResult = await generator.next();
1781
+ return { kind: "ok", generator, firstResult };
1782
+ } catch (cause) {
1783
+ if (cause instanceof NetworkError || cause instanceof RateLimitError || cause instanceof AuthenticationError) {
1784
+ if (logFallback) {
1785
+ const errCode = cause.metadata?.code ?? cause.code ?? "unknown";
1786
+ process.stderr.write(
1787
+ `[theokit-sdk] provider ${client.name} failed (${errCode}): falling back
1788
+ `
1789
+ );
1790
+ }
1791
+ return { kind: "handshake_error", error: cause };
1792
+ }
1793
+ throw cause;
1794
+ }
1795
+ }
1796
+ async function* relayStream(generator, firstResult) {
1797
+ if (firstResult.done === true) return firstResult.value;
1798
+ yield firstResult.value;
1799
+ while (true) {
1800
+ const next = await generator.next();
1801
+ if (next.done === true) return next.value;
1802
+ yield next.value;
1803
+ }
1804
+ }
1805
+
1806
+ // src/internal/llm/pool-aware-client.ts
1807
+ var PoolAwareLlmClient = class {
1808
+ constructor(pool, buildClient2, waitForAvailableMs = 3e4, resilience = {}) {
1809
+ this.pool = pool;
1810
+ this.buildClient = buildClient2;
1811
+ this.waitForAvailableMs = waitForAvailableMs;
1812
+ this.name = `pool-aware:${pool.provider}`;
1813
+ this.breaker = resilience.breaker ?? new CircuitBreaker();
1814
+ this.backoffBaseMs = resilience.backoffBaseMs;
1815
+ this.rng = resilience.rng;
1816
+ this.onRateLimit = resilience.onRateLimit;
1817
+ }
1818
+ pool;
1819
+ buildClient;
1820
+ waitForAvailableMs;
1821
+ name;
1822
+ onRateLimit;
1823
+ /** M2 #60 — provider-level circuit breaker (consecutive-failure). */
1824
+ breaker;
1825
+ backoffBaseMs;
1826
+ rng;
1827
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: stream() must serialize pool-select → build client → first-event probe → classify → retry/rotate/propagate. Extracting helpers fragments the linear narrative; the comments above each branch keep it readable.
1828
+ async *stream(request, signal) {
1829
+ if (this.breaker.shouldSkip(this.pool.provider)) {
1830
+ throw new NetworkError(`${this.pool.provider} circuit open \u2014 failing fast`, {
1831
+ code: "circuit_open"
1832
+ });
1833
+ }
1834
+ let hasRetried429 = false;
1835
+ while (true) {
1836
+ if (signal.aborted) throw abortError(signal);
1837
+ let entry = await this.pool.select();
1838
+ if (entry === null) {
1839
+ if (this.waitForAvailableMs > 0) {
1840
+ const available = await this.pool.waitForAvailable(signal, {
1841
+ maxWaitMs: this.waitForAvailableMs
1842
+ });
1843
+ if (available) {
1844
+ entry = await this.pool.select();
1845
+ }
1846
+ }
1847
+ if (entry === null) {
1848
+ this.breaker.recordTimeout(this.pool.provider);
1849
+ throw new CredentialPoolExhaustedError(
1850
+ `All ${this.pool.provider} credentials exhausted; next retry available at ${this.nextRetryHint() ?? "unknown"}`,
1851
+ { provider: this.pool.provider, nextRetryAt: this.nextRetryHint() }
1852
+ );
1853
+ }
1854
+ }
1855
+ const realClient = this.buildClient(entry.accessToken);
1856
+ const attempt = await tryFirstEvent(realClient, request, signal);
1857
+ if (attempt.kind === "ok") {
1858
+ this.breaker.recordSuccess(this.pool.provider);
1859
+ return yield* relayStream(attempt.generator, attempt.firstResult);
1860
+ }
1861
+ const decision = classifyAndDecide(attempt.error, hasRetried429);
1862
+ if (decision === "retry") {
1863
+ const backoffMs = computeBackoffMs({
1864
+ attempt: 0,
1865
+ ...this.backoffBaseMs !== void 0 ? { baseMs: this.backoffBaseMs } : {},
1866
+ ...this.rng !== void 0 ? { rng: this.rng } : {}
1867
+ });
1868
+ this.onRateLimit?.({ attempt: 1, retryAfterMs: backoffMs });
1869
+ await sleepWithAbort(backoffMs, signal);
1870
+ hasRetried429 = true;
1871
+ continue;
1872
+ }
1873
+ if (decision === "rotate") {
1874
+ try {
1875
+ await this.pool.markExhaustedAndRotate({
1876
+ entryId: entry.id,
1877
+ statusCode: attempt.error.metadata?.statusCode ?? inferStatusCode(attempt.error),
1878
+ ...parseRetryAfterMs(attempt.error) !== void 0 ? { resetAtMs: parseRetryAfterMs(attempt.error) } : {}
1879
+ });
1880
+ } catch (persistErr) {
1881
+ process.stderr.write(
1882
+ `[theokit-sdk] credential-pool: persist failed during rotate; continuing in-memory: ${persistErr instanceof Error ? persistErr.message : String(persistErr)}
1883
+ `
1884
+ );
1885
+ }
1886
+ hasRetried429 = false;
1887
+ continue;
1888
+ }
1889
+ this.breaker.recordTimeout(this.pool.provider);
1890
+ throw attempt.error;
1891
+ }
1892
+ }
1893
+ /**
1894
+ * Earliest epoch ms among entries' `lastErrorResetAt` — best estimate
1895
+ * for the caller's `CredentialPoolExhaustedError.nextRetryAt`.
1896
+ */
1897
+ nextRetryHint() {
1898
+ const resets = this.pool.list().map((e) => e.lastErrorResetAt).filter((v) => v !== void 0);
1899
+ return resets.length === 0 ? void 0 : Math.min(...resets);
1900
+ }
1901
+ };
1902
+ function classifyAndDecide(error, hasRetried429) {
1903
+ if (error instanceof NetworkError) return "propagate";
1904
+ if (error instanceof AuthenticationError) return "rotate";
1905
+ const status = error.metadata?.statusCode ?? 429;
1906
+ if (status === 402) return "rotate";
1907
+ return hasRetried429 ? "rotate" : "retry";
1908
+ }
1909
+ function parseRetryAfterMs(error) {
1910
+ const seconds = error.metadata?.retryAfter;
1911
+ if (typeof seconds === "number" && seconds > 0) {
1912
+ return Date.now() + seconds * 1e3;
1913
+ }
1914
+ return void 0;
1915
+ }
1916
+ function inferStatusCode(error) {
1917
+ if (error instanceof AuthenticationError) return 401;
1918
+ if (error instanceof RateLimitError) return 429;
1919
+ return 0;
1920
+ }
1921
+ function abortError(signal) {
1922
+ if (signal.reason instanceof Error) return signal.reason;
1923
+ return new Error("AbortError");
1924
+ }
1925
+
1926
+ // src/internal/llm/responses.ts
1927
+ function messageToInputItems(message) {
1928
+ const items = [];
1929
+ if (message.role === "user") {
1930
+ const content = [];
1931
+ for (const part of message.content) {
1932
+ if (part.type === "text") {
1933
+ content.push({ type: "input_text", text: part.text });
1934
+ } else if (part.type === "image") {
1935
+ const url = part.source.type === "base64" ? `data:${part.source.media_type};base64,${part.source.data}` : part.source.url;
1936
+ content.push({ type: "input_image", image_url: url });
1937
+ } else if (part.type === "tool_result") {
1938
+ items.push({
1939
+ type: "function_call_output",
1940
+ call_id: part.toolUseId,
1941
+ output: toStringToolResultContent(part.content, "openai-responses")
1942
+ });
1943
+ }
1944
+ }
1945
+ if (content.length > 0) items.push({ role: "user", content });
1946
+ return items;
1947
+ }
1948
+ if (message.role === "assistant") {
1949
+ const content = [];
1950
+ for (const part of message.content) {
1951
+ if (part.type === "text") {
1952
+ content.push({ type: "output_text", text: part.text });
1953
+ } else if (part.type === "tool_use") {
1954
+ items.push({
1955
+ type: "function_call",
1956
+ call_id: part.id,
1957
+ name: part.name,
1958
+ arguments: JSON.stringify(part.input)
1959
+ });
1960
+ }
1961
+ }
1962
+ if (content.length > 0) items.push({ role: "assistant", content });
1963
+ return items;
1964
+ }
1965
+ const text = message.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
1966
+ if (text.length > 0) items.push({ role: "system", content: text });
1967
+ return items;
1968
+ }
1969
+ function buildResponsesBody(request) {
1970
+ const input = [];
1971
+ for (const message of request.messages) {
1972
+ for (const item of messageToInputItems(message)) input.push(item);
1973
+ }
1974
+ const slash = request.model.lastIndexOf("/");
1975
+ const model = slash >= 0 ? request.model.slice(slash + 1) : request.model;
1976
+ const body = { model, input, stream: true, store: false };
1977
+ const instructions = collapseSystemText(request.system);
1978
+ if (instructions.length > 0) body.instructions = instructions;
1979
+ if (request.maxTokens !== void 0) body.max_output_tokens = request.maxTokens;
1980
+ if (request.temperature !== void 0) body.temperature = request.temperature;
1981
+ const tools = (request.tools ?? []).map((tool) => ({
1982
+ type: "function",
1983
+ name: tool.name,
1984
+ description: tool.description,
1985
+ parameters: tool.inputSchema,
1986
+ strict: false
1987
+ }));
1988
+ if (tools.length > 0) body.tools = tools;
1989
+ if (request.reasoning !== void 0) body.reasoning = { effort: request.reasoning.effort };
1990
+ return body;
1991
+ }
1992
+ var ResponsesApiClient = class {
1993
+ constructor(options) {
1994
+ this.options = options;
1995
+ this.name = options.providerName ?? "openai-responses";
1996
+ this.baseUrl = (options.baseUrl ?? "https://api.openai.com/v1").replace(/\/+$/, "");
1997
+ this.fetchImpl = options.fetch ?? fetch;
1998
+ }
1999
+ options;
2000
+ name;
2001
+ baseUrl;
2002
+ fetchImpl;
2003
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the SSE dispatch (text / reasoning / tool-call add+delta+done / terminal+usage / error) is one cohesive state machine, mirroring OpenAIStreamAccumulator.consume.
2004
+ async *stream(request, signal) {
2005
+ const providerId = this.options.providerName ?? "openai";
2006
+ const headers = {
2007
+ "content-type": "application/json",
2008
+ accept: "text/event-stream",
2009
+ authorization: `Bearer ${this.options.apiKey}`,
2010
+ ...this.options.extraHeaders ?? {}
2011
+ };
2012
+ const url = `${this.baseUrl}/responses`;
2013
+ const response = await this.fetchImpl(url, {
2014
+ method: "POST",
2015
+ signal,
2016
+ headers,
2017
+ body: JSON.stringify(buildResponsesBody(request))
2018
+ });
2019
+ if (!response.ok) {
2020
+ const text2 = await response.text().catch(() => "");
2021
+ let body = text2;
2022
+ try {
2023
+ body = JSON.parse(text2);
2024
+ } catch {
2025
+ }
2026
+ throw mapOpenAICompatibleError({
2027
+ providerId,
2028
+ status: response.status,
2029
+ body,
2030
+ headers: response.headers,
2031
+ endpoint: "/responses"
2032
+ });
2033
+ }
2034
+ let text = "";
2035
+ const toolCalls = [];
2036
+ let stopReason = "end_turn";
2037
+ let inputTokens;
2038
+ let outputTokens;
2039
+ let reasoningTokens;
2040
+ const pending = {};
2041
+ if (response.body !== null) {
2042
+ for await (const record of parseSseStream(response.body, signal)) {
2043
+ if (record.data === "[DONE]") break;
2044
+ let event;
2045
+ try {
2046
+ event = JSON.parse(record.data);
2047
+ } catch {
2048
+ continue;
2049
+ }
2050
+ const t = event.type;
2051
+ if (t === "response.output_text.delta") {
2052
+ const d = event.delta ?? "";
2053
+ if (d.length > 0) {
2054
+ text += d;
2055
+ yield { type: "text_delta", text: d };
2056
+ }
2057
+ } else if (t === "response.reasoning_summary_text.delta" || t === "response.reasoning_text.delta") {
2058
+ const d = event.delta ?? "";
2059
+ if (d.length > 0) yield { type: "reasoning_delta", text: d };
2060
+ } else if (t === "response.output_item.added" && event.item?.type === "function_call") {
2061
+ const id = event.item.id ?? event.item.call_id ?? "call-0";
2062
+ pending[id] = {
2063
+ callId: event.item.call_id ?? id,
2064
+ name: event.item.name ?? "",
2065
+ args: event.item.arguments ?? ""
2066
+ };
2067
+ } else if (t === "response.function_call_arguments.delta") {
2068
+ const c = event.item_id !== void 0 ? pending[event.item_id] : void 0;
2069
+ if (c !== void 0) c.args += event.delta ?? "";
2070
+ } else if (t === "response.output_item.done" && event.item?.type === "function_call") {
2071
+ const id = event.item.id ?? event.item.call_id ?? "call-0";
2072
+ const c = pending[id] ?? {
2073
+ callId: event.item.call_id ?? id,
2074
+ name: event.item.name ?? "",
2075
+ args: event.item.arguments ?? ""
2076
+ };
2077
+ const rawArgs = event.item.arguments ?? c.args;
2078
+ const call = {
2079
+ type: "tool_use",
2080
+ id: c.callId,
2081
+ name: c.name.length > 0 ? c.name : event.item.name ?? "",
2082
+ input: parseToolArguments(rawArgs)
2083
+ };
2084
+ toolCalls.push(call);
2085
+ delete pending[id];
2086
+ yield { type: "tool_use", id: call.id, name: call.name, input: call.input };
2087
+ } else if (t === "response.completed" || t === "response.incomplete") {
2088
+ const usage = event.response?.usage;
2089
+ if (usage !== void 0) {
2090
+ inputTokens = usage.input_tokens;
2091
+ outputTokens = usage.output_tokens;
2092
+ reasoningTokens = usage.output_tokens_details?.reasoning_tokens;
2093
+ }
2094
+ stopReason = t === "response.incomplete" ? "max_tokens" : "end_turn";
2095
+ } else if (t === "response.failed" || t === "error") {
2096
+ const msg = event.response?.error?.message ?? event.message ?? "responses stream failed";
2097
+ yield { type: "error", message: msg };
2098
+ throw mapOpenAICompatibleError({
2099
+ providerId,
2100
+ status: 502,
2101
+ body: { error: { message: msg } },
2102
+ headers: response.headers,
2103
+ endpoint: "/responses"
2104
+ });
2105
+ }
2106
+ }
2107
+ }
2108
+ if (toolCalls.length > 0 && stopReason === "end_turn") stopReason = "tool_use";
2109
+ yield { type: "stop", reason: stopReason };
2110
+ return makeLlmFinish({
2111
+ stopReason,
2112
+ text,
2113
+ toolCalls,
2114
+ inputTokens,
2115
+ outputTokens,
2116
+ reasoningTokens
2117
+ });
2118
+ }
2119
+ };
2120
+
2121
+ // src/internal/error-mappers/vertex.ts
2122
+ var VERTEX_RULES = [
2123
+ { test: (s, e) => s === 429 || e === "RESOURCE_EXHAUSTED", code: "rate_limit" },
2124
+ { test: (s, e) => s === 401 || e === "UNAUTHENTICATED", code: "auth_unauthenticated" },
2125
+ { test: (s, e) => s === 403 || e === "PERMISSION_DENIED", code: "auth_permission" },
2126
+ {
2127
+ test: (s, e) => s === 400 || e === "INVALID_ARGUMENT" || e === "FAILED_PRECONDITION" || e === "NOT_FOUND",
2128
+ code: "invalid_request"
2129
+ },
2130
+ { test: (s, e) => s === 408 || e === "DEADLINE_EXCEEDED", code: "timeout" },
2131
+ { test: (s) => s >= 500, code: "server_error" }
2132
+ ];
2133
+ function classifyVertexError(args, errStatus) {
2134
+ for (const rule of VERTEX_RULES) {
2135
+ if (rule.test(args.status, errStatus)) return rule.code;
2136
+ }
2137
+ return "unknown";
2138
+ }
2139
+ function vertexMetadata(args, code) {
2140
+ return buildErrorMetadata({
2141
+ provider: "vertex",
2142
+ endpoint: args.endpoint,
2143
+ code,
2144
+ status: args.status,
2145
+ headers: args.headers,
2146
+ body: args.body
2147
+ });
2148
+ }
2149
+ var VERTEX_ERROR_BUILDERS = {
2150
+ rate_limit: (args, msg) => new RateLimitError(`Vertex quota exhausted: ${msg}`, {
2151
+ metadata: vertexMetadata(args, "rate_limit")
2152
+ }),
2153
+ auth_unauthenticated: (args, msg) => new AuthenticationError(`Vertex unauthenticated: ${msg}`, {
2154
+ metadata: vertexMetadata(args, "auth_failed")
2155
+ }),
2156
+ auth_permission: (args, msg) => new AuthenticationError(`Vertex permission denied: ${msg}`, {
2157
+ metadata: vertexMetadata(args, "auth_failed")
2158
+ }),
2159
+ invalid_request: (args, msg) => new ConfigurationError(`Vertex validation: ${msg}`, {
2160
+ metadata: vertexMetadata(args, "invalid_request")
2161
+ }),
2162
+ timeout: (args, msg) => new NetworkError(`Vertex timeout: ${msg}`, { metadata: vertexMetadata(args, "timeout") }),
2163
+ server_error: (args, msg) => new NetworkError(`Vertex server error: ${msg}`, {
2164
+ metadata: vertexMetadata(args, "server_error")
2165
+ }),
2166
+ unknown: (args, msg) => new UnknownAgentError(`Vertex unknown: ${msg}`, { metadata: vertexMetadata(args, "unknown") })
2167
+ };
2168
+ function mapVertexError(args) {
2169
+ const parsed = parseBody2(args.body);
2170
+ const errStatus = parsed.error?.status ?? "";
2171
+ const message = parsed.error?.message ?? "Vertex request failed";
2172
+ const code = classifyVertexError(args, errStatus);
2173
+ return VERTEX_ERROR_BUILDERS[code](args, message);
2174
+ }
2175
+ function parseBody2(body) {
2176
+ if (body !== null && typeof body === "object") return body;
2177
+ if (typeof body === "string") {
2178
+ try {
2179
+ return JSON.parse(body);
2180
+ } catch {
2181
+ return {};
2182
+ }
2183
+ }
2184
+ return {};
2185
+ }
2186
+ var cachedAuthClient;
2187
+ var SCOPE_CLOUD_PLATFORM = "https://www.googleapis.com/auth/cloud-platform";
2188
+ async function resolveVertexAccessToken() {
2189
+ if (cachedAuthClient === void 0) {
2190
+ cachedAuthClient = await getAuthClientOrThrow();
2191
+ }
2192
+ try {
2193
+ const result = await cachedAuthClient.getAccessToken();
2194
+ return result.token ?? void 0;
2195
+ } catch {
2196
+ return void 0;
2197
+ }
2198
+ }
2199
+ async function getAuthClientOrThrow() {
2200
+ let mod;
2201
+ try {
2202
+ const r = createRequire(import.meta.url);
2203
+ mod = r("google-auth-library");
2204
+ } catch (err) {
2205
+ const isModuleMissing = err instanceof Error && (err.message.includes("Cannot find module") || err.code === "MODULE_NOT_FOUND");
2206
+ if (isModuleMissing) {
2207
+ throw new ConfigurationError(
2208
+ "Vertex provider requires `google-auth-library`. Install: `pnpm add google-auth-library`.",
2209
+ { code: "auth_failed", cause: err }
2210
+ );
2211
+ }
2212
+ throw err;
2213
+ }
2214
+ try {
2215
+ const auth = new mod.GoogleAuth({ scopes: [SCOPE_CLOUD_PLATFORM] });
2216
+ return await auth.getClient();
2217
+ } catch (err) {
2218
+ process.stderr.write(
2219
+ `[theokit-sdk] Vertex ADC initialization failed: ${err instanceof Error ? err.message : String(err)}
2220
+ `
2221
+ );
2222
+ return { getAccessToken: async () => ({ token: null }) };
2223
+ }
2224
+ }
2225
+ function resolveVertexProjectId() {
2226
+ return process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GOOGLE_CLOUD_PROJECT_ID ?? process.env.GCLOUD_PROJECT;
2227
+ }
2228
+ function resolveVertexLocation() {
2229
+ return process.env.GOOGLE_CLOUD_LOCATION ?? process.env.CLOUD_ML_REGION ?? "us-central1";
2230
+ }
2231
+
2232
+ // src/internal/llm/vertex-anthropic.ts
2233
+ var VertexAnthropicClient = class {
2234
+ constructor(options) {
2235
+ this.options = options;
2236
+ this.fetchImpl = options.fetch ?? fetch;
2237
+ }
2238
+ options;
2239
+ name = "vertex_anthropic";
2240
+ fetchImpl;
2241
+ resolveProjectOrThrow() {
2242
+ const projectId = this.options.projectId ?? resolveVertexProjectId();
2243
+ if (projectId !== void 0) return projectId;
2244
+ throw new ConfigurationError(
2245
+ "Vertex requires a project id. Set GOOGLE_CLOUD_PROJECT env var or run `gcloud config set project <id>`.",
2246
+ { code: "auth_failed" }
2247
+ );
2248
+ }
2249
+ async resolveTokenOrThrow() {
2250
+ const fromOpts = this.options.apiKey;
2251
+ if (fromOpts !== void 0 && fromOpts.length > 0) return fromOpts;
2252
+ const fromAdc = await resolveVertexAccessToken();
2253
+ if (fromAdc !== void 0 && fromAdc.length > 0) return fromAdc;
2254
+ throw new ConfigurationError(
2255
+ "Vertex could not resolve an access token. Run `gcloud auth application-default login` or set GOOGLE_APPLICATION_CREDENTIALS to a service-account JSON path.",
2256
+ { code: "auth_failed" }
2257
+ );
2258
+ }
2259
+ async *stream(request, signal) {
2260
+ const projectId = this.resolveProjectOrThrow();
2261
+ const location = this.options.location ?? resolveVertexLocation();
2262
+ const accessToken = await this.resolveTokenOrThrow();
2263
+ const baseUrl = resolveVertexBaseUrl({ projectId, location, modelDialect: "anthropic" });
2264
+ const bareModel = stripVertexPrefix(request.model);
2265
+ const endpoint = `/models/${encodeURIComponent(bareModel)}:rawPredict`;
2266
+ const url = `${baseUrl}${endpoint}`;
2267
+ const response = await postAnthropicRequest({
2268
+ fetchImpl: this.fetchImpl,
2269
+ url,
2270
+ token: accessToken,
2271
+ body: buildVertexAnthropicBody(request),
2272
+ signal
2273
+ });
2274
+ const consumed = await handleAnthropicResponse({
2275
+ response,
2276
+ endpoint,
2277
+ errorMapper: mapVertexError
2278
+ });
2279
+ if (consumed.text.length > 0) yield { type: "text_delta", text: consumed.text };
2280
+ return makeLlmFinish(consumed);
2281
+ }
2282
+ };
2283
+ function buildVertexAnthropicBody(request) {
2284
+ return {
2285
+ anthropic_version: "vertex-2023-10-16",
2286
+ ...buildAnthropicCommonBody(request)
2287
+ };
2288
+ }
2289
+
2290
+ // src/internal/llm/vertex-gemini.ts
2291
+ var VertexGeminiClient = class {
2292
+ constructor(options) {
2293
+ this.options = options;
2294
+ }
2295
+ options;
2296
+ name = "vertex_gemini";
2297
+ async *stream(request, signal) {
2298
+ const projectId = this.options.projectId ?? resolveVertexProjectId();
2299
+ if (projectId === void 0) {
2300
+ throw new ConfigurationError(
2301
+ "Vertex requires a project id. Set GOOGLE_CLOUD_PROJECT env var or run `gcloud config set project <id>`.",
2302
+ { code: "auth_failed" }
2303
+ );
2304
+ }
2305
+ const location = this.options.location ?? resolveVertexLocation();
2306
+ const accessToken = this.options.apiKey !== void 0 && this.options.apiKey.length > 0 ? this.options.apiKey : await resolveVertexAccessToken();
2307
+ if (accessToken === void 0 || accessToken.length === 0) {
2308
+ throw new ConfigurationError(
2309
+ "Vertex could not resolve an access token. Run `gcloud auth application-default login` or set GOOGLE_APPLICATION_CREDENTIALS to a service-account JSON path.",
2310
+ { code: "auth_failed" }
2311
+ );
2312
+ }
2313
+ const baseUrl = resolveVertexBaseUrl({ projectId, location, modelDialect: "gemini" });
2314
+ const url = `${baseUrl}/chat/completions`;
2315
+ const innerFetch = this.options.fetch ?? fetch;
2316
+ const rewriteFetch = (input, init) => {
2317
+ const requested = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
2318
+ const targetUrl = requested.endsWith("/v1/chat/completions") ? url : requested;
2319
+ return innerFetch(targetUrl, init);
2320
+ };
2321
+ const inner = new OpenAIClient({
2322
+ apiKey: accessToken,
2323
+ baseUrl: "https://_vertex_placeholder",
2324
+ providerName: "vertex",
2325
+ fetch: rewriteFetch
2326
+ });
2327
+ return yield* inner.stream(request, signal);
2328
+ }
2329
+ };
2330
+
2331
+ // src/internal/llm/vertex-router.ts
2332
+ var VertexRouterClient = class {
2333
+ constructor(options) {
2334
+ this.options = options;
2335
+ }
2336
+ options;
2337
+ name = "vertex";
2338
+ async *stream(request, signal) {
2339
+ const dialect = inferModelDialect(request.model);
2340
+ if (dialect === "anthropic") {
2341
+ const inner2 = new VertexAnthropicClient(this.options);
2342
+ return yield* inner2.stream(request, signal);
2343
+ }
2344
+ const inner = new VertexGeminiClient(this.options);
2345
+ return yield* inner.stream(request, signal);
2346
+ }
2347
+ };
2348
+
2349
+ // src/internal/llm/router.ts
2350
+ function resolveProviderChain(options) {
2351
+ registerBuiltins();
2352
+ return buildChain(options);
2353
+ }
2354
+ function buildChain(options) {
2355
+ warnUnknownProvidersInApiKeys(options.apiKeys);
2356
+ const seen = /* @__PURE__ */ new Set();
2357
+ const clients = [];
2358
+ const addClient = (name) => {
2359
+ const lowered = name.toLowerCase();
2360
+ if (seen.has(lowered)) return;
2361
+ seen.add(lowered);
2362
+ const client = buildClient(lowered, options);
2363
+ if (client !== void 0) clients.push(maybeWrapWithFaultInjection(client));
2364
+ };
2365
+ addClient(options.primary);
2366
+ for (const fallback of options.fallback ?? []) addClient(fallback);
2367
+ if (clients.length === 0) {
2368
+ throw new ConfigurationError(
2369
+ `No provider client could be resolved (primary=${options.primary}). Set ANTHROPIC_API_KEY or OPENAI_API_KEY / OPENROUTER_API_KEY.`,
2370
+ { code: "provider_unresolved" }
2371
+ );
2372
+ }
2373
+ return clients;
2374
+ }
2375
+ function buildClient(name, routerOptions) {
2376
+ const baseProfile = getProviderProfile(name);
2377
+ if (baseProfile === void 0) return void 0;
2378
+ const profile = routerOptions.extractToolCallsFromContent === true && baseProfile.extractToolCallsFromContent !== true ? { ...baseProfile, extractToolCallsFromContent: true } : baseProfile;
2379
+ const resilience = routerOptions.onRateLimit !== void 0 ? { onRateLimit: routerOptions.onRateLimit } : {};
2380
+ const ambient = currentCredentialPool(name);
2381
+ if (ambient !== void 0) {
2382
+ return new PoolAwareLlmClient(
2383
+ ambient,
2384
+ (apiKey) => selectTransport(profile, apiKey),
2385
+ void 0,
2386
+ resilience
2387
+ );
2388
+ }
2389
+ const poolKeys = filterPoolKeys(routerOptions.apiKeys?.[name]);
2390
+ const noAuthOverride = maybeBuildNoAuthTransport(profile, poolKeys);
2391
+ if (noAuthOverride !== void 0) return noAuthOverride;
2392
+ return buildPoolOrSingle({ name, profile, poolKeys, routerOptions });
2393
+ }
2394
+ function maybeBuildNoAuthTransport(profile, poolKeys) {
2395
+ if (profile.authType !== "none" || poolKeys === void 0 || poolKeys.length === 0) {
2396
+ return void 0;
2397
+ }
2398
+ warnNoAuthApiKeysIgnoredOnce(profile.name);
2399
+ const sentinel = sentinelForNoAuth(profile);
2400
+ if (sentinel === void 0) return void 0;
2401
+ return selectTransport(profile, sentinel);
2402
+ }
2403
+ function buildPoolOrSingle(args) {
2404
+ const { name, profile, poolKeys, routerOptions } = args;
2405
+ if (poolKeys !== void 0 && poolKeys.length >= 2) {
2406
+ const strategy = routerOptions.credentialPoolStrategy?.[name] ?? "fill_first";
2407
+ const entries = poolKeys.map(
2408
+ (accessToken, priority) => newPooledCredential({ provider: name, accessToken, priority, source: "manual" })
2409
+ );
2410
+ const pool = new CredentialPool(name, entries, strategy);
2411
+ const resilience = routerOptions.onRateLimit !== void 0 ? { onRateLimit: routerOptions.onRateLimit } : {};
2412
+ return new PoolAwareLlmClient(
2413
+ pool,
2414
+ (apiKey2) => selectTransport(profile, apiKey2),
2415
+ void 0,
2416
+ resilience
2417
+ );
2418
+ }
2419
+ const apiKey = poolKeys?.[0] ?? resolveApiKey(profile.envVars) ?? sentinelForNoAuth(profile) ?? sentinelForLazyAuth(profile);
2420
+ if (apiKey === void 0) return void 0;
2421
+ return selectTransport(profile, apiKey);
2422
+ }
2423
+ function resolveBaseUrlEnvOverride(providerName) {
2424
+ switch (providerName) {
2425
+ case "openai":
2426
+ return process.env.OPENAI_API_BASE_URL;
2427
+ case "openrouter":
2428
+ return process.env.OPENROUTER_API_BASE_URL;
2429
+ case "ollama":
2430
+ return process.env.OLLAMA_HOST;
2431
+ case "lmstudio":
2432
+ return process.env.LMSTUDIO_HOST;
2433
+ case "llamacpp":
2434
+ return process.env.LLAMACPP_HOST;
2435
+ default:
2436
+ return void 0;
2437
+ }
2438
+ }
2439
+ var warnedNoAuthApiKeys = /* @__PURE__ */ new Set();
2440
+ function warnNoAuthApiKeysIgnoredOnce(provider) {
2441
+ if (warnedNoAuthApiKeys.has(provider)) return;
2442
+ warnedNoAuthApiKeys.add(provider);
2443
+ process.stderr.write(
2444
+ `[theokit-sdk] provider "${provider}" has authType: "none" \u2014 apiKeys ignored (no auth required for local runtime).
2445
+ `
2446
+ );
2447
+ }
2448
+ function sentinelForNoAuth(profile) {
2449
+ return profile.authType === "none" ? profile.name : void 0;
2450
+ }
2451
+ function sentinelForLazyAuth(profile) {
2452
+ if (profile.authType === "aws_bearer") return "__bedrock_lazy_token__";
2453
+ if (profile.authType === "gcp_oauth") return "__vertex_lazy_token__";
2454
+ if (profile.authType === "oauth_device_code" || profile.authType === "oauth_external") {
2455
+ return "__oauth_lazy_token__";
2456
+ }
2457
+ return void 0;
2458
+ }
2459
+ function filterPoolKeys(keys) {
2460
+ if (keys === void 0) return void 0;
2461
+ const cleaned = keys.filter((k) => typeof k === "string" && k.length > 0);
2462
+ return cleaned.length === 0 ? void 0 : cleaned;
2463
+ }
2464
+ var warnedProviders = /* @__PURE__ */ new Set();
2465
+ function warnUnknownProvidersInApiKeys(apiKeys) {
2466
+ if (apiKeys === void 0) return;
2467
+ for (const name of Object.keys(apiKeys)) {
2468
+ if (warnedProviders.has(name)) continue;
2469
+ const profile = getProviderProfile(name);
2470
+ if (profile === void 0) {
2471
+ warnedProviders.add(name);
2472
+ process.stderr.write(
2473
+ `[theokit-sdk] credential-pool: unknown provider "${name}" in apiKeys (no profile registered)
2474
+ `
2475
+ );
2476
+ }
2477
+ }
2478
+ }
2479
+ function resolveApiKey(envVars) {
2480
+ for (const v of envVars) {
2481
+ const value = process.env[v];
2482
+ if (value !== void 0 && value.length > 0) return value;
2483
+ }
2484
+ return void 0;
2485
+ }
2486
+ function selectTransport(profile, apiKey) {
2487
+ const applyTransform = () => {
2488
+ if (profile.transform === void 0) return {};
2489
+ const ctx = { apiKey };
2490
+ return { fetch: profile.transform.fetch?.(ctx), headers: profile.transform.headers?.(ctx) };
2491
+ };
2492
+ const comTransform = (opts, criar) => {
2493
+ const t = applyTransform();
2494
+ assertOAuthResolved(t);
2495
+ if (t.fetch !== void 0) opts.fetch = t.fetch;
2496
+ const merged = profile.extraHeaders !== void 0 || t.headers !== void 0 ? { ...profile.extraHeaders, ...t.headers } : void 0;
2497
+ if (merged !== void 0) opts.extraHeaders = merged;
2498
+ return criar(opts);
2499
+ };
2500
+ const assertOAuthResolved = (t) => {
2501
+ if (apiKey !== "__oauth_lazy_token__") return;
2502
+ const auth = t.headers?.authorization ?? t.headers?.Authorization;
2503
+ if (t.fetch === void 0 && auth === void 0) {
2504
+ throw new ConfigurationError(
2505
+ `provider "${profile.name}" uses OAuth (authType: ${profile.authType}) but no credential was resolved. Register a ProviderProfile.transform whose fetch (or headers.authorization) supplies the bearer \u2014 typically via resolveCredential() from "@theokit/sdk/auth".`
2506
+ );
2507
+ }
2508
+ };
2509
+ if (profile.apiMode === "chat_completions") {
2510
+ if (profile.name === "ollama") {
2511
+ const ollamaBase = process.env.OLLAMA_HOST ?? profile.baseUrl;
2512
+ return new OllamaNativeClient({ apiKey, baseUrl: ollamaBase });
2513
+ }
2514
+ const opts = { apiKey };
2515
+ opts.baseUrl = profile.baseUrl;
2516
+ opts.providerName = profile.name;
2517
+ if (profile.extractToolCallsFromContent === true) {
2518
+ opts.extractToolCallsFromContent = true;
2519
+ }
2520
+ if (profile.chatCompletionsPath !== void 0) {
2521
+ opts.chatCompletionsPath = profile.chatCompletionsPath;
2522
+ }
2523
+ if (profile.name === "openai" && process.env.OPENAI_ORGANIZATION !== void 0) {
2524
+ opts.organization = process.env.OPENAI_ORGANIZATION;
2525
+ }
2526
+ const envOverride = resolveBaseUrlEnvOverride(profile.name);
2527
+ if (envOverride !== void 0) opts.baseUrl = envOverride;
2528
+ return comTransform(opts, (o) => new OpenAIClient(o));
2529
+ }
2530
+ if (profile.apiMode === "anthropic_messages") {
2531
+ if (profile.name === "vertex") {
2532
+ const realKey = apiKey === "__vertex_lazy_token__" ? void 0 : apiKey;
2533
+ return new VertexRouterClient(realKey !== void 0 ? { apiKey: realKey } : {});
2534
+ }
2535
+ const opts = { apiKey };
2536
+ opts.baseUrl = process.env.ANTHROPIC_API_BASE_URL ?? profile.baseUrl;
2537
+ return comTransform(opts, (o) => new AnthropicClient(o));
2538
+ }
2539
+ if (profile.apiMode === "bedrock_anthropic") {
2540
+ const realKey = apiKey === "__bedrock_lazy_token__" ? void 0 : apiKey;
2541
+ return new BedrockAnthropicClient(realKey !== void 0 ? { apiKey: realKey } : {});
2542
+ }
2543
+ if (profile.apiMode === "responses_api") {
2544
+ const t = applyTransform();
2545
+ assertOAuthResolved(t);
2546
+ const mergedHeaders = profile.extraHeaders !== void 0 || t.headers !== void 0 ? { ...profile.extraHeaders, ...t.headers } : void 0;
2547
+ return new ResponsesApiClient({
2548
+ apiKey,
2549
+ ...profile.baseUrl !== void 0 ? { baseUrl: profile.baseUrl } : {},
2550
+ ...mergedHeaders !== void 0 ? { extraHeaders: mergedHeaders } : {},
2551
+ ...t.fetch !== void 0 ? { fetch: t.fetch } : {},
2552
+ providerName: profile.name
2553
+ });
2554
+ }
2555
+ throw new ConfigurationError(
2556
+ `Provider "${profile.name}" requires apiMode "${profile.apiMode}" but no transport is registered. Install a third-party transport plugin (@theokit-transport-${profile.apiMode}) or use a provider with apiMode "chat_completions" or "anthropic_messages".`,
2557
+ { code: "transport_unavailable" }
2558
+ );
2559
+ }
2560
+
2561
+ // src/internal/local-agent/real-local-run-provider.ts
2562
+ function inferProviderFromApiKey(apiKey) {
2563
+ if (apiKey === void 0 || apiKey.length === 0) return void 0;
2564
+ const byPrefix = [
2565
+ { provider: "openrouter", prefix: "sk-or-" },
2566
+ { provider: "anthropic", prefix: "sk-ant-" },
2567
+ { provider: "openai", prefix: "sk-" }
2568
+ ];
2569
+ for (const { provider, prefix } of byPrefix) {
2570
+ if (apiKey.startsWith(prefix) && getProviderProfile(provider) !== void 0) {
2571
+ return provider;
2572
+ }
2573
+ }
2574
+ return void 0;
2575
+ }
2576
+ function detectPrimaryProvider() {
2577
+ if (process.env.ANTHROPIC_API_KEY !== void 0 && process.env.ANTHROPIC_API_KEY.length > 0) {
2578
+ return "anthropic";
2579
+ }
2580
+ if (process.env.OPENAI_API_KEY !== void 0 && process.env.OPENAI_API_KEY.length > 0) {
2581
+ return "openai";
2582
+ }
2583
+ if (process.env.OPENROUTER_API_KEY !== void 0 && process.env.OPENROUTER_API_KEY.length > 0) {
2584
+ return "openrouter";
2585
+ }
2586
+ return "openai";
2587
+ }
2588
+
2589
+ // src/internal/runtime/compression/compression-model-registry.ts
2590
+ var EXACT_REGISTRY = /* @__PURE__ */ new Map([
2591
+ // OpenAI family
2592
+ ["openai/gpt-4o", "openai/gpt-4o-mini"],
2593
+ ["openai/gpt-4-turbo", "openai/gpt-4o-mini"],
2594
+ ["openai/gpt-4", "openai/gpt-4o-mini"],
2595
+ ["openai/o1-preview", "openai/gpt-4o-mini"],
2596
+ ["openai/o1", "openai/gpt-4o-mini"],
2597
+ ["openai/o3", "openai/gpt-4o-mini"],
2598
+ ["openai/o3-mini", "openai/gpt-4o-mini"],
2599
+ // Anthropic family
2600
+ ["anthropic/claude-opus-4", "anthropic/claude-3-5-haiku-latest"],
2601
+ ["anthropic/claude-sonnet-4", "anthropic/claude-3-5-haiku-latest"],
2602
+ ["anthropic/claude-3-5-sonnet", "anthropic/claude-3-5-haiku-latest"],
2603
+ ["anthropic/claude-3-5-sonnet-latest", "anthropic/claude-3-5-haiku-latest"],
2604
+ ["anthropic/claude-3-opus", "anthropic/claude-3-haiku"],
2605
+ ["anthropic/claude-3-sonnet", "anthropic/claude-3-haiku"],
2606
+ // Vertex (Gemini + Anthropic-on-Vertex)
2607
+ ["vertex/gemini-1.5-pro", "vertex/gemini-1.5-flash"],
2608
+ ["vertex/gemini-2.0-pro", "vertex/gemini-1.5-flash"],
2609
+ ["vertex/claude-3-5-sonnet", "vertex/claude-3-5-haiku"],
2610
+ ["vertex/claude-3-opus", "vertex/claude-3-haiku"],
2611
+ // OpenRouter (preserve openrouter prefix, swap tier within same vendor)
2612
+ ["openrouter/openai/gpt-4o", "openrouter/openai/gpt-4o-mini"],
2613
+ ["openrouter/openai/gpt-4-turbo", "openrouter/openai/gpt-4o-mini"],
2614
+ ["openrouter/anthropic/claude-3-5-sonnet", "openrouter/anthropic/claude-3-5-haiku"],
2615
+ ["openrouter/anthropic/claude-opus-4", "openrouter/anthropic/claude-3-5-haiku"]
2616
+ ]);
2617
+ var WILDCARD_REGISTRY = [
2618
+ // Bedrock Anthropic: us.anthropic.claude-sonnet-* → us.anthropic.claude-3-haiku-*
2619
+ ["bedrock/anthropic.claude-sonnet*", "bedrock/anthropic.claude-3-haiku*"],
2620
+ ["bedrock/anthropic.claude-opus*", "bedrock/anthropic.claude-3-haiku*"],
2621
+ ["bedrock/anthropic.claude-3-5-sonnet*", "bedrock/anthropic.claude-3-5-haiku*"]
2622
+ ];
2623
+ var NO_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["ollama", "lmstudio", "llamacpp"]);
2624
+ var CompressionModelUnresolvedError = class extends Error {
2625
+ name = "CompressionModelUnresolvedError";
2626
+ agentModel;
2627
+ constructor(agentModel) {
2628
+ super(
2629
+ `Could not resolve a same-family-cheaper-tier compression model for "${agentModel}". Provide Agent.create({compression: {model: "<your-cheaper-model>"}}) OR add "${agentModel}" to the compression-model-registry (see ADR D440).`
2630
+ );
2631
+ this.agentModel = agentModel;
2632
+ }
2633
+ };
2634
+ function resolveCompressionModel(agentModel) {
2635
+ const exact = EXACT_REGISTRY.get(agentModel);
2636
+ if (exact !== void 0) return exact;
2637
+ const wildcard = matchWildcard(agentModel);
2638
+ if (wildcard !== null) return wildcard;
2639
+ if (isNoAuthProvider(agentModel)) return agentModel;
2640
+ throw new CompressionModelUnresolvedError(agentModel);
2641
+ }
2642
+ function matchWildcard(agentModel) {
2643
+ for (const [pattern, replacement] of WILDCARD_REGISTRY) {
2644
+ const prefix = pattern.endsWith("*") ? pattern.slice(0, -1) : pattern;
2645
+ if (!agentModel.startsWith(prefix)) continue;
2646
+ const suffix = agentModel.slice(prefix.length);
2647
+ const replPrefix = replacement.endsWith("*") ? replacement.slice(0, -1) : replacement;
2648
+ return `${replPrefix}${suffix}`;
2649
+ }
2650
+ return null;
2651
+ }
2652
+ function isNoAuthProvider(agentModel) {
2653
+ const slashIdx = agentModel.indexOf("/");
2654
+ if (slashIdx <= 0) return false;
2655
+ return NO_AUTH_PROVIDERS.has(agentModel.slice(0, slashIdx));
2656
+ }
2657
+
2658
+ // src/internal/runtime/compression/compression-summarizer.ts
2659
+ var CompressionFailedError = class extends Error {
2660
+ name = "CompressionFailedError";
2661
+ };
2662
+ function buildCompressionPrompt(messages) {
2663
+ const formatted = messages.map((m) => `[${m.role}]: ${m.content}`).join("\n\n");
2664
+ return `Summarize the following ${messages.length} conversation messages into a concise summary that preserves ALL facts, decisions, user preferences, and context needed for the conversation to continue naturally. The summary will replace these messages in the context window. Be thorough but concise.
2665
+
2666
+ --- CONVERSATION TO SUMMARIZE ---
2667
+ ${formatted}
2668
+ --- END ---`;
2669
+ }
2670
+ var COMPRESSION_SYSTEM = "You are a conversation summarizer. Produce a concise factual summary. Preserve all decisions, preferences, code snippets, and action items. Do not add commentary or opinions. Output ONLY the summary text.";
2671
+ async function compressConversationWindow(opts) {
2672
+ const userPrompt = buildCompressionPrompt(opts.messages);
2673
+ let summary;
2674
+ try {
2675
+ summary = await opts.callLlm(opts.model, COMPRESSION_SYSTEM, userPrompt);
2676
+ } catch (cause) {
2677
+ throw new CompressionFailedError(
2678
+ `Compression LLM call failed: ${cause instanceof Error ? cause.message : String(cause)}`,
2679
+ { cause: cause instanceof Error ? cause : void 0 }
2680
+ );
2681
+ }
2682
+ if (!summary || summary.trim().length === 0) {
2683
+ throw new CompressionFailedError(
2684
+ "Compression LLM returned empty summary \u2014 reduction ineffective."
2685
+ );
2686
+ }
2687
+ return {
2688
+ role: "system",
2689
+ content: `[Compressed conversation summary]: ${summary.trim()}`
2690
+ };
2691
+ }
2692
+
2693
+ // src/internal/session/compact-session.ts
2694
+ var COMPACT_SUMMARY_MARKER = "[[theokit:compact-summary]]";
2695
+ var COMPACT_USER_MESSAGE_MAX_TOKENS = 2e4;
2696
+ function isCompactSummary(content) {
2697
+ return content.startsWith(COMPACT_SUMMARY_MARKER) || content.startsWith("[[theokit:goal-continuation]]");
2698
+ }
2699
+ function plainText(content) {
2700
+ if (typeof content === "string") return content;
2701
+ if (!Array.isArray(content)) return void 0;
2702
+ const texts = content.filter(
2703
+ (p) => p !== null && typeof p === "object" && p.type === "text" && typeof p.text === "string"
2704
+ ).map((p) => p.text);
2705
+ return texts.length > 0 ? texts.join("\n") : void 0;
2706
+ }
2707
+ async function compactSessionTranscript(opts) {
2708
+ const prior = await opts.store.readRecords(opts.loc.agentId);
2709
+ const history = reconstructMessages(prior);
2710
+ const compressible = [];
2711
+ for (const m of history) {
2712
+ const text = plainText(m.content);
2713
+ if (text !== void 0 && (m.role === "user" || m.role === "assistant" || m.role === "system")) {
2714
+ compressible.push({ role: m.role, content: text });
2715
+ }
2716
+ }
2717
+ const preTokens = estimateTokens(compressible.map((m) => m.content).join("\n"));
2718
+ const summaryBody = await opts.summarize(compressible);
2719
+ const preserved = [];
2720
+ let budget = 0;
2721
+ for (let i = compressible.length - 1; i >= 0; i--) {
2722
+ const m = compressible[i];
2723
+ if (m === void 0 || m.role !== "user" || isCompactSummary(m.content)) continue;
2724
+ const cost = estimateTokens(m.content);
2725
+ if (budget + cost > COMPACT_USER_MESSAGE_MAX_TOKENS) {
2726
+ const remaining = COMPACT_USER_MESSAGE_MAX_TOKENS - budget;
2727
+ if (remaining > 50) {
2728
+ preserved.unshift(`${m.content.slice(0, remaining * 4)}
2729
+ [...truncated for compaction...]`);
2730
+ }
2731
+ break;
2732
+ }
2733
+ budget += cost;
2734
+ preserved.unshift(m.content);
2735
+ }
2736
+ const summary = `${COMPACT_SUMMARY_MARKER}
2737
+ ${summaryBody}`;
2738
+ const transcript = SessionTranscript.fromRecords(prior, {
2739
+ cwd: opts.loc.cwd,
2740
+ sessionId: opts.sessionId,
2741
+ model: opts.loc.model ?? "unknown"
2742
+ });
2743
+ transcript.appendCompactBoundary({ preTokens, trigger: opts.trigger });
2744
+ for (const text of preserved) transcript.appendUserTurn(text);
2745
+ transcript.appendUserTurn(summary);
2746
+ const delta = transcript.records().slice(prior.length);
2747
+ await opts.store.appendRecords(opts.loc.agentId, delta);
2748
+ invalidateSessionCache(opts.loc.cwd, opts.loc.agentId);
2749
+ const postTokens = estimateTokens([...preserved, summary].join("\n"));
2750
+ return { preTokens, postTokens };
2751
+ }
2752
+ function resolveSummarizerRoute(opts) {
2753
+ const provider = opts.keyProvider ?? (opts.prefixHasProfile && opts.modelPrefix !== void 0 ? opts.modelPrefix : opts.envProvider);
2754
+ return { provider, fullSlug: provider !== opts.modelPrefix };
2755
+ }
2756
+ function buildDefaultSummarizer(opts) {
2757
+ return async (messages) => {
2758
+ registerBuiltins();
2759
+ const modelPrefix = opts.agentModel.includes("/") ? opts.agentModel.slice(0, opts.agentModel.indexOf("/")) : void 0;
2760
+ const prefixProfile = modelPrefix !== void 0 ? getProviderProfile(modelPrefix) : void 0;
2761
+ const prefixUsable = prefixProfile !== void 0 && (prefixProfile.authType !== "api_key" || prefixProfile.envVars.some((v) => (process.env[v] ?? "").length > 0));
2762
+ const route = resolveSummarizerRoute({
2763
+ keyProvider: inferProviderFromApiKey(opts.apiKey),
2764
+ modelPrefix,
2765
+ prefixHasProfile: prefixUsable,
2766
+ envProvider: detectPrimaryProvider()
2767
+ });
2768
+ const provider = route.provider;
2769
+ let model;
2770
+ if (route.fullSlug) {
2771
+ model = opts.agentModel;
2772
+ } else {
2773
+ try {
2774
+ model = resolveCompressionModel(opts.agentModel);
2775
+ } catch {
2776
+ model = opts.agentModel;
2777
+ }
2778
+ }
2779
+ const callLlm = async (m, system, user) => {
2780
+ const chain = resolveProviderChain({
2781
+ primary: provider,
2782
+ ...opts.apiKey !== void 0 ? { apiKeys: { [provider]: [opts.apiKey] } } : {}
2783
+ });
2784
+ const client = chain[0];
2785
+ if (client === void 0) throw new Error(`no LLM client for provider "${provider}"`);
2786
+ const controller = new AbortController();
2787
+ let text = "";
2788
+ const stream = client.stream(
2789
+ { model: m, system, messages: [{ role: "user", content: [{ type: "text", text: user }] }] },
2790
+ controller.signal
2791
+ );
2792
+ for (; ; ) {
2793
+ const step = await stream.next();
2794
+ if (step.done === true) break;
2795
+ if (step.value.type === "text_delta") text += step.value.text;
2796
+ if (step.value.type === "error") throw new Error(step.value.message);
2797
+ }
2798
+ return text;
2799
+ };
2800
+ const summary = await compressConversationWindow({ messages: [...messages], model, callLlm });
2801
+ return summary.content;
2802
+ };
2803
+ }
2804
+ function shouldAutoCompact(opts) {
2805
+ if (opts.usageTotal === void 0 || opts.contextWindow === void 0) return false;
2806
+ if (opts.usageTotal <= 0 || opts.contextWindow <= 0) return false;
2807
+ return opts.usageTotal >= Math.floor(opts.contextWindow * 9 / 10);
2808
+ }
2809
+ var autoCompactAttempts = (() => {
2810
+ const g = globalThis;
2811
+ const sym = /* @__PURE__ */ Symbol.for("theokit-sdk.session.auto-compact-attempts");
2812
+ if (g[sym] === void 0) g[sym] = /* @__PURE__ */ new Map();
2813
+ return g[sym];
2814
+ })();
2815
+ async function autoCompactIfNeeded(opts) {
2816
+ if (!shouldAutoCompact(opts)) return false;
2817
+ const key = `${opts.loc.cwd}::${opts.loc.agentId}`;
2818
+ if (autoCompactAttempts.get(key) === opts.turnCount) return false;
2819
+ autoCompactAttempts.set(key, opts.turnCount);
2820
+ try {
2821
+ await compactSessionTranscript({
2822
+ store: opts.store,
2823
+ loc: opts.loc,
2824
+ sessionId: opts.sessionId,
2825
+ trigger: "auto",
2826
+ summarize: opts.summarize
2827
+ });
2828
+ return true;
2829
+ } catch (cause) {
2830
+ const msg = cause instanceof Error ? cause.message : String(cause);
2831
+ process.stderr.write(
2832
+ `[theokit-sdk] auto-compaction failed (left transcript untouched): ${msg}
2833
+ `
2834
+ );
2835
+ return false;
2836
+ }
2837
+ }
2838
+
2839
+ export { COMPACT_SUMMARY_MARKER, COMPACT_USER_MESSAGE_MAX_TOKENS, CircuitBreaker, autoCompactIfNeeded, buildDefaultSummarizer, compactSessionTranscript, detectPrimaryProvider, discoverProviderPlugins, inferProviderFromApiKey, isCompactSummary, mapOllamaHttpError, mapOllamaTransportError, mapOpenAICompatibleError, parseSseStream, relayStream, renderToolResultContentText, resolveProviderChain, resolveSummarizerRoute, shouldAutoCompact, tryFirstEvent };
2840
+ //# sourceMappingURL=chunk-NRSH7RJY.js.map
2841
+ //# sourceMappingURL=chunk-NRSH7RJY.js.map