theokit 0.48.13 → 0.50.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 (327) hide show
  1. package/README.md +1 -0
  2. package/dist/{actions-virtual-module-YQXRSSMW.js → actions-virtual-module-5WBVSDOM.js} +22 -12
  3. package/dist/actions-virtual-module-5WBVSDOM.js.map +1 -0
  4. package/dist/{actions-virtual-module-LB4NGONI.js → actions-virtual-module-ZYLMNW6K.js} +13 -8
  5. package/dist/{actions-virtual-module-LB4NGONI.js.map → actions-virtual-module-ZYLMNW6K.js.map} +1 -1
  6. package/dist/adapters/agent-mount.d.ts +127 -0
  7. package/dist/adapters/agent-mount.js +24 -0
  8. package/dist/adapters/security-headers.d.ts +132 -0
  9. package/dist/adapters/security-headers.js +64 -0
  10. package/dist/adapters/security-headers.js.map +1 -0
  11. package/dist/adapters/web-shim.d.ts +100 -13
  12. package/dist/adapters/web-shim.js +158 -54
  13. package/dist/adapters/web-shim.js.map +1 -1
  14. package/dist/agent-GKUEL6OH.js +15 -0
  15. package/dist/agent-scan-BZa-pagG.d.ts +16 -0
  16. package/dist/{app-typed-client-GHO3EWZK.js → app-typed-client-4LAIEGFX.js} +23 -13
  17. package/dist/app-typed-client-4LAIEGFX.js.map +1 -0
  18. package/dist/{app-typed-client-G3J5G7E5.js → app-typed-client-DQM3Y6YY.js} +14 -9
  19. package/dist/app-typed-client-DQM3Y6YY.js.map +1 -0
  20. package/dist/aws-lambda-VKMYUMP2.js +261 -0
  21. package/dist/aws-lambda-VKMYUMP2.js.map +1 -0
  22. package/dist/{build-WU6AKKIB.js → build-FLX6VF7Q.js} +71 -26
  23. package/dist/build-FLX6VF7Q.js.map +1 -0
  24. package/dist/{bun-33RD6K7O.js → bun-L7PP6ID5.js} +105 -13
  25. package/dist/bun-L7PP6ID5.js.map +1 -0
  26. package/dist/{check-45NKUVSQ.js → check-GIPZBFTX.js} +2 -2
  27. package/dist/{chunk-KD5G3YXI.js → chunk-2AMLGJJA.js} +35 -9
  28. package/dist/chunk-2AMLGJJA.js.map +1 -0
  29. package/dist/chunk-3PBAN2YE.js +146 -0
  30. package/dist/chunk-3PBAN2YE.js.map +1 -0
  31. package/dist/{chunk-HBU2GIMZ.js → chunk-3QPVEJ7D.js} +15 -9
  32. package/dist/chunk-3QPVEJ7D.js.map +1 -0
  33. package/dist/{chunk-47CINLWF.js → chunk-44J2ANQC.js} +2 -2
  34. package/dist/{chunk-IEES3CHD.js → chunk-47MM2JUK.js} +8 -2
  35. package/dist/chunk-47MM2JUK.js.map +1 -0
  36. package/dist/chunk-4X6H2BXP.js +108 -0
  37. package/dist/chunk-4X6H2BXP.js.map +1 -0
  38. package/dist/{chunk-A7JBF5FO.js → chunk-5K4TDLRG.js} +8 -5
  39. package/dist/chunk-5K4TDLRG.js.map +1 -0
  40. package/dist/chunk-5YPXXSYY.js +181 -0
  41. package/dist/chunk-5YPXXSYY.js.map +1 -0
  42. package/dist/{chunk-YGAXV5HR.js → chunk-6J4JWHDJ.js} +191 -112
  43. package/dist/chunk-6J4JWHDJ.js.map +1 -0
  44. package/dist/chunk-72DMJBXI.js +424 -0
  45. package/dist/chunk-72DMJBXI.js.map +1 -0
  46. package/dist/chunk-7PM6K5EV.js +513 -0
  47. package/dist/chunk-7PM6K5EV.js.map +1 -0
  48. package/dist/{chunk-MTEWLGVY.js → chunk-BPYMC7SU.js} +4 -48
  49. package/dist/chunk-BPYMC7SU.js.map +1 -0
  50. package/dist/chunk-BU6RJPPJ.js +421 -0
  51. package/dist/chunk-BU6RJPPJ.js.map +1 -0
  52. package/dist/{chunk-FB2HBNIC.js → chunk-BX6EUYHU.js} +6 -2
  53. package/dist/chunk-BX6EUYHU.js.map +1 -0
  54. package/dist/{chunk-BIVCP6X5.js → chunk-C62IZEJI.js} +465 -202
  55. package/dist/chunk-C62IZEJI.js.map +1 -0
  56. package/dist/chunk-D4VP2QPR.js +16 -0
  57. package/dist/chunk-D4VP2QPR.js.map +1 -0
  58. package/dist/{chunk-J5MXCGKA.js → chunk-DHAD2O3S.js} +6 -6
  59. package/dist/chunk-DHAD2O3S.js.map +1 -0
  60. package/dist/chunk-E43VJ3E5.js +188 -0
  61. package/dist/chunk-E43VJ3E5.js.map +1 -0
  62. package/dist/chunk-EVZKNA2W.js +110 -0
  63. package/dist/chunk-EVZKNA2W.js.map +1 -0
  64. package/dist/chunk-FD4ZDTQ6.js +181 -0
  65. package/dist/chunk-FD4ZDTQ6.js.map +1 -0
  66. package/dist/chunk-FDOOBTXJ.js +17 -0
  67. package/dist/chunk-FDOOBTXJ.js.map +1 -0
  68. package/dist/chunk-FH3SQR6P.js +275 -0
  69. package/dist/chunk-FH3SQR6P.js.map +1 -0
  70. package/dist/chunk-HJHGTFEJ.js +23 -0
  71. package/dist/chunk-HJHGTFEJ.js.map +1 -0
  72. package/dist/{chunk-LXAESF4Y.js → chunk-HK4AZBKX.js} +7 -4
  73. package/dist/chunk-HK4AZBKX.js.map +1 -0
  74. package/dist/{chunk-T5C46IIU.js → chunk-HWBRS4DZ.js} +28 -269
  75. package/dist/chunk-HWBRS4DZ.js.map +1 -0
  76. package/dist/chunk-IRQGAC4L.js +52 -0
  77. package/dist/chunk-IRQGAC4L.js.map +1 -0
  78. package/dist/{chunk-FNTLKHZ7.js → chunk-JDN5SAAD.js} +7 -4
  79. package/dist/chunk-JDN5SAAD.js.map +1 -0
  80. package/dist/{chunk-GXEX4CBH.js → chunk-JIKNR5LX.js} +673 -484
  81. package/dist/chunk-JIKNR5LX.js.map +1 -0
  82. package/dist/{chunk-GRI4XHBJ.js → chunk-JWRCZIA4.js} +266 -108
  83. package/dist/chunk-JWRCZIA4.js.map +1 -0
  84. package/dist/{chunk-E576KYYT.js → chunk-KI4DOTN4.js} +25 -2
  85. package/dist/chunk-KI4DOTN4.js.map +1 -0
  86. package/dist/{chunk-ZB7D62OA.js → chunk-KUZVRJ7M.js} +3 -3
  87. package/dist/chunk-L4VFNFDL.js +248 -0
  88. package/dist/chunk-L4VFNFDL.js.map +1 -0
  89. package/dist/chunk-M2WC5MF7.js +127 -0
  90. package/dist/chunk-M2WC5MF7.js.map +1 -0
  91. package/dist/{chunk-O3JIAU3V.js → chunk-MCTC6HSE.js} +157 -103
  92. package/dist/chunk-MCTC6HSE.js.map +1 -0
  93. package/dist/{chunk-PXFXSFJL.js → chunk-NZ5JU6D3.js} +9 -9
  94. package/dist/{chunk-YRBR424R.js → chunk-OAUI273L.js} +338 -165
  95. package/dist/chunk-OAUI273L.js.map +1 -0
  96. package/dist/chunk-OGLS76RN.js +43 -0
  97. package/dist/chunk-OGLS76RN.js.map +1 -0
  98. package/dist/{chunk-7LVM22VG.js → chunk-OQCRCXCP.js} +6 -6
  99. package/dist/chunk-OQCRCXCP.js.map +1 -0
  100. package/dist/{chunk-OLGPYOLO.js → chunk-P6OS7652.js} +5 -2
  101. package/dist/chunk-P6OS7652.js.map +1 -0
  102. package/dist/chunk-Q5L5EGWZ.js +26 -0
  103. package/dist/chunk-Q5L5EGWZ.js.map +1 -0
  104. package/dist/{chunk-S433N3CT.js → chunk-QTHVHNJJ.js} +6 -36
  105. package/dist/chunk-QTHVHNJJ.js.map +1 -0
  106. package/dist/chunk-RPET332G.js +84 -0
  107. package/dist/chunk-RPET332G.js.map +1 -0
  108. package/dist/{chunk-646CA6RV.js → chunk-RQW5YMH7.js} +12 -4
  109. package/dist/chunk-RQW5YMH7.js.map +1 -0
  110. package/dist/{chunk-XALU5LCU.js → chunk-RWNYIU6A.js} +550 -146
  111. package/dist/chunk-RWNYIU6A.js.map +1 -0
  112. package/dist/chunk-SL7YUHPV.js +111 -0
  113. package/dist/chunk-SL7YUHPV.js.map +1 -0
  114. package/dist/chunk-U5PWXS3J.js +39 -0
  115. package/dist/chunk-U5PWXS3J.js.map +1 -0
  116. package/dist/chunk-ULDCFL2O.js +273 -0
  117. package/dist/chunk-ULDCFL2O.js.map +1 -0
  118. package/dist/{chunk-RWNSW4FM.js → chunk-UMNXCU3K.js} +12 -6
  119. package/dist/{chunk-RWNSW4FM.js.map → chunk-UMNXCU3K.js.map} +1 -1
  120. package/dist/chunk-VI7ZANB3.js +193 -0
  121. package/dist/chunk-VI7ZANB3.js.map +1 -0
  122. package/dist/chunk-VQPHPQ5L.js +14 -0
  123. package/dist/chunk-VQPHPQ5L.js.map +1 -0
  124. package/dist/chunk-WNJTJRM6.js +147 -0
  125. package/dist/chunk-WNJTJRM6.js.map +1 -0
  126. package/dist/chunk-WOBSEO75.js +790 -0
  127. package/dist/chunk-WOBSEO75.js.map +1 -0
  128. package/dist/{chunk-BMHS4WAN.js → chunk-XS7EX55U.js} +156 -42
  129. package/dist/chunk-XS7EX55U.js.map +1 -0
  130. package/dist/chunk-YN4W7LCX.js +31 -0
  131. package/dist/chunk-YN4W7LCX.js.map +1 -0
  132. package/dist/chunk-YSJOZEI6.js +1 -0
  133. package/dist/{chunk-JA2ZAT5Q.js → chunk-Z4PJMZLM.js} +2 -2
  134. package/dist/chunk-Z4PJMZLM.js.map +1 -0
  135. package/dist/{chunk-7VPQHEAJ.js → chunk-ZNZISJDZ.js} +6 -2
  136. package/dist/chunk-ZNZISJDZ.js.map +1 -0
  137. package/dist/chunk-ZWN3AODA.js +11 -0
  138. package/dist/chunk-ZWN3AODA.js.map +1 -0
  139. package/dist/cli/index.js +25 -10
  140. package/dist/cli/index.js.map +1 -1
  141. package/dist/client/core.d.ts +1 -1
  142. package/dist/client/core.js +2 -0
  143. package/dist/client/core.js.map +1 -1
  144. package/dist/client/index.d.ts +44 -10
  145. package/dist/client/index.js +35 -0
  146. package/dist/client/index.js.map +1 -1
  147. package/dist/cloudflare-SIG42OIW.js +335 -0
  148. package/dist/cloudflare-SIG42OIW.js.map +1 -0
  149. package/dist/config-support-WQHQIJTA.js +104 -0
  150. package/dist/config-support-WQHQIJTA.js.map +1 -0
  151. package/dist/{csrf-BBrEZSBW.d.ts → csrf-Dgot9qSM.d.ts} +1 -1
  152. package/dist/deno-deploy-LQAYOOPW.js +196 -0
  153. package/dist/deno-deploy-LQAYOOPW.js.map +1 -0
  154. package/dist/{dev-MUWJAZRD.js → dev-ANDSEVLB.js} +28 -18
  155. package/dist/dev-ANDSEVLB.js.map +1 -0
  156. package/dist/{dev-emit-GWD6IXV7.js → dev-emit-BFNMDCPB.js} +5 -5
  157. package/dist/{dev-emit-NBT6JD7R.js → dev-emit-MVAJAFGX.js} +5 -3
  158. package/dist/dev-emit-MVAJAFGX.js.map +1 -0
  159. package/dist/engine-singleton-X4R7AZBJ.js +315 -0
  160. package/dist/engine-singleton-X4R7AZBJ.js.map +1 -0
  161. package/dist/{generate-Y3GWM57K.js → generate-EN6EDITF.js} +43 -4
  162. package/dist/generate-EN6EDITF.js.map +1 -0
  163. package/dist/index-CzxtQcnp.d.ts +1058 -0
  164. package/dist/index.d.ts +4 -0
  165. package/dist/index.js +24 -13
  166. package/dist/index.js.map +1 -1
  167. package/dist/{info-XW7HGUPH.js → info-Y55RWS42.js} +8 -6
  168. package/dist/{info-XW7HGUPH.js.map → info-Y55RWS42.js.map} +1 -1
  169. package/dist/{internal-api-V5L65YSJ.js → internal-api-IP35YU3G.js} +22 -12
  170. package/dist/{internal-api-WEOSTMSN.js → internal-api-OPU456AT.js} +30 -16
  171. package/dist/internal-api-OPU456AT.js.map +1 -0
  172. package/dist/load-config-4ATXQUN3.js +16 -0
  173. package/dist/match-DmGgxjIU.d.ts +44 -0
  174. package/dist/{mcp-ULUFJNSZ.js → mcp-5LLNAGKA.js} +7 -7
  175. package/dist/netlify-C2IRCZKI.js +245 -0
  176. package/dist/netlify-C2IRCZKI.js.map +1 -0
  177. package/dist/node-OSVPYQ3D.js +10 -0
  178. package/dist/nonce-BXt7Qu_C.d.ts +31 -0
  179. package/dist/observability-bootstrap-MZMSOE66.js +15 -0
  180. package/dist/{openapi-HZXR335W.js → openapi-VFPEZSRI.js} +13 -11
  181. package/dist/{openapi-HZXR335W.js.map → openapi-VFPEZSRI.js.map} +1 -1
  182. package/dist/{plugin-runner-BFEcgtmJ.d.ts → plugin-runner-BVMOSxvb.d.ts} +1 -1
  183. package/dist/{plugin-types-D69-lJZ9.d.ts → plugin-types-BUVfUQ0V.d.ts} +13 -1
  184. package/dist/preview-GHPT2EQ5.js +23 -0
  185. package/dist/preview-GHPT2EQ5.js.map +1 -0
  186. package/dist/provider-resolver-Dx6QuWrw.d.ts +128 -0
  187. package/dist/registry-KBZDCFVQ.js +23 -0
  188. package/dist/route-config-Cm_ZMA1C.d.ts +72 -0
  189. package/dist/route-policy-BxOycCDq.d.ts +65 -0
  190. package/dist/router/element-scroll-restoration.d.ts +28 -0
  191. package/dist/router/element-scroll-restoration.js +96 -0
  192. package/dist/router/element-scroll-restoration.js.map +1 -0
  193. package/dist/{routes-OLNQX7CT.js → routes-BPLU3ISV.js} +11 -9
  194. package/dist/{routes-OLNQX7CT.js.map → routes-BPLU3ISV.js.map} +1 -1
  195. package/dist/{scan-7MJC6PYU.js → scan-YJEE4KQK.js} +2 -2
  196. package/dist/{schema-ZO3OSWVG.js → schema-SOAD43RQ.js} +7 -5
  197. package/dist/schemas-4AXHQMV3.js +25 -0
  198. package/dist/schemas-4AXHQMV3.js.map +1 -0
  199. package/dist/security-headers-Ccj7ncNx.d.ts +129 -0
  200. package/dist/server/agent/index.d.ts +2 -90
  201. package/dist/server/agent/index.js +1 -1
  202. package/dist/server/auth/index.d.ts +2 -21
  203. package/dist/server/auth/index.js +3 -2
  204. package/dist/server/cron/index.js +4 -3
  205. package/dist/server/define/index.d.ts +51 -57
  206. package/dist/server/define/index.js +10 -1
  207. package/dist/server/http/index.d.ts +7 -5
  208. package/dist/server/http/index.js +56 -12
  209. package/dist/server/index.d.ts +47 -439
  210. package/dist/server/index.js +119 -201
  211. package/dist/server/index.js.map +1 -1
  212. package/dist/server/jobs/index.js +4 -3
  213. package/dist/server/observability/index.d.ts +109 -35
  214. package/dist/server/observability/index.js +10 -7
  215. package/dist/server/plugins/index.d.ts +3 -3
  216. package/dist/server/scan/index.d.ts +47 -12
  217. package/dist/server/scan/index.js +13 -4
  218. package/dist/server/security/index.d.ts +61 -103
  219. package/dist/server/security/index.js +15 -6
  220. package/dist/server/webhook/index.d.ts +90 -3
  221. package/dist/server/webhook/index.js +12 -2
  222. package/dist/server-boundary-6B4ZQUY7.js +148 -0
  223. package/dist/server-boundary-6B4ZQUY7.js.map +1 -0
  224. package/dist/server-boundary-7VZQRPPX.js +133 -0
  225. package/dist/server-boundary-7VZQRPPX.js.map +1 -0
  226. package/dist/{services-typed-client-PSAGRPFF.js → services-typed-client-XGT5SUUN.js} +2 -2
  227. package/dist/{start-KR2JJR7K.js → start-UZDMOT7I.js} +342 -111
  228. package/dist/start-UZDMOT7I.js.map +1 -0
  229. package/dist/{static-M2W4HUWS.js → static-SVDWIOXJ.js} +24 -13
  230. package/dist/static-SVDWIOXJ.js.map +1 -0
  231. package/dist/{theo-cloud-XAHWOUFK.js → theo-cloud-3ET4G5T6.js} +9 -2
  232. package/dist/theo-cloud-3ET4G5T6.js.map +1 -0
  233. package/dist/vercel-BMCQKCWE.js +277 -0
  234. package/dist/vercel-BMCQKCWE.js.map +1 -0
  235. package/dist/vite-plugin/index.js +24 -13
  236. package/dist/vite-plugin-VKCT2LF2.js +42 -0
  237. package/dist/vite-plugin-VKCT2LF2.js.map +1 -0
  238. package/package.json +19 -6
  239. package/dist/actions-virtual-module-YQXRSSMW.js.map +0 -1
  240. package/dist/agent-7JJW3TPB.js +0 -15
  241. package/dist/app-typed-client-G3J5G7E5.js.map +0 -1
  242. package/dist/app-typed-client-GHO3EWZK.js.map +0 -1
  243. package/dist/aws-lambda-CYNDGU5O.js +0 -172
  244. package/dist/aws-lambda-CYNDGU5O.js.map +0 -1
  245. package/dist/build-WU6AKKIB.js.map +0 -1
  246. package/dist/bun-33RD6K7O.js.map +0 -1
  247. package/dist/chunk-646CA6RV.js.map +0 -1
  248. package/dist/chunk-6E5WKMDS.js +0 -67
  249. package/dist/chunk-6E5WKMDS.js.map +0 -1
  250. package/dist/chunk-6GGWS7J3.js +0 -91
  251. package/dist/chunk-6GGWS7J3.js.map +0 -1
  252. package/dist/chunk-7LVM22VG.js.map +0 -1
  253. package/dist/chunk-7VPQHEAJ.js.map +0 -1
  254. package/dist/chunk-A7JBF5FO.js.map +0 -1
  255. package/dist/chunk-AM3QABW5.js +0 -161
  256. package/dist/chunk-AM3QABW5.js.map +0 -1
  257. package/dist/chunk-BIVCP6X5.js.map +0 -1
  258. package/dist/chunk-BMHS4WAN.js.map +0 -1
  259. package/dist/chunk-E576KYYT.js.map +0 -1
  260. package/dist/chunk-FB2HBNIC.js.map +0 -1
  261. package/dist/chunk-FNTLKHZ7.js.map +0 -1
  262. package/dist/chunk-GRI4XHBJ.js.map +0 -1
  263. package/dist/chunk-GVW5EKEU.js +0 -422
  264. package/dist/chunk-GVW5EKEU.js.map +0 -1
  265. package/dist/chunk-GXEX4CBH.js.map +0 -1
  266. package/dist/chunk-HBU2GIMZ.js.map +0 -1
  267. package/dist/chunk-IEES3CHD.js.map +0 -1
  268. package/dist/chunk-J5MXCGKA.js.map +0 -1
  269. package/dist/chunk-JA2ZAT5Q.js.map +0 -1
  270. package/dist/chunk-KD5G3YXI.js.map +0 -1
  271. package/dist/chunk-LNVMHQ2Z.js +0 -54
  272. package/dist/chunk-LNVMHQ2Z.js.map +0 -1
  273. package/dist/chunk-LXAESF4Y.js.map +0 -1
  274. package/dist/chunk-MTEWLGVY.js.map +0 -1
  275. package/dist/chunk-O3JIAU3V.js.map +0 -1
  276. package/dist/chunk-OJLL4YD6.js +0 -403
  277. package/dist/chunk-OJLL4YD6.js.map +0 -1
  278. package/dist/chunk-OLGPYOLO.js.map +0 -1
  279. package/dist/chunk-S433N3CT.js.map +0 -1
  280. package/dist/chunk-T5C46IIU.js.map +0 -1
  281. package/dist/chunk-U267AOWS.js +0 -28
  282. package/dist/chunk-U267AOWS.js.map +0 -1
  283. package/dist/chunk-UW2TEAGR.js +0 -77
  284. package/dist/chunk-UW2TEAGR.js.map +0 -1
  285. package/dist/chunk-WPJOTQA5.js +0 -1
  286. package/dist/chunk-XALU5LCU.js.map +0 -1
  287. package/dist/chunk-XR74JD4M.js +0 -457
  288. package/dist/chunk-XR74JD4M.js.map +0 -1
  289. package/dist/chunk-YGAXV5HR.js.map +0 -1
  290. package/dist/chunk-YRBR424R.js.map +0 -1
  291. package/dist/cloudflare-KYUWCEPL.js +0 -122
  292. package/dist/cloudflare-KYUWCEPL.js.map +0 -1
  293. package/dist/deno-deploy-CCTB7HGS.js +0 -107
  294. package/dist/deno-deploy-CCTB7HGS.js.map +0 -1
  295. package/dist/dev-MUWJAZRD.js.map +0 -1
  296. package/dist/dev-emit-NBT6JD7R.js.map +0 -1
  297. package/dist/generate-Y3GWM57K.js.map +0 -1
  298. package/dist/index-BEtEv6ZH.d.ts +0 -575
  299. package/dist/load-config-VKD6LKWU.js +0 -14
  300. package/dist/match-CfbEFRG4.d.ts +0 -26
  301. package/dist/netlify-AFMDNKT4.js +0 -150
  302. package/dist/netlify-AFMDNKT4.js.map +0 -1
  303. package/dist/node-ZJQEJSP3.js +0 -10
  304. package/dist/registry-GKR3EWRW.js +0 -23
  305. package/dist/start-KR2JJR7K.js.map +0 -1
  306. package/dist/static-M2W4HUWS.js.map +0 -1
  307. package/dist/theo-cloud-XAHWOUFK.js.map +0 -1
  308. package/dist/vercel-SRS2DH6O.js +0 -133
  309. package/dist/vercel-SRS2DH6O.js.map +0 -1
  310. package/dist/vite-plugin-VPJFD4HK.js +0 -36
  311. /package/dist/{chunk-WPJOTQA5.js.map → adapters/agent-mount.js.map} +0 -0
  312. /package/dist/{agent-7JJW3TPB.js.map → agent-GKUEL6OH.js.map} +0 -0
  313. /package/dist/{check-45NKUVSQ.js.map → check-GIPZBFTX.js.map} +0 -0
  314. /package/dist/{chunk-47CINLWF.js.map → chunk-44J2ANQC.js.map} +0 -0
  315. /package/dist/{chunk-ZB7D62OA.js.map → chunk-KUZVRJ7M.js.map} +0 -0
  316. /package/dist/{chunk-PXFXSFJL.js.map → chunk-NZ5JU6D3.js.map} +0 -0
  317. /package/dist/{internal-api-WEOSTMSN.js.map → chunk-YSJOZEI6.js.map} +0 -0
  318. /package/dist/{dev-emit-GWD6IXV7.js.map → dev-emit-BFNMDCPB.js.map} +0 -0
  319. /package/dist/{internal-api-V5L65YSJ.js.map → internal-api-IP35YU3G.js.map} +0 -0
  320. /package/dist/{load-config-VKD6LKWU.js.map → load-config-4ATXQUN3.js.map} +0 -0
  321. /package/dist/{mcp-ULUFJNSZ.js.map → mcp-5LLNAGKA.js.map} +0 -0
  322. /package/dist/{node-ZJQEJSP3.js.map → node-OSVPYQ3D.js.map} +0 -0
  323. /package/dist/{scan-7MJC6PYU.js.map → observability-bootstrap-MZMSOE66.js.map} +0 -0
  324. /package/dist/{registry-GKR3EWRW.js.map → registry-KBZDCFVQ.js.map} +0 -0
  325. /package/dist/{schema-ZO3OSWVG.js.map → scan-YJEE4KQK.js.map} +0 -0
  326. /package/dist/{vite-plugin-VPJFD4HK.js.map → schema-SOAD43RQ.js.map} +0 -0
  327. /package/dist/{services-typed-client-PSAGRPFF.js.map → services-typed-client-XGT5SUUN.js.map} +0 -0
