@tangle-network/agent-app 0.43.56 → 0.43.57

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 (352) hide show
  1. package/dist/DesignCanvas-UNL34LS2.js +12 -0
  2. package/dist/DesignCanvasEditor-FJIVYOA2.js +13 -0
  3. package/dist/IntakeInterview-3ZIQMZS6.js +8 -0
  4. package/dist/InvitationsPanel-CJ7DR4F7.js +8 -0
  5. package/dist/MembersPanel-BB4OD2KB.js +8 -0
  6. package/dist/{TimelineEditor-I35SJJAJ.js → TimelineEditor-2ECKSU6P.js} +3 -3
  7. package/dist/{VaultPane-DrQ_tH6c.d.ts → VaultPane-BDP6SB_K.d.ts} +1 -0
  8. package/dist/{access-BZ81Btt5.d.ts → access-490O6aOU.d.ts} +16 -1
  9. package/dist/app-auth/index.d.ts +6 -1
  10. package/dist/app-auth/index.js +3 -3
  11. package/dist/app-auth/index.js.map +1 -1
  12. package/dist/{apply-Gk4raT2j.d.ts → apply-6wlMOLf8.d.ts} +30 -1
  13. package/dist/assets/index.d.ts +41 -0
  14. package/dist/assets/index.js +1 -1
  15. package/dist/assistant/index.d.ts +26 -6
  16. package/dist/assistant/index.js +9 -9
  17. package/dist/assistant/index.js.map +1 -1
  18. package/dist/{attachment-validation-D0csjCvz.d.ts → attachment-validation-tPRp-PP1.d.ts} +7 -1
  19. package/dist/{auth-DuptSkWh.d.ts → auth-_FU8w01b.d.ts} +4 -1
  20. package/dist/billing/index.d.ts +8 -0
  21. package/dist/billing/index.js +1 -1
  22. package/dist/brand/index.d.ts +2 -0
  23. package/dist/brand/index.js.map +1 -1
  24. package/dist/brand-extraction/index.d.ts +3 -0
  25. package/dist/brand-extraction/index.js.map +1 -1
  26. package/dist/catalog/index.d.ts +2 -0
  27. package/dist/catalog/index.js +1 -1
  28. package/dist/chat-routes/index.d.ts +41 -10
  29. package/dist/chat-routes/index.js +15 -15
  30. package/dist/chat-routes/index.js.map +1 -1
  31. package/dist/chat-store/index.d.ts +18 -4
  32. package/dist/chat-store/index.js +4 -4
  33. package/dist/chat-store/index.js.map +1 -1
  34. package/dist/{chunk-H5FM75MR.js → chunk-22GLESKE.js} +3 -3
  35. package/dist/chunk-22GLESKE.js.map +1 -0
  36. package/dist/{chunk-WEBBJBDH.js → chunk-2DRYTJHI.js} +1 -1
  37. package/dist/chunk-2DRYTJHI.js.map +1 -0
  38. package/dist/{chunk-7EVZUIHW.js → chunk-3EJ6SFJI.js} +1 -1
  39. package/dist/chunk-3EJ6SFJI.js.map +1 -0
  40. package/dist/{chunk-HRJJH3RX.js → chunk-3OFQFW5K.js} +4 -4
  41. package/dist/chunk-3OFQFW5K.js.map +1 -0
  42. package/dist/{chunk-7SUPA2YT.js → chunk-4KKXZQPT.js} +2 -2
  43. package/dist/chunk-4KKXZQPT.js.map +1 -0
  44. package/dist/{chunk-5PTGEJZL.js → chunk-5SD6HT2V.js} +1 -1
  45. package/dist/chunk-5SD6HT2V.js.map +1 -0
  46. package/dist/{chunk-7C64WKAH.js → chunk-5SXS3YAB.js} +2 -2
  47. package/dist/{chunk-L2TG5DBW.js → chunk-5TESA4SN.js} +1 -1
  48. package/dist/chunk-5TESA4SN.js.map +1 -0
  49. package/dist/{chunk-63CE7FEZ.js → chunk-6XIAPIW6.js} +1 -1
  50. package/dist/chunk-6XIAPIW6.js.map +1 -0
  51. package/dist/{chunk-MFRCM32T.js → chunk-7CTIUCQ4.js} +1 -1
  52. package/dist/chunk-7CTIUCQ4.js.map +1 -0
  53. package/dist/{chunk-6KVH5SP5.js → chunk-7YW6EBDX.js} +2 -2
  54. package/dist/chunk-7YW6EBDX.js.map +1 -0
  55. package/dist/{chunk-ZXNXAQAH.js → chunk-AP2UXKUK.js} +1 -1
  56. package/dist/chunk-AP2UXKUK.js.map +1 -0
  57. package/dist/{chunk-K63OWUDJ.js → chunk-BPSV5AA2.js} +2 -2
  58. package/dist/chunk-BPSV5AA2.js.map +1 -0
  59. package/dist/{chunk-MFI4ZM7W.js → chunk-BT4QFCTB.js} +1 -1
  60. package/dist/chunk-BT4QFCTB.js.map +1 -0
  61. package/dist/{chunk-ZYBWGSAZ.js → chunk-BVKQKGRK.js} +1 -1
  62. package/dist/chunk-BVKQKGRK.js.map +1 -0
  63. package/dist/{chunk-RVUISYUE.js → chunk-C3NSB66C.js} +7 -7
  64. package/dist/chunk-C3NSB66C.js.map +1 -0
  65. package/dist/{chunk-BQV42AFK.js → chunk-CCVWR33L.js} +1 -1
  66. package/dist/chunk-CCVWR33L.js.map +1 -0
  67. package/dist/{chunk-MCJSS6SM.js → chunk-CQZSAR77.js} +1 -1
  68. package/dist/chunk-CQZSAR77.js.map +1 -0
  69. package/dist/{chunk-3SVAA3MA.js → chunk-DJ4VJIH5.js} +1 -1
  70. package/dist/chunk-DJ4VJIH5.js.map +1 -0
  71. package/dist/{chunk-XKBIYSSM.js → chunk-DTSPS2HV.js} +3 -3
  72. package/dist/chunk-DTSPS2HV.js.map +1 -0
  73. package/dist/{chunk-4QN75UHX.js → chunk-E7N67FYI.js} +8 -8
  74. package/dist/chunk-E7N67FYI.js.map +1 -0
  75. package/dist/{chunk-2QI7XV2T.js → chunk-FBVLEGEG.js} +1 -1
  76. package/dist/{chunk-2QI7XV2T.js.map → chunk-FBVLEGEG.js.map} +1 -1
  77. package/dist/{chunk-5RMIUJDI.js → chunk-FKCB5FTD.js} +1 -1
  78. package/dist/chunk-FKCB5FTD.js.map +1 -0
  79. package/dist/{chunk-EEPJGZJW.js → chunk-GPBRL2XR.js} +1 -1
  80. package/dist/chunk-GPBRL2XR.js.map +1 -0
  81. package/dist/{chunk-G3HCU7TA.js → chunk-GXE6KHTS.js} +1 -1
  82. package/dist/chunk-GXE6KHTS.js.map +1 -0
  83. package/dist/{chunk-65P3HJY3.js → chunk-HCOROIRT.js} +1 -1
  84. package/dist/chunk-HCOROIRT.js.map +1 -0
  85. package/dist/{chunk-54GSK2KK.js → chunk-HYTIHYNV.js} +2 -2
  86. package/dist/chunk-HYTIHYNV.js.map +1 -0
  87. package/dist/{chunk-5MG74GVQ.js → chunk-IG46XCZM.js} +3 -3
  88. package/dist/chunk-IG46XCZM.js.map +1 -0
  89. package/dist/{chunk-ND3XEGBE.js → chunk-JDEGS53O.js} +1 -1
  90. package/dist/chunk-JDEGS53O.js.map +1 -0
  91. package/dist/{chunk-7W5XSTUF.js → chunk-JML7WKWU.js} +1 -1
  92. package/dist/chunk-JML7WKWU.js.map +1 -0
  93. package/dist/{chunk-Q4TKVF3L.js → chunk-JTLYXUEV.js} +1 -1
  94. package/dist/chunk-JTLYXUEV.js.map +1 -0
  95. package/dist/{chunk-CCRVDLV3.js → chunk-JWBZ74TW.js} +2 -2
  96. package/dist/chunk-JWBZ74TW.js.map +1 -0
  97. package/dist/{chunk-ZU5GNSOJ.js → chunk-K3LIPKFV.js} +3 -3
  98. package/dist/chunk-K3LIPKFV.js.map +1 -0
  99. package/dist/{chunk-GQRL6VNU.js → chunk-LGBO24FL.js} +1 -1
  100. package/dist/chunk-LGBO24FL.js.map +1 -0
  101. package/dist/{chunk-SWUVTGMR.js → chunk-MEUNTJL5.js} +3 -3
  102. package/dist/{chunk-SWUVTGMR.js.map → chunk-MEUNTJL5.js.map} +1 -1
  103. package/dist/{chunk-YNALYIQ3.js → chunk-NLSSFLLS.js} +1 -1
  104. package/dist/chunk-NLSSFLLS.js.map +1 -0
  105. package/dist/{chunk-HFC4BTWJ.js → chunk-PTUMBMVH.js} +1 -1
  106. package/dist/chunk-PTUMBMVH.js.map +1 -0
  107. package/dist/{chunk-R4NOKZMD.js → chunk-ROLFDOMJ.js} +3 -3
  108. package/dist/{chunk-NYATNLRK.js → chunk-RT5RKAFX.js} +1 -1
  109. package/dist/chunk-RT5RKAFX.js.map +1 -0
  110. package/dist/{chunk-GNL3MG5J.js → chunk-S564OFTL.js} +2 -2
  111. package/dist/{chunk-GUSCYE3S.js → chunk-TQOFR6XY.js} +1 -1
  112. package/dist/chunk-TQOFR6XY.js.map +1 -0
  113. package/dist/{chunk-47C6PJCW.js → chunk-TZO3SOUL.js} +1 -1
  114. package/dist/chunk-TZO3SOUL.js.map +1 -0
  115. package/dist/{chunk-YGUNTIT5.js → chunk-UKTYKG57.js} +1 -1
  116. package/dist/chunk-UKTYKG57.js.map +1 -0
  117. package/dist/{chunk-23LUHHHN.js → chunk-VCOD5AOR.js} +1 -1
  118. package/dist/chunk-VCOD5AOR.js.map +1 -0
  119. package/dist/{chunk-5I72FRZY.js → chunk-VOJB7MVG.js} +2 -2
  120. package/dist/{chunk-XWJXZ6WE.js → chunk-VTWKH4YU.js} +2 -2
  121. package/dist/chunk-VTWKH4YU.js.map +1 -0
  122. package/dist/{chunk-4K7BZYO7.js → chunk-VWAEWOWK.js} +3 -3
  123. package/dist/chunk-VWAEWOWK.js.map +1 -0
  124. package/dist/{chunk-M2EBUDZ7.js → chunk-VWOPOLVO.js} +2 -2
  125. package/dist/chunk-VWOPOLVO.js.map +1 -0
  126. package/dist/{chunk-BN2USWIR.js → chunk-VYECQAGM.js} +2 -2
  127. package/dist/chunk-VYECQAGM.js.map +1 -0
  128. package/dist/{chunk-SIXYZ2FB.js → chunk-YJMCRXQQ.js} +1 -1
  129. package/dist/chunk-YJMCRXQQ.js.map +1 -0
  130. package/dist/{chunk-PJC4NXPA.js → chunk-YKBDH2UY.js} +3 -3
  131. package/dist/chunk-YKBDH2UY.js.map +1 -0
  132. package/dist/{chunk-XAWFPMAR.js → chunk-YKOM4ZLQ.js} +1 -1
  133. package/dist/chunk-YKOM4ZLQ.js.map +1 -0
  134. package/dist/{chunk-TKVJE63N.js → chunk-YRMLYFVR.js} +3 -3
  135. package/dist/chunk-YRMLYFVR.js.map +1 -0
  136. package/dist/{chunk-USYXHDKE.js → chunk-YZ6KQULN.js} +2 -2
  137. package/dist/{command-stack-B_-WNfZU.d.ts → command-stack-TNPWJCzW.d.ts} +14 -2
  138. package/dist/{completion-Kaqp_tWk.d.ts → completion-CvcNUz07.d.ts} +1 -1
  139. package/dist/config/index.d.ts +1 -1
  140. package/dist/{contract-KfqJh_au.d.ts → contract-TjB68uqA.d.ts} +17 -0
  141. package/dist/design-canvas/drizzle.d.ts +10 -2
  142. package/dist/design-canvas/drizzle.js.map +1 -1
  143. package/dist/design-canvas/index.d.ts +29 -11
  144. package/dist/design-canvas/index.js +1070 -17
  145. package/dist/design-canvas/index.js.map +1 -1
  146. package/dist/design-canvas-react/engine.d.ts +42 -6
  147. package/dist/design-canvas-react/engine.js +4 -4
  148. package/dist/design-canvas-react/index.d.ts +12 -7
  149. package/dist/design-canvas-react/index.js +11 -11
  150. package/dist/design-canvas-react/index.js.map +1 -1
  151. package/dist/design-canvas-react/lazy.d.ts +4 -4
  152. package/dist/design-canvas-react/lazy.js +1 -1
  153. package/dist/durable-chat/index.d.ts +46 -1
  154. package/dist/durable-chat/index.js +3 -3
  155. package/dist/eval/index.d.ts +1 -1
  156. package/dist/{export-presets-DAsU5Wal.d.ts → export-presets-mgVulRaV.d.ts} +10 -1
  157. package/dist/{flow-types-Cb_AblZs.d.ts → flow-types-CqomVAUN.d.ts} +1 -0
  158. package/dist/harness/index.d.ts +5 -0
  159. package/dist/harness/index.js +1 -1
  160. package/dist/index.d.ts +10 -18
  161. package/dist/index.js +30 -231
  162. package/dist/intakes/api.d.ts +3 -2
  163. package/dist/intakes/api.js +3 -3
  164. package/dist/intakes/api.js.map +1 -1
  165. package/dist/intakes/drizzle.d.ts +10 -2
  166. package/dist/intakes/drizzle.js +3 -3
  167. package/dist/intakes/drizzle.js.map +1 -1
  168. package/dist/intakes/index.d.ts +2 -2
  169. package/dist/intakes/index.js +2 -2
  170. package/dist/intakes-react/index.d.ts +2 -1
  171. package/dist/intakes-react/index.js +2 -2
  172. package/dist/intakes-react/lazy.d.ts +2 -1
  173. package/dist/intakes-react/lazy.js +1 -1
  174. package/dist/intakes-react/lazy.js.map +1 -1
  175. package/dist/integrations/index.d.ts +4 -0
  176. package/dist/integrations/index.js +1 -1
  177. package/dist/interactions/index.d.ts +13 -2
  178. package/dist/interactions/index.js +2 -2
  179. package/dist/{invitations-CZ5G2tIn.d.ts → invitations-DoYMvXea.d.ts} +11 -1
  180. package/dist/{invitations-schema-DaMO4hZE.d.ts → invitations-schema-_fI7HkJP.d.ts} +5 -1
  181. package/dist/knowledge/index.d.ts +2 -0
  182. package/dist/knowledge/index.js +1 -1
  183. package/dist/knowledge-loop/index.d.ts +4 -1
  184. package/dist/knowledge-loop/index.js +1 -1
  185. package/dist/{lazy-C7LiVeFt.d.ts → lazy-DrkVyDpe.d.ts} +2 -2
  186. package/dist/{mcp-BHfIoGLW.d.ts → mcp-BjPqQ-3C.d.ts} +4 -2
  187. package/dist/{mcp-rpc-DLw_r9PQ.d.ts → mcp-rpc-BfUOsalw.d.ts} +3 -0
  188. package/dist/missions/index.d.ts +25 -0
  189. package/dist/missions/index.js +1 -1
  190. package/dist/{model-Bzi5i63l.d.ts → model-C8U0HZxL.d.ts} +1 -0
  191. package/dist/{model-dF2h4xT9.d.ts → model-DmdkIteM.d.ts} +19 -0
  192. package/dist/{model-BBwyOuWG.d.ts → model-DnfO_PdJ.d.ts} +25 -1
  193. package/dist/model-resolution/index.d.ts +10 -0
  194. package/dist/model-resolution/index.js.map +1 -1
  195. package/dist/{operations-DLIvvD00.d.ts → operations-ByJ6yXOW.d.ts} +20 -2
  196. package/dist/{parts-1sRYSPjF.d.ts → parts-n9XUon0a.d.ts} +29 -1
  197. package/dist/plans/index.d.ts +6 -0
  198. package/dist/plans/index.js +1 -1
  199. package/dist/platform/index.d.ts +24 -2
  200. package/dist/platform/index.js +3 -3
  201. package/dist/platform/index.js.map +1 -1
  202. package/dist/preflight/index.d.ts +3 -0
  203. package/dist/preflight/index.js +1 -1
  204. package/dist/preset-cloudflare/index.d.ts +4 -1
  205. package/dist/preset-cloudflare/index.js +2 -2
  206. package/dist/redact/index.d.ts +5 -0
  207. package/dist/redact/index.js +1 -1
  208. package/dist/{roles-BC1n4t37.d.ts → roles-B4nU3jDi.d.ts} +12 -0
  209. package/dist/run/index.d.ts +3 -0
  210. package/dist/run/index.js +1 -1
  211. package/dist/run/index.js.map +1 -1
  212. package/dist/runtime/index.d.ts +11 -4
  213. package/dist/runtime/index.js +4 -4
  214. package/dist/sandbox/index.d.ts +96 -3
  215. package/dist/sandbox/index.js +6 -6
  216. package/dist/{sandbox-terminal-BIIC__CP.d.ts → sandbox-terminal-ChNEdHF8.d.ts} +5 -0
  217. package/dist/sequences/drizzle.d.ts +11 -1
  218. package/dist/sequences/drizzle.js +1 -1
  219. package/dist/sequences/drizzle.js.map +1 -1
  220. package/dist/sequences/index.d.ts +32 -7
  221. package/dist/sequences/index.js +1562 -47
  222. package/dist/sequences/index.js.map +1 -1
  223. package/dist/sequences-react/index.d.ts +32 -2
  224. package/dist/sequences-react/index.js +3 -3
  225. package/dist/sequences-react/index.js.map +1 -1
  226. package/dist/{sso-CNOsARMJ.d.ts → sso-Cm00S61u.d.ts} +13 -0
  227. package/dist/{stale-turn-lock-C8Na1cFZ.d.ts → stale-turn-lock-DucQzvXu.d.ts} +2 -0
  228. package/dist/store/index.d.ts +5 -0
  229. package/dist/store/index.js.map +1 -1
  230. package/dist/{store-gckrNq-g.d.ts → store-B7LLlk9p.d.ts} +22 -1
  231. package/dist/{store-CFyxgTlD.d.ts → store-CqfDtnPQ.d.ts} +8 -2
  232. package/dist/stream/index.d.ts +10 -4
  233. package/dist/stream/index.js +4 -4
  234. package/dist/{stream-normalizer-DWvtmY6F.d.ts → stream-normalizer-C3M--r6y.d.ts} +17 -1
  235. package/dist/studio/index.d.ts +37 -0
  236. package/dist/studio/index.js +1 -1
  237. package/dist/studio-react/index.d.ts +3 -0
  238. package/dist/studio-react/index.js +1 -1
  239. package/dist/studio-react/index.js.map +1 -1
  240. package/dist/tangle/index.d.ts +3 -0
  241. package/dist/tangle/index.js +1 -1
  242. package/dist/teams/drizzle.d.ts +8 -4
  243. package/dist/teams/drizzle.js +1 -1
  244. package/dist/teams/drizzle.js.map +1 -1
  245. package/dist/teams/index.d.ts +4 -2
  246. package/dist/teams/index.js +3 -3
  247. package/dist/teams/invitations-api.d.ts +10 -4
  248. package/dist/teams/invitations-api.js +4 -4
  249. package/dist/teams/invitations-api.js.map +1 -1
  250. package/dist/teams/members-api.d.ts +4 -2
  251. package/dist/teams/members-api.js +3 -3
  252. package/dist/teams/resend.d.ts +5 -4
  253. package/dist/teams/resend.js +1 -1
  254. package/dist/teams/resend.js.map +1 -1
  255. package/dist/teams-react/index.d.ts +7 -2
  256. package/dist/teams-react/index.js +3 -3
  257. package/dist/teams-react/lazy.d.ts +5 -2
  258. package/dist/teams-react/lazy.js +2 -2
  259. package/dist/teams-react/lazy.js.map +1 -1
  260. package/dist/theme/index.d.ts +2 -0
  261. package/dist/theme/index.js +1 -1
  262. package/dist/theme/tailwind-preset.d.ts +1 -0
  263. package/dist/theme/tailwind-preset.js.map +1 -1
  264. package/dist/theme-contract/cli.js +1 -1
  265. package/dist/theme-contract/index.d.ts +3 -0
  266. package/dist/theme-contract/index.js +1 -1
  267. package/dist/tools/index.d.ts +13 -6
  268. package/dist/tools/index.js +3 -3
  269. package/dist/trace/index.d.ts +6 -1
  270. package/dist/trace/index.js +2 -2
  271. package/dist/{turn-buffer-C9mEgoop.d.ts → turn-buffer-DGnAPKwa.d.ts} +6 -0
  272. package/dist/turn-stream/index.d.ts +28 -2
  273. package/dist/turn-stream/index.js +1 -1
  274. package/dist/turn-stream/index.js.map +1 -1
  275. package/dist/{types-BEOvc_ue.d.ts → types-CBRyqijY.d.ts} +10 -0
  276. package/dist/vault/index.d.ts +1 -1
  277. package/dist/vault/lazy.d.ts +3 -2
  278. package/dist/vault/lazy.js.map +1 -1
  279. package/dist/web/index.d.ts +4 -0
  280. package/dist/web/index.js +1 -1
  281. package/dist/web-react/index.d.ts +51 -8
  282. package/dist/web-react/index.js +9 -9
  283. package/dist/web-react/terminal.d.ts +2 -2
  284. package/dist/web-react/terminal.js +1 -1
  285. package/package.json +3 -2
  286. package/dist/DesignCanvas-5DJAFDNC.js +0 -12
  287. package/dist/DesignCanvasEditor-XUJZARSN.js +0 -13
  288. package/dist/IntakeInterview-WHVKMNQW.js +0 -8
  289. package/dist/InvitationsPanel-XUUWZBPV.js +0 -8
  290. package/dist/MembersPanel-FOVHWUQ3.js +0 -8
  291. package/dist/chunk-23LUHHHN.js.map +0 -1
  292. package/dist/chunk-3SVAA3MA.js.map +0 -1
  293. package/dist/chunk-47C6PJCW.js.map +0 -1
  294. package/dist/chunk-4K7BZYO7.js.map +0 -1
  295. package/dist/chunk-4QN75UHX.js.map +0 -1
  296. package/dist/chunk-54GSK2KK.js.map +0 -1
  297. package/dist/chunk-5MG74GVQ.js.map +0 -1
  298. package/dist/chunk-5PTGEJZL.js.map +0 -1
  299. package/dist/chunk-5RMIUJDI.js.map +0 -1
  300. package/dist/chunk-63CE7FEZ.js.map +0 -1
  301. package/dist/chunk-65P3HJY3.js.map +0 -1
  302. package/dist/chunk-6KVH5SP5.js.map +0 -1
  303. package/dist/chunk-7EVZUIHW.js.map +0 -1
  304. package/dist/chunk-7SUPA2YT.js.map +0 -1
  305. package/dist/chunk-7W5XSTUF.js.map +0 -1
  306. package/dist/chunk-BN2USWIR.js.map +0 -1
  307. package/dist/chunk-BQV42AFK.js.map +0 -1
  308. package/dist/chunk-CCRVDLV3.js.map +0 -1
  309. package/dist/chunk-EEPJGZJW.js.map +0 -1
  310. package/dist/chunk-G3HCU7TA.js.map +0 -1
  311. package/dist/chunk-GQRL6VNU.js.map +0 -1
  312. package/dist/chunk-GUSCYE3S.js.map +0 -1
  313. package/dist/chunk-H5FM75MR.js.map +0 -1
  314. package/dist/chunk-HFC4BTWJ.js.map +0 -1
  315. package/dist/chunk-HRJJH3RX.js.map +0 -1
  316. package/dist/chunk-K63OWUDJ.js.map +0 -1
  317. package/dist/chunk-L2TG5DBW.js.map +0 -1
  318. package/dist/chunk-M2EBUDZ7.js.map +0 -1
  319. package/dist/chunk-MCJSS6SM.js.map +0 -1
  320. package/dist/chunk-MFI4ZM7W.js.map +0 -1
  321. package/dist/chunk-MFRCM32T.js.map +0 -1
  322. package/dist/chunk-ND3XEGBE.js.map +0 -1
  323. package/dist/chunk-NEOV2NQ3.js +0 -1617
  324. package/dist/chunk-NEOV2NQ3.js.map +0 -1
  325. package/dist/chunk-NYATNLRK.js.map +0 -1
  326. package/dist/chunk-PJC4NXPA.js.map +0 -1
  327. package/dist/chunk-Q4TKVF3L.js.map +0 -1
  328. package/dist/chunk-RVUISYUE.js.map +0 -1
  329. package/dist/chunk-SIXYZ2FB.js.map +0 -1
  330. package/dist/chunk-TKVJE63N.js.map +0 -1
  331. package/dist/chunk-WEBBJBDH.js.map +0 -1
  332. package/dist/chunk-WFMUDX5J.js +0 -1092
  333. package/dist/chunk-WFMUDX5J.js.map +0 -1
  334. package/dist/chunk-XAWFPMAR.js.map +0 -1
  335. package/dist/chunk-XKBIYSSM.js.map +0 -1
  336. package/dist/chunk-XWJXZ6WE.js.map +0 -1
  337. package/dist/chunk-YGUNTIT5.js.map +0 -1
  338. package/dist/chunk-YNALYIQ3.js.map +0 -1
  339. package/dist/chunk-ZU5GNSOJ.js.map +0 -1
  340. package/dist/chunk-ZXNXAQAH.js.map +0 -1
  341. package/dist/chunk-ZYBWGSAZ.js.map +0 -1
  342. /package/dist/{DesignCanvas-5DJAFDNC.js.map → DesignCanvas-UNL34LS2.js.map} +0 -0
  343. /package/dist/{DesignCanvasEditor-XUJZARSN.js.map → DesignCanvasEditor-FJIVYOA2.js.map} +0 -0
  344. /package/dist/{IntakeInterview-WHVKMNQW.js.map → IntakeInterview-3ZIQMZS6.js.map} +0 -0
  345. /package/dist/{InvitationsPanel-XUUWZBPV.js.map → InvitationsPanel-CJ7DR4F7.js.map} +0 -0
  346. /package/dist/{MembersPanel-FOVHWUQ3.js.map → MembersPanel-BB4OD2KB.js.map} +0 -0
  347. /package/dist/{TimelineEditor-I35SJJAJ.js.map → TimelineEditor-2ECKSU6P.js.map} +0 -0
  348. /package/dist/{chunk-7C64WKAH.js.map → chunk-5SXS3YAB.js.map} +0 -0
  349. /package/dist/{chunk-R4NOKZMD.js.map → chunk-ROLFDOMJ.js.map} +0 -0
  350. /package/dist/{chunk-GNL3MG5J.js.map → chunk-S564OFTL.js.map} +0 -0
  351. /package/dist/{chunk-5I72FRZY.js.map → chunk-VOJB7MVG.js.map} +0 -0
  352. /package/dist/{chunk-USYXHDKE.js.map → chunk-YZ6KQULN.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/billing/index.ts"],"sourcesContent":["/**\n * Per-workspace budget-capped model keys — app-owned billing, metered on Tangle.\n *\n * Each workspace (the paying entity) runs the agent on its OWN child API key\n * minted from the platform parent key. The child carries a hard USD budget the\n * Tangle Router enforces AT THE KEY — model spend can't exceed the allowance,\n * zero app-side accounting. The app charges its own subscription (e.g. 5× the\n * allowance) and re-provisions each period. Child budgets are IMMUTABLE on the\n * platform, so a new budget = a fresh key + revoke the prior (rotate).\n *\n * The mint / rotate / rollover / usage LOGIC is generic and lives here.\n * Persistence (which D1 table), secret encryption, and key provisioning are\n * SEAMS each product supplies — so this module imports no DB and no key-mgmt\n * SDK (structural contracts only, like `../tangle`). The `@tangle-network/tcloud`\n * SDK is the provisioner a product passes in; it is not a dependency here.\n */\n\n/** The key-provisioning operations the key manager needs. Wire it from the\n * platform via {@link createTcloudKeyProvisioner} rather than casting. */\nexport interface KeyProvisioner {\n createKey(input: { name: string; product: string; budgetUsd: number; expiresAt: string }): Promise<{ id?: string; key?: string }>\n revokeKey(keyId: string): Promise<unknown>\n getKey(keyId: string): Promise<{ budgetUsd?: number; budgetSpent?: number; expiresAt?: string | null }>\n}\n\n/**\n * The subset of the `@tangle-network/tcloud` `TCloudClient` the provisioner uses\n * — declared with METHOD syntax so the real client (whose `product` is a narrow\n * union and whose budgets are `number | null`) is assignable bivariantly. The\n * real SDK client satisfies this; pass it straight in.\n */\nexport interface TcloudKeyClient {\n createKey(opts: {\n name: string\n product?: string\n budgetUsd?: number\n expiresAt?: string\n parentKeyId?: string\n allowedModels?: string[]\n rpmLimit?: number\n }): Promise<{ id: string; key: string }>\n getKey(id: string): Promise<{ budgetUsd?: number | null; budgetSpent?: number; expiresAt?: string | null }>\n revokeKey(id: string): Promise<unknown>\n}\n\n/**\n * Adapt the tcloud SDK client to {@link KeyProvisioner} — the typed seam that\n * replaces the `as unknown as KeyProvisioner` cast every consumer otherwise\n * repeats. The platform already exposes child-key minting (parent→child key,\n * per-key USD budget, expiry); this maps its shapes (`product` union,\n * `number | null` budgets) onto the manager's contract (`null → undefined`).\n */\nexport function createTcloudKeyProvisioner(client: TcloudKeyClient): KeyProvisioner {\n return {\n createKey: async (input) => {\n const created = await client.createKey(input)\n return { id: created.id, key: created.key }\n },\n revokeKey: (keyId) => client.revokeKey(keyId),\n getKey: async (keyId) => {\n const info = await client.getKey(keyId)\n return {\n budgetUsd: info.budgetUsd ?? undefined,\n budgetSpent: info.budgetSpent ?? undefined,\n expiresAt: info.expiresAt ?? null,\n }\n },\n }\n}\n\n/** A stored child-key record (the app's row, shape-normalized). */\nexport interface WorkspaceKeyRecord {\n /** App row id (opaque). */\n id: string\n keyId: string\n /** The encrypted secret — decrypted via {@link KeyCrypto.decrypt}. */\n keyEncrypted: string\n budgetUsd: number\n expiresAt: Date | null\n}\n\n/** Persistence seam — the product implements this against its own D1 table. */\nexport interface WorkspaceKeyStore {\n /** Most-recent active key for the workspace, or null. */\n getActive(workspaceId: string): Promise<WorkspaceKeyRecord | null>\n /** All active keys (to revoke priors on rotate). */\n listActive(workspaceId: string): Promise<Array<{ id: string; keyId: string }>>\n /** Persist a freshly minted active key. */\n insert(record: { workspaceId: string; keyId: string; keyEncrypted: string; budgetUsd: number; expiresAt: Date }): Promise<void>\n /** Mark a prior row revoked. */\n markRevoked(id: string, now: Date): Promise<void>\n}\n\n/** Secret encryption seam (the app's at-rest crypto). */\nexport interface KeyCrypto {\n encrypt(secret: string): Promise<string>\n decrypt(encrypted: string): Promise<string>\n}\n\n/** Define configuration options for managing workspace keys including provisioning, storage, and cryptography */\nexport interface WorkspaceKeyManagerOptions {\n provisioner: KeyProvisioner\n store: WorkspaceKeyStore\n crypto: KeyCrypto\n /** Default monthly allowance (USD) when a call doesn't specify one. */\n defaultBudgetUsd: number\n /** Injectable clock. Default `() => new Date()`. */\n now?: () => Date\n /** tcloud product the key is scoped to. Default `'router'`. */\n product?: string\n}\n\n/** Describe usage and budget details for a workspace model key including expiration and exhaustion status */\nexport interface WorkspaceModelKeyUsage {\n keyId: string\n budgetUsd: number\n budgetSpent: number\n budgetRemaining: number\n expiresAt: string | null\n exhausted: boolean\n}\n\n/** Manage workspace keys by ensuring, rotating, and tracking usage of active child-key secrets */\nexport interface WorkspaceKeyManager {\n /** The workspace's active child-key secret, provisioning one if absent/expired. */\n ensureKey(workspaceId: string, opts?: { budgetUsd?: number }): Promise<string>\n /** Mint a fresh key + revoke priors (period renewal / top-up). `rollover`\n * carries the prior key's unused budget into the new one, bounded by\n * `rolloverCapUsd`. Returns the new secret. */\n rotateKey(workspaceId: string, opts?: { budgetUsd?: number; rollover?: boolean; rolloverCapUsd?: number }): Promise<string>\n /** Live budget usage for the active key (drives the \"$X of $Y used\" panel). */\n getUsage(workspaceId: string): Promise<WorkspaceModelKeyUsage | null>\n}\n\n/** Period end = first day of next month, midnight UTC. Keys expire at the period\n * boundary so a forgotten rotation fails closed rather than running free. */\nfunction nextPeriodEnd(now: Date): Date {\n return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1, 0, 0, 0, 0))\n}\n\n/** A round-trip probe through the at-rest crypto seam (no platform key needed).\n * Runs BEFORE minting so a consumer with a missing/invalid FIELD_ENCRYPTION_KEY\n * fails loud with zero platform spend instead of orphaning a freshly minted key\n * when encrypt later throws. The probe never touches the real secret. */\nasync function assertCryptoUsable(crypto: KeyCrypto): Promise<void> {\n const probe = 'agent-app:key-manager:crypto-probe'\n let roundTripped: string\n try {\n roundTripped = await crypto.decrypt(await crypto.encrypt(probe))\n } catch (err) {\n throw new Error(\n 'Key encryption is misconfigured: the crypto seam threw before minting. ' +\n 'Validate FIELD_ENCRYPTION_KEY (64-char hex) at startup. No platform key was minted.',\n { cause: err },\n )\n }\n if (roundTripped !== probe) {\n throw new Error(\n 'Key encryption is misconfigured: encrypt/decrypt round-trip did not preserve the plaintext. ' +\n 'No platform key was minted.',\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared-platform-balance billing\n//\n// A DIFFERENT model from the per-workspace child-key manager above: here every\n// user runs against a SHARED platform balance (id.tangle.tools), keyed by the\n// user's platform identity. The app owns no key minting — it reads the balance,\n// gates a billable turn, and deducts spend through the platform billing API.\n// Plan limits, the platform transport, and identity resolution are SEAMS the\n// product supplies; this module imports no DB and no HTTP client.\n// ---------------------------------------------------------------------------\n\n/** A user's resolved platform identity (from the app's SSO account store). */\nexport interface PlatformIdentity {\n platformUserId: string\n /** The user's per-user platform API key (reads), or null when unlinked. */\n apiKey: string | null\n}\n\n/** Spendable balance for a platform user. */\nexport interface PlatformBalanceInfo {\n balance: number\n lifetimeSpent: number\n}\n\n/** Per-product spend aggregate. */\nexport interface PlatformProductUsage {\n product: string | null\n totalSpent: number\n count: number\n}\n\n/** Plan limits — a PARAMETER per product (dollar allowance, concurrency,\n * overage policy). Never baked into the framework. */\nexport interface PlanLimit {\n monthlyBalanceUsd: number\n concurrency: number\n overageAllowed: boolean\n}\n\n/**\n * The platform billing transport — the product wires these to id.tangle.tools\n * (or any balance backend). Reads authenticate as the user (their `apiKey`);\n * the deduct write is a service-token call naming the target user. This module\n * never touches HTTP — it only sequences these calls.\n */\nexport interface PlatformBillingClient<Plan extends string> {\n /** Resolve the user's platform identity, or null when there is no SSO account. */\n resolveIdentity(userId: string): Promise<PlatformIdentity | null>\n /** Subscription plan for the user (via their platform key). */\n getPlan(apiKey: string): Promise<Plan>\n /** Spendable balance for the user (via their platform key). */\n getBalance(apiKey: string): Promise<PlatformBalanceInfo>\n /** Per-product usage rows for the user (via their platform key). */\n getUsageByProduct(apiKey: string): Promise<PlatformProductUsage[]>\n /** Deduct spend against the user's balance (service-token write). */\n deduct(input: { platformUserId: string; amountUsd: number; type: string; description: string; referenceId: string }): Promise<void>\n}\n\n/** Define shared billing state including user ID, plan, balances, concurrency, and overage permission */\nexport interface SharedBillingState<Plan extends string> {\n /** Platform user id, or null when the user has no Tangle SSO account. */\n platformUserId: string | null\n plan: Plan\n monthlyBalanceUsd: number\n remainingBalanceUsd: number\n lifetimeSpentUsd: number\n concurrency: number\n overageAllowed: boolean\n}\n\n/** Define configuration options for managing platform balance based on billing plans */\nexport interface PlatformBalanceManagerOptions<Plan extends string> {\n client: PlatformBillingClient<Plan>\n /** Plan → limits map (the product's pricing). */\n planLimits: Record<Plan, PlanLimit>\n /** The plan an unlinked / outage user falls to (fails CLOSED). */\n freePlan: Plan\n /** The product slug to attribute usage to (for `getProductUsage`). */\n productSlug: string\n}\n\n/** Manage user plans and balances including state retrieval, billing authorization, deduction, and usage tracking */\nexport interface PlatformBalanceManager<Plan extends string> {\n /** Resolve the user's plan + balance. Unlinked or platform-outage users fail\n * CLOSED: free plan, zero remaining balance — a billable run is never started\n * against an unknown balance. */\n getState(userId: string): Promise<SharedBillingState<Plan>>\n /** Gate a billable turn: allowed when the plan permits overage or remaining\n * balance is positive. Returns the state so the caller deducts against it. */\n canStartBillableTurn(userId: string): Promise<{ allowed: boolean; state: SharedBillingState<Plan> }>\n /** Deduct `amountUsd` against the user's platform balance. Throws when the\n * user is not platform-linked. */\n deduct(userId: string, params: { amountUsd: number; type: string; description: string; referenceId: string }): Promise<void>\n /** This product's spend for the user (drives a usage panel). */\n getProductUsage(userId: string): Promise<{ spentUsd: number; transactionCount: number }>\n}\n\n/** Create a platform balance manager to handle user plan limits and state based on provided options */\nexport function createPlatformBalanceManager<Plan extends string>(\n opts: PlatformBalanceManagerOptions<Plan>,\n): PlatformBalanceManager<Plan> {\n const { client, planLimits, freePlan, productSlug } = opts\n\n const getState: PlatformBalanceManager<Plan>['getState'] = async (userId) => {\n const identity = await client.resolveIdentity(userId)\n // No SSO account, or linked without a platform key: unlinked free tier with\n // zero balance. Reads require the user's key — never call them empty.\n if (!identity || !identity.apiKey) {\n const limits = planLimits[freePlan]\n return {\n platformUserId: identity?.platformUserId ?? null,\n plan: freePlan,\n monthlyBalanceUsd: limits.monthlyBalanceUsd,\n remainingBalanceUsd: 0,\n lifetimeSpentUsd: 0,\n concurrency: limits.concurrency,\n overageAllowed: limits.overageAllowed,\n }\n }\n const [plan, balance] = await Promise.all([client.getPlan(identity.apiKey), client.getBalance(identity.apiKey)])\n const limits = planLimits[plan]\n return {\n platformUserId: identity.platformUserId,\n plan,\n monthlyBalanceUsd: limits.monthlyBalanceUsd,\n remainingBalanceUsd: balance.balance,\n lifetimeSpentUsd: balance.lifetimeSpent,\n concurrency: limits.concurrency,\n overageAllowed: limits.overageAllowed,\n }\n }\n\n const canStartBillableTurn: PlatformBalanceManager<Plan>['canStartBillableTurn'] = async (userId) => {\n const state = await getState(userId)\n if (!state.platformUserId) return { allowed: false, state }\n const allowed = state.overageAllowed || state.remainingBalanceUsd > 0\n return { allowed, state }\n }\n\n const deduct: PlatformBalanceManager<Plan>['deduct'] = async (userId, params) => {\n const identity = await client.resolveIdentity(userId)\n if (!identity) throw new Error('Shared billing requires a platform-linked user')\n await client.deduct({\n platformUserId: identity.platformUserId,\n amountUsd: params.amountUsd,\n type: params.type,\n description: params.description,\n referenceId: params.referenceId,\n })\n }\n\n const getProductUsage: PlatformBalanceManager<Plan>['getProductUsage'] = async (userId) => {\n const identity = await client.resolveIdentity(userId)\n if (!identity?.apiKey) return { spentUsd: 0, transactionCount: 0 }\n const rows = await client.getUsageByProduct(identity.apiKey)\n const product = rows.find((row) => row.product === productSlug)\n return { spentUsd: product?.totalSpent ?? 0, transactionCount: product?.count ?? 0 }\n }\n\n return { getState, canStartBillableTurn, deduct, getProductUsage }\n}\n\n/** Create a workspace key manager that handles key provisioning and budget tracking */\nexport function createWorkspaceKeyManager(opts: WorkspaceKeyManagerOptions): WorkspaceKeyManager {\n const clock = opts.now ?? (() => new Date())\n const product = opts.product ?? 'router'\n\n const getUsage: WorkspaceKeyManager['getUsage'] = async (workspaceId) => {\n const active = await opts.store.getActive(workspaceId)\n if (!active) return null\n const info = await opts.provisioner.getKey(active.keyId)\n const budgetUsd = info.budgetUsd ?? active.budgetUsd\n const budgetSpent = info.budgetSpent ?? 0\n const budgetRemaining = Math.max(0, budgetUsd - budgetSpent)\n return {\n keyId: active.keyId,\n budgetUsd,\n budgetSpent,\n budgetRemaining,\n expiresAt: info.expiresAt ?? (active.expiresAt ? active.expiresAt.toISOString() : null),\n exhausted: budgetRemaining <= 0,\n }\n }\n\n const rotateKey: WorkspaceKeyManager['rotateKey'] = async (workspaceId, ropts) => {\n const now = clock()\n const allowance = ropts?.budgetUsd ?? opts.defaultBudgetUsd\n\n let budgetUsd = allowance\n if (ropts?.rollover) {\n const prior = await getUsage(workspaceId).catch(() => null)\n budgetUsd = allowance + (prior?.budgetRemaining ?? 0)\n if (ropts.rolloverCapUsd != null) budgetUsd = Math.min(budgetUsd, ropts.rolloverCapUsd)\n }\n\n // Prove the at-rest crypto seam is usable BEFORE spending a platform mint.\n // The `KeyCrypto` impl may close over an env-resolved key (createFieldCrypto)\n // that is empty/invalid and only throws at encrypt time — after the mint —\n // orphaning the child key. A round-trip probe makes a misconfigured consumer\n // fail loud with ZERO platform spend.\n await assertCryptoUsable(opts.crypto)\n\n const expiresAt = nextPeriodEnd(now)\n const created = await opts.provisioner.createKey({ name: `ws:${workspaceId}`, product, budgetUsd, expiresAt: expiresAt.toISOString() })\n if (!created.key || !created.id) throw new Error('tcloud createKey returned no key')\n const mintedKeyId = created.id\n\n // Past this point the platform has minted a budget-bearing child key. ANY\n // failure before the row is persisted (encrypt, insert) MUST revoke the\n // just-minted key, else it leaks as a zombie holding parent-key headroom —\n // a missing/invalid encryption key would otherwise burn the child budget\n // per mint attempt until the parent can no longer mint (fleet-wide outage).\n let keyEncrypted: string\n let priors: Array<{ id: string; keyId: string }>\n try {\n keyEncrypted = await opts.crypto.encrypt(created.key)\n priors = await opts.store.listActive(workspaceId)\n await opts.store.insert({ workspaceId, keyId: mintedKeyId, keyEncrypted, budgetUsd, expiresAt })\n } catch (err) {\n // Best-effort revoke of the orphan. A revoke failure must NOT mask the\n // original cause — log it loudly and rethrow the original error.\n try {\n await opts.provisioner.revokeKey(mintedKeyId)\n } catch (revokeErr) {\n console.error(\n `[workspace-key-manager] FAILED to revoke orphaned child key ${mintedKeyId} ` +\n `for workspace ${workspaceId} after a post-mint failure — it now leaks parent-key budget. ` +\n `Revoke error:`,\n revokeErr,\n )\n }\n throw err\n }\n\n for (const p of priors) {\n await opts.store.markRevoked(p.id, now)\n // Best-effort upstream revoke — the row is already revoked and an expired\n // key fails closed regardless, so a transient error is non-fatal.\n try {\n await opts.provisioner.revokeKey(p.keyId)\n } catch {\n /* non-fatal */\n }\n }\n return created.key\n }\n\n const ensureKey: WorkspaceKeyManager['ensureKey'] = async (workspaceId, eopts) => {\n const now = clock()\n const active = await opts.store.getActive(workspaceId)\n if (active && (!active.expiresAt || active.expiresAt.getTime() > now.getTime())) {\n return opts.crypto.decrypt(active.keyEncrypted)\n }\n return rotateKey(workspaceId, { budgetUsd: eopts?.budgetUsd })\n }\n\n return { ensureKey, rotateKey, getUsage }\n}\n"],"mappings":";AAoDO,SAAS,2BAA2B,QAAyC;AAClF,SAAO;AAAA,IACL,WAAW,OAAO,UAAU;AAC1B,YAAM,UAAU,MAAM,OAAO,UAAU,KAAK;AAC5C,aAAO,EAAE,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAAI;AAAA,IAC5C;AAAA,IACA,WAAW,CAAC,UAAU,OAAO,UAAU,KAAK;AAAA,IAC5C,QAAQ,OAAO,UAAU;AACvB,YAAM,OAAO,MAAM,OAAO,OAAO,KAAK;AACtC,aAAO;AAAA,QACL,WAAW,KAAK,aAAa;AAAA,QAC7B,aAAa,KAAK,eAAe;AAAA,QACjC,WAAW,KAAK,aAAa;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACF;AAoEA,SAAS,cAAc,KAAiB;AACtC,SAAO,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AACtF;AAMA,eAAe,mBAAmB,QAAkC;AAClE,QAAM,QAAQ;AACd,MAAI;AACJ,MAAI;AACF,mBAAe,MAAM,OAAO,QAAQ,MAAM,OAAO,QAAQ,KAAK,CAAC;AAAA,EACjE,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MAEA,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AACA,MAAI,iBAAiB,OAAO;AAC1B,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACF;AAoGO,SAAS,6BACd,MAC8B;AAC9B,QAAM,EAAE,QAAQ,YAAY,UAAU,YAAY,IAAI;AAEtD,QAAM,WAAqD,OAAO,WAAW;AAC3E,UAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AAGpD,QAAI,CAAC,YAAY,CAAC,SAAS,QAAQ;AACjC,YAAMA,UAAS,WAAW,QAAQ;AAClC,aAAO;AAAA,QACL,gBAAgB,UAAU,kBAAkB;AAAA,QAC5C,MAAM;AAAA,QACN,mBAAmBA,QAAO;AAAA,QAC1B,qBAAqB;AAAA,QACrB,kBAAkB;AAAA,QAClB,aAAaA,QAAO;AAAA,QACpB,gBAAgBA,QAAO;AAAA,MACzB;AAAA,IACF;AACA,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,QAAQ,IAAI,CAAC,OAAO,QAAQ,SAAS,MAAM,GAAG,OAAO,WAAW,SAAS,MAAM,CAAC,CAAC;AAC/G,UAAM,SAAS,WAAW,IAAI;AAC9B,WAAO;AAAA,MACL,gBAAgB,SAAS;AAAA,MACzB;AAAA,MACA,mBAAmB,OAAO;AAAA,MAC1B,qBAAqB,QAAQ;AAAA,MAC7B,kBAAkB,QAAQ;AAAA,MAC1B,aAAa,OAAO;AAAA,MACpB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,uBAA6E,OAAO,WAAW;AACnG,UAAM,QAAQ,MAAM,SAAS,MAAM;AACnC,QAAI,CAAC,MAAM,eAAgB,QAAO,EAAE,SAAS,OAAO,MAAM;AAC1D,UAAM,UAAU,MAAM,kBAAkB,MAAM,sBAAsB;AACpE,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAEA,QAAM,SAAiD,OAAO,QAAQ,WAAW;AAC/E,UAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AACpD,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,gDAAgD;AAC/E,UAAM,OAAO,OAAO;AAAA,MAClB,gBAAgB,SAAS;AAAA,MACzB,WAAW,OAAO;AAAA,MAClB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,aAAa,OAAO;AAAA,IACtB,CAAC;AAAA,EACH;AAEA,QAAM,kBAAmE,OAAO,WAAW;AACzF,UAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AACpD,QAAI,CAAC,UAAU,OAAQ,QAAO,EAAE,UAAU,GAAG,kBAAkB,EAAE;AACjE,UAAM,OAAO,MAAM,OAAO,kBAAkB,SAAS,MAAM;AAC3D,UAAM,UAAU,KAAK,KAAK,CAAC,QAAQ,IAAI,YAAY,WAAW;AAC9D,WAAO,EAAE,UAAU,SAAS,cAAc,GAAG,kBAAkB,SAAS,SAAS,EAAE;AAAA,EACrF;AAEA,SAAO,EAAE,UAAU,sBAAsB,QAAQ,gBAAgB;AACnE;AAGO,SAAS,0BAA0B,MAAuD;AAC/F,QAAM,QAAQ,KAAK,QAAQ,MAAM,oBAAI,KAAK;AAC1C,QAAM,UAAU,KAAK,WAAW;AAEhC,QAAM,WAA4C,OAAO,gBAAgB;AACvE,UAAM,SAAS,MAAM,KAAK,MAAM,UAAU,WAAW;AACrD,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,OAAO,MAAM,KAAK,YAAY,OAAO,OAAO,KAAK;AACvD,UAAM,YAAY,KAAK,aAAa,OAAO;AAC3C,UAAM,cAAc,KAAK,eAAe;AACxC,UAAM,kBAAkB,KAAK,IAAI,GAAG,YAAY,WAAW;AAC3D,WAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,cAAc,OAAO,YAAY,OAAO,UAAU,YAAY,IAAI;AAAA,MAClF,WAAW,mBAAmB;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,YAA8C,OAAO,aAAa,UAAU;AAChF,UAAM,MAAM,MAAM;AAClB,UAAM,YAAY,OAAO,aAAa,KAAK;AAE3C,QAAI,YAAY;AAChB,QAAI,OAAO,UAAU;AACnB,YAAM,QAAQ,MAAM,SAAS,WAAW,EAAE,MAAM,MAAM,IAAI;AAC1D,kBAAY,aAAa,OAAO,mBAAmB;AACnD,UAAI,MAAM,kBAAkB,KAAM,aAAY,KAAK,IAAI,WAAW,MAAM,cAAc;AAAA,IACxF;AAOA,UAAM,mBAAmB,KAAK,MAAM;AAEpC,UAAM,YAAY,cAAc,GAAG;AACnC,UAAM,UAAU,MAAM,KAAK,YAAY,UAAU,EAAE,MAAM,MAAM,WAAW,IAAI,SAAS,WAAW,WAAW,UAAU,YAAY,EAAE,CAAC;AACtI,QAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,GAAI,OAAM,IAAI,MAAM,kCAAkC;AACnF,UAAM,cAAc,QAAQ;AAO5B,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,qBAAe,MAAM,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACpD,eAAS,MAAM,KAAK,MAAM,WAAW,WAAW;AAChD,YAAM,KAAK,MAAM,OAAO,EAAE,aAAa,OAAO,aAAa,cAAc,WAAW,UAAU,CAAC;AAAA,IACjG,SAAS,KAAK;AAGZ,UAAI;AACF,cAAM,KAAK,YAAY,UAAU,WAAW;AAAA,MAC9C,SAAS,WAAW;AAClB,gBAAQ;AAAA,UACN,+DAA+D,WAAW,kBACvD,WAAW;AAAA,UAE9B;AAAA,QACF;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAEA,eAAW,KAAK,QAAQ;AACtB,YAAM,KAAK,MAAM,YAAY,EAAE,IAAI,GAAG;AAGtC,UAAI;AACF,cAAM,KAAK,YAAY,UAAU,EAAE,KAAK;AAAA,MAC1C,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,YAA8C,OAAO,aAAa,UAAU;AAChF,UAAM,MAAM,MAAM;AAClB,UAAM,SAAS,MAAM,KAAK,MAAM,UAAU,WAAW;AACrD,QAAI,WAAW,CAAC,OAAO,aAAa,OAAO,UAAU,QAAQ,IAAI,IAAI,QAAQ,IAAI;AAC/E,aAAO,KAAK,OAAO,QAAQ,OAAO,YAAY;AAAA,IAChD;AACA,WAAO,UAAU,aAAa,EAAE,WAAW,OAAO,UAAU,CAAC;AAAA,EAC/D;AAEA,SAAO,EAAE,WAAW,WAAW,SAAS;AAC1C;","names":["limits"]}
@@ -163,4 +163,4 @@ export {
163
163
  useSandboxTerminalConnection,
164
164
  tabTerminalConnectionId
165
165
  };
166
- //# sourceMappingURL=chunk-65P3HJY3.js.map
166
+ //# sourceMappingURL=chunk-HCOROIRT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-react/sandbox-terminal.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react'\n\n/** Define the connection details and status for a sandbox terminal session */\nexport interface SandboxTerminalConnection {\n runtimeUrl: string | null\n sidecarUrl: string | null\n token: string | null\n expiresAt: string | null\n status: string\n error: string | null\n loading: boolean\n sandboxId?: string\n}\n\n/** Define the response structure for a sandbox terminal connection including URLs, token, status, and errors */\nexport interface SandboxTerminalConnectionResponse {\n runtimeUrl?: string\n sidecarUrl?: string\n token?: string\n expiresAt?: string\n status?: string\n error?: string\n sandboxId?: string\n}\n\n/** Define options for configuring a sandbox terminal connection including workspace ID and connection parameters */\nexport interface UseSandboxTerminalConnectionOptions {\n workspaceId: string\n connectionUrl?: string | ((workspaceId: string) => string)\n fetcher?: typeof fetch\n provisionPollIntervalMs?: number\n provisionPollTimeoutMs?: number\n tokenRefreshSkewMs?: number\n}\n\n/** Resolve sandbox terminal connection status and provide a method to initiate the connection */\nexport interface UseSandboxTerminalConnectionResult extends SandboxTerminalConnection {\n connect: () => Promise<void>\n}\n\nconst DEFAULT_PROVISION_POLL_INTERVAL_MS = 2_000\nconst DEFAULT_PROVISION_POLL_TIMEOUT_MS = 90_000\nconst DEFAULT_TOKEN_REFRESH_SKEW_MS = 120_000\n\nconst EMPTY_CONNECTION: SandboxTerminalConnection = {\n runtimeUrl: null,\n sidecarUrl: null,\n token: null,\n expiresAt: null,\n status: 'idle',\n error: null,\n loading: false,\n}\n\n/** Manage and maintain a sandbox terminal connection with automatic polling and token refresh handling */\nexport function useSandboxTerminalConnection(opts: UseSandboxTerminalConnectionOptions): UseSandboxTerminalConnectionResult {\n const [conn, setConn] = useState<SandboxTerminalConnection>(EMPTY_CONNECTION)\n const mountedRef = useRef(false)\n const generationRef = useRef(0)\n const fetcher = opts.fetcher ?? fetch\n const pollIntervalMs = opts.provisionPollIntervalMs ?? DEFAULT_PROVISION_POLL_INTERVAL_MS\n const pollTimeoutMs = opts.provisionPollTimeoutMs ?? DEFAULT_PROVISION_POLL_TIMEOUT_MS\n const refreshSkewMs = opts.tokenRefreshSkewMs ?? DEFAULT_TOKEN_REFRESH_SKEW_MS\n\n const connectionUrl = useCallback(() => {\n if (typeof opts.connectionUrl === 'function') return opts.connectionUrl(opts.workspaceId)\n return opts.connectionUrl ?? `/api/workspaces/${encodeURIComponent(opts.workspaceId)}/sandbox/connection`\n }, [opts.connectionUrl, opts.workspaceId])\n\n const connect = useCallback(async () => {\n const generation = generationRef.current + 1\n generationRef.current = generation\n const isCurrent = () => mountedRef.current && generationRef.current === generation\n const setCurrentConn: typeof setConn = (value) => {\n if (!isCurrent()) return\n setConn(value)\n }\n\n setCurrentConn((current) => ({ ...current, loading: true, error: null }))\n const deadline = Date.now() + pollTimeoutMs\n while (isCurrent()) {\n try {\n const res = await fetcher(connectionUrl())\n const data = (await res.json()) as SandboxTerminalConnectionResponse\n if (!isCurrent()) return\n const runtimeUrl = data.runtimeUrl ?? data.sidecarUrl\n if (res.ok && runtimeUrl && data.token && data.expiresAt) {\n setCurrentConn({\n runtimeUrl,\n sidecarUrl: data.sidecarUrl ?? runtimeUrl,\n token: data.token,\n expiresAt: data.expiresAt,\n status: data.status ?? 'running',\n error: null,\n loading: false,\n ...(data.sandboxId ? { sandboxId: data.sandboxId } : {}),\n })\n return\n }\n if (res.ok) {\n setCurrentConn({\n runtimeUrl: null,\n sidecarUrl: null,\n token: null,\n expiresAt: null,\n loading: false,\n error: 'Sandbox connection response is missing required fields',\n status: data.status ?? 'error',\n ...(data.sandboxId ? { sandboxId: data.sandboxId } : {}),\n })\n return\n }\n if (res.status === 503 && Date.now() < deadline) {\n setCurrentConn((current) => ({\n ...current,\n loading: true,\n status: data.status ?? 'provisioning',\n error: null,\n }))\n await sleep(pollIntervalMs)\n continue\n }\n setCurrentConn((current) => ({\n ...current,\n runtimeUrl: null,\n sidecarUrl: null,\n token: null,\n expiresAt: null,\n loading: false,\n error: data.error ?? 'Sandbox not available',\n status: data.status ?? 'error',\n }))\n return\n } catch (err) {\n if (!isCurrent()) return\n if (Date.now() < deadline) {\n await sleep(pollIntervalMs)\n continue\n }\n setCurrentConn((current) => ({\n ...current,\n runtimeUrl: null,\n sidecarUrl: null,\n token: null,\n expiresAt: null,\n loading: false,\n error: err instanceof Error ? err.message : 'Connection failed',\n }))\n return\n }\n }\n }, [connectionUrl, fetcher, pollIntervalMs, pollTimeoutMs])\n\n useEffect(() => {\n mountedRef.current = true\n return () => {\n mountedRef.current = false\n generationRef.current += 1\n }\n }, [])\n\n useEffect(() => {\n void connect()\n }, [connect])\n\n useEffect(() => {\n if (!conn.runtimeUrl || !conn.token || !conn.expiresAt) return\n const refreshAt = Date.parse(conn.expiresAt) - Date.now() - refreshSkewMs\n if (!Number.isFinite(refreshAt)) {\n setConn((current) => ({\n ...current,\n runtimeUrl: null,\n sidecarUrl: null,\n token: null,\n expiresAt: null,\n status: 'error',\n error: 'Sandbox token expiry is invalid',\n }))\n return\n }\n const timer = window.setTimeout(() => {\n void connect()\n }, Math.max(1_000, refreshAt))\n return () => window.clearTimeout(timer)\n }, [conn.runtimeUrl, conn.token, conn.expiresAt, connect, refreshSkewMs])\n\n return { ...conn, connect }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => window.setTimeout(resolve, ms))\n}\n\nconst DEFAULT_TERMINAL_CID_KEY = 'agent-app:terminal-connection-id'\n\nfunction newConnectionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n return `cid-${Date.now().toString(36)}-${Math.floor(Math.random() * 1e9).toString(36)}`\n}\n\n/**\n * Stable-per-tab, unique-per-client terminal connection id.\n *\n * Persists in `sessionStorage` so a reload in the same tab reuses the id (the\n * sidecar restores the same PTY session via `TerminalView.connectionId`), while\n * separate tabs/windows each get a distinct id. Pass the result as\n * `TerminalView`'s `connectionId`. Without it (e.g. gtm-agent today) every tab\n * shares one connection id and their reconnects evict each other.\n *\n * Falls back to an ephemeral id when `sessionStorage` is unavailable (SSR,\n * privacy mode) — still unique per call, just not reload-stable.\n */\nexport function tabTerminalConnectionId(storageKey: string = DEFAULT_TERMINAL_CID_KEY): string {\n try {\n const store = globalThis.sessionStorage\n const existing = store?.getItem(storageKey)\n if (existing) return existing\n const id = newConnectionId()\n store?.setItem(storageKey, id)\n return id\n } catch {\n return newConnectionId()\n }\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-TA5Q4I2K.js";
4
4
  import {
5
5
  createWorkspaceKeyManager
6
- } from "./chunk-G3HCU7TA.js";
6
+ } from "./chunk-GXE6KHTS.js";
7
7
 
8
8
  // src/preset-cloudflare/index.ts
9
9
  var PRESET_TABLES = {
@@ -294,4 +294,4 @@ export {
294
294
  createPresetWorkspaceKeyStore,
295
295
  createPresetWorkspaceKeyManager
296
296
  };
297
- //# sourceMappingURL=chunk-54GSK2KK.js.map
297
+ //# sourceMappingURL=chunk-HYTIHYNV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preset-cloudflare/index.ts"],"sourcesContent":["/**\n * `@tangle-network/agent-app/preset-cloudflare` — the batteries-included default\n * stack.\n *\n * Every fleet agent runs the SAME backend: Cloudflare D1 (SQLite) through\n * Drizzle for state, a KV namespace as the artifact vault, AES-GCM field crypto\n * for PII, and per-workspace budget-capped model keys. The other agent-app\n * modules are pure SEAMS — `./tools` needs an `AppToolHandlers`, `./knowledge`\n * needs a `KnowledgeStateAccessor`, `./billing` needs a `WorkspaceKeyStore` +\n * `KeyCrypto`. This module is the ONE implementation of those seams against the\n * house stack, so a consumer that runs D1 + KV stands the whole shell up with\n * config + bindings and ZERO handler code.\n *\n * Layering:\n * - Drizzle is a PEER (the consumer installs `drizzle-orm`, never bundled). The\n * schema is therefore expressed two ways that need no import here: the plain\n * DDL ({@link PRESET_MIGRATION_SQL}) a consumer runs to create the tables, and\n * a {@link createPresetDrizzleSchema} factory that takes the consumer's\n * `drizzle-orm/sqlite-core` builder module and returns the typed tables. The\n * column names in {@link PRESET_TABLES} are the contract the handlers,\n * accessor, and DDL all agree on.\n * - D1 + KV are STRUCTURAL: {@link D1Like} (Cloudflare `D1Database` satisfies it)\n * and `KvLike` from `../web` (Cloudflare `KVNamespace` satisfies it). No\n * `@cloudflare/workers-types` dependency.\n * - Crypto/billing reuse `../crypto` + `../billing` exactly — this only wires\n * them to the D1 key table.\n */\n\nimport { createFieldCrypto } from '../crypto/index'\nimport {\n createWorkspaceKeyManager,\n type KeyCrypto,\n type KeyProvisioner,\n type WorkspaceKeyManager,\n type WorkspaceKeyRecord,\n type WorkspaceKeyStore,\n} from '../billing/index'\nimport type { KnowledgeStateAccessor } from '../knowledge/index'\nimport type {\n AddCitationArgs,\n AddCitationResult,\n AppToolContext,\n AppToolHandlers,\n RenderUiArgs,\n RenderUiResult,\n ScheduleFollowupArgs,\n ScheduleFollowupResult,\n SubmitProposalArgs,\n SubmitProposalResult,\n} from '../tools/index'\nimport type { KvLike } from '../web/index'\n\n// ---------------------------------------------------------------------------\n// D1 structural seam\n//\n// The minimal surface of Cloudflare's `D1Database` the handlers + accessor use.\n// `D1Database` satisfies it structurally, so the consumer passes `env.DB`\n// directly and tests pass an in-memory fake. We keep it to the\n// prepare/bind/first/run/all shape D1 already exposes — no Drizzle here, so the\n// default handlers run on a fresh Worker with only the D1 binding.\n// ---------------------------------------------------------------------------\n\n/** A prepared, bound D1 statement. */\nexport interface D1PreparedLike {\n bind(...values: unknown[]): D1PreparedLike\n first<T = Record<string, unknown>>(colName?: string): Promise<T | null>\n run(): Promise<unknown>\n all<T = Record<string, unknown>>(): Promise<{ results: T[] }>\n}\n\n/** The D1 surface the preset needs. Cloudflare `D1Database` satisfies it. */\nexport interface D1Like {\n prepare(query: string): D1PreparedLike\n}\n\n// ---------------------------------------------------------------------------\n// Default schema\n//\n// The four tables the default handlers + accessor read/write. Column names are\n// the single source of truth shared by the DDL, the Drizzle factory, the\n// handlers, and the accessor. Every table is workspace-scoped on `workspace_id`\n// (the accessor's default `where` column) so the knowledge `count` rule and the\n// tool writes agree without per-consumer wiring.\n//\n// Deliberately NOT here: chat thread/message tables. `/chat-store` is the\n// single thread-schema owner (`createChatTables` + `createChatStore`); the\n// preset once declared an unconsumed `threads` DDL, removed so two schemas\n// can't drift.\n// ---------------------------------------------------------------------------\n\n/** The preset table + column names — the contract the DDL, Drizzle schema,\n * handlers, and accessor share. Exposed so a consumer can reference a column\n * without a string literal. */\nexport const PRESET_TABLES = {\n proposals: {\n name: 'proposals',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n threadId: 'thread_id',\n type: 'type',\n title: 'title',\n description: 'description',\n status: 'status',\n createdBy: 'created_by',\n createdAt: 'created_at',\n },\n },\n knowledge: {\n name: 'knowledge',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n path: 'path',\n kind: 'kind',\n label: 'label',\n content: 'content',\n createdAt: 'created_at',\n },\n },\n deadlines: {\n name: 'deadlines',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n threadId: 'thread_id',\n title: 'title',\n dueDate: 'due_date',\n priority: 'priority',\n status: 'status',\n createdAt: 'created_at',\n },\n },\n workspaceKeys: {\n name: 'workspace_keys',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n keyId: 'key_id',\n keyEncrypted: 'key_encrypted',\n budgetUsd: 'budget_usd',\n expiresAt: 'expires_at',\n revokedAt: 'revoked_at',\n createdAt: 'created_at',\n },\n },\n} as const\n\n/**\n * Plain DDL for the preset schema — run by a consumer to create the tables with\n * ZERO drizzle (`for (const sql of PRESET_MIGRATION_SQL) await db.prepare(sql).run()`,\n * or paste into a `.sql` migration). One statement per table so D1's\n * single-statement `prepare` accepts each. Matches {@link PRESET_TABLES} exactly.\n */\nexport const PRESET_MIGRATION_SQL: readonly string[] = [\n `CREATE TABLE IF NOT EXISTS proposals (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n thread_id TEXT,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n created_by TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS knowledge (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n path TEXT NOT NULL,\n kind TEXT NOT NULL,\n label TEXT,\n content TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS deadlines (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n thread_id TEXT,\n title TEXT NOT NULL,\n due_date TEXT NOT NULL,\n priority TEXT,\n status TEXT NOT NULL DEFAULT 'scheduled',\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS workspace_keys (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n key_id TEXT NOT NULL,\n key_encrypted TEXT NOT NULL,\n budget_usd REAL NOT NULL,\n expires_at INTEGER NOT NULL,\n revoked_at INTEGER,\n created_at INTEGER NOT NULL\n )`,\n `CREATE INDEX IF NOT EXISTS idx_proposals_ws ON proposals (workspace_id, status)`,\n `CREATE INDEX IF NOT EXISTS idx_deadlines_ws ON deadlines (workspace_id, status)`,\n `CREATE INDEX IF NOT EXISTS idx_knowledge_ws ON knowledge (workspace_id)`,\n `CREATE INDEX IF NOT EXISTS idx_workspace_keys_ws ON workspace_keys (workspace_id, revoked_at)`,\n]\n\n/** A chainable column builder — every modifier returns the builder so calls\n * like `.notNull().default('pending')` typecheck. The concrete drizzle builders\n * satisfy this structurally. */\nexport interface DrizzleColumnLike {\n primaryKey: () => DrizzleColumnLike\n notNull: () => DrizzleColumnLike\n default: (v: unknown) => DrizzleColumnLike\n}\n\n/** The shape of a `drizzle-orm/sqlite-core` module — the few builders the\n * preset schema uses. The consumer passes the real module; agent-app never\n * imports it (it stays a peer). */\nexport interface DrizzleSqliteCoreLike {\n sqliteTable: (name: string, columns: Record<string, DrizzleColumnLike>) => unknown\n text: (name?: string) => DrizzleColumnLike\n integer: (name?: string, config?: unknown) => DrizzleColumnLike\n real: (name?: string) => DrizzleColumnLike\n}\n\n/**\n * Build the typed Drizzle schema for the preset, given the consumer's\n * `drizzle-orm/sqlite-core` module. Returns one table object per\n * {@link PRESET_TABLES} entry — pass to `drizzle(db, { schema })` for typed\n * queries, or to drizzle-kit for migration generation. agent-app never imports\n * drizzle; the builder module is the seam.\n *\n * ```ts\n * import * as d from 'drizzle-orm/sqlite-core'\n * const schema = createPresetDrizzleSchema(d)\n * ```\n */\nexport function createPresetDrizzleSchema(d: DrizzleSqliteCoreLike) {\n const { sqliteTable, text, integer, real } = d\n const C = PRESET_TABLES\n return {\n proposals: sqliteTable(C.proposals.name, {\n id: text(C.proposals.columns.id).primaryKey(),\n workspaceId: text(C.proposals.columns.workspaceId).notNull(),\n threadId: text(C.proposals.columns.threadId),\n type: text(C.proposals.columns.type).notNull(),\n title: text(C.proposals.columns.title).notNull(),\n description: text(C.proposals.columns.description),\n status: text(C.proposals.columns.status).notNull().default('pending'),\n createdBy: text(C.proposals.columns.createdBy),\n createdAt: integer(C.proposals.columns.createdAt).notNull(),\n }),\n knowledge: sqliteTable(C.knowledge.name, {\n id: text(C.knowledge.columns.id).primaryKey(),\n workspaceId: text(C.knowledge.columns.workspaceId).notNull(),\n path: text(C.knowledge.columns.path).notNull(),\n kind: text(C.knowledge.columns.kind).notNull(),\n label: text(C.knowledge.columns.label),\n content: text(C.knowledge.columns.content),\n createdAt: integer(C.knowledge.columns.createdAt).notNull(),\n }),\n deadlines: sqliteTable(C.deadlines.name, {\n id: text(C.deadlines.columns.id).primaryKey(),\n workspaceId: text(C.deadlines.columns.workspaceId).notNull(),\n threadId: text(C.deadlines.columns.threadId),\n title: text(C.deadlines.columns.title).notNull(),\n dueDate: text(C.deadlines.columns.dueDate).notNull(),\n priority: text(C.deadlines.columns.priority),\n status: text(C.deadlines.columns.status).notNull().default('scheduled'),\n createdAt: integer(C.deadlines.columns.createdAt).notNull(),\n }),\n workspaceKeys: sqliteTable(C.workspaceKeys.name, {\n id: text(C.workspaceKeys.columns.id).primaryKey(),\n workspaceId: text(C.workspaceKeys.columns.workspaceId).notNull(),\n keyId: text(C.workspaceKeys.columns.keyId).notNull(),\n keyEncrypted: text(C.workspaceKeys.columns.keyEncrypted).notNull(),\n budgetUsd: real(C.workspaceKeys.columns.budgetUsd).notNull(),\n expiresAt: integer(C.workspaceKeys.columns.expiresAt).notNull(),\n revokedAt: integer(C.workspaceKeys.columns.revokedAt),\n createdAt: integer(C.workspaceKeys.columns.createdAt).notNull(),\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Default AppToolHandlers over D1 + KV\n// ---------------------------------------------------------------------------\n\n/** The KV-backed vault. `KvLike` (from `../web`) is the structural KV contract;\n * Cloudflare `KVNamespace` satisfies it. Artifacts are stored under their path. */\nexport type VaultKv = KvLike\n\n/** Define configuration options for handling preset tools including database, vault, and optional utilities */\nexport interface PresetToolHandlerOptions {\n /** The D1 database (Cloudflare `D1Database` satisfies {@link D1Like}). */\n db: D1Like\n /** The KV namespace used as the artifact vault. */\n vault: VaultKv\n /** Id generator. Default `crypto.randomUUID`. Injectable for deterministic tests. */\n newId?: () => string\n /** Clock (epoch ms). Default `Date.now`. Injectable for deterministic tests. */\n now?: () => number\n /** Vault path prefix for `render_ui` artifacts. Default `'ui'`. */\n uiPathPrefix?: string\n /** Vault path prefix for `add_citation` artifacts. Default `'citations'`. */\n citationPathPrefix?: string\n}\n\nfunction slug(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64) || 'item'\n}\n\n/**\n * The default {@link AppToolHandlers} for the house stack:\n * - `submit_proposal` → insert a `proposals` row (`status='pending'`), deduped\n * on (workspace, title) so a retried turn doesn't double-queue.\n * - `schedule_followup` → insert a `deadlines` row, deduped on (workspace, title, due_date).\n * - `render_ui` → write the schema JSON as a `ui/<thread>/<slug>.json`\n * vault artifact AND a `knowledge` row pointing at it.\n * - `add_citation` → write the quote as a `citations/<slug>.json` artifact AND\n * a `knowledge` row.\n *\n * Returns the EXACT persisted content from `render_ui` (per the seam contract) so\n * a completion oracle sees real bytes. Pure seam wiring: a consumer that runs\n * D1 + KV gets all four tools with no handler code.\n */\nexport function createPresetToolHandlers(opts: PresetToolHandlerOptions): AppToolHandlers {\n const { db, vault } = opts\n const newId = opts.newId ?? (() => crypto.randomUUID())\n const now = opts.now ?? (() => Date.now())\n const uiPrefix = opts.uiPathPrefix ?? 'ui'\n const citationPrefix = opts.citationPathPrefix ?? 'citations'\n const P = PRESET_TABLES.proposals\n const D = PRESET_TABLES.deadlines\n const K = PRESET_TABLES.knowledge\n\n async function persistArtifact(path: string, body: string): Promise<void> {\n await vault.put(path, body)\n }\n\n async function insertKnowledge(workspaceId: string, path: string, kind: string, label: string | null, content: string): Promise<void> {\n await db\n .prepare(\n `INSERT INTO ${K.name} (${K.columns.id}, ${K.columns.workspaceId}, ${K.columns.path}, ${K.columns.kind}, ${K.columns.label}, ${K.columns.content}, ${K.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n )\n .bind(newId(), workspaceId, path, kind, label, content, now())\n .run()\n }\n\n return {\n async submitProposal(args: SubmitProposalArgs, ctx: AppToolContext): Promise<SubmitProposalResult> {\n const existing = await db\n .prepare(`SELECT ${P.columns.id} AS id FROM ${P.name} WHERE ${P.columns.workspaceId} = ? AND ${P.columns.title} = ? LIMIT 1`)\n .bind(ctx.workspaceId, args.title)\n .first<{ id: string }>()\n if (existing) return { proposalId: existing.id, deduped: true }\n\n const id = newId()\n await db\n .prepare(\n `INSERT INTO ${P.name} (${P.columns.id}, ${P.columns.workspaceId}, ${P.columns.threadId}, ${P.columns.type}, ${P.columns.title}, ${P.columns.description}, ${P.columns.status}, ${P.columns.createdBy}, ${P.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?)`,\n )\n .bind(id, ctx.workspaceId, ctx.threadId, args.type, args.title, args.description ?? null, ctx.userId, now())\n .run()\n return { proposalId: id, deduped: false }\n },\n\n async scheduleFollowup(args: ScheduleFollowupArgs, ctx: AppToolContext): Promise<ScheduleFollowupResult> {\n const existing = await db\n .prepare(\n `SELECT ${D.columns.id} AS id, ${D.columns.dueDate} AS dueDate FROM ${D.name} WHERE ${D.columns.workspaceId} = ? AND ${D.columns.title} = ? AND ${D.columns.dueDate} = ? LIMIT 1`,\n )\n .bind(ctx.workspaceId, args.title, args.dueDate)\n .first<{ id: string; dueDate: string }>()\n if (existing) return { id: existing.id, dueDate: existing.dueDate, deduped: true }\n\n const id = newId()\n await db\n .prepare(\n `INSERT INTO ${D.name} (${D.columns.id}, ${D.columns.workspaceId}, ${D.columns.threadId}, ${D.columns.title}, ${D.columns.dueDate}, ${D.columns.priority}, ${D.columns.status}, ${D.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'scheduled', ?)`,\n )\n .bind(id, ctx.workspaceId, ctx.threadId, args.title, args.dueDate, args.priority ?? null, now())\n .run()\n return { id, dueDate: args.dueDate, deduped: false }\n },\n\n async renderUi(args: RenderUiArgs, ctx: AppToolContext): Promise<RenderUiResult> {\n const content = JSON.stringify(args.schema)\n const path = `${uiPrefix}/${ctx.threadId ?? 'global'}/${slug(args.title)}.json`\n await persistArtifact(path, content)\n await insertKnowledge(ctx.workspaceId, path, 'ui', args.title, content)\n return { path, content }\n },\n\n async addCitation(args: AddCitationArgs, ctx: AppToolContext): Promise<AddCitationResult> {\n const citationId = newId()\n const path = `${citationPrefix}/${slug(args.label ?? args.path)}-${citationId.slice(0, 8)}.json`\n const body = JSON.stringify({ sourcePath: args.path, quote: args.quote, label: args.label ?? null })\n await persistArtifact(path, body)\n await insertKnowledge(ctx.workspaceId, path, 'citation', args.label ?? null, body)\n return { citationId, path }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// D1-backed KnowledgeStateAccessor\n// ---------------------------------------------------------------------------\n\n/** Define options for accessing preset knowledge scoped to a specific workspace and configuration */\nexport interface PresetKnowledgeAccessorOptions {\n db: D1Like\n /** The active workspace — every `count` is scoped to it. */\n workspaceId: string\n /** Workspace config the `satisfiedBy: { config }` rules read. A resolved\n * object (dot-path lookup), or a function the accessor calls per path. */\n config: Record<string, unknown> | ((path: string) => unknown)\n /** The default workspace fk column a `count` rule scopes on when its rule\n * omits `where`. Default `'workspace_id'` (the preset schema convention). */\n defaultWhereColumn?: string\n}\n\nfunction readDotPath(obj: Record<string, unknown>, path: string): unknown {\n let cur: unknown = obj\n for (const part of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\n/**\n * The {@link KnowledgeStateAccessor} over the preset D1 schema — the seam that\n * lets the declarative `satisfiedBy` rules resolve with ZERO consumer code:\n * - `config(path)` reads the supplied workspace config by dot-path.\n * - `count({ table, where, statusIn })` runs `SELECT count(*)` scoped to the\n * active workspace (the rule's `where` column, default `workspace_id`),\n * optionally filtered to `statusIn` via a parameterized `IN (...)`.\n *\n * Identifiers (table/column) are validated against a safe pattern before\n * interpolation — they originate from the product's own config, never model\n * input, but we fail loud rather than build a malformed/injectable query.\n */\nexport function createD1KnowledgeStateAccessor(opts: PresetKnowledgeAccessorOptions): KnowledgeStateAccessor {\n const { db, workspaceId } = opts\n const defaultWhere = opts.defaultWhereColumn ?? 'workspace_id'\n const configFn = typeof opts.config === 'function' ? opts.config : (path: string) => readDotPath(opts.config as Record<string, unknown>, path)\n\n const isIdentifier = (s: string) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s)\n\n return {\n config: configFn,\n async count(query) {\n if (!isIdentifier(query.table)) throw new Error(`unsafe table identifier: ${query.table}`)\n const whereCol = query.where ?? defaultWhere\n if (!isIdentifier(whereCol)) throw new Error(`unsafe where identifier: ${whereCol}`)\n\n let sql = `SELECT count(*) AS n FROM ${query.table} WHERE ${whereCol} = ?`\n const binds: unknown[] = [workspaceId]\n if (query.statusIn && query.statusIn.length > 0) {\n sql += ` AND status IN (${query.statusIn.map(() => '?').join(', ')})`\n binds.push(...query.statusIn)\n }\n const row = await db.prepare(sql).bind(...binds).first<{ n: number }>()\n return row?.n ?? 0\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Crypto + per-workspace key wiring\n// ---------------------------------------------------------------------------\n\n/** Build the {@link KeyCrypto} the billing key store uses — AES-256-GCM field\n * crypto bound to the product's 64-char-hex `ENCRYPTION_KEY` (or a resolver).\n * This is the concrete impl behind the `../billing` `KeyCrypto` seam. */\nexport function createPresetFieldCrypto(key: string | (() => string)): KeyCrypto {\n return createFieldCrypto(key)\n}\n\n/**\n * The {@link WorkspaceKeyStore} over the preset `workspace_keys` table — the\n * persistence seam the per-workspace key manager needs. \"Active\" = a row with a\n * null `revoked_at`. Pure D1 wiring; no key minting (that's the provisioner).\n */\nexport function createPresetWorkspaceKeyStore(db: D1Like): WorkspaceKeyStore {\n const W = PRESET_TABLES.workspaceKeys\n return {\n async getActive(workspaceId: string): Promise<WorkspaceKeyRecord | null> {\n const row = await db\n .prepare(\n `SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId, ${W.columns.keyEncrypted} AS keyEncrypted, ${W.columns.budgetUsd} AS budgetUsd, ${W.columns.expiresAt} AS expiresAt FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL ORDER BY ${W.columns.createdAt} DESC LIMIT 1`,\n )\n .bind(workspaceId)\n .first<{ id: string; keyId: string; keyEncrypted: string; budgetUsd: number; expiresAt: number | null }>()\n if (!row) return null\n return {\n id: row.id,\n keyId: row.keyId,\n keyEncrypted: row.keyEncrypted,\n budgetUsd: row.budgetUsd,\n expiresAt: row.expiresAt == null ? null : new Date(row.expiresAt),\n }\n },\n async listActive(workspaceId: string): Promise<Array<{ id: string; keyId: string }>> {\n const res = await db\n .prepare(`SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL`)\n .bind(workspaceId)\n .all<{ id: string; keyId: string }>()\n return res.results\n },\n async insert(record): Promise<void> {\n await db\n .prepare(\n `INSERT INTO ${W.name} (${W.columns.id}, ${W.columns.workspaceId}, ${W.columns.keyId}, ${W.columns.keyEncrypted}, ${W.columns.budgetUsd}, ${W.columns.expiresAt}, ${W.columns.revokedAt}, ${W.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, NULL, ?)`,\n )\n .bind(crypto.randomUUID(), record.workspaceId, record.keyId, record.keyEncrypted, record.budgetUsd, record.expiresAt.getTime(), Date.now())\n .run()\n },\n async markRevoked(id: string, now: Date): Promise<void> {\n await db\n .prepare(`UPDATE ${W.name} SET ${W.columns.revokedAt} = ? WHERE ${W.columns.id} = ?`)\n .bind(now.getTime(), id)\n .run()\n },\n }\n}\n\n/** Define preset billing options including database, provisioner, encryption key, budget, and optional settings */\nexport interface PresetBillingOptions {\n db: D1Like\n /** The key provisioner (`@tangle-network/tcloud`'s client satisfies it structurally). */\n provisioner: KeyProvisioner\n /** Field-crypto key (64-char hex) or resolver — encrypts the minted key at rest. */\n encryptionKey: string | (() => string)\n /** Default monthly USD allowance when a call doesn't specify one. */\n defaultBudgetUsd: number\n /** Injectable clock. */\n now?: () => Date\n /** tcloud product the key is scoped to. Default `'router'`. */\n product?: string\n}\n\n/**\n * Stand up the per-workspace budget-capped {@link WorkspaceKeyManager} on the\n * house stack: the preset `workspace_keys` D1 store + AES-GCM field crypto +\n * the consumer's tcloud provisioner. The mint/rotate/rollover/usage LOGIC lives\n * in `../billing`; this only binds it to the preset table + crypto.\n */\nexport function createPresetWorkspaceKeyManager(opts: PresetBillingOptions): WorkspaceKeyManager {\n return createWorkspaceKeyManager({\n provisioner: opts.provisioner,\n store: createPresetWorkspaceKeyStore(opts.db),\n crypto: createPresetFieldCrypto(opts.encryptionKey),\n defaultBudgetUsd: opts.defaultBudgetUsd,\n now: opts.now,\n product: opts.product,\n })\n}\n"],"mappings":"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","names":[]}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  canTransitionInteractionStatus,
3
3
  persistedPartToInteraction
4
- } from "./chunk-XAWFPMAR.js";
4
+ } from "./chunk-YKOM4ZLQ.js";
5
5
  import {
6
6
  canTransitionPlanStatus,
7
7
  persistedPartToPlan,
8
8
  planPartKey,
9
9
  planToPersistedPart
10
- } from "./chunk-SIXYZ2FB.js";
10
+ } from "./chunk-YJMCRXQQ.js";
11
11
 