@@ -1,7 +1,13 @@
1
+ import {
2
+ resolveTheoRootDir
3
+ } from "./chunk-VQPHPQ5L.js";
1
4
  import {
2
5
  buildServicesProxyConfig,
3
6
  servicesConfigSchema
4
7
  } from "./chunk-SSYDEKWF.js";
8
+ import {
9
+ dispatchControllerRequest
10
+ } from "./chunk-SL7YUHPV.js";
5
11
  import {
6
12
  broadcastToDevtools
7
13
  } from "./chunk-OIGJOYX6.js";
@@ -9,38 +15,53 @@ import {
9
15
  RUN_ID_HEADER,
10
16
  SSE_BASE_HEADERS,
11
17
  SSE_DONE_FRAME,
12
- dispatchControllerRequest,
18
+ admitAgentRequest,
19
+ agentAccessDenied,
20
+ cacheSchema,
21
+ closePauseSpan,
22
+ createObservabilityPluginFromConfig,
13
23
  encodeSse,
14
24
  formatSseFrame,
15
25
  getApprovalRegistry,
16
26
  getRunEventCache,
27
+ loggingSchema,
17
28
  makeThreadStartRun,
18
29
  mintRunId,
19
30
  mountAgent,
20
- parseAgentRequestBody
21
- } from "./chunk-OJLL4YD6.js";
31
+ observabilitySchema,
32
+ parseAgentRequestBody,
33
+ rateLimitSchema,
34
+ readAgentPolicy,
35
+ securitySchema,
36
+ storageSchema,
37
+ uploadSchema
38
+ } from "./chunk-WOBSEO75.js";
39
+ import {
40
+ loadEnv
41
+ } from "./chunk-4X6H2BXP.js";
22
42
  import {
23
- loadEnv,
24
43
  resolveTransformer
25
- } from "./chunk-XR74JD4M.js";
44
+ } from "./chunk-OGLS76RN.js";
26
45
  import {
27
46
  BATCH_PATH,
28
- TRACE_HEADER,
29
47
  createCorsHandler,
48
+ createServerContext,
49
+ createWebRequestSource,
30
50
  executeAction,
31
51
  executeRoute,
32
- extractTraceId,
33
52
  handleBatchRequest,
34
53
  incomingMessageToHandlerRequest,
35
- incomingMessageToWebRequest,
36
- sendError
37
- } from "./chunk-XALU5LCU.js";
54
+ sendError,
55
+ writeWebResponseToServerResponse
56
+ } from "./chunk-RWNYIU6A.js";
38
57
  import {
39
58
  findSuggestion
40
59
  } from "./chunk-NTDN54XO.js";
41
60
  import {
61
+ TRACE_HEADER,
62
+ extractTraceId,
42
63
  logRequest
43
- } from "./chunk-KD5G3YXI.js";
64
+ } from "./chunk-72DMJBXI.js";
44
65
  import {
45
66
  createPluginRunnerFromConfig
46
67
  } from "./chunk-VSLTKX5L.js";
@@ -49,24 +70,26 @@ import {
49
70
  } from "./chunk-TNI4MARH.js";
50
71
  import {
51
72
  createViteLoader
52
- } from "./chunk-7LVM22VG.js";
73
+ } from "./chunk-OQCRCXCP.js";
53
74
  import {
54
75
  matchRoute,
55
- scanAgents,
56
76
  scanServerActions,
57
77
  scanServerRoutes,
58
78
  scanWebSocketRoutes
59
- } from "./chunk-YGAXV5HR.js";
79
+ } from "./chunk-6J4JWHDJ.js";
80
+ import {
81
+ scanAgents
82
+ } from "./chunk-L4VFNFDL.js";
60
83
  import {
61
84
  CSP_REPORT_PATH,
62
85
  CsrfReadinessStore,
63
86
  applySecurityHeaders,
64
87
  handleCspReport,
65
88
  handleCsrfReadiness
66
- } from "./chunk-MTEWLGVY.js";
89
+ } from "./chunk-BPYMC7SU.js";
67
90
  import {
68
91
  validateCsrfRequest
69
- } from "./chunk-OLGPYOLO.js";
92
+ } from "./chunk-P6OS7652.js";
70
93
  import {
71
94
  extractAppResources,
72
95
  handleMcpJsonRpc,
@@ -74,17 +97,20 @@ import {
74
97
  } from "./chunk-YOIVCCZT.js";
75
98
  import {
76
99
  resolveProvider
77
- } from "./chunk-6E5WKMDS.js";
100
+ } from "./chunk-3PBAN2YE.js";
78
101
  import {
79
102
  generateNonce
80
- } from "./chunk-JA2ZAT5Q.js";
103
+ } from "./chunk-Z4PJMZLM.js";
81
104
  import {
82
105
  importUserModule
83
106
  } from "./chunk-52PDWL5S.js";
107
+ import {
108
+ subjectFromContext
109
+ } from "./chunk-YN4W7LCX.js";
84
110
 
85
111
  // src/vite-plugin/index.ts
86
- import { existsSync as existsSync9 } from "fs";
87
- import { resolve as resolve9, dirname as dirname4 } from "path";
112
+ import { existsSync as existsSync8 } from "fs";
113
+ import { resolve as resolve10, dirname as dirname4 } from "path";
88
114
  import { fileURLToPath } from "url";
89
115
 
90
116
  // src/vite-plugin/config-hook.ts
@@ -129,42 +155,56 @@ function runConfigHook(ctx) {
129
155
  },
130
156
  resolve: {
131
157
  alias: [
132
- // Order matters: most-specific first so `theokit/X` doesn't
133
- // get matched by the bare `theokit` alias.
134
- { find: "theokit/server", replacement: resolve(ctx.theoSrcDir, `server/index${ext}`) },
135
- { find: "theokit/client", replacement: resolve(ctx.theoSrcDir, `client/index${ext}`) },
158
+ // Order still matters — first match wins — but the entries below are
159
+ // EXACT (#377). A Vite alias whose `find` is a string matches by prefix,
160
+ // and every replacement here points at a FILE, so the old string form
161
+ // rewrote `theokit/client/core` into `…/client/index.ts/core` and the
162
+ // build died with ENOTDIR. It did that for every subpath nobody had
163
+ // listed, which made the failure open-ended rather than a typo.
164
+ { find: /^theokit\/server$/u, replacement: resolve(ctx.theoSrcDir, `server/index${ext}`) },
165
+ { find: /^theokit\/client$/u, replacement: resolve(ctx.theoSrcDir, `client/index${ext}`) },
136
166
  {
137
167
  // T2.1 (M5 lonely folders) moved the source from
138
168
  // `react-query/index.ts` into `client/react-query.ts` (sibling of
139
169
  // `client/index.ts`). The package.json export `./react-query`
140
170
  // still points at the build artifact `./dist/react-query/index.{js,d.ts}`,
141
171
  // but the dev-time source alias must follow the new location.
142
- find: "theokit/react-query",
172
+ find: /^theokit\/react-query$/u,
143
173
  replacement: resolve(ctx.theoSrcDir, `client/react-query${ext}`)
144
174
  },
145
175
  {
146
- find: "theokit/vite-plugin",
176
+ find: /^theokit\/vite-plugin$/u,
147
177
  replacement: resolve(ctx.theoSrcDir, `vite-plugin/index${ext}`)
148
178
  },
149
179
  {
150
- find: "theokit/adapters/web-shim",
180
+ find: /^theokit\/adapters\/web-shim$/u,
151
181
  replacement: resolve(ctx.theoSrcDir, `adapters/web-shim${ext}`)
152
182
  },
153
183
  {
154
- find: "theokit/adapters/ws-shim",
184
+ find: /^theokit\/adapters\/ws-shim$/u,
155
185
  replacement: resolve(ctx.theoSrcDir, `adapters/ws-shim${ext}`)
156
186
  },
157
187
  // T1.2 — devtools entry (DEV only; tree-shaken in build).
158
188
  // Source layout: src/devtools/dom/entry.tsx (has /dom/ segment)
159
189
  // Dist layout: dist/devtools/entry.js (tsup flattens /dom/)
160
190
  {
161
- find: "theokit/devtools/entry",
191
+ find: /^theokit\/devtools\/entry$/u,
162
192
  replacement: resolve(
163
193
  ctx.theoSrcDir,
164
194
  ext === ".ts" ? "devtools/dom/entry.tsx" : "devtools/entry.js"
165
195
  )
166
196
  },
167
- { find: "theokit", replacement: resolve(ctx.theoSrcDir, `index${ext}`) }
197
+ // Everything else under the package, by one rule rather than a list that
198
+ // has to grow with the exports map. The source layout mirrors the
199
+ // subpath, so `theokit/cache` is `<src>/cache` and Vite's own extension
200
+ // resolution finds the file. The two entries above that do NOT mirror it
201
+ // — react-query moved to a sibling, devtools' source carries a `/dom/`
202
+ // segment the dist flattens — are matched before this and keep their
203
+ // mapping.
204
+ { find: /^theokit\/(.+)$/u, replacement: resolve(ctx.theoSrcDir, "$1") },
205
+ // Exact, so a package merely NAMED like ours — `theokit-anything` — is
206
+ // left alone. The prefix form ate those too.
207
+ { find: /^theokit$/u, replacement: resolve(ctx.theoSrcDir, `index${ext}`) }
168
208
  ]
169
209
  }
170
210
  };
@@ -196,177 +236,13 @@ ${issueLines}
196
236
  };
197
237
 
198
238
  // src/config/schema.ts
199
- import { z as z8 } from "zod";
239
+ import { z } from "zod";
200
240
 
201
241
  // src/core/contracts/theo-ui-theme.ts
202
242
  var THEME_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
203
243
 