12
12
  // src/stream/stream-normalizer.ts
13
13
  function asRecord(value) {
@@ -376,4 +376,4 @@ export {
376
376
  terminalizeDanglingAssistantToolUpdates,
377
377
  encodeEvent
378
378
  };
379
- //# sourceMappingURL=chunk-5MG74GVQ.js.map
379
+ //# sourceMappingURL=chunk-IG46XCZM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stream/stream-normalizer.ts"],"sourcesContent":["import {\n canTransitionInteractionStatus,\n persistedPartToInteraction,\n type ChatInteractionStatus,\n} from '../interactions/contract'\nimport {\n canTransitionPlanStatus,\n persistedPartToPlan,\n planPartKey,\n planToPersistedPart,\n type ChatPlanStatus,\n} from '../plans/index'\n\n/** Represent a JSON-compatible object with string keys and values of any type */\nexport type JsonRecord = Record<string, unknown>\n\n/** Define an event object carrying a type and optional JSON data payload */\nexport interface StreamEvent {\n type: string\n data?: JsonRecord\n}\n\n/** Resolve an unknown value to a JsonRecord if it is a non-array object or return undefined */\nexport function asRecord(value: unknown): JsonRecord | undefined {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as JsonRecord\n : undefined\n}\n\n/** Resolve a non-empty string from a value or return undefined */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\n/** Resolve a unique tool identifier from various possible properties or generate a fallback ID */\nexport function resolveToolId(part: JsonRecord): string {\n return String(\n part.id ??\n part.callID ??\n part.callId ??\n part.toolUseId ??\n part.toolCallId ??\n part.tool ??\n part.name ??\n `tool-${Date.now()}`,\n )\n}\n\n/** Resolve the tool name from a JSON record using tool, name, or a default value */\nexport function resolveToolName(part: JsonRecord): string {\n return String(part.tool ?? part.name ?? 'tool')\n}\n\n/** Resolve time properties from various keys into a normalized record with numeric start and end fields */\nexport function normalizeTime(value: unknown): JsonRecord | undefined {\n const record = asRecord(value)\n if (!record) return undefined\n\n const start = Number(record.start ?? record.startedAt ?? record.started_at)\n const end = Number(record.end ?? record.completedAt ?? record.completed_at)\n if (!Number.isFinite(start) && !Number.isFinite(end)) return undefined\n\n return {\n start: Number.isFinite(start) ? start : undefined,\n end: Number.isFinite(end) ? end : undefined,\n }\n}\n\n/** Normalize tool-related events into a standardized message.part.updated format */\nexport function normalizeToolEvent(event: StreamEvent): StreamEvent {\n if (event.type === 'tool_call' || event.type === 'tool.call') {\n const data = event.data ?? {}\n return {\n type: 'message.part.updated',\n data: {\n part: {\n type: 'tool',\n id: data.id ?? data.callId ?? data.callID ?? data.name,\n tool: data.name ?? data.tool ?? 'tool',\n input: data.arguments ?? data.input,\n status: 'running',\n },\n },\n }\n }\n\n if (event.type === 'tool_result' || event.type === 'tool.result') {\n const data = event.data ?? {}\n const error = asString(data.error)\n return {\n type: 'message.part.updated',\n data: {\n part: {\n type: 'tool',\n id: data.id ?? data.callId ?? data.callID ?? data.name,\n tool: data.name ?? data.tool ?? 'tool',\n output: data.output,\n error,\n status: error ? 'error' : 'completed',\n },\n },\n }\n }\n\n return event\n}\n\n/** Normalize a persisted part object by standardizing its structure and fields */\nexport function normalizePersistedPart(rawPart: JsonRecord): JsonRecord | null {\n const type = String(rawPart.type ?? '')\n\n if (type === 'text') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'text',\n text: asString(rawPart.text) ?? asString(rawPart.content) ?? '',\n // id: per-segment identity from the harness; absent on legacy parts,\n // which collapse to a single keyed segment. Never invented here.\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'reasoning') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'reasoning',\n text: asString(rawPart.text) ?? asString(rawPart.content) ?? '',\n time: normalizeTime(rawPart.time),\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'file' || type === 'image') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type,\n ...(id ? { id } : {}),\n ...(asString(rawPart.filename) ? { filename: asString(rawPart.filename) } : {}),\n ...(asString(rawPart.mediaType) ? { mediaType: asString(rawPart.mediaType) } : {}),\n ...(asString(rawPart.url) ? { url: asString(rawPart.url) } : {}),\n ...(asString(rawPart.path) ? { path: asString(rawPart.path) } : {}),\n ...(type === 'file' && asString(rawPart.content) ? { content: asString(rawPart.content) } : {}),\n }\n }\n\n if (type === 'step-start') {\n return { type: 'step-start' }\n }\n\n // The harness's per-step usage receipt. Dropping it here silently loses the\n // turn's token/cost accounting from the persisted transcript.\n if (type === 'step-finish') {\n const tokens = asRecord(rawPart.tokens)\n const cost = Number(rawPart.cost)\n return {\n type: 'step-finish',\n ...(asString(rawPart.reason) ? { reason: asString(rawPart.reason) } : {}),\n ...(tokens ? { tokens } : {}),\n ...(Number.isFinite(cost) ? { cost } : {}),\n }\n }\n\n if (type === 'subtask') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'subtask',\n prompt: asString(rawPart.prompt) ?? '',\n description: asString(rawPart.description) ?? '',\n agent: asString(rawPart.agent) ?? '',\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'interaction') {\n return persistedPartToInteraction(rawPart) ? rawPart : null\n }\n\n if (type === 'plan') {\n const plan = persistedPartToPlan(rawPart)\n return plan ? { ...rawPart, ...planToPersistedPart(plan) } : null\n }\n\n // System-authored notices pass through verbatim; `/chat-store` owns their\n // final typed validation before persistence.\n if (type === 'notice') {\n return rawPart\n }\n\n if (type === 'tool') {\n const state = asRecord(rawPart.state)\n const output = state?.output ?? rawPart.output\n const error = asString(state?.error ?? rawPart.error)\n const terminalError =\n state?.status === 'error' ||\n state?.status === 'failed' ||\n rawPart.status === 'error' ||\n rawPart.status === 'failed' ||\n Boolean(error)\n const status =\n state?.status === 'completed' || rawPart.status === 'completed'\n ? 'completed'\n : terminalError\n ? 'error'\n : output !== undefined\n ? 'completed'\n : 'running'\n\n return {\n type: 'tool',\n id: resolveToolId(rawPart),\n tool: resolveToolName(rawPart),\n callID:\n rawPart.callID != null || rawPart.callId != null\n ? String(rawPart.callID ?? rawPart.callId)\n : undefined,\n state: {\n status,\n input: state?.input ?? rawPart.input,\n output,\n error,\n metadata: asRecord(state?.metadata) ?? asRecord(rawPart.metadata),\n time: normalizeTime(state?.time ?? rawPart.time),\n },\n }\n }\n\n return null\n}\n\n/** Stream/transcript part key for a promoted (path-bearing) attachment,\n * keyed on its storage path — re-emitting the same path folds into the same\n * segment instead of duplicating it. */\nexport function attachmentPartKey(path: string): string {\n return `attachment:${path}`\n}\n\n/** Resolve a unique key string for a part based on its type and identifying properties */\nexport function getPartKey(part: JsonRecord): string {\n const type = String(part.type ?? 'unknown')\n if (type === 'tool') {\n return `tool:${resolveToolId(part)}`\n }\n if (type === 'plan') return planPartKey(String(part.planId ?? ''))\n if ((type === 'file' || type === 'image') && asString(part.path)) {\n return attachmentPartKey(String(part.path))\n }\n\n // Keyed by the part's OWN type so distinct kinds never merge into each\n // other. Untyped parts fall back to the text lane (legacy shape).\n const lane = type && type !== 'unknown' ? type : 'text'\n return `${lane}:${String(part.id ?? part.partId ?? part.index ?? 'current')}`\n}\n\n/** Shallow overlay that skips `undefined` incoming values, so a later partial\n * update never erases a field an earlier one captured. */\nfunction overlayDefined(base: JsonRecord, patch: JsonRecord): JsonRecord {\n const out: JsonRecord = { ...base }\n for (const [key, value] of Object.entries(patch)) {\n if (value !== undefined) out[key] = value\n }\n return out\n}\n\n/** Merge incoming JSON with existing persisted data, applying delta for text types when provided */\nexport function mergePersistedPart(existing: JsonRecord | undefined, incoming: JsonRecord, delta?: string): JsonRecord {\n const type = String(incoming.type ?? '')\n if (!existing) {\n if (type === 'text' && delta) {\n return { type: 'text', text: delta }\n }\n return incoming\n }\n\n if (type === 'text' && String(existing.type ?? '') === 'text') {\n const existingText = String(existing.text ?? '')\n const incomingText = String(incoming.text ?? '')\n return {\n ...existing,\n ...incoming,\n // An empty snapshot never erases accumulated text (matches reasoning).\n text: delta ? `${existingText}${delta}` : incomingText || existingText,\n }\n }\n\n if (type === 'reasoning' && String(existing.type ?? '') === 'reasoning') {\n const existingText = String(existing.text ?? '')\n const incomingText = String(incoming.text ?? '')\n return {\n ...existing,\n ...incoming,\n text: delta && incomingText === existingText ? `${existingText}${delta}` : incomingText || existingText,\n time: incoming.time ?? existing.time,\n }\n }\n\n if (type === 'tool' && String(existing.type ?? '') === 'tool') {\n const existingState = asRecord(existing.state) ?? {}\n const incomingState = asRecord(incoming.state) ?? {}\n // Overlay only DEFINED incoming fields: a normalized tool part always\n // carries `output`/`error` keys (undefined when not captured), so a plain\n // spread would clobber a completed tool's output with a later empty update.\n const mergedState = overlayDefined(existingState, incomingState)\n // A partial update with no captured status/output/error normalizes to\n // `running`; never let it downgrade a tool that already settled.\n const existingStatus = String(existingState.status ?? '')\n if (\n (existingStatus === 'completed' || existingStatus === 'error') &&\n String(incomingState.status ?? '') === 'running'\n ) {\n mergedState.status = existingStatus\n }\n return {\n ...overlayDefined(existing, incoming),\n state: mergedState,\n }\n }\n\n if (type === 'interaction' && String(existing.type ?? '') === 'interaction') {\n const merged = overlayDefined(existing, incoming)\n const existingStatus = existing.status as ChatInteractionStatus | undefined\n const incomingStatus = incoming.status as ChatInteractionStatus | undefined\n if (\n existingStatus &&\n incomingStatus &&\n existingStatus !== incomingStatus &&\n !canTransitionInteractionStatus(existingStatus, incomingStatus)\n ) {\n merged.status = existingStatus\n }\n if (incoming.answers === undefined && existing.answers !== undefined) {\n merged.answers = existing.answers\n }\n return merged\n }\n\n if (type === 'plan' && String(existing.type ?? '') === 'plan') {\n const existingRevision = Number(existing.revision)\n const incomingRevision = Number(incoming.revision)\n if (Number.isInteger(existingRevision) && Number.isInteger(incomingRevision)) {\n if (incomingRevision < existingRevision) return existing\n if (incomingRevision > existingRevision) return incoming\n }\n const merged = overlayDefined(existing, incoming)\n const existingStatus = existing.status as ChatPlanStatus | undefined\n const incomingStatus = incoming.status as ChatPlanStatus | undefined\n if (\n existingStatus &&\n incomingStatus &&\n existingStatus !== incomingStatus &&\n !canTransitionPlanStatus(existingStatus, incomingStatus)\n ) {\n merged.status = existingStatus\n }\n return merged\n }\n\n return incoming\n}\n\n/** Resolve errors when a tool fails to report a terminal result before the assistant turn ends */\nexport const MISSING_TOOL_TERMINAL_ERROR = 'Tool did not report a terminal result before the assistant turn completed.'\n/** Provide the reason identifier for a missing tool in the terminal environment */\nexport const MISSING_TOOL_TERMINAL_REASON = 'missing-tool-terminal'\n\n/** Closes a tool part left `running` when a stream ended abnormally: settles\n * it as a terminal `error` and stamps `state.metadata.terminalized` so the\n * synthetic settlement is distinguishable from a real tool failure. Parts\n * that already settled (and non-tool parts) pass through untouched. */\nexport function terminalizeDanglingToolPart(part: JsonRecord): JsonRecord {\n if (String(part.type ?? '') !== 'tool') return part\n\n const state = asRecord(part.state) ?? {}\n if (String(state.status ?? part.status ?? '') !== 'running') return part\n\n const metadata = asRecord(state.metadata) ?? {}\n return {\n ...part,\n state: {\n ...state,\n status: 'error',\n error: asString(state.error ?? part.error) ?? MISSING_TOOL_TERMINAL_ERROR,\n metadata: {\n ...metadata,\n terminalized: true,\n terminalReason: MISSING_TOOL_TERMINAL_REASON,\n },\n },\n }\n}\n\n/** Resolve dangling tool parts into terminal forms within the given JSON records array */\nexport function terminalizeDanglingToolParts(parts: JsonRecord[]): JsonRecord[] {\n return parts.map(terminalizeDanglingToolPart)\n}\n\n/** Settles still-pending interaction parts at persist time. The broker\n * guarantees a resolved question either answered (run unblocked, no cancel\n * event) or cancelled/timed out (cancel event already updated the part), so\n * the success path finalizes remaining pendings as `answered` and the\n * failure/terminalize paths as `expired`. */\nexport function finalizePendingInteractionParts(\n parts: JsonRecord[],\n outcome: Extract<ChatInteractionStatus, 'answered' | 'expired'>,\n): JsonRecord[] {\n return parts.map((part) => {\n if (String(part.type ?? '') !== 'interaction') return part\n if (String(part.status ?? '') !== 'pending') return part\n return { ...part, status: outcome }\n })\n}\n\n/** Collapses text-part artifacts of unstable upstream segment identity: the\n * same text arriving under two keys (id-less delta stream, then an\n * id-bearing snapshot) folds into two segments, and interleaved empty\n * segments survive as blank parts. Consecutive identical text parts merge\n * into one; empty text parts drop when any non-empty text part exists. */\nexport function collapseRedundantTextParts(parts: JsonRecord[]): JsonRecord[] {\n const hasNonEmptyText = parts.some(\n (part) => String(part.type ?? '') === 'text' && String(part.text ?? '').trim().length > 0,\n )\n const collapsed: JsonRecord[] = []\n for (const part of parts) {\n if (String(part.type ?? '') !== 'text') {\n collapsed.push(part)\n continue\n }\n const text = String(part.text ?? '')\n if (hasNonEmptyText && text.trim().length === 0) continue\n const previous = collapsed[collapsed.length - 1]\n if (previous && String(previous.type ?? '') === 'text' && String(previous.text ?? '') === text) continue\n collapsed.push(part)\n }\n return collapsed\n}\n\nfunction assembleAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n const parts = partOrder\n .map((key) => partMap.get(key))\n .filter((part): part is JsonRecord => Boolean(part))\n\n const textParts = parts.filter((part) => String(part.type ?? '') === 'text')\n\n if (textParts.length === 0) {\n if (finalText.trim()) {\n parts.push({ type: 'text', text: finalText })\n }\n return parts\n }\n\n // Id-less text parts form a single logical stream — the final text is\n // authoritative for it.\n if (!textParts.some((part) => asString(part.id))) {\n return parts.map((part) => {\n if (String(part.type ?? '') !== 'text') return part\n return {\n ...part,\n text: finalText || String(part.text ?? ''),\n }\n })\n }\n\n // Per-id text segments: invariant is concat(text parts) === persisted final\n // text, so segment boundaries survive without duplicating the answer into\n // every segment.\n const joined = textParts.map((part) => String(part.text ?? '')).join('')\n if (finalText === joined || finalText.trimEnd() === joined.trimEnd()) {\n return parts\n }\n\n if (finalText.startsWith(joined)) {\n // Final text extends the streamed segments (e.g. a failure diagnostic\n // appended after the stream) — persist the remainder as a trailing\n // id-less segment.\n return [...parts, { type: 'text', text: finalText.slice(joined.length) }]\n }\n\n // Final text replaced the streamed text outright. Keep non-text chronology;\n // collapse text to one authoritative segment at the last text position.\n const lastTextPart = textParts[textParts.length - 1]\n return parts\n .filter((part) => String(part.type ?? '') !== 'text' || part === lastTextPart)\n .map((part) => (part === lastTextPart ? { ...part, text: finalText } : part))\n}\n\n/** Resolve and clean up assistant parts by terminalizing and collapsing redundant segments */\nexport function finalizeAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n // A stream that ended abnormally can leave tool parts `running` — never\n // persist one; collapsing then removes the duplicate/blank text segments an\n // unstable upstream segment identity produced.\n return collapseRedundantTextParts(terminalizeDanglingToolParts(\n assembleAssistantParts(partOrder, partMap, finalText),\n ))\n}\n\nfunction partStatus(part: JsonRecord | undefined): string {\n const state = asRecord(part?.state)\n return String(state?.status ?? part?.status ?? '')\n}\n\n/** Finalizes, then folds each synthetic tool settlement back into `partMap`\n * and returns just those updates — the shape a streaming loop needs to emit\n * closing `message.part.updated` frames for tools the stream never settled. */\nexport function terminalizeDanglingAssistantToolUpdates(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n const finalizedParts = finalizeAssistantParts(partOrder, partMap, finalText)\n const updates: JsonRecord[] = []\n\n for (const part of finalizedParts) {\n if (String(part.type ?? '') !== 'tool') continue\n\n const key = getPartKey(part)\n const existing = partMap.get(key)\n if (partStatus(existing) !== 'running' || partStatus(part) === 'running') continue\n\n partMap.set(key, mergePersistedPart(existing, part))\n updates.push(part)\n }\n\n return updates\n}\n\n/** Encode a StreamEvent object into a Uint8Array using the provided TextEncoder */\nexport function encodeEvent(encoder: TextEncoder, event: StreamEvent): Uint8Array {\n return encoder.encode(`${JSON.stringify(event)}\\n`)\n}\n"],"mappings":"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","names":[]}
@@ -116,4 +116,4 @@ export {
116
116
  intakeProgress,
117
117
  reachableQuestions
118
118
  };
119
- //# sourceMappingURL=chunk-ND3XEGBE.js.map
119
+ //# sourceMappingURL=chunk-JDEGS53O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/intakes/model.ts"],"sourcesContent":["/**\n * Pure intake MODEL — the question-graph and the answer/completion algebra a\n * one-question-at-a-time interview runs on. Zero dependencies: no drizzle, no\n * env, no react, no I/O. The DB layer (`./intakes/drizzle`), the handlers\n * (`./intakes/api`) and the React surface (`./intakes-react`) all build on\n * these functions; this leaf imports nothing back, so a consumer can pull just\n * the intake math.\n *\n * An intake is an ordered list of questions, optionally branching: a question\n * can declare `next` as a function of the answers so far, so the next prompt\n * depends on what was said (e.g. \"do you have a website?\" → no → skip the URL\n * question). Traversal is pure and deterministic — `nextQuestion(graph, answers)`\n * folds the answers into the single question to ask next (or null = done), and\n * `isComplete` is true exactly when every REQUIRED reachable question has a\n * valid answer.\n *\n * Answers are a flat `Record<questionId, value>`. Each question declares an\n * answer `type` that `validateAnswer` checks against — the same validation the\n * UI runs before advancing and the store runs before persisting, so an invalid\n * answer can never enter the payload.\n */\n\n/** The kinds of answer a question accepts. */\nexport type IntakeAnswerType =\n | 'text'\n | 'long-text'\n | 'single-select'\n | 'multi-select'\n | 'boolean'\n | 'number'\n | 'url'\n | 'email'\n\n/** A selectable option for single/multi-select questions. */\nexport interface IntakeOption {\n value: string\n label: string\n}\n\n/** A flat map of answers keyed by question id. */\nexport type IntakeAnswers = Record<string, IntakeAnswerValue>\n\n/** Any value an answer can hold; the type is validated per-question. */\nexport type IntakeAnswerValue = string | string[] | number | boolean | null\n\n/**\n * One question in the graph. `next` (optional) makes the graph branch: given\n * the answers so far it returns the id of the question to ask next, or null to\n * end the interview early. With no `next`, traversal falls through to the next\n * question in declaration order.\n */\nexport interface IntakeQuestion {\n id: string\n /** The prompt the interviewer asks. */\n prompt: string\n type: IntakeAnswerType\n /** Required questions gate completion; optional ones may be skipped. */\n required?: boolean\n /** Help text shown under the prompt. */\n help?: string\n /** Options for single/multi-select questions. */\n options?: IntakeOption[]\n /** Min length (text) or min value (number); inclusive. */\n min?: number\n /** Max length (text) or max value (number); inclusive. */\n max?: number\n /**\n * Branch override: given the answers so far, the id of the next question\n * (or null to end early). Omit for linear flow (next in declaration order).\n */\n next?(answers: IntakeAnswers): string | null\n}\n\n/** The intake definition: an ordered, addressable set of questions. */\nexport interface IntakeGraph {\n /** Stable id for the intake definition (e.g. 'user-onboarding-v1'). */\n id: string\n /** Human title shown at the top of the interview. */\n title: string\n /** Optional one-line description. */\n description?: string\n /** Questions in declaration (default traversal) order. */\n questions: IntakeQuestion[]\n}\n\n/** The reason an answer failed validation. */\nexport type AnswerRejectionReason =\n | 'required'\n | 'wrong-type'\n | 'too-short'\n | 'too-long'\n | 'too-small'\n | 'too-large'\n | 'not-an-option'\n | 'invalid-url'\n | 'invalid-email'\n | 'unknown-question'\n\n/** Describe validation outcome of an answer including success status and optional rejection reason */\nexport interface AnswerValidationResult {\n ok: boolean\n reason?: AnswerRejectionReason\n}\n\nconst URL_PATTERN = /^https?:\\/\\/[^\\s/$.?#].[^\\s]*$/i\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n/** Look up a question by id, or null if the graph has none. */\nexport function getQuestion(graph: IntakeGraph, questionId: string): IntakeQuestion | null {\n return graph.questions.find((q) => q.id === questionId) ?? null\n}\n\n/** True when an answer value is present (not null/undefined/empty). */\nexport function hasAnswer(value: IntakeAnswerValue | undefined): boolean {\n if (value == null) return false\n if (typeof value === 'string') return value.trim().length > 0\n if (Array.isArray(value)) return value.length > 0\n return true\n}\n\n/**\n * Validate one answer against its question. Pure: no I/O, no graph mutation.\n * An empty value is rejected as `required` only when the question is required;\n * an empty value for an optional question is OK (the user skipped it).\n */\nexport function validateAnswer(\n question: IntakeQuestion,\n value: IntakeAnswerValue | undefined,\n): AnswerValidationResult {\n if (!hasAnswer(value)) {\n return question.required ? { ok: false, reason: 'required' } : { ok: true }\n }\n\n switch (question.type) {\n case 'text':\n case 'long-text': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n const len = value.trim().length\n if (question.min != null && len < question.min) return { ok: false, reason: 'too-short' }\n if (question.max != null && len > question.max) return { ok: false, reason: 'too-long' }\n return { ok: true }\n }\n case 'url': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n return URL_PATTERN.test(value.trim()) ? { ok: true } : { ok: false, reason: 'invalid-url' }\n }\n case 'email': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n return EMAIL_PATTERN.test(value.trim()) ? { ok: true } : { ok: false, reason: 'invalid-email' }\n }\n case 'number': {\n if (typeof value !== 'number' || !Number.isFinite(value)) return { ok: false, reason: 'wrong-type' }\n if (question.min != null && value < question.min) return { ok: false, reason: 'too-small' }\n if (question.max != null && value > question.max) return { ok: false, reason: 'too-large' }\n return { ok: true }\n }\n case 'boolean': {\n return typeof value === 'boolean' ? { ok: true } : { ok: false, reason: 'wrong-type' }\n }\n case 'single-select': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n return optionExists(question, value) ? { ok: true } : { ok: false, reason: 'not-an-option' }\n }\n case 'multi-select': {\n if (!Array.isArray(value)) return { ok: false, reason: 'wrong-type' }\n if (!value.every((v) => typeof v === 'string' && optionExists(question, v))) {\n return { ok: false, reason: 'not-an-option' }\n }\n if (question.min != null && value.length < question.min) return { ok: false, reason: 'too-short' }\n if (question.max != null && value.length > question.max) return { ok: false, reason: 'too-long' }\n return { ok: true }\n }\n default:\n return { ok: false, reason: 'wrong-type' }\n }\n}\n\n/**\n * The single question to ask next given the answers so far, or null when the\n * interview is done. Walks the graph from the first question, following each\n * question's `next` branch (or declaration order) and stopping at the first\n * reachable question that has no valid answer yet. Deterministic and pure.\n *\n * A `next` that points at a missing id, or returns null, ends the walk — so a\n * malformed graph terminates rather than looping. A visited-set guards against\n * a cyclic `next`.\n */\nexport function nextQuestion(graph: IntakeGraph, answers: IntakeAnswers): IntakeQuestion | null {\n if (graph.questions.length === 0) return null\n const visited = new Set<string>()\n let current: IntakeQuestion | null = graph.questions[0] ?? null\n\n while (current) {\n if (visited.has(current.id)) return null\n visited.add(current.id)\n\n const answer = answers[current.id]\n const validity = validateAnswer(current, answer)\n if (!validity.ok || (current.required && !hasAnswer(answer))) {\n return current\n }\n\n current = advance(graph, current, answers)\n }\n\n return null\n}\n\n/**\n * True when every REQUIRED question reachable under the current answers has a\n * valid answer. Pure: it replays the same traversal `nextQuestion` uses and is\n * complete exactly when that traversal has no question left to ask.\n */\nexport function isComplete(graph: IntakeGraph, answers: IntakeAnswers): boolean {\n return nextQuestion(graph, answers) === null\n}\n\n/**\n * Progress as answered-vs-total over the REACHABLE required questions under the\n * current answers — what a progress bar renders. Optional questions don't count\n * toward the denominator (they never block completion).\n */\nexport function intakeProgress(graph: IntakeGraph, answers: IntakeAnswers): { answered: number; total: number } {\n const reachable = reachableQuestions(graph, answers)\n const required = reachable.filter((q) => q.required)\n const answered = required.filter((q) => validateAnswer(q, answers[q.id]).ok && hasAnswer(answers[q.id])).length\n return { answered, total: required.length }\n}\n\n/** The questions reachable under the current answers, in traversal order. */\nexport function reachableQuestions(graph: IntakeGraph, answers: IntakeAnswers): IntakeQuestion[] {\n if (graph.questions.length === 0) return []\n const visited = new Set<string>()\n const out: IntakeQuestion[] = []\n let current: IntakeQuestion | null = graph.questions[0] ?? null\n\n while (current) {\n if (visited.has(current.id)) break\n visited.add(current.id)\n out.push(current)\n current = advance(graph, current, answers)\n }\n return out\n}\n\nfunction advance(graph: IntakeGraph, current: IntakeQuestion, answers: IntakeAnswers): IntakeQuestion | null {\n if (current.next) {\n const nextId = current.next(answers)\n return nextId == null ? null : getQuestion(graph, nextId)\n }\n const index = graph.questions.indexOf(current)\n return graph.questions[index + 1] ?? null\n}\n\nfunction optionExists(question: IntakeQuestion, value: string): boolean {\n return (question.options ?? []).some((option) => option.value === value)\n}\n"],"mappings":"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","names":[]}
@@ -137,4 +137,4 @@ export {
137
137
  createTangleRouterModelConfig,
138
138
  resolveTangleModelConfig
139
139
  };
140
- //# sourceMappingURL=chunk-7W5XSTUF.js.map
140
+ //# sourceMappingURL=chunk-JML7WKWU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/model.ts"],"sourcesContent":["/**\n * Resolve the model config a Tangle agent's sandbox/runtime runs on.\n *\n * Every Tangle agent product resolves the SAME thing from env: the Tangle Router\n * (OpenAI-compatible, metered at the platform markup against a single\n * `TANGLE_API_KEY`) by default, with a direct-Anthropic BYOK escape hatch. The\n * shape feeds the sandbox SDK's `backend.model`. Lifted here so no product\n * hand-rolls the env parsing + the router default.\n */\n\nexport interface TangleModelConfig {\n /** The Tangle Router is OpenAI-compatible → driven via `openai-compat`.\n * `anthropic` is the BYOK escape hatch. */\n provider: 'openai-compat' | 'anthropic'\n model: string\n apiKey: string\n baseUrl: string\n}\n\n/** Define the environment context for executing Tangle operations */\nexport type TangleExecutionEnvironment = 'development' | 'staging' | 'production' | 'test'\n/** Resolve the source of the Tangle execution key as either local environment or user input */\nexport type TangleExecutionKeySource = 'local-env' | 'user'\n/** Define error codes for Tangle execution key issues related to API key and account connection */\nexport type TangleExecutionKeyErrorCode =\n | 'local_tangle_api_key_required'\n | 'tangle_account_not_connected'\n\n/** Resolve options for model configuration including environment variables and default router base URL */\nexport interface ResolveModelOptions {\n /** Env to read (defaults to process.env). */\n env?: Record<string, string | undefined>\n /** Router base URL default when `TANGLE_ROUTER_BASE_URL` is unset. */\n defaultRouterBaseUrl?: string\n}\n\n/** Resolve options for retrieving user API keys within a specific Tangle execution environment */\nexport interface ResolveUserTangleExecutionKeyOptions {\n /** Deployment context. Only local development may fall back to env keys. */\n environment?: TangleExecutionEnvironment\n /** Env to read for the local-development fallback. */\n env?: Record<string, string | undefined>\n /** App-owned lookup for the caller's linked platform API key. */\n getUserApiKey: () => string | null | undefined | Promise<string | null | undefined>\n}\n\n/** Resolve options for retrieving a user's Tangle execution key with environment and API key access parameters */\nexport interface ResolveUserTangleExecutionKeyForUserOptions<UserId = string> {\n userId: UserId\n environment?: TangleExecutionEnvironment\n env?: Record<string, string | undefined>\n getUserApiKey: (userId: UserId) => string | null | undefined | Promise<string | null | undefined>\n}\n\n/** Define a resolved key combining an API key with its Tangle execution source */\nexport interface ResolvedTangleExecutionKey {\n apiKey: string\n source: TangleExecutionKeySource\n}\n\n/** Resolve options for retrieving a Tangle developer or user API key based on environment and context */\nexport interface ResolveTangleDevOrUserKeyOptions {\n /** Deployment context. Only local development may use the env key. */\n environment?: TangleExecutionEnvironment\n /** Env to read for the local-development fallback. */\n env?: Record<string, string | undefined>\n /** App-owned lookup for the caller's linked platform API key. */\n getUserApiKey: () => string | null | undefined | Promise<string | null | undefined>\n}\n\n/** Represent HTTP error response containing status, error message, and specific error code */\nexport interface TangleExecutionKeyHttpError {\n status: number\n body: {\n error: string\n code: TangleExecutionKeyErrorCode\n }\n}\n\n/** Define configuration options for creating a Tangle router model including API key and model details */\nexport interface CreateTangleRouterModelConfigOptions {\n apiKey: string\n model: string\n baseUrl?: string\n}\n\n/** Define options for configuring billing enforcement environment variables and overrides */\nexport interface TangleBillingEnforcementOptions {\n /** Env to read (defaults to process.env). */\n env?: Record<string, string | undefined>\n /**\n * Optional app-specific override flag, e.g. `GTM_BILLING_ENFORCEMENT`.\n * Defaults to the shared `TANGLE_BILLING_ENFORCEMENT`.\n */\n enforcementEnvVar?: string\n}\n\n/** Provide the default base URL for the Tangle router API endpoint */\nexport const DEFAULT_TANGLE_ROUTER_BASE_URL = 'https://router.tangle.tools/v1'\n/** Define the default environment variable name for Tangle billing enforcement */\nexport const DEFAULT_TANGLE_BILLING_ENFORCEMENT_ENV_VAR = 'TANGLE_BILLING_ENFORCEMENT'\n\nfunction requireEnv(env: Record<string, string | undefined>, name: string): string {\n const value = env[name]?.trim()\n if (!value) throw new Error(`${name} is required`)\n return value\n}\n\n/** Resolve a string by trimming whitespace or returning null if empty or undefined */\nexport function trimOrNull(value: string | null | undefined): string | null {\n const trimmed = value?.trim()\n return trimmed ? trimmed : null\n}\n\nfunction isTangleExecutionKeyErrorCode(value: unknown): value is TangleExecutionKeyErrorCode {\n return value === 'local_tangle_api_key_required' || value === 'tangle_account_not_connected'\n}\n\n/** Represent execution key errors with specific codes and HTTP status information */\nexport class TangleExecutionKeyError extends Error {\n readonly code: TangleExecutionKeyErrorCode\n readonly status: number\n\n constructor(code: TangleExecutionKeyErrorCode, message: string, status: number) {\n super(message)\n this.name = 'TangleExecutionKeyError'\n this.code = code\n this.status = status\n }\n}\n\n/** Identify whether an error is a TangleExecutionKeyError based on its properties and type */\nexport function isTangleExecutionKeyError(error: unknown): error is TangleExecutionKeyError {\n return error instanceof TangleExecutionKeyError\n || (\n typeof error === 'object'\n && error !== null\n && (error as { name?: unknown }).name === 'TangleExecutionKeyError'\n && typeof (error as { message?: unknown }).message === 'string'\n && isTangleExecutionKeyErrorCode((error as { code?: unknown }).code)\n && typeof (error as { status?: unknown }).status === 'number'\n )\n}\n\n/** Resolve the current Tangle execution environment based on provided or process environment variables */\nexport function resolveTangleExecutionEnvironment(\n env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,\n): TangleExecutionEnvironment {\n const raw = (env.APP_ENV ?? env.NODE_ENV ?? '').trim().toLowerCase()\n if (raw === 'development' || raw === 'dev' || raw === 'local') return 'development'\n if (raw === 'staging') return 'staging'\n if (raw === 'test') return 'test'\n return 'production'\n}\n\n/**\n * Shared policy for agent products that bill through the Tangle Platform.\n *\n * Local development defaults billing enforcement off so apps can use a local\n * `TANGLE_API_KEY` without requiring a browser-linked platform account. Any\n * non-development environment defaults enforcement on. Apps may pass their own\n * override flag (`FOO_BILLING_ENFORCEMENT`) while new apps can use the shared\n * `TANGLE_BILLING_ENFORCEMENT`.\n */\nexport function isTangleBillingEnforcementDisabled(\n opts: TangleBillingEnforcementOptions = {},\n): boolean {\n const env = opts.env ?? (process.env as Record<string, string | undefined>)\n const enforcementEnvVar = opts.enforcementEnvVar ?? DEFAULT_TANGLE_BILLING_ENFORCEMENT_ENV_VAR\n const override = env[enforcementEnvVar]?.trim().toLowerCase()\n\n if (override === 'disabled') return true\n if (override === 'enabled') return false\n\n return resolveTangleExecutionEnvironment(env) === 'development'\n}\n\n/** Resolve and format TangleExecutionKey HTTP errors into a standardized error object or return null */\nexport function tangleExecutionKeyHttpError(error: unknown): TangleExecutionKeyHttpError | null {\n if (!isTangleExecutionKeyError(error)) return null\n return {\n status: error.status,\n body: {\n error: error.message,\n code: error.code,\n },\n }\n}\n\n/**\n * Shared dev-aware Tangle key resolution. Local development may use a server\n * `TANGLE_API_KEY`; otherwise the caller's linked platform key is used. Returns\n * null when neither resolves, so each caller can throw its own domain error\n * (e.g. model-execution 503/401 vs hub `tangle_link_required` 412).\n */\nexport async function resolveTangleDevOrUserKey(\n opts: ResolveTangleDevOrUserKeyOptions,\n): Promise<ResolvedTangleExecutionKey | null> {\n const env = opts.env ?? (process.env as Record<string, string | undefined>)\n const environment = opts.environment ?? resolveTangleExecutionEnvironment(env)\n\n if (environment === 'development') {\n const apiKey = trimOrNull(env.TANGLE_API_KEY)\n if (apiKey) return { apiKey, source: 'local-env' }\n }\n\n const apiKey = trimOrNull(await opts.getUserApiKey())\n if (apiKey) return { apiKey, source: 'user' }\n\n return null\n}\n\n/**\n * Resolve the user-facing Tangle API key for model execution.\n *\n * Local development may use a server env key so apps remain easy to run.\n * Deployed contexts must use the caller's linked platform key; this keeps\n * model execution, billing, and account ownership aligned across products.\n */\nexport async function resolveUserTangleExecutionKey(\n opts: ResolveUserTangleExecutionKeyOptions,\n): Promise<ResolvedTangleExecutionKey> {\n const env = opts.env ?? (process.env as Record<string, string | undefined>)\n const environment = opts.environment ?? resolveTangleExecutionEnvironment(env)\n\n const resolved = await resolveTangleDevOrUserKey({ environment, env, getUserApiKey: opts.getUserApiKey })\n if (resolved) return resolved\n\n if (environment === 'development') {\n throw new TangleExecutionKeyError(\n 'local_tangle_api_key_required',\n 'TANGLE_API_KEY or a linked Tangle account is required for local Tangle model execution.',\n 503,\n )\n }\n\n throw new TangleExecutionKeyError(\n 'tangle_account_not_connected',\n 'Connect your Tangle account before invoking this agent.',\n 401,\n )\n}\n\n/** Resolve the Tangle execution key for a specified user using provided environment and API key options */\nexport async function resolveUserTangleExecutionKeyForUser<UserId = string>(\n opts: ResolveUserTangleExecutionKeyForUserOptions<UserId>,\n): Promise<ResolvedTangleExecutionKey> {\n return resolveUserTangleExecutionKey({\n environment: opts.environment,\n env: opts.env,\n getUserApiKey: () => opts.getUserApiKey(opts.userId),\n })\n}\n\n/**\n * Build an OpenAI-compatible Tangle Router model config from an already\n * resolved execution key. This intentionally does not read TANGLE_API_KEY.\n */\nexport function createTangleRouterModelConfig(\n opts: CreateTangleRouterModelConfigOptions,\n): TangleModelConfig {\n const apiKey = opts.apiKey.trim()\n if (!apiKey) throw new Error('apiKey is required')\n const model = opts.model.trim()\n if (!model) throw new Error('model is required')\n return {\n provider: 'openai-compat',\n model,\n apiKey,\n baseUrl: (opts.baseUrl?.trim() || DEFAULT_TANGLE_ROUTER_BASE_URL).replace(/\\/+$/, ''),\n }\n}\n\n/**\n * Resolve the model config from env. DEFAULT path (`MODEL_PROVIDER` unset or\n * `openai-compat`/`tangle-router`/`tcloud`): the Tangle Router, authenticated\n * with `TANGLE_API_KEY`, model from `MODEL_NAME`. BYOK path\n * (`MODEL_PROVIDER=anthropic`): direct Anthropic with `ANTHROPIC_API_KEY` +\n * `ANTHROPIC_BASE_URL`. Throws (fail-loud) on a missing required var so a\n * misconfigured deploy fails at boot, not mid-turn.\n */\nexport function resolveTangleModelConfig(opts: ResolveModelOptions = {}): TangleModelConfig {\n const env = opts.env ?? (process.env as Record<string, string | undefined>)\n const provider = env.MODEL_PROVIDER?.trim() || 'openai-compat'\n const model = requireEnv(env, 'MODEL_NAME')\n\n if (provider === 'openai-compat' || provider === 'tangle-router' || provider === 'tcloud') {\n return {\n provider: 'openai-compat',\n model,\n apiKey: requireEnv(env, 'TANGLE_API_KEY'),\n baseUrl: (env.TANGLE_ROUTER_BASE_URL?.trim() || opts.defaultRouterBaseUrl || DEFAULT_TANGLE_ROUTER_BASE_URL).replace(/\\/+$/, ''),\n }\n }\n\n if (provider === 'anthropic') {\n return {\n provider,\n model,\n apiKey: requireEnv(env, 'ANTHROPIC_API_KEY'),\n baseUrl: requireEnv(env, 'ANTHROPIC_BASE_URL'),\n }\n }\n\n throw new Error(`Unsupported MODEL_PROVIDER: ${provider} (use openai-compat for the Tangle Router, or anthropic for BYOK)`)\n}\n"],"mappings":";AAkGO,IAAM,iCAAiC;AAEvC,IAAM,6CAA6C;AAE1D,SAAS,WAAW,KAAyC,MAAsB;AACjF,QAAM,QAAQ,IAAI,IAAI,GAAG,KAAK;AAC9B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,GAAG,IAAI,cAAc;AACjD,SAAO;AACT;AAGO,SAAS,WAAW,OAAiD;AAC1E,QAAM,UAAU,OAAO,KAAK;AAC5B,SAAO,UAAU,UAAU;AAC7B;AAEA,SAAS,8BAA8B,OAAsD;AAC3F,SAAO,UAAU,mCAAmC,UAAU;AAChE;AAGO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACxC;AAAA,EACA;AAAA,EAET,YAAY,MAAmC,SAAiB,QAAgB;AAC9E,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAGO,SAAS,0BAA0B,OAAkD;AAC1F,SAAO,iBAAiB,2BAEpB,OAAO,UAAU,YACd,UAAU,QACT,MAA6B,SAAS,6BACvC,OAAQ,MAAgC,YAAY,YACpD,8BAA+B,MAA6B,IAAI,KAChE,OAAQ,MAA+B,WAAW;AAE3D;AAGO,SAAS,kCACd,MAA0C,QAAQ,KACtB;AAC5B,QAAM,OAAO,IAAI,WAAW,IAAI,YAAY,IAAI,KAAK,EAAE,YAAY;AACnE,MAAI,QAAQ,iBAAiB,QAAQ,SAAS,QAAQ,QAAS,QAAO;AACtE,MAAI,QAAQ,UAAW,QAAO;AAC9B,MAAI,QAAQ,OAAQ,QAAO;AAC3B,SAAO;AACT;AAWO,SAAS,mCACd,OAAwC,CAAC,GAChC;AACT,QAAM,MAAM,KAAK,OAAQ,QAAQ;AACjC,QAAM,oBAAoB,KAAK,qBAAqB;AACpD,QAAM,WAAW,IAAI,iBAAiB,GAAG,KAAK,EAAE,YAAY;AAE5D,MAAI,aAAa,WAAY,QAAO;AACpC,MAAI,aAAa,UAAW,QAAO;AAEnC,SAAO,kCAAkC,GAAG,MAAM;AACpD;AAGO,SAAS,4BAA4B,OAAoD;AAC9F,MAAI,CAAC,0BAA0B,KAAK,EAAG,QAAO;AAC9C,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,MAAM;AAAA,MACJ,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,IACd;AAAA,EACF;AACF;AAQA,eAAsB,0BACpB,MAC4C;AAC5C,QAAM,MAAM,KAAK,OAAQ,QAAQ;AACjC,QAAM,cAAc,KAAK,eAAe,kCAAkC,GAAG;AAE7E,MAAI,gBAAgB,eAAe;AACjC,UAAMA,UAAS,WAAW,IAAI,cAAc;AAC5C,QAAIA,QAAQ,QAAO,EAAE,QAAAA,SAAQ,QAAQ,YAAY;AAAA,EACnD;AAEA,QAAM,SAAS,WAAW,MAAM,KAAK,cAAc,CAAC;AACpD,MAAI,OAAQ,QAAO,EAAE,QAAQ,QAAQ,OAAO;AAE5C,SAAO;AACT;AASA,eAAsB,8BACpB,MACqC;AACrC,QAAM,MAAM,KAAK,OAAQ,QAAQ;AACjC,QAAM,cAAc,KAAK,eAAe,kCAAkC,GAAG;AAE7E,QAAM,WAAW,MAAM,0BAA0B,EAAE,aAAa,KAAK,eAAe,KAAK,cAAc,CAAC;AACxG,MAAI,SAAU,QAAO;AAErB,MAAI,gBAAgB,eAAe;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGA,eAAsB,qCACpB,MACqC;AACrC,SAAO,8BAA8B;AAAA,IACnC,aAAa,KAAK;AAAA,IAClB,KAAK,KAAK;AAAA,IACV,eAAe,MAAM,KAAK,cAAc,KAAK,MAAM;AAAA,EACrD,CAAC;AACH;AAMO,SAAS,8BACd,MACmB;AACnB,QAAM,SAAS,KAAK,OAAO,KAAK;AAChC,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oBAAoB;AACjD,QAAM,QAAQ,KAAK,MAAM,KAAK;AAC9B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,mBAAmB;AAC/C,SAAO;AAAA,IACL,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU,KAAK,SAAS,KAAK,KAAK,gCAAgC,QAAQ,QAAQ,EAAE;AAAA,EACtF;AACF;AAUO,SAAS,yBAAyB,OAA4B,CAAC,GAAsB;AAC1F,QAAM,MAAM,KAAK,OAAQ,QAAQ;AACjC,QAAM,WAAW,IAAI,gBAAgB,KAAK,KAAK;AAC/C,QAAM,QAAQ,WAAW,KAAK,YAAY;AAE1C,MAAI,aAAa,mBAAmB,aAAa,mBAAmB,aAAa,UAAU;AACzF,WAAO;AAAA,MACL,UAAU;AAAA,MACV;AAAA,MACA,QAAQ,WAAW,KAAK,gBAAgB;AAAA,MACxC,UAAU,IAAI,wBAAwB,KAAK,KAAK,KAAK,wBAAwB,gCAAgC,QAAQ,QAAQ,EAAE;AAAA,IACjI;AAAA,EACF;AAEA,MAAI,aAAa,aAAa;AAC5B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ,WAAW,KAAK,mBAAmB;AAAA,MAC3C,SAAS,WAAW,KAAK,oBAAoB;AAAA,IAC/C;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,+BAA+B,QAAQ,mEAAmE;AAC5H;","names":["apiKey"]}
@@ -237,4 +237,4 @@ export {
237
237
  runPreflight,
238
238
  formatPreflightReport
239
239
  };
240
- //# sourceMappingURL=chunk-Q4TKVF3L.js.map
240
+ //# sourceMappingURL=chunk-JTLYXUEV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preflight/index.ts"],"sourcesContent":["/**\n * `/preflight` — deploy-time secret-liveness probes.\n *\n * WHY THIS EXISTS: on 2026-07-15 four secrets were simultaneously dead in one\n * production day — a dead `SANDBOX_API_KEY`, a stale `SANDBOX_API_URL`, and a\n * dead LiteLLM router key + URL. Each one was present in `wrangler secret list`\n * (so nothing looked wrong) yet invalid against its live endpoint, and nothing\n * anywhere checked liveness. CI cannot hold production secrets, so this binds\n * at DEPLOY time instead: a product declares a handful of probes built from its\n * real env, the deploy workflow runs `agent-app-preflight` as a step, and a\n * dead secret fails the deploy with a message that names exactly which secret\n * to rotate.\n *\n * A probe is `{ name, run, critical? }`; `run()` returns `{ ok, detail? }`.\n * The standard builders (`routerChatProbe`, `sandboxAuthProbe`, `httpHeadProbe`)\n * each take explicit config — they read nothing global — so the same probe runs\n * identically in a deploy step, a test, or a local check. `runPreflight` fans\n * the probes out, times each, and folds them into a pass/fail report: any\n * failed CRITICAL probe fails the whole run (probes are critical by default).\n *\n * Server-only: probes carry live API keys and hit live endpoints. This subpath\n * must never reach a browser bundle.\n */\n\n/** One probe's outcome. `detail` should name the secret to rotate on failure. */\nexport interface PreflightProbeResult {\n ok: boolean\n detail?: string\n}\n\n/**\n * A liveness probe. `run` performs one cheap live call and maps the result to\n * `{ ok, detail }`. `critical` defaults to `true` — a failed critical probe\n * fails the whole preflight (and the deploy).\n */\nexport interface PreflightProbe {\n name: string\n run: () => Promise<PreflightProbeResult>\n critical?: boolean\n}\n\n/** Per-probe verdict enriched with the resolved criticality and measured latency. */\nexport interface PreflightProbeVerdict {\n name: string\n ok: boolean\n critical: boolean\n latencyMs: number\n detail?: string\n}\n\n/** Aggregate of every probe verdict plus the overall pass/fail decision. */\nexport interface PreflightReport {\n /** `false` if any critical probe failed. */\n ok: boolean\n probes: PreflightProbeVerdict[]\n passed: number\n failed: number\n criticalFailures: number\n durationMs: number\n}\n\n/** Deploy-time deadline for a single probe. Cold upstreams are slow; a dead\n * endpoint should still fail fast, so 10s is the ceiling, not the target. */\nconst DEFAULT_TIMEOUT_MS = 10_000\n\nfunction nowMs(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now()\n}\n\nfunction isAbortLike(err: unknown): boolean {\n return err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError')\n}\n\n/** Strip bearer tokens / key material before an upstream string is surfaced in\n * a report (deploy logs are not always private). */\nfunction sanitizeUpstreamMessage(input: unknown): string {\n const message = input instanceof Error ? input.message : String(input)\n return message\n .replace(/Bearer\\s+[^\\s]+/gi, 'Bearer [redacted]')\n .replace(/\\b(?:sk|pk|tc)[_-][A-Za-z0-9_-]{8,}\\b/g, '[redacted-key]')\n}\n\nfunction snippet(body: string): string {\n const trimmed = body.trim()\n if (!trimmed) return ''\n const clipped = trimmed.length > 180 ? `${trimmed.slice(0, 180)}…` : trimmed\n return `: ${sanitizeUpstreamMessage(clipped)}`\n}\n\ntype ProbeOutcome =\n | { kind: 'status'; status: number; bodyText: string }\n | { kind: 'timeout'; timeoutMs: number }\n | { kind: 'network'; message: string }\n\ninterface HttpProbeCall {\n fetchImpl: typeof fetch\n url: string\n method: string\n headers?: Record<string, string>\n body?: string\n timeoutMs: number\n}\n\n/** One live HTTP call, folded to a probe outcome. Never throws: a timeout, a\n * DNS/connection failure, and any thrown error all become an outcome so the\n * probe can classify them into an actionable detail. */\nasync function runHttp(call: HttpProbeCall): Promise<ProbeOutcome> {\n let response: Response\n try {\n response = await call.fetchImpl(call.url, {\n method: call.method,\n headers: call.headers,\n body: call.body,\n signal: AbortSignal.timeout(call.timeoutMs),\n })\n } catch (err) {\n if (isAbortLike(err)) return { kind: 'timeout', timeoutMs: call.timeoutMs }\n return { kind: 'network', message: sanitizeUpstreamMessage(err) }\n }\n let bodyText = ''\n try {\n bodyText = await response.text()\n } catch {\n bodyText = ''\n }\n return { kind: 'status', status: response.status, bodyText }\n}\n\ninterface AuthedClassifyContext {\n /** Full endpoint reached, for the message. */\n endpoint: string\n /** How to name the API-key secret when the endpoint reports auth failure. */\n keyLabel: string\n /** How to name the URL secret when the endpoint is unreachable. */\n urlLabel: string\n}\n\n/**\n * Shared classification for an authed liveness endpoint (router, sandbox):\n * 2xx → live; 401/403 → the KEY is dead, name it; 503 → the UPSTREAM is down,\n * the key still looks valid, don't rotate; timeout / unreachable → the URL is\n * likely stale, name it; anything else → an unexpected status with a snippet.\n */\nfunction classifyAuthed(outcome: ProbeOutcome, ctx: AuthedClassifyContext): PreflightProbeResult {\n switch (outcome.kind) {\n case 'status': {\n const { status, bodyText } = outcome\n if (status >= 200 && status < 300) return { ok: true, detail: `${status} OK` }\n if (status === 401 || status === 403) {\n return {\n ok: false,\n detail: `DEAD KEY — ${ctx.endpoint} returned ${status}; rotate ${ctx.keyLabel}`,\n }\n }\n if (status === 503) {\n return {\n ok: false,\n detail: `UPSTREAM DOWN — ${ctx.endpoint} returned 503; ${ctx.keyLabel} still looks valid, retry or check the provider (do NOT rotate)`,\n }\n }\n return { ok: false, detail: `UNEXPECTED ${status} from ${ctx.endpoint}${snippet(bodyText)}` }\n }\n case 'timeout':\n return {\n ok: false,\n detail: `TIMEOUT after ${outcome.timeoutMs}ms reaching ${ctx.endpoint} — check ${ctx.urlLabel}`,\n }\n case 'network':\n return {\n ok: false,\n detail: `UNREACHABLE ${ctx.endpoint} (${outcome.message}) — check ${ctx.urlLabel}`,\n }\n }\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '')\n}\n\n// --- Standard probe builders --------------------------------------------------\n\n/** Define configuration options for probing an LLM router with authentication and model details */\nexport interface RouterChatProbeConfig {\n /** LLM router base URL (LiteLLM / OpenAI-compatible), e.g. `https://router…`. */\n baseUrl: string\n apiKey: string\n /** A cheap model id available on the router. */\n model: string\n /** Probe name in the report. Default `'router-chat'`. */\n name?: string\n /** Default `true`. */\n critical?: boolean\n /** Env-var name of the API key, named verbatim in a dead-key failure. */\n keySecret?: string\n /** Env-var name of the base URL, named verbatim in an unreachable failure. */\n urlSecret?: string\n /** Per-probe deadline. Default 10s. */\n timeoutMs?: number\n /** Injection seam for tests; defaults to global `fetch`. */\n fetchImpl?: typeof fetch\n}\n\n/**\n * Probe an OpenAI-compatible LLM router with one cheap `POST /chat/completions`\n * (`max_tokens: 1`). 200 → live; 401/403 → dead router key; 503 → upstream\n * provider down (key still valid); timeout / unreachable → check the router URL.\n */\nexport function routerChatProbe(config: RouterChatProbeConfig): PreflightProbe {\n const keyLabel = config.keySecret ?? 'the router API key'\n const urlLabel = config.urlSecret ?? 'the router base URL'\n return {\n name: config.name ?? 'router-chat',\n critical: config.critical,\n run: async () => {\n const base = trimTrailingSlash(config.baseUrl)\n const endpoint = `${base}/chat/completions`\n const outcome = await runHttp({\n fetchImpl: config.fetchImpl ?? fetch,\n url: endpoint,\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model: config.model,\n messages: [{ role: 'user', content: 'ping' }],\n max_tokens: 1,\n }),\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n })\n return classifyAuthed(outcome, { endpoint, keyLabel, urlLabel })\n },\n }\n}\n\n/** Define configuration options for probing sandbox authentication endpoints */\nexport interface SandboxAuthProbeConfig {\n /** Sandbox API base URL. */\n baseUrl: string\n apiKey: string\n /** Probe name in the report. Default `'sandbox-auth'`. */\n name?: string\n /** Default `true`. */\n critical?: boolean\n /** Env-var name of the API key, named verbatim in a dead-key failure. */\n keySecret?: string\n /** Env-var name of the base URL, named verbatim in an unreachable failure. */\n urlSecret?: string\n /** Per-probe deadline. Default 10s. */\n timeoutMs?: number\n /** Injection seam for tests; defaults to global `fetch`. */\n fetchImpl?: typeof fetch\n}\n\n/**\n * Probe the sandbox API with a cheap authed `GET /v1/sandboxes?limit=1`.\n * 200 → live; 401/403 → dead sandbox key; 503 → sandbox platform down (key\n * still valid); timeout / unreachable → check the sandbox URL.\n */\nexport function sandboxAuthProbe(config: SandboxAuthProbeConfig): PreflightProbe {\n const keyLabel = config.keySecret ?? 'the sandbox API key'\n const urlLabel = config.urlSecret ?? 'the sandbox base URL'\n return {\n name: config.name ?? 'sandbox-auth',\n critical: config.critical,\n run: async () => {\n const base = trimTrailingSlash(config.baseUrl)\n const endpoint = `${base}/v1/sandboxes?limit=1`\n const outcome = await runHttp({\n fetchImpl: config.fetchImpl ?? fetch,\n url: endpoint,\n method: 'GET',\n headers: { Authorization: `Bearer ${config.apiKey}` },\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n })\n return classifyAuthed(outcome, { endpoint, keyLabel, urlLabel })\n },\n }\n}\n\n/** Define configuration options for performing an HTTP HEAD probe to check URL availability */\nexport interface HttpHeadProbeConfig {\n /** Probe name in the report. */\n name: string\n /** URL to `HEAD`. */\n url: string\n /**\n * Accepted status(es). A single number requires an exact match; an array\n * requires membership. Omitted → any 2xx/3xx (the host is up and the path\n * resolves) counts as live.\n */\n expectStatus?: number | number[]\n /** Default `true`. */\n critical?: boolean\n /** Env-var name of the URL, named verbatim in a failure. */\n urlSecret?: string\n /** Per-probe deadline. Default 10s. */\n timeoutMs?: number\n /** Injection seam for tests; defaults to global `fetch`. */\n fetchImpl?: typeof fetch\n}\n\nfunction statusMatches(status: number, expect?: number | number[]): boolean {\n if (expect === undefined) return status >= 200 && status < 400\n if (Array.isArray(expect)) return expect.includes(status)\n return status === expect\n}\n\nfunction describeExpected(expect?: number | number[]): string {\n if (expect === undefined) return '2xx/3xx'\n if (Array.isArray(expect)) return expect.join(' or ')\n return String(expect)\n}\n\n/**\n * Probe a plain reachability endpoint (e.g. a platform base URL) with a `HEAD`.\n * Confirms the URL is live and resolving — the class of failure behind a stale\n * platform URL that still sits in the secret store.\n */\nexport function httpHeadProbe(config: HttpHeadProbeConfig): PreflightProbe {\n const urlLabel = config.urlSecret ?? `the URL for ${config.name}`\n return {\n name: config.name,\n critical: config.critical,\n run: async () => {\n const outcome = await runHttp({\n fetchImpl: config.fetchImpl ?? fetch,\n url: config.url,\n method: 'HEAD',\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n })\n switch (outcome.kind) {\n case 'status': {\n if (statusMatches(outcome.status, config.expectStatus)) {\n return { ok: true, detail: `${outcome.status} OK` }\n }\n return {\n ok: false,\n detail: `UNEXPECTED ${outcome.status} from ${config.url} (expected ${describeExpected(config.expectStatus)}) — check ${urlLabel}`,\n }\n }\n case 'timeout':\n return {\n ok: false,\n detail: `TIMEOUT after ${outcome.timeoutMs}ms reaching ${config.url} — check ${urlLabel}`,\n }\n case 'network':\n return {\n ok: false,\n detail: `UNREACHABLE ${config.url} (${outcome.message}) — check ${urlLabel}`,\n }\n }\n },\n }\n}\n\n// --- Runner + report ----------------------------------------------------------\n\nasync function runOne(probe: PreflightProbe): Promise<PreflightProbeVerdict> {\n const critical = probe.critical ?? true\n const start = nowMs()\n try {\n const result = await probe.run()\n return {\n name: probe.name,\n ok: result.ok,\n critical,\n latencyMs: Math.round(nowMs() - start),\n detail: result.detail,\n }\n } catch (err) {\n return {\n name: probe.name,\n ok: false,\n critical,\n latencyMs: Math.round(nowMs() - start),\n detail: `probe threw: ${sanitizeUpstreamMessage(err)}`,\n }\n }\n}\n\n/**\n * Run every probe (concurrently), time each, and fold into a report. The run\n * fails (`ok: false`) iff a critical probe fails; a failed non-critical probe\n * is a warning that does not block the deploy.\n */\nexport async function runPreflight(probes: PreflightProbe[]): Promise<PreflightReport> {\n const start = nowMs()\n const verdicts = await Promise.all(probes.map(runOne))\n const failed = verdicts.filter((v) => !v.ok)\n const criticalFailures = failed.filter((v) => v.critical).length\n return {\n ok: criticalFailures === 0,\n probes: verdicts,\n passed: verdicts.length - failed.length,\n failed: failed.length,\n criticalFailures,\n durationMs: Math.round(nowMs() - start),\n }\n}\n\ninterface FormatRow {\n status: string\n name: string\n latency: string\n detail: string\n}\n\n/** Render a report as an aligned, operator-readable table + verdict line. Pure\n * (no I/O) so it is trivially testable and reusable by the bin. */\nexport function formatPreflightReport(report: PreflightReport): string {\n const header: FormatRow = { status: 'STATUS', name: 'PROBE', latency: 'LATENCY', detail: 'DETAIL' }\n const rows: FormatRow[] = report.probes.map((p) => ({\n status: p.ok ? 'PASS' : p.critical ? 'FAIL' : 'WARN',\n name: p.name,\n latency: `${p.latencyMs}ms`,\n detail: p.detail ?? '',\n }))\n const statusW = Math.max(header.status.length, ...rows.map((r) => r.status.length))\n const nameW = Math.max(header.name.length, ...rows.map((r) => r.name.length))\n const latencyW = Math.max(header.latency.length, ...rows.map((r) => r.latency.length))\n const line = (r: FormatRow): string =>\n `${r.status.padEnd(statusW)} ${r.name.padEnd(nameW)} ${r.latency.padStart(latencyW)} ${r.detail}`.trimEnd()\n\n const out: string[] = [\n line(header),\n `${'-'.repeat(statusW)} ${'-'.repeat(nameW)} ${'-'.repeat(latencyW)} ------`,\n ...rows.map(line),\n '',\n ]\n if (report.ok) {\n const warn = report.failed > 0 ? ` (${report.failed} non-critical warning(s))` : ''\n out.push(`Preflight PASSED — ${report.passed}/${report.probes.length} probe(s) live${warn}`)\n } else {\n const dead = report.probes\n .filter((p) => !p.ok && p.critical)\n .map((p) => p.name)\n .join(', ')\n out.push(`Preflight FAILED — ${report.criticalFailures} critical probe(s) dead: ${dead}`)\n out.push('Rotate the secret named in each FAIL row above, then redeploy.')\n }\n return out.join('\\n')\n}\n"],"mappings":";AA+DA,IAAM,qBAAqB;AAE3B,SAAS,QAAgB;AACvB,SAAO,OAAO,gBAAgB,cAAc,YAAY,IAAI,IAAI,KAAK,IAAI;AAC3E;AAEA,SAAS,YAAY,KAAuB;AAC1C,SAAO,eAAe,UAAU,IAAI,SAAS,kBAAkB,IAAI,SAAS;AAC9E;AAIA,SAAS,wBAAwB,OAAwB;AACvD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAO,QACJ,QAAQ,qBAAqB,mBAAmB,EAChD,QAAQ,0CAA0C,gBAAgB;AACvE;AAEA,SAAS,QAAQ,MAAsB;AACrC,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,UAAU,QAAQ,SAAS,MAAM,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,WAAM;AACrE,SAAO,KAAK,wBAAwB,OAAO,CAAC;AAC9C;AAmBA,eAAe,QAAQ,MAA4C;AACjE,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,KAAK,UAAU,KAAK,KAAK;AAAA,MACxC,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,QAAQ,YAAY,QAAQ,KAAK,SAAS;AAAA,IAC5C,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,YAAY,GAAG,EAAG,QAAO,EAAE,MAAM,WAAW,WAAW,KAAK,UAAU;AAC1E,WAAO,EAAE,MAAM,WAAW,SAAS,wBAAwB,GAAG,EAAE;AAAA,EAClE;AACA,MAAI,WAAW;AACf,MAAI;AACF,eAAW,MAAM,SAAS,KAAK;AAAA,EACjC,QAAQ;AACN,eAAW;AAAA,EACb;AACA,SAAO,EAAE,MAAM,UAAU,QAAQ,SAAS,QAAQ,SAAS;AAC7D;AAiBA,SAAS,eAAe,SAAuB,KAAkD;AAC/F,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK,UAAU;AACb,YAAM,EAAE,QAAQ,SAAS,IAAI;AAC7B,UAAI,UAAU,OAAO,SAAS,IAAK,QAAO,EAAE,IAAI,MAAM,QAAQ,GAAG,MAAM,MAAM;AAC7E,UAAI,WAAW,OAAO,WAAW,KAAK;AACpC,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ,mBAAc,IAAI,QAAQ,aAAa,MAAM,YAAY,IAAI,QAAQ;AAAA,QAC/E;AAAA,MACF;AACA,UAAI,WAAW,KAAK;AAClB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ,wBAAmB,IAAI,QAAQ,kBAAkB,IAAI,QAAQ;AAAA,QACvE;AAAA,MACF;AACA,aAAO,EAAE,IAAI,OAAO,QAAQ,cAAc,MAAM,SAAS,IAAI,QAAQ,GAAG,QAAQ,QAAQ,CAAC,GAAG;AAAA,IAC9F;AAAA,IACA,KAAK;AACH,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,iBAAiB,QAAQ,SAAS,eAAe,IAAI,QAAQ,iBAAY,IAAI,QAAQ;AAAA,MAC/F;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,eAAe,IAAI,QAAQ,KAAK,QAAQ,OAAO,kBAAa,IAAI,QAAQ;AAAA,MAClF;AAAA,EACJ;AACF;AAEA,SAAS,kBAAkB,KAAqB;AAC9C,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;AA8BO,SAAS,gBAAgB,QAA+C;AAC7E,QAAM,WAAW,OAAO,aAAa;AACrC,QAAM,WAAW,OAAO,aAAa;AACrC,SAAO;AAAA,IACL,MAAM,OAAO,QAAQ;AAAA,IACrB,UAAU,OAAO;AAAA,IACjB,KAAK,YAAY;AACf,YAAM,OAAO,kBAAkB,OAAO,OAAO;AAC7C,YAAM,WAAW,GAAG,IAAI;AACxB,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,WAAW,OAAO,aAAa;AAAA,QAC/B,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,OAAO,MAAM;AAAA,UACtC,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,OAAO,OAAO;AAAA,UACd,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,UAC5C,YAAY;AAAA,QACd,CAAC;AAAA,QACD,WAAW,OAAO,aAAa;AAAA,MACjC,CAAC;AACD,aAAO,eAAe,SAAS,EAAE,UAAU,UAAU,SAAS,CAAC;AAAA,IACjE;AAAA,EACF;AACF;AA0BO,SAAS,iBAAiB,QAAgD;AAC/E,QAAM,WAAW,OAAO,aAAa;AACrC,QAAM,WAAW,OAAO,aAAa;AACrC,SAAO;AAAA,IACL,MAAM,OAAO,QAAQ;AAAA,IACrB,UAAU,OAAO;AAAA,IACjB,KAAK,YAAY;AACf,YAAM,OAAO,kBAAkB,OAAO,OAAO;AAC7C,YAAM,WAAW,GAAG,IAAI;AACxB,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,WAAW,OAAO,aAAa;AAAA,QAC/B,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,UAAU,OAAO,MAAM,GAAG;AAAA,QACpD,WAAW,OAAO,aAAa;AAAA,MACjC,CAAC;AACD,aAAO,eAAe,SAAS,EAAE,UAAU,UAAU,SAAS,CAAC;AAAA,IACjE;AAAA,EACF;AACF;AAwBA,SAAS,cAAc,QAAgB,QAAqC;AAC1E,MAAI,WAAW,OAAW,QAAO,UAAU,OAAO,SAAS;AAC3D,MAAI,MAAM,QAAQ,MAAM,EAAG,QAAO,OAAO,SAAS,MAAM;AACxD,SAAO,WAAW;AACpB;AAEA,SAAS,iBAAiB,QAAoC;AAC5D,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,MAAM,QAAQ,MAAM,EAAG,QAAO,OAAO,KAAK,MAAM;AACpD,SAAO,OAAO,MAAM;AACtB;AAOO,SAAS,cAAc,QAA6C;AACzE,QAAM,WAAW,OAAO,aAAa,eAAe,OAAO,IAAI;AAC/D,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB,KAAK,YAAY;AACf,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,WAAW,OAAO,aAAa;AAAA,QAC/B,KAAK,OAAO;AAAA,QACZ,QAAQ;AAAA,QACR,WAAW,OAAO,aAAa;AAAA,MACjC,CAAC;AACD,cAAQ,QAAQ,MAAM;AAAA,QACpB,KAAK,UAAU;AACb,cAAI,cAAc,QAAQ,QAAQ,OAAO,YAAY,GAAG;AACtD,mBAAO,EAAE,IAAI,MAAM,QAAQ,GAAG,QAAQ,MAAM,MAAM;AAAA,UACpD;AACA,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,QAAQ,cAAc,QAAQ,MAAM,SAAS,OAAO,GAAG,cAAc,iBAAiB,OAAO,YAAY,CAAC,kBAAa,QAAQ;AAAA,UACjI;AAAA,QACF;AAAA,QACA,KAAK;AACH,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,QAAQ,iBAAiB,QAAQ,SAAS,eAAe,OAAO,GAAG,iBAAY,QAAQ;AAAA,UACzF;AAAA,QACF,KAAK;AACH,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,QAAQ,eAAe,OAAO,GAAG,KAAK,QAAQ,OAAO,kBAAa,QAAQ;AAAA,UAC5E;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AACF;AAIA,eAAe,OAAO,OAAuD;AAC3E,QAAM,WAAW,MAAM,YAAY;AACnC,QAAM,QAAQ,MAAM;AACpB,MAAI;AACF,UAAM,SAAS,MAAM,MAAM,IAAI;AAC/B,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,IAAI,OAAO;AAAA,MACX;AAAA,MACA,WAAW,KAAK,MAAM,MAAM,IAAI,KAAK;AAAA,MACrC,QAAQ,OAAO;AAAA,IACjB;AAAA,EACF,SAAS,KAAK;AACZ,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,IAAI;AAAA,MACJ;AAAA,MACA,WAAW,KAAK,MAAM,MAAM,IAAI,KAAK;AAAA,MACrC,QAAQ,gBAAgB,wBAAwB,GAAG,CAAC;AAAA,IACtD;AAAA,EACF;AACF;AAOA,eAAsB,aAAa,QAAoD;AACrF,QAAM,QAAQ,MAAM;AACpB,QAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,IAAI,MAAM,CAAC;AACrD,QAAM,SAAS,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE;AAC3C,QAAM,mBAAmB,OAAO,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE;AAC1D,SAAO;AAAA,IACL,IAAI,qBAAqB;AAAA,IACzB,QAAQ;AAAA,IACR,QAAQ,SAAS,SAAS,OAAO;AAAA,IACjC,QAAQ,OAAO;AAAA,IACf;AAAA,IACA,YAAY,KAAK,MAAM,MAAM,IAAI,KAAK;AAAA,EACxC;AACF;AAWO,SAAS,sBAAsB,QAAiC;AACrE,QAAM,SAAoB,EAAE,QAAQ,UAAU,MAAM,SAAS,SAAS,WAAW,QAAQ,SAAS;AAClG,QAAM,OAAoB,OAAO,OAAO,IAAI,CAAC,OAAO;AAAA,IAClD,QAAQ,EAAE,KAAK,SAAS,EAAE,WAAW,SAAS;AAAA,IAC9C,MAAM,EAAE;AAAA,IACR,SAAS,GAAG,EAAE,SAAS;AAAA,IACvB,QAAQ,EAAE,UAAU;AAAA,EACtB,EAAE;AACF,QAAM,UAAU,KAAK,IAAI,OAAO,OAAO,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,MAAM,CAAC;AAClF,QAAM,QAAQ,KAAK,IAAI,OAAO,KAAK,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAC5E,QAAM,WAAW,KAAK,IAAI,OAAO,QAAQ,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM,CAAC;AACrF,QAAM,OAAO,CAAC,MACZ,GAAG,EAAE,OAAO,OAAO,OAAO,CAAC,KAAK,EAAE,KAAK,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,SAAS,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;AAE/G,QAAM,MAAgB;AAAA,IACpB,KAAK,MAAM;AAAA,IACX,GAAG,IAAI,OAAO,OAAO,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,IAAI,OAAO,QAAQ,CAAC;AAAA,IACrE,GAAG,KAAK,IAAI,IAAI;AAAA,IAChB;AAAA,EACF;AACA,MAAI,OAAO,IAAI;AACb,UAAM,OAAO,OAAO,SAAS,IAAI,KAAK,OAAO,MAAM,8BAA8B;AACjF,QAAI,KAAK,2BAAsB,OAAO,MAAM,IAAI,OAAO,OAAO,MAAM,iBAAiB,IAAI,EAAE;AAAA,EAC7F,OAAO;AACL,UAAM,OAAO,OAAO,OACjB,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EACjC,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,IAAI;AACZ,QAAI,KAAK,2BAAsB,OAAO,gBAAgB,4BAA4B,IAAI,EAAE;AACxF,QAAI,KAAK,gEAAgE;AAAA,EAC3E;AACA,SAAO,IAAI,KAAK,IAAI;AACtB;","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  buildAppToolOpenAITools,
3
3
  createAppToolRuntimeExecutor,
4
4
  isAppToolName
5
- } from "./chunk-MFRCM32T.js";
5
+ } from "./chunk-7CTIUCQ4.js";
6
6
 
7
7
  // src/runtime/openai-stream.ts
8
8
  async function* toLoopEvents(chunks) {
@@ -338,4 +338,4 @@ export {
338
338
  createSurfaceRegistry,
339
339
  mergeSurfaceOverlay
340
340
  };
341
- //# sourceMappingURL=chunk-CCRVDLV3.js.map
341
+ //# sourceMappingURL=chunk-JWBZ74TW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/openai-stream.ts","../src/runtime/loop.ts","../src/runtime/agent.ts","../src/runtime/certified-delivery.ts","../src/runtime/surface-profile.ts"],"sourcesContent":["/**\n * OpenAI-compatible stream → `LoopEvent` adapter, for NON-sandbox copilots.\n *\n * `streamAppToolLoop` takes a `streamTurn` seam that yields `LoopEvent`s. A\n * sandboxed agent produces those from its container; a browser/edge copilot\n * instead calls a model directly. The Tangle Router, the tcloud SDK, and most\n * providers all speak the OpenAI Chat Completions streaming shape — so the ONE\n * reusable piece is assembling that stream (content deltas + FRAGMENTED\n * tool-call deltas) into `LoopEvent`s. That assembly is the boilerplate every\n * copilot would re-write (and get wrong — OpenAI streams tool-call arguments in\n * pieces across chunks).\n *\n * This does NOT implement an HTTP client beyond a minimal `fetch` + SSE reader\n * (browser/edge/Node-safe, zero deps). For richer transport use the tcloud SDK\n * or the Vercel AI SDK and pipe their stream through {@link toLoopEvents}.\n */\nimport type { LoopEvent, LoopMessage, LoopToolCall } from './loop'\n\n/** Minimal OpenAI Chat Completions streaming chunk (structural — no `openai` dep). */\nexport interface OpenAIStreamChunk {\n choices?: Array<{\n delta?: {\n content?: string | null\n /** Reasoning deltas — DeepSeek/router use `reasoning_content`; some proxies use `thinking`. */\n reasoning_content?: string | null\n thinking?: string | null\n tool_calls?: Array<{\n index: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n }\n finish_reason?: string | null\n }>\n /** Final-chunk token accounting (requires `stream_options.include_usage`). */\n usage?: {\n prompt_tokens?: number\n completion_tokens?: number\n } | null\n}\n\ninterface PartialToolCall {\n id?: string\n name: string\n args: string\n}\n\n/**\n * Map an OpenAI-compat streaming chunk iterator to `LoopEvent`s: each content\n * delta → a `text` event; tool-call deltas are accumulated by index across\n * chunks and emitted as one complete `tool_call` event when the stream finishes\n * (arguments JSON-parsed; an empty/garbled args string yields `{}` rather than\n * throwing). Works for the Tangle Router, tcloud, or any OpenAI-compat source.\n */\nexport async function* toLoopEvents(chunks: AsyncIterable<OpenAIStreamChunk>): AsyncIterable<LoopEvent> {\n const calls = new Map<number, PartialToolCall>()\n for await (const chunk of chunks) {\n // Usage rides the final chunk, which has an empty choices array — handle\n // it before the choice guard.\n if (chunk.usage?.prompt_tokens != null || chunk.usage?.completion_tokens != null) {\n yield {\n type: 'usage',\n usage: {\n promptTokens: chunk.usage.prompt_tokens ?? 0,\n completionTokens: chunk.usage.completion_tokens ?? 0,\n },\n }\n }\n const choice = chunk.choices?.[0]\n if (!choice) continue\n const content = choice.delta?.content\n if (content) yield { type: 'text', text: content }\n const reasoning = choice.delta?.reasoning_content ?? choice.delta?.thinking\n if (reasoning) yield { type: 'reasoning', text: reasoning }\n for (const tc of choice.delta?.tool_calls ?? []) {\n const cur = calls.get(tc.index) ?? { name: '', args: '' }\n if (tc.id) cur.id = tc.id\n if (tc.function?.name) cur.name += tc.function.name\n if (tc.function?.arguments) cur.args += tc.function.arguments\n calls.set(tc.index, cur)\n }\n }\n for (const [, c] of [...calls.entries()].sort((a, b) => a[0] - b[0])) {\n if (!c.name) continue\n yield { type: 'tool_call', call: { toolCallId: c.id, toolName: c.name, args: safeParse(c.args) } satisfies LoopToolCall }\n }\n}\n\nfunction safeParse(s: string): Record<string, unknown> {\n if (!s.trim()) return {}\n try {\n const v = JSON.parse(s)\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {}\n } catch {\n return {}\n }\n}\n\n/** Define options for configuring an OpenAI-compatible streaming chat turn including API details and tools */\nexport interface OpenAICompatStreamTurnOptions {\n /** OpenAI-compat base URL (e.g. the Tangle Router `https://router.tangle.tools/v1`). */\n baseUrl: string\n apiKey: string\n model: string\n /** OpenAI tool definitions — pass `buildAppToolOpenAITools(taxonomy)` so the\n * model can call the app tools. Omit for a tool-free copilot. */\n tools?: unknown[]\n temperature?: number\n fetchImpl?: typeof fetch\n /** Extra body fields (e.g. `max_tokens`). */\n extraBody?: Record<string, unknown>\n}\n\n/**\n * Build a `streamTurn` that calls an OpenAI-compatible `/chat/completions`\n * endpoint (Tangle Router / tcloud / any compat provider) with `stream: true`\n * and yields `LoopEvent`s via {@link toLoopEvents}. Browser/edge/Node-safe —\n * just `fetch` + an SSE reader. Drop straight into `streamAppToolLoop`:\n *\n * const cfg = resolveTangleModelConfig() // or { baseUrl, apiKey, model }\n * streamAppToolLoop({ streamTurn: createOpenAICompatStreamTurn({ ...cfg, tools }), executeToolCall, ... })\n */\nexport function createOpenAICompatStreamTurn(\n opts: OpenAICompatStreamTurnOptions,\n): (messages: LoopMessage[]) => AsyncIterable<LoopEvent> {\n const base = opts.baseUrl.replace(/\\/+$/, '')\n const doFetch = opts.fetchImpl ?? fetch\n return (messages) =>\n toLoopEvents(\n streamChatCompletions(doFetch, `${base}/chat/completions`, opts.apiKey, {\n model: opts.model,\n messages,\n stream: true,\n stream_options: { include_usage: true },\n ...(opts.tools && opts.tools.length > 0 ? { tools: opts.tools } : {}),\n ...(opts.temperature != null ? { temperature: opts.temperature } : {}),\n ...opts.extraBody,\n }),\n )\n}\n\n/** Stream + parse an OpenAI-compat SSE response into chunks. Tolerates `data:`\n * framing, multi-line buffers, and the terminal `[DONE]`. */\nasync function* streamChatCompletions(\n doFetch: typeof fetch,\n url: string,\n apiKey: string,\n body: Record<string, unknown>,\n): AsyncIterable<OpenAIStreamChunk> {\n const res = await doFetch(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', Accept: 'text/event-stream' },\n body: JSON.stringify(body),\n })\n if (!res.ok || !res.body) {\n const text = res.body ? await res.text().catch(() => '') : ''\n throw new Error(`OpenAI-compat stream failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : ''}`)\n }\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed.startsWith('data:')) continue\n const data = trimmed.slice(5).trim()\n if (data === '[DONE]') return\n try {\n yield JSON.parse(data) as OpenAIStreamChunk\n } catch {\n /* skip a partial/garbled SSE frame */\n }\n }\n }\n}\n","/**\n * The bounded agent tool-loop — owned by `@tangle-network/agent-runtime`.\n *\n * A model turn may emit tool calls (integration-hub actions, the app tools from\n * `../tools`, delegation). The loop streams a turn, collects the executable tool\n * calls, dispatches each, appends the results to history in OpenAI\n * function-calling shape, and re-runs so the model reads them — bounded by\n * `maxToolTurns`, a wall-clock `deadlineMs`, and a `maxCostUsd` budget.\n *\n * The history shape is the OpenAI function-calling contract: the assistant turn\n * that emitted tool calls is preserved as an `assistant` message carrying its\n * `tool_calls` array, and each result is its own `{ role: 'tool', tool_call_id,\n * content }` message keyed to the call. A strict model (Claude, and any\n * OpenAI-compatible provider that validates tool history) needs this to read its\n * own tool use back; folding results into a `user` message makes such models\n * re-issue the same call in a loop.\n *\n * The loop is substrate-owned (`runToolLoop` / `streamToolLoop`); the app\n * supplies `streamTurn` (wrapping its model endpoint) and `executeToolCall`\n * (routing to its integration + app-tool executors). The app-facing names below\n * are 1:1 aliases of the canonical symbols, kept so this package's consumers and\n * the in-package `createAgentRuntime` read against a single, stable vocabulary.\n *\n * This is the LEAF the runtime barrel and its children both import — keeping the\n * tool-loop vocabulary out of any import cycle. The barrel re-exports it.\n */\n\nexport {\n runToolLoop as runAppToolLoop,\n streamToolLoop as streamAppToolLoop,\n} from '@tangle-network/agent-runtime'\nexport type {\n ToolLoopCall as LoopToolCall,\n ToolLoopAssistantToolCall as LoopAssistantToolCall,\n ToolLoopMessage as LoopMessage,\n ToolLoopEvent,\n ToolLoopStopReason,\n ToolLoopResult,\n RunToolLoopOptions as AppToolLoopOptions,\n StreamToolLoopOptions as StreamAppToolLoopOptions,\n StreamToolLoopYield as StreamLoopYield,\n} from '@tangle-network/agent-runtime'\n\n/**\n * Events the app's OpenAI-compat stream adapter ({@link toLoopEvents}) yields.\n *\n * This is the app's own `Raw` event type for the streaming loop — the canonical\n * `streamToolLoop<Raw>` is generic over it. It widens the substrate's\n * tool-loop event with `reasoning` (DeepSeek/router `reasoning_content` /\n * `thinking` deltas, rendered as thinking sections) and `usage` (per-message\n * token accounting) — neither belongs in the substrate's loop contract, so they\n * stay here. The adapter maps each into the `streamTurn` seam; `text` and\n * `tool_call` drive the loop, `reasoning` / `usage` pass through to the UI.\n */\nexport type LoopEvent =\n | { type: 'text'; text: string }\n | { type: 'reasoning'; text: string }\n | { type: 'tool_call'; call: import('@tangle-network/agent-runtime').ToolLoopCall }\n | { type: 'usage'; usage: { promptTokens: number; completionTokens: number } }\n | { type: 'other'; event: unknown }\n","/**\n * `createAgentRuntime` — the in-process agent core, assembled.\n *\n * The bricks to run an agent turn WITHOUT a sandbox already exist in this\n * package, but a consumer must hand-wire five of them every time: resolve the\n * model config, build the OpenAI tool schemas from the taxonomy, build a\n * `streamTurn` over the model endpoint, build an `executeToolCall` over the\n * product's handlers, and drive `runAppToolLoop` / `streamAppToolLoop` with an\n * `isExecutableTool` predicate. That boilerplate is identical across every\n * sandbox-free surface (an edge/browser copilot, an eval harness, a Node CLI),\n * and getting it subtly wrong — e.g. NOT advertising the tools, so the model\n * never emits a `tool_call` and no side effect ever fires — is exactly the\n * failure that makes a tool-driven agent score zero off-sandbox.\n *\n * This factory bundles those five into one object configured for ONE agent:\n *\n * const runtime = createAgentRuntime({ model, taxonomy, handlers, systemPrompt })\n * const result = await runtime.run(userMessage, { ctx }) // awaitable\n * for await (const y of runtime.stream(userMessage, { ctx })) {…} // streaming\n *\n * The model is advertised the app tools (so it CAN call them); each call is\n * dispatched against the product's `handlers` (so the side effect is real); the\n * `onProduced` hook fires at the real side-effect site (so an eval/UI credits a\n * persisted proposal or artifact). Substrate-free: no `@tangle-network/sandbox`,\n * no Durable Object, no `@tangle-network/agent-runtime` import. The SAME core\n * the Cloudflare Worker runs, runnable anywhere a `fetch` to an OpenAI-compatible\n * endpoint works.\n *\n * Domain stays out: the proposal taxonomy, the handlers, and the system prompt\n * are all injected — the factory knows nothing about insurance, law, tax, etc.\n */\nimport {\n type AppToolHandlers,\n type AppToolContext,\n type AppToolOutcome,\n type AppToolProducedEvent,\n type AppToolTaxonomy,\n} from '../tools/types'\nimport { buildAppToolOpenAITools, isAppToolName } from '../tools/openai'\nimport { createAppToolRuntimeExecutor } from '../tools/runtime'\nimport {\n runAppToolLoop,\n streamAppToolLoop,\n type LoopEvent,\n type LoopMessage,\n type LoopToolCall,\n type StreamLoopYield,\n type ToolLoopEvent,\n type ToolLoopResult,\n} from './loop'\nimport { createOpenAICompatStreamTurn } from './openai-stream'\n\n/** OpenAI-compatible model endpoint (Tangle Router / tcloud / any compat\n * provider). Build from {@link resolveTangleModelConfig} or pass literals. */\nexport interface AgentRuntimeModelConfig {\n baseUrl: string\n apiKey: string\n model: string\n temperature?: number\n fetchImpl?: typeof fetch\n /** Extra request-body fields (e.g. `max_tokens`, a `reasoning` block). */\n extraBody?: Record<string, unknown>\n}\n\n/** The agent's resolved profile surfaces for one turn — the things a delivered\n * / certified `AgentProfile` can change. Profile-WIDE on purpose: certified\n * delivery folds prompt-surface + skills into `systemPrompt` AND can add\n * certified `tool` artifacts to `extraTools` (the model's advertised tools is\n * rebuilt when these change). MCP servers / memory / RAG that materialize as\n * files or servers deliver through the sandbox-provisioning seam, not here. */\nexport interface ResolvedAgentProfile {\n systemPrompt: string\n extraTools: unknown[]\n}\n\n/** Define options for creating an agent runtime including model config and optional profile transformation */\nexport interface CreateAgentRuntimeOptions {\n /** The model endpoint the turns stream from. */\n model: AgentRuntimeModelConfig\n /**\n * Optional transform applied to the resolved profile surfaces each turn —\n * the seam for certified-artifact delivery (`createCertifiedDelivery`). It is\n * profile-WIDE (not prompt-only): it returns the effective `systemPrompt` +\n * advertised `extraTools`. Kept generic + injected so this substrate-free core\n * never imports `@tangle-network/agent-runtime`. Fail-closed by contract: an\n * impl that can't reach the plane returns the base surfaces unchanged.\n */\n composeProfile?: (base: ResolvedAgentProfile) => ResolvedAgentProfile | Promise<ResolvedAgentProfile>\n /** The product's proposal taxonomy — advertises `submit_proposal`'s `type`\n * enum to the model and labels the regulated subset on the result. */\n taxonomy: AppToolTaxonomy\n /** Domain handlers persisting each tool to the product's store/vault. */\n handlers: AppToolHandlers\n /** Default agent identity / system prompt. A turn may override it. */\n systemPrompt: string\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow.\n * For per-workflow limits use `deadlineMs` or `maxCostUsd` on the loop options. */\n maxToolTurns?: number\n /** Extra OpenAI tool definitions advertised ALONGSIDE the four app tools\n * (e.g. `integration_invoke`). Pair with {@link executeOtherTool}. */\n extraTools?: unknown[]\n /** Execute a tool that is NOT one of the four app tools (e.g. an integration\n * action). Only consulted for names {@link isOtherExecutableTool} accepts. */\n executeOtherTool?: (call: LoopToolCall, ctx: AppToolContext) => Promise<AppToolOutcome>\n /** Which non-app tool names are executable here. Required if {@link executeOtherTool} is set. */\n isOtherExecutableTool?: (toolName: string) => boolean\n}\n\n/** Define options for configuring a single agent turn including context, prior messages, prompts, and event handlers */\nexport interface AgentTurnOptions {\n /** The trusted per-turn context (who/where the turn runs as). */\n ctx: AppToolContext\n /** Prior conversation turns, in order. */\n priorMessages?: Array<{ role: string; content: string }>\n /** Override the factory's default system prompt for this turn. */\n systemPrompt?: string\n /** Fires at the real side-effect site for each produced proposal/artifact. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\n/** Resolve and stream tool execution loops with final results and intermediate events for agent runtime */\nexport interface AgentRuntime {\n /** Run the bounded tool loop to completion; resolve with final text + every\n * executed tool outcome. */\n run(userMessage: string, turn: AgentTurnOptions): Promise<ToolLoopResult>\n /** Stream the bounded tool loop: yields each raw model event and each executed\n * tool result as it happens (for SSE re-emission + telemetry). */\n stream(userMessage: string, turn: AgentTurnOptions): AsyncGenerator<StreamLoopYield<LoopEvent>, void, unknown>\n}\n\n/**\n * Create an in-process agent runtime for one agent. See the module doc for the\n * full rationale; the short version: it advertises the app tools to the model,\n * dispatches each emitted call against `handlers`, and drives the bounded loop —\n * the whole agent core, sandbox-free.\n */\nexport function createAgentRuntime(opts: CreateAgentRuntimeOptions): AgentRuntime {\n if (opts.executeOtherTool && !opts.isOtherExecutableTool) {\n throw new Error('createAgentRuntime: isOtherExecutableTool is required when executeOtherTool is set')\n }\n\n // Tool schemas + the streamTurn are stable across turns — build once. The\n // model MUST be advertised the tools or it never emits a tool_call (the exact\n // failure that scores a tool-driven agent zero off-sandbox).\n const m = opts.model\n const buildStreamTurn = (extraTools: unknown[]) =>\n createOpenAICompatStreamTurn({\n baseUrl: m.baseUrl,\n apiKey: m.apiKey,\n model: m.model,\n tools: [...buildAppToolOpenAITools(opts.taxonomy), ...extraTools],\n temperature: m.temperature,\n fetchImpl: m.fetchImpl,\n extraBody: m.extraBody,\n })\n\n // The advertised tool set is stable across turns UNLESS a delivered profile\n // changes `extraTools` (certified-tool delivery, on the cache-refresh cadence\n // — not per turn). Memoize the streamTurn by the active extraTools identity so\n // it rebuilds only when the certified tools actually change.\n const baseExtraTools = opts.extraTools ?? []\n let activeExtraTools = baseExtraTools\n let activeStreamTurn = buildStreamTurn(baseExtraTools)\n const streamTurnFor = (extraTools: unknown[]) => {\n if (extraTools !== activeExtraTools) {\n activeExtraTools = extraTools\n activeStreamTurn = buildStreamTurn(extraTools)\n }\n return activeStreamTurn\n }\n\n // Resolve the per-turn profile surfaces, applying the optional profile\n // transform (certified-artifact delivery). Profile-wide: system prompt +\n // advertised tools.\n const resolveProfile = async (turn: AgentTurnOptions): Promise<ResolvedAgentProfile> => {\n const base: ResolvedAgentProfile = {\n systemPrompt: turn.systemPrompt ?? opts.systemPrompt,\n extraTools: baseExtraTools,\n }\n return opts.composeProfile ? opts.composeProfile(base) : base\n }\n\n const isExecutableTool = (name: string): boolean =>\n isAppToolName(name) || (opts.isOtherExecutableTool?.(name) ?? false)\n\n const buildExecutor = (turn: AgentTurnOptions) => {\n const appExecutor = createAppToolRuntimeExecutor({\n handlers: opts.handlers,\n taxonomy: opts.taxonomy,\n ctx: turn.ctx,\n onProduced: turn.onProduced,\n })\n return async (call: LoopToolCall): Promise<AppToolOutcome> => {\n if (isAppToolName(call.toolName)) return appExecutor({ toolName: call.toolName, args: call.args })\n if (opts.executeOtherTool && opts.isOtherExecutableTool?.(call.toolName)) {\n return opts.executeOtherTool(call, turn.ctx)\n }\n return { ok: false, code: 'unknown_tool', message: `No executor for tool: ${call.toolName}` }\n }\n }\n\n return {\n async run(userMessage, turn) {\n const profile = await resolveProfile(turn)\n return runAppToolLoop({\n systemPrompt: profile.systemPrompt,\n userMessage,\n priorMessages: turn.priorMessages,\n // The awaitable loop consumes only text + tool_call; the app's UI-only\n // reasoning/usage events ride the substrate's `other` channel.\n streamTurn: narrowToToolLoopEvents(streamTurnFor(profile.extraTools)),\n executeToolCall: buildExecutor(turn),\n isExecutableTool,\n maxToolTurns: opts.maxToolTurns,\n })\n },\n async *stream(userMessage, turn) {\n const profile = await resolveProfile(turn)\n yield* streamAppToolLoop<LoopEvent>({\n systemPrompt: profile.systemPrompt,\n userMessage,\n priorMessages: turn.priorMessages,\n streamTurn: streamTurnFor(profile.extraTools),\n extractText: (ev) => (ev.type === 'text' ? ev.text : ''),\n extractToolCall: (ev) => (ev.type === 'tool_call' ? ev.call : null),\n isExecutableTool,\n executeToolCall: buildExecutor(turn),\n maxToolTurns: opts.maxToolTurns,\n })\n },\n }\n}\n\n/**\n * Adapt the app's rich {@link LoopEvent} stream to the substrate awaitable\n * loop's `ToolLoopEvent` contract. The loop reads only `text` (accumulated into\n * the answer) and `tool_call` (dispatched); the app's UI-only `reasoning` /\n * `usage` events have no awaitable meaning, so they collapse onto the\n * substrate's `other` channel and are ignored by the loop.\n */\nfunction narrowToToolLoopEvents(\n streamTurn: (messages: LoopMessage[]) => AsyncIterable<LoopEvent>,\n): (messages: LoopMessage[]) => AsyncIterable<ToolLoopEvent> {\n return (messages) =>\n (async function* () {\n for await (const ev of streamTurn(messages)) {\n if (ev.type === 'text') yield { type: 'text', text: ev.text }\n else if (ev.type === 'tool_call') yield { type: 'tool_call', call: ev.call }\n else yield { type: 'other', event: ev }\n }\n })()\n}\n","/**\n * `createCertifiedDelivery` — the delivery truck for Tangle Intelligence.\n *\n * Pulls a tenant's CERTIFIED `AgentProfile` from the deployed plane\n * (`GET /v1/profiles/:target/composed`) and applies it to the agent's resolved\n * surfaces each turn, so an approved improvement reaches the running agent with\n * NO redeploy. This is the `composeProfile` transform `createAgentRuntime`\n * accepts — opt-in per product, fail-closed, cached + refreshed.\n *\n * Profile-WIDE by design (not prompt-only): the composed profile carries every\n * promoted artifact type keyed by kind. What's folded where:\n * - `prompt-surface` + `skill` → the system prompt (via `composeCertifiedPrompt`).\n * - `tool` artifacts that carry an OpenAI tool definition → `extraTools`\n * (advertised to the model; the matching executor is supplied by the product\n * via `executeOtherTool`). Until a `tool` artifact carries a runnable def,\n * it is surfaced (see `current()`) but not advertised — advertising a tool\n * with no executor would make the model call into a dead end.\n * - `mcp` / `memory` / `rag` artifacts materialize as servers/files and deliver\n * through the SANDBOX-provisioning seam, not this in-process one. The full\n * certified profile is exposed via `current()` so that seam can consume it.\n *\n * Substrate boundary: THIS module imports `@tangle-network/agent-runtime`; the\n * `createAgentRuntime` core does not (it only consumes the generic transform).\n */\n\nimport {\n composeCertifiedPrompt,\n type CertifiedProfile,\n pullCertified,\n} from '@tangle-network/agent-runtime/intelligence'\nimport type { ResolvedAgentProfile } from './agent'\n\nconst defaultRefreshMs = 300_000\n\n/** Define configuration options for delivering certified artifacts to a specified tenant target */\nexport interface CertifiedDeliveryConfig {\n /** The tenant target whose certified artifacts to deliver (the agent id). */\n target: string\n /** Bearer for the plane. Reads `TANGLE_API_KEY` when omitted. */\n apiKey?: string\n /** Plane base URL. Reads `TANGLE_INTELLIGENCE_URL` then the public plane. */\n baseUrl?: string\n /** Min interval between certified-profile pulls. Default 5m. */\n refreshMs?: number\n /** fetch impl (tests / non-global-fetch runtimes). */\n fetchImpl?: typeof fetch\n}\n\n/** Resolve and manage certified profiles with refresh and composition capabilities */\nexport interface CertifiedDelivery {\n /** The `composeProfile` transform to pass to `createAgentRuntime`. Applies the\n * cached certified profile to the base surfaces; refreshes on the cadence. */\n composeProfile(base: ResolvedAgentProfile): Promise<ResolvedAgentProfile>\n /** Force a pull now (ignores the refresh window). Best-effort. */\n refresh(): Promise<void>\n /** The certified profile currently in effect (null = none promoted / pull\n * failed). Lets the sandbox-provisioning seam deliver the file/server\n * artifact types this in-process seam doesn't. */\n current(): CertifiedProfile | null\n}\n\n/**\n * Build a certified-delivery transform for one agent target. Fail-closed: a pull\n * error or 404 keeps the last-known certified profile (or null), and the agent\n * runs on its base surfaces — it never breaks because Intelligence is down.\n */\nexport function createCertifiedDelivery(config: CertifiedDeliveryConfig): CertifiedDelivery {\n const refreshMs = config.refreshMs ?? defaultRefreshMs\n let certified: CertifiedProfile | null = null\n let lastPullAt = 0\n let inflight: Promise<void> | null = null\n\n async function refresh(force = false): Promise<void> {\n if (!force && Date.now() - lastPullAt < refreshMs) return\n if (inflight) return inflight\n inflight = (async () => {\n const outcome = await pullCertified({\n target: config.target,\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n fetchImpl: config.fetchImpl,\n })\n lastPullAt = Date.now()\n // Only replace on a real pull; a 404/error keeps the last-known profile.\n if (outcome.succeeded) certified = outcome.value\n })()\n try {\n await inflight\n } finally {\n inflight = null\n }\n }\n\n return {\n async composeProfile(base) {\n await refresh()\n return {\n // prompt-surface + skill fold into the system prompt.\n systemPrompt: composeCertifiedPrompt(base.systemPrompt, certified),\n // Certified `tool` artifacts deliver here once they carry a runnable\n // OpenAI def + the product wires the executor; until then pass through.\n extraTools: base.extraTools,\n }\n },\n refresh: () => refresh(true),\n current: () => certified,\n }\n}\n","/**\n * Surface-scoped profile overlay — the seam letting any product page (a\n * sequence editor, a brief composer, a dataset view) add MCP servers, a prompt\n * addendum, and permission tightening to the workspace agent profile for turns\n * initiated FROM that surface, without the chat orchestrator knowing any\n * surface's specifics. The orchestrator resolves `(kind, ctx)` through a\n * registry the REQUEST HANDLER constructs per request (construction is a Map\n * build — cheap) and merges the result into the base profile it was about to\n * send to the sandbox. Per-request construction is the trust mechanism, not an\n * optimization target: each `build()` closes over server-trusted request state\n * (env bindings, secrets, the AUTHENTICATED user/workspace), which on Workers\n * exists only per request — a startup-built registry would force identity\n * through the untrusted client `ctx`.\n *\n * SECURITY INVARIANT: the surface `kind` and the ids inside `ctx` arrive on\n * the client request and are pure ROUTING data — never trusted content, never\n * identity. Identity comes from the closure (see above). The registered\n * `build()` runs server-side only: it validates the routing ids against the\n * product's access control, then mints its own URLs and capability tokens from\n * server configuration (`buildHttpMcpServer` + `createCapabilityToken` in\n * ../tools). A client can therefore never inject an arbitrary MCP url, header,\n * or token into the agent profile: the overlay's `mcp` values are typed as\n * {@link SurfaceMcpServer} (= the server-built `AppToolMcpServer` entry shape),\n * and only build() constructs them.\n */\n\nimport type { AppToolMcpServer } from '../tools/mcp'\n\n/** Sandbox permission posture values, ranked deny > ask > allow for merging. */\nexport type SurfacePermissionValue = 'allow' | 'ask' | 'deny'\n\n/** The only MCP entry shape an overlay may carry: the server-built bridge\n * entry from ../tools/mcp (transport, url, headers, and capability token all\n * assembled server-side). The alias exists so overlay authors reach for the\n * builders in ../tools rather than hand-rolling `{ url: ctx.url }` shapes\n * that would let request data become a dialable endpoint. */\nexport type SurfaceMcpServer = AppToolMcpServer\n\n/** What one surface contributes to the agent profile for a single turn. */\nexport interface SurfaceOverlay {\n /** MCP servers to mount for this turn, keyed by tool-routing name. Names\n * must not collide with the base profile's — see {@link mergeSurfaceOverlay}. */\n mcp?: Record<string, SurfaceMcpServer>\n /** Appended to the base system-prompt addendum with a blank-line separator. */\n promptAddendum?: string\n /** Per-key posture the surface wants for its turns. Merging is monotone\n * fail-closed: the stricter of base/overlay wins, so a surface can tighten\n * the workspace posture but never relax it. */\n permissions?: Record<string, SurfacePermissionValue>\n}\n\n/**\n * One registered surface kind. `TCtx` is the shape build() expects — a CLAIM\n * about the client payload, not a guarantee: the registry hands build() the\n * request's `ctx` unvalidated, so build() must treat every field as an\n * untrusted id (resolve it through access control that throws on a bad or\n * foreign id) before minting anything from it.\n */\nexport interface SurfaceKindDefinition<TCtx> {\n kind: string\n build: (ctx: TCtx) => SurfaceOverlay | Promise<SurfaceOverlay>\n}\n\n/** The variance-erased form a registry accepts (`build` is contravariant in\n * `TCtx`, so every concrete definition is assignable to this). */\nexport type AnySurfaceKind = SurfaceKindDefinition<never>\n\n/**\n * Declare one surface kind. The `kind` string is the client-visible routing\n * key (e.g. `'sequences'`); `build` is the server-side factory that turns a\n * validated ctx into the overlay for one turn.\n */\nexport function defineSurfaceKind<TCtx>(opts: {\n kind: string\n build: (ctx: TCtx) => SurfaceOverlay | Promise<SurfaceOverlay>\n}): SurfaceKindDefinition<TCtx> {\n if (typeof opts.kind !== 'string' || opts.kind.length === 0 || /\\s/.test(opts.kind)) {\n throw new Error(`surface kind must be a non-empty string without whitespace (got ${JSON.stringify(opts.kind)})`)\n }\n if (typeof opts.build !== 'function') {\n throw new Error(`surface kind '${opts.kind}' requires a build function`)\n }\n return { kind: opts.kind, build: opts.build }\n}\n\n/** Resolve and build the overlay for a given surface kind within a turn context */\nexport interface SurfaceRegistry {\n /** Build the overlay for one turn. Throws on an unknown kind — an unknown\n * surface is a routing bug (client and server registries drifted), and\n * silently returning an empty overlay would strip the surface's tools from\n * the turn with no signal anywhere. Build errors propagate unwrapped. */\n resolve(kind: string, ctx: unknown): Promise<SurfaceOverlay>\n}\n\n/**\n * Assemble the product's surface registry from its registered kinds. Duplicate\n * kinds throw at construction: two builders behind one routing key would make\n * the mounted toolset depend on registration order.\n */\nexport function createSurfaceRegistry(kinds: readonly AnySurfaceKind[]): SurfaceRegistry {\n const byKind = new Map<string, AnySurfaceKind>()\n for (const definition of kinds) {\n if (byKind.has(definition.kind)) {\n throw new Error(`duplicate surface kind '${definition.kind}' — each kind must be registered exactly once`)\n }\n byKind.set(definition.kind, definition)\n }\n\n return {\n async resolve(kind, ctx) {\n const definition = byKind.get(kind)\n if (!definition) {\n const known = [...byKind.keys()].join(', ') || '(none)'\n throw new Error(\n `unknown surface kind '${kind}' — registered kinds: ${known}. ` +\n 'An unknown surface is a routing bug: register the kind via defineSurfaceKind before clients can reference it.',\n )\n }\n // The trust boundary where static ctx typing ends: the request payload is\n // handed to build() as-is, and build() validates it (see SurfaceKindDefinition).\n const overlay = await definition.build(ctx as never)\n assertSurfaceOverlay(overlay, `surface kind '${kind}'`)\n return overlay\n },\n }\n}\n\n/** Base-profile slice the merge reads/writes. Real callers pass their full\n * profile object; every field outside this slice passes through untouched. */\nexport interface SurfaceMergeBase {\n mcp?: Record<string, unknown>\n systemPromptAddendum?: string\n permissions?: Record<string, SurfacePermissionValue>\n}\n\nconst PERMISSION_SEVERITY: Record<SurfacePermissionValue, number> = { allow: 0, ask: 1, deny: 2 }\n\n/**\n * Merge one surface overlay into a base profile, returning a new object\n * (the base is never mutated; untouched nested records are shared by\n * reference).\n *\n * - `mcp`: overlay servers are added under their own names. A name already\n * present on the base THROWS — a collision is two servers claiming one\n * routing name, and renaming either silently would corrupt tool routing for\n * whichever caller expected the original binding.\n * - `systemPromptAddendum`: the overlay's `promptAddendum` appends after a\n * blank-line separator (no separator when the base has no addendum).\n * - `permissions`: per key the STRICTER value wins (deny > ask > allow). A\n * surface can tighten the base posture for its turns; a base 'deny' survives\n * any overlay.\n */\nexport function mergeSurfaceOverlay<TBase extends SurfaceMergeBase>(\n base: TBase,\n overlay: SurfaceOverlay,\n): TBase & SurfaceMergeBase {\n assertSurfaceOverlay(overlay, 'surface overlay')\n const merged: SurfaceMergeBase = { ...base }\n\n if (overlay.mcp && Object.keys(overlay.mcp).length > 0) {\n const baseMcp = base.mcp ?? {}\n const collisions = Object.keys(overlay.mcp).filter((name) => name in baseMcp)\n if (collisions.length > 0) {\n throw new Error(\n `surface overlay MCP name collision: ${collisions.map((n) => `'${n}'`).join(', ')} already exist on the base profile. ` +\n 'Two servers cannot claim one name — give the surface server a distinct name.',\n )\n }\n merged.mcp = { ...baseMcp, ...overlay.mcp }\n }\n\n if (overlay.promptAddendum !== undefined) {\n merged.systemPromptAddendum = base.systemPromptAddendum\n ? `${base.systemPromptAddendum}\\n\\n${overlay.promptAddendum}`\n : overlay.promptAddendum\n }\n\n if (overlay.permissions && Object.keys(overlay.permissions).length > 0) {\n const permissions: Record<string, SurfacePermissionValue> = { ...(base.permissions ?? {}) }\n for (const [key, value] of Object.entries(overlay.permissions)) {\n const existing = permissions[key]\n permissions[key] =\n existing === undefined || PERMISSION_SEVERITY[value] > PERMISSION_SEVERITY[existing] ? value : existing\n }\n merged.permissions = permissions\n }\n\n // merged began as a shallow copy of base; only the three slice fields were\n // replaced, so the intersection type is the true shape.\n return merged as TBase & SurfaceMergeBase\n}\n\n/** Reject overlays a build() (or hand-rolled caller) malformed, with the exact\n * field named: a relative MCP url, a blank addendum, or an off-vocabulary\n * permission would otherwise surface only as an opaque sandbox failure. */\nfunction assertSurfaceOverlay(overlay: SurfaceOverlay, label: string): void {\n if (overlay.promptAddendum !== undefined) {\n if (typeof overlay.promptAddendum !== 'string' || overlay.promptAddendum.trim().length === 0) {\n throw new Error(`${label}: promptAddendum must be a non-blank string when provided`)\n }\n }\n if (overlay.mcp !== undefined) {\n for (const [name, server] of Object.entries(overlay.mcp)) {\n if (name.trim().length === 0) throw new Error(`${label}: MCP server names must be non-empty`)\n if (server.transport !== 'http') {\n throw new Error(`${label}: MCP server '${name}' must use transport 'http' (got ${JSON.stringify((server as { transport?: unknown }).transport)})`)\n }\n let parsed: URL\n try {\n parsed = new URL(server.url)\n } catch {\n throw new Error(`${label}: MCP server '${name}' url must be an absolute URL (got ${JSON.stringify(server.url)})`)\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(`${label}: MCP server '${name}' url must be http(s) (got ${JSON.stringify(server.url)})`)\n }\n }\n }\n if (overlay.permissions !== undefined) {\n for (const [key, value] of Object.entries(overlay.permissions)) {\n if (!(value in PERMISSION_SEVERITY)) {\n throw new Error(`${label}: permission '${key}' must be 'allow' | 'ask' | 'deny' (got ${JSON.stringify(value)})`)\n }\n }\n }\n}\n"],"mappings":"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","names":[]}