204
- // src/config/schemas/rate-limit.ts
205
- import { z } from "zod";
206
- var baseRateLimitSchema = z.object({
207
- windowMs: z.number().min(1),
208
- max: z.number().int().min(1)
209
- });
210
- var rateLimitSchema = z.union([
211
- baseRateLimitSchema,
212
- z.object({
213
- default: baseRateLimitSchema.optional(),
214
- routes: z.record(z.string(), baseRateLimitSchema).optional(),
215
- keyBy: z.union([
216
- z.enum(["ip", "session", "user"]),
217
- z.function({ input: z.tuple([z.unknown()]), output: z.string() })
218
- ]).optional(),
219
- cookieName: z.string().min(1).optional(),
220
- /**
221
- * How many reverse proxies sit in front of the app, for `keyBy: 'ip'`. `false` (default)
222
- * trusts none; `true` means one; a number names a longer chain.
223
- *
224
- * Required for correct limiting behind Caddy/nginx/a load balancer — without it every visitor
225
- * keys on the proxy address and shares a single bucket. Off by default because
226
- * `x-forwarded-for` is client-writable.
227
- */
228
- trustProxy: z.union([z.boolean(), z.number().int().min(0)]).optional()
229
- })
230
- ]);
231
-
232
- // src/config/schemas/upload.ts
233
- import { z as z2 } from "zod";
234
- var uploadSchema = z2.object({
235
- maxFileSize: z2.number().min(1).default(10 * 1024 * 1024),
236
- // 10MB
237
- maxFiles: z2.number().int().min(1).default(10),
238
- maxFieldSize: z2.number().min(1).default(1 * 1024 * 1024)
239
- // 1MB
240
- });
241
-
242
- // src/config/schemas/logging.ts
243
- import { z as z3 } from "zod";
244
- var loggingSchema = z3.object({
245
- level: z3.enum(["debug", "info", "warn", "error", "silent"]).default("info")
246
- });
247
-
248
- // src/config/schemas/cache.ts
249
- import { z as z4 } from "zod";
250
- var routeRuleSchema = z4.object({
251
- maxAge: z4.number().nonnegative().optional(),
252
- swr: z4.number().nonnegative().optional(),
253
- tags: z4.array(z4.string()).optional()
254
- });
255
- var cacheSchema = z4.object({
256
- enabled: z4.boolean().default(true),
257
- /** 'memory' uses InMemoryCacheAdapter; otherwise pass a custom adapter instance. */
258
- storage: z4.union([z4.literal("memory"), z4.custom()]).default("memory"),
259
- maxEntries: z4.number().int().positive().default(1e3),
260
- defaults: z4.object({
261
- maxAge: z4.number().nonnegative().default(1),
262
- swr: z4.number().nonnegative().optional(),
263
- cacheErrors: z4.boolean().default(false)
264
- }).default({ maxAge: 1, cacheErrors: false }),
265
- keyDerivation: z4.object({
266
- excludeQuery: z4.array(z4.string()).optional(),
267
- sortQuery: z4.boolean().default(true),
268
- lowercaseHost: z4.boolean().default(true)
269
- }).default({ sortQuery: true, lowercaseHost: true }),
270
- routeRules: z4.record(z4.string(), routeRuleSchema).optional()
271
- });
272
-
273
- // src/config/schemas/security.ts
274
- import { z as z6 } from "zod";
275
-
276
- // src/config/schemas/header-safe.ts
277
- import { z as z5 } from "zod";
278
- var headerSafeString = z5.string().refine((s) => !/[\r\n]/.test(s), { message: "Header value must not contain CR/LF" });
279
-
280
- // src/config/schemas/security.ts
281
- var securityHeadersSchema = z6.object({
282
- csp: z6.union([headerSafeString, z6.literal(false)]).optional(),
283
- // T6.1 — default flipped from 'report-only' to 'enforce' for 0.3.0.
284
- // Users who want the old behaviour set `cspMode: 'report-only'`
285
- // explicitly.
286
- cspMode: z6.enum(["enforce", "report-only", "off"]).default("enforce"),
287
- hsts: z6.union([headerSafeString, z6.literal(false)]).optional(),
288
- frameOptions: z6.enum(["DENY", "SAMEORIGIN"]).default("DENY"),
289
- contentTypeOptions: z6.literal("nosniff").default("nosniff"),
290
- referrerPolicy: headerSafeString.default("strict-origin-when-cross-origin"),
291
- /**
292
- * T1.1 — Permissions-Policy directive string. EC-3-refined: rejects CR/LF.
293
- * Pass `false` to suppress the header.
294
- */
295
- permissionsPolicy: z6.union([headerSafeString, z6.literal(false)]).optional()
296
- });
297
- var disallowedConfigSchema = z6.object({
298
- routes: z6.array(z6.union([z6.string(), z6.instanceof(RegExp)])),
299
- behavior: z6.enum(["warn", "raise"]).default("raise")
300
- });
301
- var corsSchema = z6.object({
302
- origins: z6.union([
303
- z6.literal("*"),
304
- headerSafeString,
305
- z6.instanceof(RegExp),
306
- z6.array(z6.union([headerSafeString, z6.instanceof(RegExp)])),
307
- z6.function({ input: z6.tuple([z6.string()]), output: z6.boolean() })
308
- ]),
309
- methods: z6.array(z6.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"])).optional(),
310
- allowedHeaders: z6.array(headerSafeString).optional(),
311
- exposedHeaders: z6.array(headerSafeString).optional(),
312
- credentials: z6.boolean().default(false),
313
- maxAge: z6.number().int().min(0).max(86400).default(600)
314
- }).refine((c) => !(c.origins === "*" && c.credentials), {
315
- message: 'CORS spec forbids origins:"*" with credentials:true \u2014 browsers ignore the wildcard'
316
- });
317
- var securitySchema = z6.object({
318
- // T6.1 — default flipped from 'warn' to 'strict' for 0.3.0. Apps that
319
- // grep their warn-mode logs from 0.2.x already know which endpoints
320
- // break; opt back into 'warn' globally OR use disallowedRoutes for
321
- // surgical migration.
322
- csrf: z6.enum(["off", "warn", "strict"]).default("strict"),
323
- headers: securityHeadersSchema.optional(),
324
- /** T5.1 — per-route escalation (Rails disallowed_warnings pattern). */
325
- disallowed: disallowedConfigSchema.optional(),
326
- /** T1.2 — Cross-Origin Resource Sharing. Single global config; runs first in pipeline. */
327
- cors: corsSchema.optional()
328
- });
329
-
330
- // src/config/schemas/storage.ts
331
- import { z as z7 } from "zod";
332
- var tlsConfigSchema = z7.object({
333
- rejectUnauthorized: z7.boolean().optional(),
334
- caCert: z7.string().optional(),
335
- clientCert: z7.string().optional(),
336
- clientKey: z7.string().optional()
337
- });
338
- var serverConfigSchema = z7.object({
339
- host: z7.string().min(1),
340
- port: z7.number().int().positive().max(65535).optional(),
341
- user: z7.string().min(1),
342
- /** Password may be the empty string (some providers permit it). */
343
- password: z7.string(),
344
- tls: tlsConfigSchema.optional()
345
- });
346
- var postgresPoolConfigSchema = z7.object({
347
- min: z7.number().int().nonnegative().optional(),
348
- max: z7.number().int().positive().optional(),
349
- connectionTimeoutMillis: z7.number().int().positive().optional(),
350
- idleTimeoutMillis: z7.number().int().nonnegative().optional()
351
- });
352
- var postgresDatabaseConfigSchema = z7.object({
353
- /** References a key in `storage.servers`. Resolved at `usePostgres()` call time. */
354
- server: z7.string().min(1),
355
- database: z7.string().min(1),
356
- pool: postgresPoolConfigSchema.optional()
357
- });
358
- var redisServerConfigSchema = serverConfigSchema.extend({
359
- db: z7.number().int().nonnegative().optional(),
360
- maxRetriesPerRequest: z7.number().int().nonnegative().optional()
361
- });
362
- var storageSchema = z7.object({
363
- servers: z7.record(z7.string(), serverConfigSchema).optional(),
364
- databases: z7.record(z7.string(), postgresDatabaseConfigSchema).optional(),
365
- redis: z7.record(z7.string(), redisServerConfigSchema).optional()
366
- });
367
-
368
244
  // src/config/schema.ts
369
- var theoConfigSchema = z8.object({
245
+ var theoConfigSchema = z.object({
370
246
  /**
371
247
  * Plan v1.2 T2.1 — project identifier propagated into
372
248
  * `.theokit/services.json` v2 `project` field. DNS-1123 compatible (drives
@@ -374,27 +250,27 @@ var theoConfigSchema = z8.object({
374
250
  * emits services.json v1 with the legacy `services-bundle` fallback
375
251
  * (ADR D10) and logs a deprecation warning.
376
252
  */
377
- name: z8.string().max(63).refine(
253
+ name: z.string().max(63).refine(
378
254
  // DNS-1123 equivalent expressed as explicit single-char anchors so
379
255
  // security/detect-unsafe-regex stays clean (no backtracking).
380
256
  (value) => value.length > 0 && /^[a-z0-9-]+$/u.test(value) && !value.startsWith("-") && !value.endsWith("-"),
381
257
  "name must match DNS-1123 (lowercase alphanumeric+hyphens, 1-63 chars, no leading/trailing hyphen)"
382
258
  ).optional(),
383
- appDir: z8.string().default("app"),
384
- serverDir: z8.string().default("server"),
259
+ appDir: z.string().default("app"),
260
+ serverDir: z.string().default("server"),
385
261
  /**
386
262
  * Directory (relative to the project root) holding `agents/<name>.ts` definitions, scanned by
387
263
  * dev/build/terminal/mcp. Defaults to `"agents"` — apps that keep the canonical layout are
388
264
  * unaffected. Set to e.g. `"core/agents"` to organize agents under a domain root (#95 follow-up).
389
265
  */
390
- agentsDir: z8.string().default("agents"),
266
+ agentsDir: z.string().default("agents"),
391
267
  /**
392
268
  * T2.2 / EC-4 — Build output directory. Must be a relative path inside
393
269
  * the project root. Refused absolute or parent-relative paths to prevent
394
270
  * `cleanOutDir` from wiping arbitrary locations (defense-in-depth on
395
271
  * top of cleanOutDir's runtime EC-3 guard).
396
272
  */
397
- distDir: z8.string().default(".theokit").refine(
273
+ distDir: z.string().default(".theokit").refine(
398
274
  (d) => !/^([A-Za-z]:)?[/\\]/.test(d) && !d.startsWith(".."),
399
275
  'distDir must be a relative path inside the project root (e.g., ".theokit")'
400
276
  ),
@@ -413,73 +289,79 @@ var theoConfigSchema = z8.object({
413
289
  * * `idleTimeoutMs` — auto-evict idle agents (default 30 min). 0 disables
414
290
  * idle eviction (LRU only).
415
291
  */
416
- agents: z8.object({
417
- maxRegistries: z8.number().int().positive().default(100),
418
- registry: z8.object({
292
+ agents: z.object({
293
+ maxRegistries: z.number().int().positive().default(100),
294
+ registry: z.object({
419
295
  /** LRU cap — MUST be ≥ max-concurrent-active-conversations. */
420
- maxAgents: z8.number().int().positive().max(1e4).default(100),
296
+ maxAgents: z.number().int().positive().max(1e4).default(100),
421
297
  /** Idle eviction window in ms. 0 disables idle eviction (LRU only). */
422
- idleTimeoutMs: z8.number().int().nonnegative().default(30 * 6e4)
298
+ idleTimeoutMs: z.number().int().nonnegative().default(30 * 6e4)
423
299
  }).optional()
424
300
  }).optional(),
425
- port: z8.number().int().min(1).max(65535).default(3e3),
301
+ port: z.number().int().min(1).max(65535).default(3e3),
426
302
  /** Listen on all addresses (0.0.0.0) for LAN/mobile testing. */
427
- host: z8.union([z8.string(), z8.boolean()]).default("localhost"),
303
+ host: z.union([z.string(), z.boolean()]).default("localhost"),
428
304
  /** Automatically open browser on `theokit dev`. */
429
- open: z8.union([z8.boolean(), z8.string()]).default(false),
305
+ open: z.union([z.boolean(), z.string()]).default(false),
430
306
  /** Exit if port is already in use instead of trying next available. */
431
- strictPort: z8.boolean().default(false),
307
+ strictPort: z.boolean().default(false),
432
308
  /** Forward browser console/errors to terminal — useful for AI agent dev. */
433
- forwardConsole: z8.union([
434
- z8.boolean(),
435
- z8.object({
436
- unhandledErrors: z8.boolean().optional(),
437
- logLevels: z8.array(z8.enum(["error", "warn", "info", "log", "debug"])).optional()
309
+ forwardConsole: z.union([
310
+ z.boolean(),
311
+ z.object({
312
+ unhandledErrors: z.boolean().optional(),
313
+ logLevels: z.array(z.enum(["error", "warn", "info", "log", "debug"])).optional()
438
314
  })
439
315
  ]).default(false),
440
316
  /** Pre-transform files on startup for faster first page load. */
441
- warmup: z8.object({
442
- clientFiles: z8.array(z8.string()).optional(),
443
- ssrFiles: z8.array(z8.string()).optional()
317
+ warmup: z.object({
318
+ clientFiles: z.array(z.string()).optional(),
319
+ ssrFiles: z.array(z.string()).optional()
444
320
  }).optional(),
445
- ssr: z8.boolean().default(false),
321
+ ssr: z.boolean().default(false),
446
322
  /** When true (and ssr === true), use renderToPipeableStream with progressive
447
323
  * shell flush instead of single-shot renderToString. Opt-in for streaming
448
324
  * SSR; default false preserves the current behavior. */
449
- ssrStreaming: z8.boolean().default(false),
325
+ ssrStreaming: z.boolean().default(false),
450
326
  rateLimit: rateLimitSchema.optional(),
451
327
  upload: uploadSchema.optional(),
452
328
  logging: loggingSchema.optional(),
453
329
  security: securitySchema.optional(),
454
- serialization: z8.enum(["json", "superjson"]).default("json"),
330
+ serialization: z.enum(["json", "superjson"]).default("json"),
455
331
  // Plugins are validated structurally at runtime by createPluginRunnerFromConfig.
456
332
  // Zod only checks the shape minimally (must be array). Type-level safety is
457
333
  // provided through defineConfig at the user surface.
458
- plugins: z8.array(z8.unknown()).optional(),
334
+ //
335
+ // #425 — an entry MAY also be a module SPECIFIER (a string), resolved to that module's default
336
+ // export by `config/resolve-plugin-specifiers.ts`. A constructed plugin closes over state and
337
+ // therefore has no literal, so it is the string form — and only the string form — that a
338
+ // generated deploy entry can carry. Both shapes are accepted here for the same reason they are
339
+ // accepted at runtime: one declaration has to serve the local server and the deployed one.
340
+ plugins: z.array(z.unknown()).optional(),
459
341
  /** Enable client-side batching of theoFetch calls and the
460
342
  * /api/__theo_batch__ server endpoint. */
461
- batching: z8.union([z8.boolean(), z8.object({ max: z8.number().int().positive().optional() })]).optional(),
343
+ batching: z.union([z.boolean(), z.object({ max: z.number().int().positive().optional() })]).optional(),
462
344
  /** T4.1 — Audit log. When `logger` is provided, framework events
463
345
  * (csrf.warn, rate-limit.exceeded, session.rotated, csp.violation) are
464
346
  * emitted to it. Default: noop. */
465
- audit: z8.object({
466
- logger: z8.unknown().optional()
347
+ audit: z.object({
348
+ logger: z.unknown().optional()
467
349
  }).optional(),
468
350
  /** TheoUI auto-wire (T2.1). `false` = opt-out; object = explicit theme/fonts;
469
351
  * undefined = enabled when @theokit/ui is detected in node_modules. */
470
- ui: z8.union([
471
- z8.literal(false),
472
- z8.object({
352
+ ui: z.union([
353
+ z.literal(false),
354
+ z.object({
473
355
  // Any theme @theokit/ui knows — built-in or from `defineTheme()`. This was a closed
474
356
  // enum of `violet-forge | noir | paper`, two of which never existed in @theokit/ui, so
475
357
  // the only accepted value was the default and every real theme was rejected. The pattern
476
358
  // mirrors what @theokit/ui's ThemeProvider enforces, and it is load-bearing: the value is
477
359
  // interpolated into the generated entry and into a CSS selector.
478
- theme: z8.string().regex(
360
+ theme: z.string().regex(
479
361
  THEME_NAME_PATTERN,
480
362
  "must start with a letter and contain only lowercase letters, digits and hyphens"
481
363
  ).optional(),
482
- fonts: z8.enum(["bundled", "cdn"]).optional()
364
+ fonts: z.enum(["bundled", "cdn"]).optional()
483
365
  })
484
366
  ]).optional(),
485
367
  /**
@@ -488,6 +370,12 @@ var theoConfigSchema = z8.object({
488
370
  * Pass `cache: {}` to opt in with defaults.
489
371
  */
490
372
  cache: cacheSchema.optional(),
373
+ /**
374
+ * Observability (#353). Default `undefined` keeps telemetry off. Present and
375
+ * not `enabled: false` opts in; `provider` overrides the environment-derived
376
+ * adapter, which is the priority `adapter-registry.ts` has always documented.
377
+ */
378
+ observability: observabilitySchema.optional(),
491
379
  /**
492
380
  * Devtools overlay (Phase 0.4.0+ —
493
381
  *
@@ -498,11 +386,11 @@ var theoConfigSchema = z8.object({
498
386
  * Tree-shaken to noop in prod via the dual-export pattern in
499
387
  * `packages/theo/src/devtools/index.ts` (EC-17 positive prod check).
500
388
  */
501
- devtools: z8.union([
502
- z8.literal(false),
503
- z8.object({
504
- position: z8.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional(),
505
- theme: z8.enum(["light", "dark", "system"]).optional()
389
+ devtools: z.union([
390
+ z.literal(false),
391
+ z.object({
392
+ position: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional(),
393
+ theme: z.enum(["light", "dark", "system"]).optional()
506
394
  })
507
395
  ]).optional(),
508
396
  /**
@@ -511,7 +399,7 @@ var theoConfigSchema = z8.object({
511
399
  * (EC-201 / ADR D2). If `config.adapters` includes a target NOT
512
400
  * matching `--target`, `theokit build` emits a cross-reference note.
513
401
  */
514
- adapters: z8.array(z8.string()).optional(),
402
+ adapters: z.array(z.string()).optional(),
515
403
  /**
516
404
  * Extra package names to pre-bundle via Vite's `optimizeDeps.include`.
517
405
  *
@@ -526,14 +414,14 @@ var theoConfigSchema = z8.object({
526
414
  * Example:
527
415
  * viteOptimizeDeps: ['mermaid']
528
416
  */
529
- viteOptimizeDeps: z8.array(z8.string()).optional(),
417
+ viteOptimizeDeps: z.array(z.string()).optional(),
530
418
  /**
531
419
  * Jobs backend (ADR D3 + T2.1). When configured, every request gets
532
420
  * `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a
533
421
  * `JobBackend` instance (InMemoryJobBackend, PostgresJobBackend, etc.).
534
422
  */
535
- jobs: z8.object({
536
- backend: z8.custom((v) => typeof v === "object" && v !== null)
423
+ jobs: z.object({
424
+ backend: z.custom((v) => typeof v === "object" && v !== null)
537
425
  }).optional(),
538
426
  /**
539
427
  * StorageManager configuration (T1.1 / ADR-0007).
@@ -568,17 +456,17 @@ var theoConfigSchema = z8.object({
568
456
  * The env var `THEOKIT_OPENAPI_SERVERS` (CSV of URLs) overrides
569
457
  * `servers[].url` at emit time without rebuilding the config.
570
458
  */
571
- openapi: z8.object({
572
- servers: z8.array(
573
- z8.object({
574
- url: z8.url(),
575
- description: z8.string().optional()
459
+ openapi: z.object({
460
+ servers: z.array(
461
+ z.object({
462
+ url: z.url(),
463
+ description: z.string().optional()
576
464
  })
577
465
  ).default([{ url: "http://localhost:3000", description: "Local development" }]),
578
- specVersion: z8.enum(["3.1.0", "3.0.3"]).default("3.1.0"),
579
- title: z8.string().default("TheoKit App"),
580
- version: z8.string().default("0.0.0"),
581
- outDir: z8.string().default(".theokit")
466
+ specVersion: z.enum(["3.1.0", "3.0.3"]).default("3.1.0"),
467
+ title: z.string().default("TheoKit App"),
468
+ version: z.string().default("0.0.0"),
469
+ outDir: z.string().default(".theokit")
582
470
  }).optional(),
583
471
  /**
584
472
  * G5 T1.3 — error envelope transformer hook (blueprint ADR D3).
@@ -595,7 +483,7 @@ var theoConfigSchema = z8.object({
595
483
  * with a function-typed runtime guard preserves the call-site type
596
484
  * without trading off Zod runtime validation.
597
485
  */
598
- formatError: z8.custom((val) => typeof val === "function", {
486
+ formatError: z.custom((val) => typeof val === "function", {
599
487
  message: "formatError must be a function"
600
488
  }).optional()
601
489
  }).refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {
@@ -674,6 +562,46 @@ async function loadConfig(dir) {
674
562
  return result.data;
675
563
  }
676
564
 
565
+ // src/config/resolve-plugin-specifiers.ts
566
+ import { isAbsolute, resolve as resolve3 } from "path";
567
+ import { pathToFileURL } from "url";
568
+ var UnresolvablePluginSpecifierError = class extends Error {
569
+ constructor(index, specifier, reason) {
570
+ super(`plugins[${index}] (${specifier}) could not be loaded: ${reason}`);
571
+ this.name = "UnresolvablePluginSpecifierError";
572
+ }
573
+ };
574
+ async function resolvePluginSpecifiers(plugins, cwd) {
575
+ const resolved = [];
576
+ for (const [index, entry] of plugins.entries()) {
577
+ resolved.push(typeof entry === "string" ? await importPlugin(entry, index, cwd) : entry);
578
+ }
579
+ return resolved;
580
+ }
581
+ async function importPlugin(specifier, index, cwd) {
582
+ const target = isAbsolute(specifier) ? specifier : resolve3(cwd, specifier);
583
+ let mod;
584
+ try {
585
+ mod = await import(
586
+ /* @vite-ignore */
587
+ pathToFileURL(target).href
588
+ );
589
+ } catch (err) {
590
+ throw new UnresolvablePluginSpecifierError(index, specifier, messageOf(err));
591
+ }
592
+ if (mod.default === void 0) {
593
+ throw new UnresolvablePluginSpecifierError(
594
+ index,
595
+ specifier,
596
+ "the module has no default export; a plugin module must `export default` its plugin"
597
+ );
598
+ }
599
+ return mod.default;
600
+ }
601
+ function messageOf(err) {
602
+ return err instanceof Error ? err.message : String(err);
603
+ }
604
+
677
605
  // src/vite-plugin/theoui-detect.ts
678
606
  import { existsSync as existsSync3, readFileSync } from "fs";
679
607
  import { dirname, join } from "path";
@@ -773,7 +701,14 @@ var DEFAULT_RESOLVED = {
773
701
  async function resolvePluginConfig(projectRoot) {
774
702
  try {
775
703
  const userConfig = await loadConfig(projectRoot);
776
- const pluginRunner = await createPluginRunnerFromConfig(userConfig.plugins);
704
+ const observabilityPlugin = createObservabilityPluginFromConfig(
705
+ userConfig.observability,
706
+ process.env
707
+ );
708
+ const declaredPlugins = await resolvePluginSpecifiers(userConfig.plugins ?? [], projectRoot);
709
+ const pluginRunner = await createPluginRunnerFromConfig(
710
+ observabilityPlugin === void 0 ? declaredPlugins : [observabilityPlugin, ...declaredPlugins]
711
+ );
777
712
  const transformer = resolveTransformer(userConfig.serialization);
778
713
  let resolvedBatching;
779
714
  if (userConfig.batching === true) {
@@ -807,7 +742,7 @@ async function resolvePluginConfig(projectRoot) {
807
742
  }
808
743
 
809
744
  // src/vite-plugin/configure-server-hook.ts
810
- import { resolve as resolve6, basename } from "path";
745
+ import { resolve as resolve8, basename } from "path";
811
746
 
812
747
  // src/devtools/server-side/route-manifest.ts
813
748
  function buildPath(parents, segment) {
@@ -845,7 +780,7 @@ function broadcastRouteManifest(tree) {
845
780
 
846
781
  // src/router/scan.ts
847
782
  import { readdirSync, statSync, existsSync as existsSync4 } from "fs";
848
- import { join as join2, resolve as resolve3 } from "path";
783
+ import { join as join2, resolve as resolve4 } from "path";
849
784
 
850
785
  // src/router/types.ts
851
786
  var ROUTE_FILE_NAMES = ["page", "layout", "error", "loading", "not-found"];
@@ -886,7 +821,7 @@ function attachRouteFiles(node, dir) {
886
821
  for (const ext of ROUTE_FILE_EXTENSIONS) {
887
822
  const filename = `${name}${ext}`;
888
823
  if (existsSync4(join2(dir, filename))) {
889
- setRouteFile(node, key, resolve3(dir, filename));
824
+ setRouteFile(node, key, resolve4(dir, filename));
890
825
  break;
891
826
  }
892
827
  }
@@ -935,7 +870,13 @@ function scanChild(parentDir, dirName, routePath) {
935
870
  function scanDir(dir, segment, routePath) {
936
871
  const node = { segment, path: routePath, children: [] };
937
872
  attachRouteFiles(node, dir);
938
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
873
+ const entries = readdirSync(dir, { withFileTypes: true });
874
+ entries.sort((a, b) => {
875
+ if (a.name < b.name) return -1;
876
+ if (a.name > b.name) return 1;
877
+ return 0;
878
+ });
879
+ for (const entry of entries) {
939
880
  if (!entry.isDirectory()) continue;
940
881
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
941
882
  const child = scanChild(dir, entry.name, routePath);
@@ -1089,6 +1030,11 @@ function handleAgentCard(mod, name, route, baseUrl) {
1089
1030
 
1090
1031
  // src/server/agent/approve-agent.ts
1091
1032
  var APPROVE_SEGMENT = "/approve/";
1033
+ var APPROVE_PATH = /^\/api\/agents\/([^/]+)\/approve\/([^/]+)$/;
1034
+ function parseApprovalAgentName(urlPath) {
1035
+ const match = APPROVE_PATH.exec(urlPath);
1036
+ return match ? decodeURIComponent(match[1]) : null;
1037
+ }
1092
1038
  function parseApprovalId(urlPath) {
1093
1039
  const at = urlPath.indexOf(APPROVE_SEGMENT);
1094
1040
  if (at === -1) return null;
@@ -1121,7 +1067,17 @@ function parseApprovalBody(body) {
1121
1067
  }
1122
1068
  return decision;
1123
1069
  }
1124
- async function handleAgentApproval(request, urlPath, registry, csrfMode = "strict") {
1070
+ async function refuseIfNotOwner(registry, approvalId, resolveSubject, params) {
1071
+ const owner = registry.ownerOf(approvalId);
1072
+ if (owner === void 0) return void 0;
1073
+ const subject = resolveSubject === void 0 ? null : await resolveSubject();
1074
+ if (subject?.id === owner) return void 0;
1075
+ return agentAccessDenied(
1076
+ { allowed: false, reason: "the approval belongs to another subject" },
1077
+ params
1078
+ );
1079
+ }
1080
+ async function handleAgentApproval(request, urlPath, registry, csrfMode = "strict", access = {}) {
1125
1081
  if (csrfMode !== "off") {
1126
1082
  const csrf = validateCsrfRequest(request);
1127
1083
  if (!csrf.valid && csrfMode === "strict") {
@@ -1132,6 +1088,15 @@ async function handleAgentApproval(request, urlPath, registry, csrfMode = "stric
1132
1088
  if (approvalId === null) {
1133
1089
  return jsonError(400, "BAD_REQUEST", "Approval path must be /api/agents/<name>/approve/<id>.");
1134
1090
  }
1091
+ const params = {
1092
+ agent: parseApprovalAgentName(urlPath) ?? "unknown",
1093
+ endpoint: "approve",
1094
+ approvalId
1095
+ };
1096
+ const decision = await admitAgentRequest(access.policy, access.resolveSubject, params);
1097
+ if (!decision.allowed) return agentAccessDenied(decision, params);
1098
+ const notOwner = await refuseIfNotOwner(registry, approvalId, access.resolveSubject, params);
1099
+ if (notOwner !== void 0) return notOwner;
1135
1100
  let body = null;
1136
1101
  try {
1137
1102
  body = await request.json();
@@ -1145,25 +1110,13 @@ async function handleAgentApproval(request, urlPath, registry, csrfMode = "stric
1145
1110
  if (!resolved) {
1146
1111
  return jsonError(404, "NOT_PENDING", `No pending approval for id '${approvalId}'.`);
1147
1112
  }
1113
+ closePauseSpan(approvalId, { resumeObserved: true });
1148
1114
  return new Response(JSON.stringify({ resolved: true }), {
1149
1115
  status: 200,
1150
1116
  headers: { "content-type": "application/json" }
1151
1117
  });
1152
1118
  }
1153
1119
 
1154
- // src/server/agent/list-approvals-handler.ts
1155
- var LIST_PATH = /^\/api\/agents\/([^/]+)\/approvals$/;
1156
- function isListApprovalsPath(urlPath) {
1157
- const match = LIST_PATH.exec(urlPath);
1158
- return match ? decodeURIComponent(match[1]) : null;
1159
- }
1160
- function handleListApprovals(registry) {
1161
- return new Response(JSON.stringify({ approvals: registry.list() }), {
1162
- status: 200,
1163
- headers: { "content-type": "application/json; charset=utf-8" }
1164
- });
1165
- }
1166
-
1167
1120
  // src/server/agent/handle-agent-run-reconnect.ts
1168
1121
  var RUN_STREAM_PATH = /^\/api\/agents\/([^/]+)\/runs\/([^/]+)\/stream$/;
1169
1122
  function isAgentRunStreamPath(urlPath) {
@@ -1328,7 +1281,7 @@ function jsonError2(status, code, message) {
1328
1281
  });
1329
1282
  }
1330
1283
  async function handleThreadMessage(args) {
1331
- const { mod, apiKey, sessionId, request, source, csrfMode = "strict" } = args;
1284
+ const { mod, apiKey, sessionId, request, source, agentName, csrfMode = "strict" } = args;
1332
1285
  if (csrfMode === "strict") {
1333
1286
  const csrf = validateCsrfRequest(request);
1334
1287
  if (!csrf.valid) return jsonError2(403, "CSRF_FAILED", `CSRF check failed: ${csrf.reason}`);
@@ -1346,7 +1299,9 @@ async function handleThreadMessage(args) {
1346
1299
  {
1347
1300
  registry: getThreadRunRegistry(),
1348
1301
  cache: getRunEventCache(),
1349
- startRun: makeThreadStartRun(mod, apiKey, source)
1302
+ // usetheokit/theokit#381 — the request goes with it, so the run's spans join the trace of
1303
+ // the caller that queued it instead of opening one of their own.
1304
+ startRun: makeThreadStartRun(mod, apiKey, source, agentName, request)
1350
1305
  },
1351
1306
  sessionId,
1352
1307
  input.message
@@ -1424,6 +1379,19 @@ function waitThenAttachStream(sessionId, request, registry, cache, idleWaitMs) {
1424
1379
  return new Response(stream, { headers: { ...SSE_BASE_HEADERS } });
1425
1380
  }
1426
1381
 
1382
+ // src/server/agent/list-approvals-handler.ts
1383
+ var LIST_PATH = /^\/api\/agents\/([^/]+)\/approvals$/;
1384
+ function isListApprovalsPath(urlPath) {
1385
+ const match = LIST_PATH.exec(urlPath);
1386
+ return match ? decodeURIComponent(match[1]) : null;
1387
+ }
1388
+ function handleListApprovals(registry) {
1389
+ return new Response(JSON.stringify({ approvals: registry.list() }), {
1390
+ status: 200,
1391
+ headers: { "content-type": "application/json; charset=utf-8" }
1392
+ });
1393
+ }
1394
+
1427
1395
  // src/server/agent/serve-aux-routes.ts
1428
1396
  function jsonError3(status, code, message) {
1429
1397
  return new Response(JSON.stringify({ error: { code, message } }), {
@@ -1431,71 +1399,135 @@ function jsonError3(status, code, message) {
1431
1399
  headers: { "content-type": "application/json; charset=utf-8" }
1432
1400
  });
1433
1401
  }
1434
- async function serveAgentAuxRoute(request, urlPath, deps) {
1435
- const method = request.method.toUpperCase();
1436
- const cardName = isAgentCardPath(urlPath);
1437
- if (cardName !== null) {
1438
- if (method !== "GET") return null;
1439
- const agent = deps.agents.find((a) => a.name === cardName);
1440
- if (!agent) return null;
1441
- const mod = await deps.loadModule(agent.filePath);
1442
- return handleAgentCard(mod, agent.name, agent.agentPath, deps.baseUrl);
1443
- }
1444
- if (isListApprovalsPath(urlPath)) {
1445
- if (method !== "GET") return null;
1446
- return handleListApprovals(getApprovalRegistry());
1447
- }
1448
- const runStream = isAgentRunStreamPath(urlPath);
1449
- if (runStream !== null) {
1450
- if (method !== "GET") return null;
1451
- if (!deps.agents.some((a) => a.name === runStream.name)) return null;
1452
- return handleAgentRunReconnect(runStream.runId, request, getRunEventCache());
1453
- }
1454
- const threadResponse = await serveThreadRoute(request, method, urlPath, deps);
1455
- if (threadResponse !== null) return threadResponse;
1456
- const mcpName = isMcpPath(urlPath);
1457
- if (mcpName !== null) {
1458
- return serveMcpRoute(request, method, mcpName, deps);
1402
+ function routeFor(deps, name, build) {
1403
+ if (name === null) return null;
1404
+ const agent = deps.agents.find((a) => a.name === name);
1405
+ return agent === void 0 ? null : build(agent);
1406
+ }
1407
+ function matchGetAuxRoute(verb, urlPath, deps) {
1408
+ if (verb !== "GET") return null;
1409
+ const card = routeFor(
1410
+ deps,
1411
+ isAgentCardPath(urlPath),
1412
+ (agent) => ({ kind: "card", agent })
1413
+ );
1414
+ if (card !== null) return card;
1415
+ const approvals = routeFor(
1416
+ deps,
1417
+ isListApprovalsPath(urlPath),
1418
+ (agent) => ({ kind: "approvals", agent })
1419
+ );
1420
+ if (approvals !== null) return approvals;
1421
+ const run = isAgentRunStreamPath(urlPath);
1422
+ if (run !== null) {
1423
+ return routeFor(
1424
+ deps,
1425
+ run.name,
1426
+ (agent) => ({ kind: "run-stream", agent, runId: run.runId })
1427
+ );
1459
1428
  }
1460
- return null;
1461
- }
1462
- async function serveThreadRoute(request, method, urlPath, deps) {
1463
1429
  const stream = isThreadStreamPath(urlPath);
1464
- if (stream !== null) {
1465
- if (method !== "GET") return null;
1466
- if (!deps.agents.some((a) => a.name === stream.name)) return null;
1467
- return handleThreadStream(stream.sessionId, request);
1468
- }
1430
+ if (stream === null) return null;
1431
+ return routeFor(
1432
+ deps,
1433
+ stream.name,
1434
+ (agent) => ({ kind: "thread-stream", agent, sessionId: stream.sessionId })
1435
+ );
1436
+ }
1437
+ async function matchPostAuxRoute(verb, urlPath, deps) {
1438
+ if (verb !== "POST") return null;
1469
1439
  const msg = isThreadMessagePath(urlPath);
1470
1440
  if (msg !== null) {
1471
- if (method !== "POST") return null;
1472
- const agent = deps.agents.find((a) => a.name === msg.name);
1473
- if (!agent) return null;
1474
- if (deps.resolveApiKey === void 0) {
1475
- return jsonError3(
1476
- 501,
1477
- "NOT_CONFIGURED",
1478
- "Thread follow-up requires a provider API key (resolveApiKey was not provided to serveAgentAuxRoute)."
1479
- );
1480
- }
1481
- const mod = await deps.loadModule(agent.filePath);
1482
- return handleThreadMessage({
1483
- mod,
1484
- apiKey: deps.resolveApiKey(),
1485
- sessionId: msg.sessionId,
1486
- request,
1487
- source: `agent "${msg.name}"`,
1488
- csrfMode: deps.csrfMode ?? "strict"
1441
+ return routeFor(
1442
+ deps,
1443
+ msg.name,
1444
+ (agent2) => ({ kind: "thread-message", agent: agent2, sessionId: msg.sessionId })
1445
+ );
1446
+ }
1447
+ const agent = routeFor(deps, isMcpPath(urlPath), (found) => found);
1448
+ if (agent === null) return null;
1449
+ const mod = await deps.loadModule(agent.filePath);
1450
+ return isMcpExposed(mod) ? { kind: "mcp", agent, mod } : null;
1451
+ }
1452
+ async function matchAgentAuxRoute(method, urlPath, deps) {
1453
+ const verb = method.toUpperCase();
1454
+ return matchGetAuxRoute(verb, urlPath, deps) ?? await matchPostAuxRoute(verb, urlPath, deps);
1455
+ }
1456
+ async function admitAux(deps, agent, params, body) {
1457
+ const mod = await deps.loadModule(agent.filePath);
1458
+ const decision = await admitAgentRequest(
1459
+ readAgentPolicy(mod, agent.filePath),
1460
+ deps.resolveSubject,
1461
+ params,
1462
+ body
1463
+ );
1464
+ return decision.allowed ? null : agentAccessDenied(decision, params);
1465
+ }
1466
+ async function serveMatchedAuxRoute(route, request, deps) {
1467
+ if (route.kind === "card") {
1468
+ const mod = await deps.loadModule(route.agent.filePath);
1469
+ return handleAgentCard(mod, route.agent.name, route.agent.agentPath, deps.baseUrl);
1470
+ }
1471
+ if (route.kind === "approvals") {
1472
+ const refusal = await admitAux(deps, route.agent, {
1473
+ agent: route.agent.name,
1474
+ endpoint: "approvals"
1489
1475
  });
1476
+ return refusal ?? handleListApprovals(getApprovalRegistry());
1490
1477
  }
1491
- return null;
1478
+ if (route.kind === "run-stream") {
1479
+ return handleAgentRunReconnect(route.runId, request, getRunEventCache());
1480
+ }
1481
+ if (route.kind === "mcp") return serveMcpRoute(route, request, deps);
1482
+ return serveThreadRoute(route, request, deps);
1492
1483
  }
1493
- async function serveMcpRoute(request, method, mcpName, deps) {
1494
- if (method !== "POST") return null;
1495
- const agent = deps.agents.find((a) => a.name === mcpName);
1496
- if (!agent) return null;
1484
+ async function serveThreadRoute(route, request, deps) {
1485
+ const { agent, sessionId } = route;
1486
+ if (route.kind === "thread-stream") {
1487
+ const refusal2 = await admitAux(deps, agent, {
1488
+ agent: agent.name,
1489
+ endpoint: "thread-stream",
1490
+ sessionId
1491
+ });
1492
+ return refusal2 ?? handleThreadStream(sessionId, request);
1493
+ }
1494
+ const refusal = await admitAux(deps, agent, {
1495
+ agent: agent.name,
1496
+ endpoint: "thread-message",
1497
+ sessionId
1498
+ });
1499
+ if (refusal !== null) return refusal;
1500
+ if (deps.resolveApiKey === void 0) {
1501
+ return jsonError3(
1502
+ 501,
1503
+ "NOT_CONFIGURED",
1504
+ "Thread follow-up requires a provider API key (resolveApiKey was not provided to serveMatchedAuxRoute)."
1505
+ );
1506
+ }
1497
1507
  const mod = await deps.loadModule(agent.filePath);
1498
- if (!isMcpExposed(mod)) return null;
1508
+ return handleThreadMessage({
1509
+ mod,
1510
+ // Passed unresolved: `makeThreadStartRun` calls it once the module is compiled and the
1511
+ // model is known (theokit#328).
1512
+ apiKey: deps.resolveApiKey,
1513
+ sessionId,
1514
+ request,
1515
+ source: `agent "${agent.name}"`,
1516
+ // usetheokit/theokit#406 — the label above reads well in an `AgentDefinitionError` and is not
1517
+ // a name; the spans get the name, so the same agent is one series whichever route started it.
1518
+ agentName: agent.name,
1519
+ csrfMode: deps.csrfMode ?? "strict"
1520
+ });
1521
+ }
1522
+ async function serveMcpRoute(route, request, deps) {
1523
+ const { agent, mod } = route;
1524
+ const mcpParams = { agent: agent.name, endpoint: "mcp" };
1525
+ const mcpDecision = await admitAgentRequest(
1526
+ readAgentPolicy(mod, agent.filePath),
1527
+ deps.resolveSubject,
1528
+ mcpParams
1529
+ );
1530
+ if (!mcpDecision.allowed) return agentAccessDenied(mcpDecision, mcpParams);
1499
1531
  const csrfMode = deps.csrfMode ?? "strict";
1500
1532
  if (csrfMode === "strict") {
1501
1533
  const csrf = validateCsrfRequest(request);
@@ -1512,38 +1544,53 @@ function isMcpExposed(mod) {
1512
1544
  return mod?.mcp === true;
1513
1545
  }
1514
1546
 
1515
- // src/server/http/node-web-adapter.ts
1516
- async function writeWebResponseToServerResponse(response, res) {
1517
- const setCookies = response.headers.getSetCookie();
1518
- if (setCookies.length > 0) {
1519
- res.setHeader("Set-Cookie", setCookies);
1520
- }
1521
- const otherHeaders = {};
1522
- for (const [key, value] of response.headers.entries()) {
1523
- if (key.toLowerCase() === "set-cookie") continue;
1524
- otherHeaders[key] = value;
1525
- }
1526
- res.writeHead(response.status, response.statusText, otherHeaders);
1527
- if (response.body === null) {
1528
- res.end();
1547
+ // src/server/http/plugin-lifecycle.ts
1548
+ function messageOf2(err, fallback) {
1549
+ return err instanceof Error ? err.message : fallback;
1550
+ }
1551
+ async function serveThroughPluginLifecycle(target, serve) {
1552
+ const { res, requestId, pluginRunner, failureMessage } = target;
1553
+ let request;
1554
+ try {
1555
+ request = target.source.toRequest();
1556
+ } catch (err) {
1557
+ sendError(res, "INTERNAL", messageOf2(err, failureMessage), 500, void 0, requestId);
1529
1558
  return;
1530
1559
  }
1531
- const reader = response.body.getReader();
1560
+ const pluginCtx = { request, response: res, ctx: {}, requestId };
1561
+ if (pluginRunner) {
1562
+ pluginRunner.applyDecorations(pluginCtx.ctx);
1563
+ const onRequest = await pluginRunner.runOnRequest(pluginCtx);
1564
+ if (onRequest.shortCircuited) return;
1565
+ }
1532
1566
  try {
1533
- for (; ; ) {
1534
- const { done, value } = await reader.read();
1535
- if (done) break;
1536
- res.write(value);
1537
- }
1538
- res.end();
1539
- } finally {
1540
- reader.releaseLock();
1567
+ await serve(request, pluginCtx);
1568
+ } catch (err) {
1569
+ if (pluginRunner) await pluginRunner.runOnError(pluginCtx, err);
1570
+ sendError(res, "INTERNAL", messageOf2(err, failureMessage), 500, void 0, requestId);
1541
1571
  }
1572
+ if (pluginRunner) await pluginRunner.runOnResponse(pluginCtx);
1573
+ }
1574
+
1575
+ // src/server/http/resolve-agent-subject.ts
1576
+ function createAgentSubjectResolver(sources) {
1577
+ let pending;
1578
+ return () => {
1579
+ pending ??= resolve5(sources);
1580
+ return pending;
1581
+ };
1582
+ }
1583
+ async function resolve5(sources) {
1584
+ const { req, res, loadModule, serverDir, pluginRunner } = sources;
1585
+ const produced = serverDir === void 0 ? {} : await createServerContext(req, res, loadModule, serverDir);
1586
+ const ctx = produced ?? {};
1587
+ pluginRunner?.applyDecorations(ctx);
1588
+ return subjectFromContext(ctx);
1542
1589
  }
1543
1590
 
1544
1591
  // src/vite-plugin/agent-middleware.ts
1545
1592
  var PREFIX2 = "/api/agents/";
1546
- async function serveApprove(req, res, urlPath, csrfMode, log) {
1593
+ async function serveApprove(req, res, urlPath, deps, log) {
1547
1594
  const method = (req.method ?? "POST").toUpperCase();
1548
1595
  if (method !== "POST") {
1549
1596
  sendError(
@@ -1563,24 +1610,36 @@ async function serveApprove(req, res, urlPath, csrfMode, log) {
1563
1610
  });
1564
1611
  return;
1565
1612
  }
1566
- try {
1567
- const response = await handleAgentApproval(
1568
- incomingMessageToWebRequest(req),
1569
- urlPath,
1570
- getApprovalRegistry(),
1571
- csrfMode
1572
- );
1573
- await writeWebResponseToServerResponse(response, res);
1574
- } catch (err) {
1575
- sendError(
1613
+ await serveThroughPluginLifecycle(
1614
+ {
1615
+ source: createWebRequestSource(req),
1576
1616
  res,
1577
- "INTERNAL",
1578
- err instanceof Error ? err.message : "Approve handler failed",
1579
- 500,
1580
- void 0,
1581
- log.requestId
1582
- );
1583
- }
1617
+ requestId: log.requestId,
1618
+ pluginRunner: deps.pluginRunner,
1619
+ failureMessage: "Approve handler failed"
1620
+ },
1621
+ async (request) => {
1622
+ const agent = scanAgents(deps.projectRoot, deps.agentsDir).find(
1623
+ (a) => a.name === parseApprovalAgentName(urlPath)
1624
+ );
1625
+ const resolveSubject = createAgentSubjectResolver({
1626
+ req,
1627
+ res,
1628
+ loadModule: deps.loadModule,
1629
+ serverDir: deps.serverDir,
1630
+ pluginRunner: deps.pluginRunner
1631
+ });
1632
+ const policy = agent === void 0 ? void 0 : readAgentPolicy(await deps.loadModule(agent.filePath), agent.filePath);
1633
+ const response = await handleAgentApproval(
1634
+ request,
1635
+ urlPath,
1636
+ getApprovalRegistry(),
1637
+ deps.csrfMode,
1638
+ { policy, resolveSubject }
1639
+ );
1640
+ await writeWebResponseToServerResponse(response, res);
1641
+ }
1642
+ );
1584
1643
  logRequest({
1585
1644
  method,
1586
1645
  url: req.url ?? "",
@@ -1592,53 +1651,69 @@ async function serveApprove(req, res, urlPath, csrfMode, log) {
1592
1651
  async function serveAux(req, res, next, urlPath, deps) {
1593
1652
  const requestId = randomUUID2();
1594
1653
  const start = Date.now();
1595
- try {
1596
- const request = incomingMessageToWebRequest(req);
1597
- const response = await serveAgentAuxRoute(request, urlPath, {
1598
- agents: scanAgents(deps.projectRoot, deps.agentsDir),
1599
- loadModule: deps.loadModule,
1600
- baseUrl: `http://${req.headers.host ?? "localhost"}`,
1601
- csrfMode: deps.csrfMode,
1602
- // M39 — the thread follow-up route drives the agent; resolve the key on demand.
1603
- resolveApiKey: () => resolveProvider().apiKey
1604
- });
1605
- if (response === null) {
1606
- next();
1607
- return;
1608
- }
1609
- res.setHeader("x-request-id", requestId);
1610
- await writeWebResponseToServerResponse(response, res);
1611
- } catch (err) {
1612
- res.setHeader("x-request-id", requestId);
1613
- sendError(
1654
+ const auxDeps = {
1655
+ agents: scanAgents(deps.projectRoot, deps.agentsDir),
1656
+ loadModule: deps.loadModule,
1657
+ baseUrl: `http://${req.headers.host ?? "localhost"}`,
1658
+ csrfMode: deps.csrfMode,
1659
+ // M39 — the thread follow-up route drives the agent; resolve the key on demand.
1660
+ // theokit#328 — the thread route drives an agent, so its key follows the model too.
1661
+ resolveApiKey: (model) => resolveProvider(model).apiKey,
1662
+ // usetheokit/theokit#365 — parity with `theokit start`; lazy for the same reason.
1663
+ resolveSubject: createAgentSubjectResolver({
1664
+ req,
1614
1665
  res,
1615
- "INTERNAL",
1616
- err instanceof Error ? err.message : "Agent aux handler failed",
1617
- 500,
1618
- void 0,
1619
- requestId
1620
- );
1666
+ loadModule: deps.loadModule,
1667
+ serverDir: deps.serverDir,
1668
+ pluginRunner: deps.pluginRunner
1669
+ })
1670
+ };
1671
+ const method = (req.method ?? "GET").toUpperCase();
1672
+ const route = await matchAgentAuxRoute(method, urlPath, auxDeps);
1673
+ if (route === null) {
1674
+ next();
1675
+ return;
1621
1676
  }
1677
+ res.setHeader("x-request-id", requestId);
1678
+ await serveThroughPluginLifecycle(
1679
+ {
1680
+ source: createWebRequestSource(req),
1681
+ res,
1682
+ requestId,
1683
+ pluginRunner: deps.pluginRunner,
1684
+ failureMessage: "Agent aux handler failed"
1685
+ },
1686
+ async (request) => {
1687
+ await writeWebResponseToServerResponse(
1688
+ await serveMatchedAuxRoute(route, request, auxDeps),
1689
+ res
1690
+ );
1691
+ }
1692
+ );
1622
1693
  logRequest({
1623
- method: (req.method ?? "GET").toUpperCase(),
1694
+ method,
1624
1695
  url: req.url ?? "",
1625
1696
  status: res.statusCode,
1626
1697
  duration: Date.now() - start,
1627
1698
  requestId
1628
1699
  });
1629
1700
  }
1630
- function createAgentMiddleware(vite, projectRoot, csrfMode = "strict", agentsDir = "agents") {
1701
+ function createAgentMiddleware(vite, projectRoot, csrfMode = "strict", agentsDir = "agents", deps = {}) {
1702
+ const { pluginRunner, serverDir } = deps;
1631
1703
  const loadModule = createViteLoader(vite);
1704
+ const cardDeps = () => ({
1705
+ projectRoot,
1706
+ loadModule,
1707
+ agentsDir,
1708
+ csrfMode,
1709
+ serverDir,
1710
+ pluginRunner
1711
+ });
1632
1712
  return (req, res, next) => {
1633
1713
  void (async () => {
1634
1714
  const url = req.url ?? "";
1635
1715
  if (isAgentCardPath(url.split("?")[0]) !== null) {
1636
- await serveAux(req, res, next, url.split("?")[0], {
1637
- projectRoot,
1638
- loadModule,
1639
- agentsDir,
1640
- csrfMode
1641
- });
1716
+ await serveAux(req, res, next, url.split("?")[0], cardDeps());
1642
1717
  return;
1643
1718
  }
1644
1719
  if (!url.startsWith(PREFIX2)) {
@@ -1650,16 +1725,12 @@ function createAgentMiddleware(vite, projectRoot, csrfMode = "strict", agentsDir
1650
1725
  res.setHeader("x-request-id", requestId);
1651
1726
  const urlPath = url.split("?")[0];
1652
1727
  if (isApprovalPath(urlPath)) {
1653
- await serveApprove(req, res, urlPath, csrfMode, { requestId, start });
1654
- return;
1655
- }
1656
- if (isListApprovalsPath(urlPath) !== null || isMcpPath(urlPath) !== null) {
1657
- await serveAux(req, res, next, urlPath, { projectRoot, loadModule, agentsDir, csrfMode });
1728
+ await serveApprove(req, res, urlPath, cardDeps(), { requestId, start });
1658
1729
  return;
1659
1730
  }
1660
1731
  const agent = scanAgents(projectRoot, agentsDir).find((a) => a.agentPath === urlPath);
1661
1732
  if (!agent) {
1662
- next();
1733
+ await serveAux(req, res, next, urlPath, cardDeps());
1663
1734
  return;
1664
1735
  }
1665
1736
  const method = (req.method ?? "POST").toUpperCase();
@@ -1675,33 +1746,73 @@ function createAgentMiddleware(vite, projectRoot, csrfMode = "strict", agentsDir
1675
1746
  logRequest({ method, url, status: 405, duration: Date.now() - start, requestId });
1676
1747
  return;
1677
1748
  }
1678
- try {
1679
- const mod = await loadModule(agent.filePath);
1680
- const apiKey = resolveProvider().apiKey;
1681
- const request = incomingMessageToWebRequest(req);
1682
- const response = await mountAgent(mod, request, apiKey, {
1683
- source: agent.filePath,
1684
- csrfMode,
1685
- projectRoot
1686
- });
1687
- await writeWebResponseToServerResponse(response, res);
1688
- } catch (err) {
1689
- sendError(
1690
- res,
1691
- "INTERNAL",
1692
- err instanceof Error ? err.message : "Agent handler failed",
1693
- 500,
1694
- void 0,
1695
- requestId
1696
- );
1697
- }
1698
- logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId });
1749
+ await serveAgentTurn({
1750
+ req,
1751
+ res,
1752
+ agent,
1753
+ method,
1754
+ url,
1755
+ requestId,
1756
+ start,
1757
+ loadModule,
1758
+ csrfMode,
1759
+ projectRoot,
1760
+ pluginRunner,
1761
+ serverDir
1762
+ });
1699
1763
  })();
1700
1764
  };
1701
1765
  }
1766
+ async function serveAgentTurn(t) {
1767
+ const {
1768
+ req,
1769
+ res,
1770
+ agent,
1771
+ method,
1772
+ url,
1773
+ requestId,
1774
+ start,
1775
+ loadModule,
1776
+ csrfMode,
1777
+ projectRoot,
1778
+ pluginRunner,
1779
+ serverDir
1780
+ } = t;
1781
+ const resolveSubject = createAgentSubjectResolver({
1782
+ req,
1783
+ res,
1784
+ loadModule,
1785
+ serverDir,
1786
+ pluginRunner
1787
+ });
1788
+ await serveThroughPluginLifecycle(
1789
+ {
1790
+ source: createWebRequestSource(req),
1791
+ res,
1792
+ requestId,
1793
+ pluginRunner,
1794
+ failureMessage: "Agent handler failed"
1795
+ },
1796
+ async (request) => {
1797
+ const mod = await loadModule(agent.filePath);
1798
+ const apiKey = (model) => resolveProvider(model).apiKey;
1799
+ const response = await mountAgent(mod, request, apiKey, {
1800
+ source: agent.filePath,
1801
+ csrfMode,
1802
+ projectRoot,
1803
+ // usetheokit/theokit#365 — the policy comes off `mod`; the caller owes the identity.
1804
+ // usetheokit/theokit#406 — and the same name labels the run's spans.
1805
+ agentName: agent.name,
1806
+ resolveSubject
1807
+ });
1808
+ await writeWebResponseToServerResponse(response, res);
1809
+ }
1810
+ );
1811
+ logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId });
1812
+ }
1702
1813
 
1703
1814
  // src/vite-plugin/api-middleware.ts
1704
- import { resolve as resolve4 } from "path";
1815
+ import { resolve as resolve6 } from "path";
1705
1816
  function shouldBypassApiMiddleware(url, prefixes) {
1706
1817
  if (!url.startsWith("/api/")) return true;
1707
1818
  if (prefixes.length === 0) return false;
@@ -1749,10 +1860,10 @@ async function handleBatchIfMatch(req, res, ctx) {
1749
1860
  async function handleBatchInline(req, res, batching, requestId) {
1750
1861
  try {
1751
1862
  const chunks = [];
1752
- await new Promise((resolve10, reject) => {
1863
+ await new Promise((resolve11, reject) => {
1753
1864
  req.on("data", (c) => chunks.push(c));
1754
1865
  req.on("end", () => {
1755
- resolve10();
1866
+ resolve11();
1756
1867
  });
1757
1868
  req.on("error", reject);
1758
1869
  });
@@ -1808,7 +1919,7 @@ async function runPluginsBeforeRouteMatch(pluginRunner, reqCtx) {
1808
1919
  }
1809
1920
  async function tryControllerFallthrough(ctx) {
1810
1921
  const handled = await dispatchControllerRequest({
1811
- controllersDir: resolve4(ctx.serverDir, "controllers"),
1922
+ controllersDir: resolve6(ctx.serverDir, "controllers"),
1812
1923
  loadModule: ctx.loadModule,
1813
1924
  req: ctx.req,
1814
1925
  res: ctx.res,
@@ -1817,7 +1928,18 @@ async function tryControllerFallthrough(ctx) {
1817
1928
  requestId: ctx.requestId,
1818
1929
  // M47 — serve @Expose-bound agent routes via the ONE runtime (mountAgent). CSRF is already enforced
1819
1930
  // once at the controller-dispatch boundary (G5), so mountAgent runs with csrfMode 'off' (no double gate).
1820
- serveAgent: (agent, request) => mountAgent(agent, request, resolveProvider().apiKey, { source: "expose", csrfMode: "off" })
1931
+ //
1932
+ // usetheokit/theokit#365 gated the CONVENTION endpoints (`agents/<name>.ts`) and deliberately
1933
+ // left this one to the decorator stack: an `@Expose` route is a controller method, so its
1934
+ // access decision is the controller's `@Guard` chain, which the dispatcher already runs before
1935
+ // it gets here. Passing a policy as well would give this one surface two authorization
1936
+ // mechanisms and no rule for which wins. What is NOT solved by that: an `@Expose`d agent shares
1937
+ // the conversation store with the convention endpoints, so a guard that admits a caller admits
1938
+ // them to whatever `sessionId` the body names — see the advisory.
1939
+ serveAgent: (agent, request) => mountAgent(agent, request, (model) => resolveProvider(model).apiKey, {
1940
+ source: "expose",
1941
+ csrfMode: "off"
1942
+ })
1821
1943
  });
1822
1944
  if (!handled) return false;
1823
1945
  logRequest(
@@ -1941,7 +2063,7 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
1941
2063
 
1942
2064
  // src/vite-plugin/ssr-dev-middleware.ts
1943
2065
  import { readFileSync as readFileSync2 } from "fs";
1944
- import { resolve as resolve5 } from "path";
2066
+ import { resolve as resolve7 } from "path";
1945
2067
 
1946
2068
  // src/core/contracts/find-root-div.ts
1947
2069
  function maskHtmlComments(html) {
@@ -2032,7 +2154,7 @@ function setupSsrDevMiddleware(server, opts) {
2032
2154
  return;
2033
2155
  }
2034
2156
  try {
2035
- const indexPath = resolve5(opts.projectRoot, "index.html");
2157
+ const indexPath = resolve7(opts.projectRoot, "index.html");
2036
2158
  let template = readFileSync2(indexPath, "utf-8");
2037
2159
  const nonce = generateNonce();
2038
2160
  template = await server.transformIndexHtml(url, template);
@@ -2151,7 +2273,12 @@ async function runConfigureServer(server, ctx) {
2151
2273
  })
2152
2274
  );
2153
2275
  server.middlewares.use(
2154
- createAgentMiddleware(server, ctx.projectRoot, ctx.csrfMode, ctx.agentsDir)
2276
+ createAgentMiddleware(server, ctx.projectRoot, ctx.csrfMode, ctx.agentsDir, {
2277
+ pluginRunner: ctx.pluginRunner,
2278
+ // usetheokit/theokit#365 — the same backend root the route middleware scans, so an agent's
2279
+ // declared policy is judged against the identity `server/context.ts` establishes.
2280
+ serverDir
2281
+ })
2155
2282
  );
2156
2283
  const servicesProxyPrefixes = ctx.services && Object.keys(ctx.services).length > 0 ? Object.values(ctx.services).map((s) => s.proxy) : [];
2157
2284
  const csrfReadinessStore = ctx.csrfReadinessStoreOverride ?? new CsrfReadinessStore();
@@ -2170,7 +2297,7 @@ async function runConfigureServer(server, ctx) {
2170
2297
  csrfReadinessStore
2171
2298
  })
2172
2299
  );
2173
- const configPath = resolve6(ctx.projectRoot, "theo.config.ts");
2300
+ const configPath = resolve8(ctx.projectRoot, "theo.config.ts");
2174
2301
  server.watcher.on("change", (file) => {
2175
2302
  if (file === configPath) {
2176
2303
  console.warn(
@@ -2198,9 +2325,9 @@ async function runConfigureServer(server, ctx) {
2198
2325
  server.watcher.on("unlink", handleRouteChange);
2199
2326
  if (ctx.resolvedOpenApi !== void 0) {
2200
2327
  const openApiCfg = ctx.resolvedOpenApi;
2201
- const { reEmitOpenApi } = await import("./dev-emit-NBT6JD7R.js");
2328
+ const { reEmitOpenApi } = await import("./dev-emit-MVAJAFGX.js");
2202
2329
  const openApiServerDir = ctx.serverDir;
2203
- const openApiDistDir = resolve6(ctx.projectRoot, ctx.resolvedDistDir);
2330
+ const openApiDistDir = resolve8(ctx.projectRoot, ctx.resolvedDistDir);
2204
2331
  const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
2205
2332
  void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2206
2333
  server.watcher.on("change", (file) => {
@@ -2250,7 +2377,7 @@ async function integrateStudio(importStudio = () => import(
2250
2377
  // src/vite-plugin/integrate-ui.ts
2251
2378
  import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
2252
2379
  import { dirname as dirname3, join as join4 } from "path";
2253
- import { pathToFileURL } from "url";
2380
+ import { pathToFileURL as pathToFileURL2 } from "url";
2254
2381
 
2255
2382
  // src/vite-plugin/auto-detect.ts
2256
2383
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
@@ -2382,7 +2509,7 @@ function resolveConsumerImport(name, cwd) {
2382
2509
  entry ??= pkg.module ?? pkg.main;
2383
2510
  if (!entry) return null;
2384
2511
  const cleaned = entry.replace(/^\.\//, "");
2385
- return pathToFileURL(join4(pkgDir, cleaned)).href;
2512
+ return pathToFileURL2(join4(pkgDir, cleaned)).href;
2386
2513
  } catch {
2387
2514
  return null;
2388
2515
  }
@@ -2455,7 +2582,7 @@ async function integrateUseTheoUI(cwd, opts) {
2455
2582
  return [];
2456
2583
  }
2457
2584
  try {
2458
- uiMod = await import(pathToFileURL(uiPluginPath).href);
2585
+ uiMod = await import(pathToFileURL2(uiPluginPath).href);
2459
2586
  } catch (err) {
2460
2587
  console.warn(
2461
2588
  `[theokit] @theokit/ui/vite-plugin dynamic import failed (${err.message}). Skipping auto-config.`
@@ -2505,19 +2632,9 @@ async function integrateUseTheoUI(cwd, opts) {
2505
2632
  return [...tailwindPlugins, ...uiPlugins];
2506
2633
  }
2507
2634
 
2508
- // src/vite-plugin/resolve-theo-root.ts
2509
- import { existsSync as existsSync7 } from "fs";
2510
- import { resolve as resolve7 } from "path";
2511
- function resolveTheoRootDir(currentDir) {
2512
- if (existsSync7(resolve7(currentDir, "client"))) {
2513
- return currentDir;
2514
- }
2515
- return resolve7(currentDir, "..");
2516
- }
2517
-
2518
2635
  // src/vite-plugin/transform-html-hook.ts
2519
- import { existsSync as existsSync8 } from "fs";
2520
- import { resolve as resolve8 } from "path";
2636
+ import { existsSync as existsSync7 } from "fs";
2637
+ import { resolve as resolve9 } from "path";
2521
2638
 
2522
2639
  // src/vite-plugin/inject-devtools.ts
2523
2640
  var DEVTOOLS_VIRTUAL_ID = "/@theo/devtools/entry.js";
@@ -2617,7 +2734,7 @@ function runTransformIndexHtml(html, ctx) {
2617
2734
  next = devtools.html;
2618
2735
  const styles = injectStylesheets(next, {
2619
2736
  isDev: ctx.isDevMode.value,
2620
- hasPackage: (name) => existsSync8(resolve8(ctx.projectRoot, "node_modules", ...name.split("/")))
2737
+ hasPackage: (name) => existsSync7(resolve9(ctx.projectRoot, "node_modules", ...name.split("/")))
2621
2738
  });
2622
2739
  next = styles.html;
2623
2740
  return next;
@@ -2720,6 +2837,46 @@ function generateSingleShotEntry(options) {
2720
2837
  `}`
2721
2838
  ].join("\n");
2722
2839
  }
2840
+ function streamingDocumentHelpers() {
2841
+ return [
2842
+ `function __theoHydrationScript(hydrationData, nonce) {`,
2843
+ // `<` is escaped rather than the whole payload: loader data is developer
2844
+ // data, and the only sequence that can break out of a script element is
2845
+ // `</script`. Mirrors what the non-streaming path does in request-handler.ts.
2846
+ ` const json = JSON.stringify(hydrationData).replace(/</g, '\\u003c')`,
2847
+ ` const attr = nonce ? ' nonce="' + nonce + '"' : ''`,
2848
+ ` return '<script' + attr + '>window.__staticRouterHydrationData=' + json + '<' + '/script>'`,
2849
+ `}`,
2850
+ ``,
2851
+ `function __theoDocumentStream(appStream, hydrationData, options) {`,
2852
+ ` const encoder = new TextEncoder()`,
2853
+ ` const head = options.htmlHead ?? ''`,
2854
+ ` const tail = options.htmlTail ?? ''`,
2855
+ ` return new ReadableStream({`,
2856
+ ` async start(controller) {`,
2857
+ // Its own chunk: the reader gets <head> before React has rendered anything.
2858
+ ` if (head) controller.enqueue(encoder.encode(head))`,
2859
+ ` const reader = appStream.getReader()`,
2860
+ ` try {`,
2861
+ ` for (;;) {`,
2862
+ ` const { done, value } = await reader.read()`,
2863
+ ` if (done) break`,
2864
+ ` controller.enqueue(value)`,
2865
+ ` }`,
2866
+ ` } finally {`,
2867
+ ` reader.releaseLock()`,
2868
+ ` }`,
2869
+ // The hydration script trails the app markup and precedes the tail, so it
2870
+ // still runs before the client entry the tail loads.
2871
+ ` controller.enqueue(`,
2872
+ ` encoder.encode(__theoHydrationScript(hydrationData, options.nonce) + tail),`,
2873
+ ` )`,
2874
+ ` controller.close()`,
2875
+ ` },`,
2876
+ ` })`,
2877
+ `}`
2878
+ ];
2879
+ }
2723
2880
  function streamingWebRenderer(appTree) {
2724
2881
  return [
2725
2882
  `// T2.3 \u2014 Web Standards streaming entry for edge runtimes (Cloudflare,`,
@@ -2739,13 +2896,16 @@ function streamingWebRenderer(appTree) {
2739
2896
  // Streaming still applies to genuine data-fetching Suspense, which is where it earns its keep.
2740
2897
  // A failed import is swallowed on purpose: React.lazy will retry and suspend as before, which
2741
2898
  // is strictly no worse than not having preloaded at all.
2742
- ` const __theoMatches = matchRoutes(routes, url.split('?')[0]) ?? []`,
2899
+ // `url` is a URL here, not the string the other renderers receive as a parameter —
2900
+ // so the match key is `.pathname`, never `.split('?')`. Hoisting the declaration
2901
+ // without changing the accessor trades the TDZ for a TypeError (#344).
2902
+ ` const url = new URL(request.url)`,
2903
+ ` const __theoMatches = matchRoutes(routes, url.pathname) ?? []`,
2743
2904
  ` await Promise.all(`,
2744
2905
  ` __theoPreloadPathsFor(__theoMatches).map((p) => __theoPreloadMap[p]().catch(() => null)),`,
2745
2906
  ` )`,
2746
2907
  ``,
2747
2908
  ` const handler = createStaticHandler(routes)`,
2748
- ` const url = new URL(request.url)`,
2749
2909
  ` const context = await handler.query(request)`,
2750
2910
  ``,
2751
2911
  ` if (context instanceof Response) {`,
@@ -2755,12 +2915,14 @@ function streamingWebRenderer(appTree) {
2755
2915
  ` const router = createStaticRouter(handler.dataRoutes, context)`,
2756
2916
  ` const app = ${appTree}`,
2757
2917
  ``,
2918
+ hydrationDataExtractSnippet(),
2919
+ ``,
2758
2920
  ` const stream = await renderToReadableStream(app, {`,
2759
2921
  ` signal: request.signal,`,
2760
2922
  ` nonce: options.nonce,`,
2761
2923
  ` onError(err) { console.error('[SSR Web Stream Error]', err) },`,
2762
2924
  ` })`,
2763
- ` return new Response(stream, {`,
2925
+ ` return new Response(__theoDocumentStream(stream, hydrationData, options), {`,
2764
2926
  ` status: 200,`,
2765
2927
  ` headers: {`,
2766
2928
  ` 'Content-Type': 'text/html; charset=utf-8',`,
@@ -2804,15 +2966,29 @@ function streamingNodeRenderer(appTree) {
2804
2966
  ` const router = createStaticRouter(handler.dataRoutes, context)`,
2805
2967
  ` const app = ${appTree}`,
2806
2968
  ``,
2969
+ hydrationDataExtractSnippet(),
2970
+ ``,
2971
+ ` const { PassThrough } = await import('node:stream')`,
2807
2972
  ` return new Promise((resolve, reject) => {`,
2808
2973
  ` let didError = false`,
2974
+ // React's `pipe(dest)` ends `dest` when the source ends, which would close the
2975
+ // response before the hydration script could be written. Piping into a
2976
+ // PassThrough keeps the response open for the tail.
2977
+ ` const passthrough = new PassThrough()`,
2978
+ ` passthrough.on('data', (chunk) => { response.write(chunk) })`,
2979
+ ` passthrough.on('end', () => {`,
2980
+ ` response.end(__theoHydrationScript(hydrationData, options.nonce) + (options.htmlTail ?? ''))`,
2981
+ ` })`,
2982
+ ` passthrough.on('error', reject)`,
2809
2983
  ` const stream = renderToPipeableStream(app, {`,
2810
2984
  ` nonce: options.nonce,`,
2811
2985
  ` onShellReady() {`,
2812
2986
  ` response.statusCode = didError ? 500 : 200`,
2813
2987
  ` response.setHeader('Content-Type', 'text/html; charset=utf-8')`,
2814
2988
  ` response.setHeader('Transfer-Encoding', 'chunked')`,
2815
- ` stream.pipe(response)`,
2989
+ // The head goes out on its own write, before any React byte.
2990
+ ` if (options.htmlHead) response.write(options.htmlHead)`,
2991
+ ` stream.pipe(passthrough)`,
2816
2992
  ` resolve({ streaming: true })`,
2817
2993
  ` },`,
2818
2994
  ` onShellError(err) { reject(err) },`,
@@ -2896,6 +3072,8 @@ function generateStreamingEntry(options) {
2896
3072
  `import { routes, __theoPreloadMap, __theoPreloadPathsFor } from '/@theo/route-manifest'`,
2897
3073
  theoUiImport,
2898
3074
  ``,
3075
+ ...streamingDocumentHelpers(),
3076
+ ``,
2899
3077
  ...streamingWebRenderer(appTree),
2900
3078
  ``,
2901
3079
  ...streamingNodeRenderer(appTree),
@@ -3005,7 +3183,6 @@ function walkRouteTree(node, parents, acc) {
3005
3183
  routePath
3006
3184
  });
3007
3185
  }
3008
- if (node.layout !== void 0) acc.layoutState.found = true;
3009
3186
  pushIf(acc.staticImports, node.layout, safeVarName(seg, "Layout"));
3010
3187
  pushIf(acc.staticImports, node.error, safeVarName(seg, "Error"));
3011
3188
  pushIf(acc.staticImports, node.loading, safeVarName(seg, "Loading"));
@@ -3020,18 +3197,20 @@ function generateRouteManifest(tree, options = {}) {
3020
3197
  const lazyPages_ = options.lazyPages !== false;
3021
3198
  const acc = {
3022
3199
  staticImports: [],
3023
- lazyPages: [],
3024
- layoutState: { found: false }
3200
+ lazyPages: []
3025
3201
  };
3026
3202
  walkRouteTree(tree, [], acc);
3027
3203
  const staticImports = acc.staticImports;
3028
3204
  const lazyPages = acc.lazyPages;
3029
- const layoutState = acc.layoutState;
3030
3205
  const lines = [`import React, { Suspense } from 'react'`];
3031
- if (layoutState.found) {
3032
- lines.push(`import { Outlet } from 'react-router'`);
3033
- }
3034
- lines.push("");
3206
+ lines.push(
3207
+ `import { Outlet, ScrollRestoration } from 'react-router'`,
3208
+ // #421 — react-router restores the DOCUMENT. The layout this framework scaffolds scrolls an
3209
+ // inner element, so the restoration that was mounted and running restored nothing. Both are
3210
+ // emitted: one owns the document, the other owns the elements the app marked.
3211
+ `import { ElementScrollRestoration } from 'theokit/router/element-scroll-restoration'`,
3212
+ ""
3213
+ );
3035
3214
  for (const imp of staticImports) {
3036
3215
  lines.push(`import ${imp.varName} from '${imp.importPath}'`);
3037
3216
  }
@@ -3085,16 +3264,20 @@ function generateRouteManifest(tree, options = {}) {
3085
3264
  function genRouteConfig(node, isRoot) {
3086
3265
  const seg = node.segment || "root";
3087
3266
  const childrenArray = buildChildrenArray(node, seg);
3267
+ const withScrollRestoration = (element) => `React.createElement(React.Fragment, null, React.createElement(ScrollRestoration), React.createElement(ElementScrollRestoration), ${element})`;
3088
3268
  if (node.layout) {
3089
3269
  const layoutVar = safeVarName(seg, "Layout");
3090
3270
  const pathPart = isRoot ? `path: '/'` : `path: '${segmentPath(node)}'`;
3091
- return `{ ${pathPart}, element: React.createElement(${layoutVar}, { children: React.createElement(Outlet) }), children: ${childrenArray} }`;
3271
+ const layoutElement = `React.createElement(${layoutVar}, { children: React.createElement(Outlet) })`;
3272
+ const element = isRoot ? withScrollRestoration(layoutElement) : layoutElement;
3273
+ return `{ ${pathPart}, element: ${element}, children: ${childrenArray} }`;
3092
3274
  }
3093
3275
  if (isRoot) {
3276
+ const rootElement = withScrollRestoration(`React.createElement(Outlet)`);
3094
3277
  if (node.children.length === 0 && !node.page && !node.notFound && !node.error) {
3095
- return `{ path: '/', children: [] }`;
3278
+ return `{ path: '/', element: ${rootElement}, children: [] }`;
3096
3279
  }
3097
- return `{ path: '/', children: ${childrenArray} }`;
3280
+ return `{ path: '/', element: ${rootElement}, children: ${childrenArray} }`;
3098
3281
  }
3099
3282
  if (node.page && node.children.length === 0 && !node.error && !node.notFound) {
3100
3283
  const pageVar = safeVarName(seg, "Page");
@@ -3262,8 +3445,8 @@ function findConsumerConfig(projectRoot, basename2) {
3262
3445
  let dir = projectRoot;
3263
3446
  for (let level = 0; level < 3; level++) {
3264
3447
  for (const ext of extensions) {
3265
- const candidate = resolve9(dir, `${basename2}${ext}`);
3266
- if (existsSync9(candidate)) return candidate;
3448
+ const candidate = resolve10(dir, `${basename2}${ext}`);
3449
+ if (existsSync8(candidate)) return candidate;
3267
3450
  }
3268
3451
  const parent = dirname4(dir);
3269
3452
  if (parent === dir) break;
@@ -3274,7 +3457,7 @@ function findConsumerConfig(projectRoot, basename2) {
3274
3457
  async function theoPluginAsync(rootOrOptions) {
3275
3458
  const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
3276
3459
  const projectRoot = options.root ?? process.cwd();
3277
- const serverDirAbs = resolve9(projectRoot, options.serverDir ?? "server");
3460
+ const serverDirAbs = resolve10(projectRoot, options.serverDir ?? "server");
3278
3461
  const agentsDirName = options.agentsDir ?? "agents";
3279
3462
  const consumerTailwindConfig = findConsumerConfig(projectRoot, "tailwind.config");
3280
3463
  const consumerPostcssConfig = findConsumerConfig(projectRoot, "postcss.config");
@@ -3293,23 +3476,23 @@ async function theoPluginAsync(rootOrOptions) {
3293
3476
  })
3294
3477
  );
3295
3478
  }
3296
- const { appTypedClientPlugin } = await import("./app-typed-client-GHO3EWZK.js");
3479
+ const { appTypedClientPlugin } = await import("./app-typed-client-4LAIEGFX.js");
3297
3480
  const appClientPlugin = appTypedClientPlugin({
3298
3481
  cwd: projectRoot,
3299
3482
  serverDir: serverDirAbs,
3300
- distDir: resolve9(projectRoot, ".theokit")
3483
+ distDir: resolve10(projectRoot, ".theokit")
3301
3484
  });
3302
- const { actionsVirtualModule } = await import("./actions-virtual-module-YQXRSSMW.js");
3485
+ const { actionsVirtualModule } = await import("./actions-virtual-module-5WBVSDOM.js");
3303
3486
  const actionsPlugin = actionsVirtualModule({
3304
3487
  serverDir: serverDirAbs,
3305
- distDir: resolve9(projectRoot, ".theokit")
3488
+ distDir: resolve10(projectRoot, ".theokit")
3306
3489
  });
3307
3490
  const { agentsTypedClientPlugin } = await import("./agents-typed-client-INR2E7B3.js");
3308
- const { generateManifest } = await import("./internal-api-WEOSTMSN.js");
3491
+ const { generateManifest } = await import("./internal-api-OPU456AT.js");
3309
3492
  const agentsClientPlugin = agentsTypedClientPlugin({
3310
3493
  projectRoot,
3311
3494
  agentsDir: agentsDirName,
3312
- distDir: resolve9(projectRoot, ".theokit"),
3495
+ distDir: resolve10(projectRoot, ".theokit"),
3313
3496
  // Agents live at <projectRoot>/<agentsDir>; generateManifest scans them via the server dir's
3314
3497
  // parent (projectRoot for the canonical layout) + the agents dir name (#95 follow-up).
3315
3498
  scanManifest: () => generateManifest(serverDirAbs, projectRoot, agentsDirName)
@@ -3322,11 +3505,17 @@ async function theoPluginAsync(rootOrOptions) {
3322
3505
  const controllerTransformPlugin = controllerSwcTransformPlugin({
3323
3506
  serverDir: serverDirAbs
3324
3507
  });
3508
+ const { serverOnlyImportBoundary } = await import("./server-boundary-6B4ZQUY7.js");
3509
+ const serverBoundaryPlugin = serverOnlyImportBoundary({
3510
+ projectRoot,
3511
+ serverDir: serverDirAbs
3512
+ });
3325
3513
  return [
3326
3514
  theoPlugin(rootOrOptions),
3327
3515
  ...uiPlugins,
3328
3516
  ...studioPlugins,
3329
3517
  ...servicesPlugins,
3518
+ serverBoundaryPlugin,
3330
3519
  // #122 — swc-compile controllers/** (enforce:'pre'; order-independent).
3331
3520
  controllerTransformPlugin,
3332
3521
  appClientPlugin,
@@ -3337,10 +3526,10 @@ async function theoPluginAsync(rootOrOptions) {
3337
3526
  }
3338
3527
  function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
3339
3528
  const include = [];
3340
- if (existsSync9(resolve9(projectRoot, "node_modules", "@theokit", "ui"))) {
3529
+ if (existsSync8(resolve10(projectRoot, "node_modules", "@theokit", "ui"))) {
3341
3530
  include.push("@theokit/ui");
3342
3531
  }
3343
- if (existsSync9(resolve9(projectRoot, "node_modules", "lucide-react"))) {
3532
+ if (existsSync8(resolve10(projectRoot, "node_modules", "lucide-react"))) {
3344
3533
  include.push("lucide-react");
3345
3534
  }
3346
3535
  include.push("devalue");
@@ -3366,8 +3555,8 @@ var VIRTUAL_MODULE_IDS = {
3366
3555
  function theoPlugin(rootOrOptions) {
3367
3556
  const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
3368
3557
  const projectRoot = options.root ?? process.cwd();
3369
- const appDir = resolve9(projectRoot, options.appDir ?? "app");
3370
- const serverDir = resolve9(projectRoot, options.serverDir ?? "server");
3558
+ const appDir = resolve10(projectRoot, options.appDir ?? "app");
3559
+ const serverDir = resolve10(projectRoot, options.serverDir ?? "server");
3371
3560
  const agentsDir = options.agentsDir ?? "agents";
3372
3561
  const ssrEnabled = options.ssr ?? false;
3373
3562
  const currentDir = dirname4(fileURLToPath(import.meta.url));
@@ -3499,4 +3688,4 @@ export {
3499
3688
  theoPluginAsync,
3500
3689
  theoPlugin
3501
3690
  };
3502
- //# sourceMappingURL=chunk-GXEX4CBH.js.map
3691
+ //# sourceMappingURL=chunk-JIKNR5LX.js.map