@theoremjs/agents 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1264 -2
  3. package/esm/_dnt.polyfills.d.ts +66 -0
  4. package/esm/_dnt.polyfills.js +51 -0
  5. package/esm/mod.d.ts +65 -0
  6. package/esm/mod.js +42 -0
  7. package/esm/package.json +3 -0
  8. package/esm/src/cli/bin.d.ts +7 -0
  9. package/esm/src/cli/bin.js +9 -0
  10. package/esm/src/cli/commands/bench.d.ts +6 -0
  11. package/esm/src/cli/commands/bench.js +458 -0
  12. package/esm/src/cli/commands/egress-compile.d.ts +17 -0
  13. package/esm/src/cli/commands/egress-compile.js +25 -0
  14. package/esm/src/cli/commands/eval.d.ts +37 -0
  15. package/esm/src/cli/commands/eval.js +190 -0
  16. package/esm/src/cli/commands/fuzz-canary.d.ts +15 -0
  17. package/esm/src/cli/commands/fuzz-canary.js +202 -0
  18. package/esm/src/cli/commands/fuzz-guardrails.d.ts +2 -0
  19. package/esm/src/cli/commands/fuzz-guardrails.js +5 -0
  20. package/esm/src/cli/commands/profile.d.ts +2 -0
  21. package/esm/src/cli/commands/profile.js +80 -0
  22. package/esm/src/cli/commands/run.d.ts +13 -0
  23. package/esm/src/cli/commands/run.js +45 -0
  24. package/esm/src/cli/commands/test.d.ts +25 -0
  25. package/esm/src/cli/commands/test.js +143 -0
  26. package/esm/src/cli/event-log.d.ts +15 -0
  27. package/esm/src/cli/event-log.js +136 -0
  28. package/esm/src/cli/index.d.ts +10 -0
  29. package/esm/src/cli/index.js +267 -0
  30. package/esm/src/cli/matrix/fixtures.d.ts +10 -0
  31. package/esm/src/cli/matrix/fixtures.js +72 -0
  32. package/esm/src/cli/matrix/synthesizer.d.ts +18 -0
  33. package/esm/src/cli/matrix/synthesizer.js +103 -0
  34. package/esm/src/evals/attachments.d.ts +4 -0
  35. package/esm/src/evals/attachments.js +53 -0
  36. package/esm/src/evals/breakdown.d.ts +28 -0
  37. package/esm/src/evals/breakdown.js +68 -0
  38. package/esm/src/evals/graders/answer.d.ts +13 -0
  39. package/esm/src/evals/graders/answer.js +133 -0
  40. package/esm/src/evals/graders/code.d.ts +64 -0
  41. package/esm/src/evals/graders/code.js +205 -0
  42. package/esm/src/evals/graders/judge.d.ts +20 -0
  43. package/esm/src/evals/graders/judge.js +365 -0
  44. package/esm/src/evals/graders/latency.d.ts +5 -0
  45. package/esm/src/evals/graders/latency.js +81 -0
  46. package/esm/src/evals/graders/live.d.ts +11 -0
  47. package/esm/src/evals/graders/live.js +40 -0
  48. package/esm/src/evals/graders/media.d.ts +18 -0
  49. package/esm/src/evals/graders/media.js +92 -0
  50. package/esm/src/evals/graders/shared.d.ts +11 -0
  51. package/esm/src/evals/graders/shared.js +62 -0
  52. package/esm/src/evals/graders/transcript.d.ts +17 -0
  53. package/esm/src/evals/graders/transcript.js +200 -0
  54. package/esm/src/evals/mod.d.ts +42 -0
  55. package/esm/src/evals/mod.js +30 -0
  56. package/esm/src/evals/record.d.ts +35 -0
  57. package/esm/src/evals/record.js +105 -0
  58. package/esm/src/evals/rubrics/catalog.d.ts +148 -0
  59. package/esm/src/evals/rubrics/catalog.js +846 -0
  60. package/esm/src/evals/rubrics/mod.d.ts +24 -0
  61. package/esm/src/evals/rubrics/mod.js +55 -0
  62. package/esm/src/evals/rubrics/mustache.d.ts +23 -0
  63. package/esm/src/evals/rubrics/mustache.js +154 -0
  64. package/esm/src/evals/rubrics/types.d.ts +54 -0
  65. package/esm/src/evals/rubrics/types.js +74 -0
  66. package/esm/src/evals/run.d.ts +118 -0
  67. package/esm/src/evals/run.js +452 -0
  68. package/esm/src/evals/suite.d.ts +19 -0
  69. package/esm/src/evals/suite.js +92 -0
  70. package/esm/src/evals/summary.d.ts +32 -0
  71. package/esm/src/evals/summary.js +30 -0
  72. package/esm/src/evals/trial.d.ts +27 -0
  73. package/esm/src/evals/trial.js +158 -0
  74. package/esm/src/evals/types.d.ts +260 -0
  75. package/esm/src/evals/types.js +126 -0
  76. package/esm/src/evals/verdict.d.ts +18 -0
  77. package/esm/src/evals/verdict.js +45 -0
  78. package/esm/src/guardrails/canary-gate.d.ts +21 -0
  79. package/esm/src/guardrails/canary-gate.js +40 -0
  80. package/esm/src/guardrails/canary.d.ts +112 -0
  81. package/esm/src/guardrails/canary.js +901 -0
  82. package/esm/src/guardrails/compile-egress.d.ts +11 -0
  83. package/esm/src/guardrails/compile-egress.js +11 -0
  84. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +22 -0
  85. package/esm/src/guardrails/corpus/canary-egress-attacks.js +303 -0
  86. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +5 -0
  87. package/esm/src/guardrails/corpus/fuzz-inbound.js +202 -0
  88. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +3 -0
  89. package/esm/src/guardrails/corpus/inbound-payloads.js +118 -0
  90. package/esm/src/guardrails/corpus/live-attacks.d.ts +13 -0
  91. package/esm/src/guardrails/corpus/live-attacks.js +224 -0
  92. package/esm/src/guardrails/corpus/mod.d.ts +7 -0
  93. package/esm/src/guardrails/corpus/mod.js +4 -0
  94. package/esm/src/guardrails/corpus/secrets.d.ts +12 -0
  95. package/esm/src/guardrails/corpus/secrets.js +16 -0
  96. package/esm/src/guardrails/corpus/strings.d.ts +23 -0
  97. package/esm/src/guardrails/corpus/strings.js +29 -0
  98. package/esm/src/guardrails/corpus/types.d.ts +32 -0
  99. package/esm/src/guardrails/corpus/types.js +1 -0
  100. package/esm/src/guardrails/egress-automata.d.ts +14 -0
  101. package/esm/src/guardrails/egress-automata.js +6059 -0
  102. package/esm/src/guardrails/egress-compiler.d.ts +33 -0
  103. package/esm/src/guardrails/egress-compiler.js +237 -0
  104. package/esm/src/guardrails/egress-patterns.d.ts +24 -0
  105. package/esm/src/guardrails/egress-patterns.js +32 -0
  106. package/esm/src/guardrails/egress-policy.d.ts +27 -0
  107. package/esm/src/guardrails/egress-policy.js +76 -0
  108. package/esm/src/guardrails/egress-rules.d.ts +54 -0
  109. package/esm/src/guardrails/egress-rules.js +49 -0
  110. package/esm/src/guardrails/egress-stream.d.ts +105 -0
  111. package/esm/src/guardrails/egress-stream.js +645 -0
  112. package/esm/src/guardrails/egress-urls.d.ts +83 -0
  113. package/esm/src/guardrails/egress-urls.js +917 -0
  114. package/esm/src/guardrails/egress.d.ts +106 -0
  115. package/esm/src/guardrails/egress.js +374 -0
  116. package/esm/src/guardrails/error.d.ts +26 -0
  117. package/esm/src/guardrails/error.js +91 -0
  118. package/esm/src/guardrails/event-schemas.d.ts +57 -0
  119. package/esm/src/guardrails/event-schemas.js +41 -0
  120. package/esm/src/guardrails/events.d.ts +9 -0
  121. package/esm/src/guardrails/events.js +42 -0
  122. package/esm/src/guardrails/hits.d.ts +8 -0
  123. package/esm/src/guardrails/hits.js +19 -0
  124. package/esm/src/guardrails/injection-patterns.d.ts +15 -0
  125. package/esm/src/guardrails/injection-patterns.js +94 -0
  126. package/esm/src/guardrails/injection.d.ts +14 -0
  127. package/esm/src/guardrails/injection.js +237 -0
  128. package/esm/src/guardrails/lexicon.d.ts +30 -0
  129. package/esm/src/guardrails/lexicon.js +478 -0
  130. package/esm/src/guardrails/live-outbound-gate.d.ts +54 -0
  131. package/esm/src/guardrails/live-outbound-gate.js +305 -0
  132. package/esm/src/guardrails/mod.d.ts +46 -0
  133. package/esm/src/guardrails/mod.js +29 -0
  134. package/esm/src/guardrails/network.d.ts +42 -0
  135. package/esm/src/guardrails/network.js +328 -0
  136. package/esm/src/guardrails/normalize.d.ts +6 -0
  137. package/esm/src/guardrails/normalize.js +132 -0
  138. package/esm/src/guardrails/policy.d.ts +19 -0
  139. package/esm/src/guardrails/policy.js +50 -0
  140. package/esm/src/guardrails/progressive-yield.d.ts +69 -0
  141. package/esm/src/guardrails/progressive-yield.js +237 -0
  142. package/esm/src/guardrails/prompt-echo.d.ts +38 -0
  143. package/esm/src/guardrails/prompt-echo.js +225 -0
  144. package/esm/src/guardrails/quota.d.ts +20 -0
  145. package/esm/src/guardrails/quota.js +80 -0
  146. package/esm/src/guardrails/rules.d.ts +56 -0
  147. package/esm/src/guardrails/rules.js +54 -0
  148. package/esm/src/guardrails/sanitize.d.ts +23 -0
  149. package/esm/src/guardrails/sanitize.js +184 -0
  150. package/esm/src/guardrails/sensitive.d.ts +29 -0
  151. package/esm/src/guardrails/sensitive.js +113 -0
  152. package/esm/src/guardrails/serialize.d.ts +14 -0
  153. package/esm/src/guardrails/serialize.js +37 -0
  154. package/esm/src/guardrails/testing.d.ts +9 -0
  155. package/esm/src/guardrails/testing.js +8 -0
  156. package/esm/src/guardrails/theorem-error.d.ts +18 -0
  157. package/esm/src/guardrails/theorem-error.js +53 -0
  158. package/esm/src/guardrails/thought-guard.d.ts +55 -0
  159. package/esm/src/guardrails/thought-guard.js +464 -0
  160. package/esm/src/guardrails/tool-directives.d.ts +19 -0
  161. package/esm/src/guardrails/tool-directives.js +109 -0
  162. package/esm/src/guardrails/tool-result.d.ts +61 -0
  163. package/esm/src/guardrails/tool-result.js +248 -0
  164. package/esm/src/guardrails/types.d.ts +242 -0
  165. package/esm/src/guardrails/types.js +54 -0
  166. package/esm/src/host/client-turn.d.ts +9 -0
  167. package/esm/src/host/client-turn.js +38 -0
  168. package/esm/src/host/mint-trace.d.ts +28 -0
  169. package/esm/src/host/mint-trace.js +59 -0
  170. package/esm/src/host/mod.d.ts +13 -0
  171. package/esm/src/host/mod.js +11 -0
  172. package/esm/src/host/readStreamingJsonStringField.d.ts +2 -0
  173. package/esm/src/host/readStreamingJsonStringField.js +68 -0
  174. package/esm/src/host/reply.d.ts +7 -0
  175. package/esm/src/host/reply.js +35 -0
  176. package/esm/src/interface/blocks.d.ts +8 -0
  177. package/esm/src/interface/blocks.js +213 -0
  178. package/esm/src/interface/composer-actions.d.ts +20 -0
  179. package/esm/src/interface/composer-actions.js +31 -0
  180. package/esm/src/interface/draft.d.ts +14 -0
  181. package/esm/src/interface/draft.js +28 -0
  182. package/esm/src/interface/from-profile.d.ts +12 -0
  183. package/esm/src/interface/from-profile.js +144 -0
  184. package/esm/src/interface/history.d.ts +40 -0
  185. package/esm/src/interface/history.js +269 -0
  186. package/esm/src/interface/inputs.d.ts +10 -0
  187. package/esm/src/interface/inputs.js +64 -0
  188. package/esm/src/interface/mod.d.ts +31 -0
  189. package/esm/src/interface/mod.js +21 -0
  190. package/esm/src/interface/models.d.ts +19 -0
  191. package/esm/src/interface/models.js +45 -0
  192. package/esm/src/interface/pending.d.ts +48 -0
  193. package/esm/src/interface/pending.js +146 -0
  194. package/esm/src/interface/profile-interface.d.ts +4 -0
  195. package/esm/src/interface/profile-interface.js +223 -0
  196. package/esm/src/interface/session.d.ts +64 -0
  197. package/esm/src/interface/session.js +121 -0
  198. package/esm/src/interface/tool-calls.d.ts +20 -0
  199. package/esm/src/interface/tool-calls.js +73 -0
  200. package/esm/src/interface/tool-invoke.d.ts +4 -0
  201. package/esm/src/interface/tool-invoke.js +20 -0
  202. package/esm/src/interface/tool-media.d.ts +9 -0
  203. package/esm/src/interface/tool-media.js +112 -0
  204. package/esm/src/interface/types.d.ts +213 -0
  205. package/esm/src/interface/types.js +4 -0
  206. package/esm/src/kernel/auth/credential-source.d.ts +23 -0
  207. package/esm/src/kernel/auth/credential-source.js +11 -0
  208. package/esm/src/kernel/auth/crypto.d.ts +51 -0
  209. package/esm/src/kernel/auth/crypto.js +161 -0
  210. package/esm/src/kernel/auth/mod.d.ts +5 -0
  211. package/esm/src/kernel/auth/mod.js +5 -0
  212. package/esm/src/kernel/auth/oauth.d.ts +17 -0
  213. package/esm/src/kernel/auth/oauth.js +438 -0
  214. package/esm/src/kernel/auth/scope-refusal.d.ts +9 -0
  215. package/esm/src/kernel/auth/scope-refusal.js +8 -0
  216. package/esm/src/kernel/auth/typed-secret.d.ts +13 -0
  217. package/esm/src/kernel/auth/typed-secret.js +23 -0
  218. package/esm/src/kernel/auth/types.d.ts +139 -0
  219. package/esm/src/kernel/auth/types.js +1 -0
  220. package/esm/src/kernel/default-scope.d.ts +34 -0
  221. package/esm/src/kernel/default-scope.js +72 -0
  222. package/esm/src/kernel/engine/compaction.d.ts +70 -0
  223. package/esm/src/kernel/engine/compaction.js +202 -0
  224. package/esm/src/kernel/engine/decision.d.ts +41 -0
  225. package/esm/src/kernel/engine/decision.js +412 -0
  226. package/esm/src/kernel/engine/hash.d.ts +6 -0
  227. package/esm/src/kernel/engine/hash.js +22 -0
  228. package/esm/src/kernel/engine/live-inbound.d.ts +9 -0
  229. package/esm/src/kernel/engine/live-inbound.js +19 -0
  230. package/esm/src/kernel/engine/live-ingress.d.ts +9 -0
  231. package/esm/src/kernel/engine/live-ingress.js +37 -0
  232. package/esm/src/kernel/engine/media-probe/audio.d.ts +7 -0
  233. package/esm/src/kernel/engine/media-probe/audio.js +342 -0
  234. package/esm/src/kernel/engine/media-probe/bytes.d.ts +6 -0
  235. package/esm/src/kernel/engine/media-probe/bytes.js +26 -0
  236. package/esm/src/kernel/engine/media-probe/image.d.ts +5 -0
  237. package/esm/src/kernel/engine/media-probe/image.js +171 -0
  238. package/esm/src/kernel/engine/media-probe/iso-bmff.d.ts +19 -0
  239. package/esm/src/kernel/engine/media-probe/iso-bmff.js +48 -0
  240. package/esm/src/kernel/engine/media-probe/matroska.d.ts +35 -0
  241. package/esm/src/kernel/engine/media-probe/matroska.js +226 -0
  242. package/esm/src/kernel/engine/media-probe/mp4.d.ts +22 -0
  243. package/esm/src/kernel/engine/media-probe/mp4.js +279 -0
  244. package/esm/src/kernel/engine/media-probe/opus.d.ts +4 -0
  245. package/esm/src/kernel/engine/media-probe/opus.js +23 -0
  246. package/esm/src/kernel/engine/media-probe/pdf.d.ts +5 -0
  247. package/esm/src/kernel/engine/media-probe/pdf.js +117 -0
  248. package/esm/src/kernel/engine/media-probe/video.d.ts +15 -0
  249. package/esm/src/kernel/engine/media-probe/video.js +49 -0
  250. package/esm/src/kernel/engine/record.d.ts +3 -0
  251. package/esm/src/kernel/engine/record.js +11 -0
  252. package/esm/src/kernel/engine/repair.d.ts +7 -0
  253. package/esm/src/kernel/engine/repair.js +36 -0
  254. package/esm/src/kernel/engine/runner/gates.d.ts +5 -0
  255. package/esm/src/kernel/engine/runner/gates.js +322 -0
  256. package/esm/src/kernel/engine/runner/mod.d.ts +15 -0
  257. package/esm/src/kernel/engine/runner/mod.js +574 -0
  258. package/esm/src/kernel/engine/runner/schema-validation.d.ts +14 -0
  259. package/esm/src/kernel/engine/runner/schema-validation.js +105 -0
  260. package/esm/src/kernel/engine/runner/stages.d.ts +28 -0
  261. package/esm/src/kernel/engine/runner/stages.js +48 -0
  262. package/esm/src/kernel/engine/runner/state.d.ts +76 -0
  263. package/esm/src/kernel/engine/runner/state.js +40 -0
  264. package/esm/src/kernel/engine/runner/steps.d.ts +14 -0
  265. package/esm/src/kernel/engine/runner/steps.js +360 -0
  266. package/esm/src/kernel/engine/runner/stream.d.ts +40 -0
  267. package/esm/src/kernel/engine/runner/stream.js +225 -0
  268. package/esm/src/kernel/engine/runner/usage.d.ts +45 -0
  269. package/esm/src/kernel/engine/runner/usage.js +150 -0
  270. package/esm/src/kernel/engine/session/mod.d.ts +31 -0
  271. package/esm/src/kernel/engine/session/mod.js +951 -0
  272. package/esm/src/kernel/engine/session/session-trace.d.ts +115 -0
  273. package/esm/src/kernel/engine/session/session-trace.js +489 -0
  274. package/esm/src/kernel/engine/token-estimate.d.ts +77 -0
  275. package/esm/src/kernel/engine/token-estimate.js +218 -0
  276. package/esm/src/kernel/engine/tool-trace.d.ts +58 -0
  277. package/esm/src/kernel/engine/tool-trace.js +131 -0
  278. package/esm/src/kernel/engine/tree.d.ts +2 -0
  279. package/esm/src/kernel/engine/tree.js +17 -0
  280. package/esm/src/kernel/engine/turn-trace.d.ts +118 -0
  281. package/esm/src/kernel/engine/turn-trace.js +863 -0
  282. package/esm/src/kernel/engine/usage.d.ts +30 -0
  283. package/esm/src/kernel/engine/usage.js +134 -0
  284. package/esm/src/kernel/interaction-parts.d.ts +10 -0
  285. package/esm/src/kernel/interaction-parts.js +22 -0
  286. package/esm/src/kernel/mod.d.ts +47 -0
  287. package/esm/src/kernel/mod.js +30 -0
  288. package/esm/src/kernel/profile-graph.d.ts +140 -0
  289. package/esm/src/kernel/profile-graph.js +151 -0
  290. package/esm/src/kernel/profile-presence.d.ts +9 -0
  291. package/esm/src/kernel/profile-presence.js +143 -0
  292. package/esm/src/kernel/profile-scope.d.ts +36 -0
  293. package/esm/src/kernel/profile-scope.js +262 -0
  294. package/esm/src/kernel/registry/attachments.d.ts +35 -0
  295. package/esm/src/kernel/registry/attachments.js +211 -0
  296. package/esm/src/kernel/registry/catalog.d.ts +19 -0
  297. package/esm/src/kernel/registry/catalog.js +78 -0
  298. package/esm/src/kernel/registry/ingress.d.ts +8 -0
  299. package/esm/src/kernel/registry/ingress.js +134 -0
  300. package/esm/src/kernel/registry/kernel-registry.d.ts +12 -0
  301. package/esm/src/kernel/registry/kernel-registry.js +8 -0
  302. package/esm/src/kernel/registry/profile-outputs.d.ts +3 -0
  303. package/esm/src/kernel/registry/profile-outputs.js +7 -0
  304. package/esm/src/kernel/registry/profiles.d.ts +92 -0
  305. package/esm/src/kernel/registry/profiles.js +698 -0
  306. package/esm/src/kernel/registry/provider-request.d.ts +6 -0
  307. package/esm/src/kernel/registry/provider-request.js +42 -0
  308. package/esm/src/kernel/registry/resolve.d.ts +14 -0
  309. package/esm/src/kernel/registry/resolve.js +237 -0
  310. package/esm/src/kernel/registry/schemas.d.ts +9 -0
  311. package/esm/src/kernel/registry/schemas.js +25 -0
  312. package/esm/src/kernel/registry/sole-model.d.ts +2 -0
  313. package/esm/src/kernel/registry/sole-model.js +4 -0
  314. package/esm/src/kernel/registry/system-prompt.d.ts +8 -0
  315. package/esm/src/kernel/registry/system-prompt.js +33 -0
  316. package/esm/src/kernel/registry/system-role.d.ts +3 -0
  317. package/esm/src/kernel/registry/system-role.js +9 -0
  318. package/esm/src/kernel/registry/vault.d.ts +13 -0
  319. package/esm/src/kernel/registry/vault.js +14 -0
  320. package/esm/src/kernel/schema.d.ts +172 -0
  321. package/esm/src/kernel/schema.js +653 -0
  322. package/esm/src/kernel/scope.d.ts +22 -0
  323. package/esm/src/kernel/scope.js +25 -0
  324. package/esm/src/kernel/stages.d.ts +141 -0
  325. package/esm/src/kernel/stages.js +388 -0
  326. package/esm/src/kernel/stop.d.ts +93 -0
  327. package/esm/src/kernel/stop.js +125 -0
  328. package/esm/src/kernel/system-parts.d.ts +26 -0
  329. package/esm/src/kernel/system-parts.js +98 -0
  330. package/esm/src/kernel/tools/activity-label.d.ts +16 -0
  331. package/esm/src/kernel/tools/activity-label.js +209 -0
  332. package/esm/src/kernel/tools/events.d.ts +51 -0
  333. package/esm/src/kernel/tools/events.js +149 -0
  334. package/esm/src/kernel/tools/execute.d.ts +64 -0
  335. package/esm/src/kernel/tools/execute.js +801 -0
  336. package/esm/src/kernel/tools/gate-answer.d.ts +50 -0
  337. package/esm/src/kernel/tools/gate-answer.js +54 -0
  338. package/esm/src/kernel/tools/harness.d.ts +16 -0
  339. package/esm/src/kernel/tools/harness.js +42 -0
  340. package/esm/src/kernel/tools/invoke.d.ts +10 -0
  341. package/esm/src/kernel/tools/invoke.js +129 -0
  342. package/esm/src/kernel/tools/mcp-sessions.d.ts +20 -0
  343. package/esm/src/kernel/tools/mcp-sessions.js +64 -0
  344. package/esm/src/kernel/tools/mod.d.ts +9 -0
  345. package/esm/src/kernel/tools/mod.js +6 -0
  346. package/esm/src/kernel/tools/model-text.d.ts +11 -0
  347. package/esm/src/kernel/tools/model-text.js +25 -0
  348. package/esm/src/kernel/tools/permission.d.ts +6 -0
  349. package/esm/src/kernel/tools/permission.js +38 -0
  350. package/esm/src/kernel/tools/project.d.ts +8 -0
  351. package/esm/src/kernel/tools/project.js +25 -0
  352. package/esm/src/kernel/tools/registry.d.ts +20 -0
  353. package/esm/src/kernel/tools/registry.js +84 -0
  354. package/esm/src/kernel/tools/remote.d.ts +110 -0
  355. package/esm/src/kernel/tools/remote.js +998 -0
  356. package/esm/src/kernel/tools/resolve.d.ts +28 -0
  357. package/esm/src/kernel/tools/resolve.js +259 -0
  358. package/esm/src/kernel/tools/schema.d.ts +10 -0
  359. package/esm/src/kernel/tools/schema.js +174 -0
  360. package/esm/src/kernel/tools/signed-in-fetch.d.ts +18 -0
  361. package/esm/src/kernel/tools/signed-in-fetch.js +45 -0
  362. package/esm/src/kernel/tools/stage-run.d.ts +93 -0
  363. package/esm/src/kernel/tools/stage-run.js +140 -0
  364. package/esm/src/kernel/tools/types.d.ts +287 -0
  365. package/esm/src/kernel/tools/types.js +1 -0
  366. package/esm/src/kernel/turn-events.d.ts +599 -0
  367. package/esm/src/kernel/turn-events.js +425 -0
  368. package/esm/src/kernel/types.d.ts +884 -0
  369. package/esm/src/kernel/types.js +1 -0
  370. package/esm/src/kernel/util/base64.d.ts +2 -0
  371. package/esm/src/kernel/util/base64.js +13 -0
  372. package/esm/src/kernel/util/exact-type.d.ts +31 -0
  373. package/esm/src/kernel/util/exact-type.js +1 -0
  374. package/esm/src/kernel/util/find-last.d.ts +3 -0
  375. package/esm/src/kernel/util/find-last.js +9 -0
  376. package/esm/src/kernel/util/mime.d.ts +2 -0
  377. package/esm/src/kernel/util/mime.js +4 -0
  378. package/esm/src/kernel/util/record.d.ts +1 -0
  379. package/esm/src/kernel/util/record.js +3 -0
  380. package/esm/src/observability/destinations.d.ts +9 -0
  381. package/esm/src/observability/destinations.js +38 -0
  382. package/esm/src/observability/jsonl.d.ts +18 -0
  383. package/esm/src/observability/jsonl.js +117 -0
  384. package/esm/src/observability/mod.d.ts +22 -0
  385. package/esm/src/observability/mod.js +16 -0
  386. package/esm/src/observability/openinference.d.ts +11 -0
  387. package/esm/src/observability/openinference.js +205 -0
  388. package/esm/src/observability/otlp.d.ts +68 -0
  389. package/esm/src/observability/otlp.js +98 -0
  390. package/esm/src/observability/phoenix.d.ts +29 -0
  391. package/esm/src/observability/phoenix.js +71 -0
  392. package/esm/src/observability/policy.d.ts +14 -0
  393. package/esm/src/observability/policy.js +69 -0
  394. package/esm/src/observability/resolve-policy.d.ts +8 -0
  395. package/esm/src/observability/resolve-policy.js +58 -0
  396. package/esm/src/observability/spans.d.ts +16 -0
  397. package/esm/src/observability/spans.js +56 -0
  398. package/esm/src/observability/trace-catalog.d.ts +111 -0
  399. package/esm/src/observability/trace-catalog.js +865 -0
  400. package/esm/src/observability/trace-record.d.ts +23 -0
  401. package/esm/src/observability/trace-record.js +223 -0
  402. package/esm/src/observability/trace-schema.d.ts +54 -0
  403. package/esm/src/observability/trace-schema.js +59 -0
  404. package/esm/src/observability/trace-sink.d.ts +13 -0
  405. package/esm/src/observability/trace-sink.js +3 -0
  406. package/esm/src/observability/trace-span.d.ts +92 -0
  407. package/esm/src/observability/trace-span.js +199 -0
  408. package/esm/src/observability/trace.d.ts +11 -0
  409. package/esm/src/observability/trace.js +35 -0
  410. package/esm/src/observability/types.d.ts +85 -0
  411. package/esm/src/observability/types.js +3 -0
  412. package/esm/src/presets/google/speech-voices.d.ts +8 -0
  413. package/esm/src/presets/google/speech-voices.js +38 -0
  414. package/esm/src/presets/google-limits.d.ts +12 -0
  415. package/esm/src/presets/google-limits.js +18 -0
  416. package/esm/src/presets/google.d.ts +104 -0
  417. package/esm/src/presets/google.js +188 -0
  418. package/esm/src/presets/mod.d.ts +10 -0
  419. package/esm/src/presets/mod.js +9 -0
  420. package/esm/src/presets/openrouter.d.ts +13 -0
  421. package/esm/src/presets/openrouter.js +13 -0
  422. package/esm/src/presets/typesafe.d.ts +2 -0
  423. package/esm/src/presets/typesafe.js +2 -0
  424. package/esm/src/providers/create-provider.d.ts +21 -0
  425. package/esm/src/providers/create-provider.js +90 -0
  426. package/esm/src/providers/decision/usage.d.ts +3 -0
  427. package/esm/src/providers/decision/usage.js +25 -0
  428. package/esm/src/providers/google/api-error.d.ts +8 -0
  429. package/esm/src/providers/google/api-error.js +123 -0
  430. package/esm/src/providers/google/grounding.d.ts +5 -0
  431. package/esm/src/providers/google/grounding.js +226 -0
  432. package/esm/src/providers/google/interactions/framing.d.ts +15 -0
  433. package/esm/src/providers/google/interactions/framing.js +263 -0
  434. package/esm/src/providers/google/interactions/mod.d.ts +2 -0
  435. package/esm/src/providers/google/interactions/mod.js +2 -0
  436. package/esm/src/providers/google/interactions/steps.d.ts +12 -0
  437. package/esm/src/providers/google/interactions/steps.js +200 -0
  438. package/esm/src/providers/google/interactions/stream.d.ts +24 -0
  439. package/esm/src/providers/google/interactions/stream.js +297 -0
  440. package/esm/src/providers/google/keys.d.ts +20 -0
  441. package/esm/src/providers/google/keys.js +87 -0
  442. package/esm/src/providers/google/live/framing.d.ts +51 -0
  443. package/esm/src/providers/google/live/framing.js +597 -0
  444. package/esm/src/providers/google/live/mod.d.ts +7 -0
  445. package/esm/src/providers/google/live/mod.js +7 -0
  446. package/esm/src/providers/google/live/openapi-schema.d.ts +1 -0
  447. package/esm/src/providers/google/live/openapi-schema.js +42 -0
  448. package/esm/src/providers/google/live/session.d.ts +14 -0
  449. package/esm/src/providers/google/live/session.js +150 -0
  450. package/esm/src/providers/google/live/stream.d.ts +51 -0
  451. package/esm/src/providers/google/live/stream.js +263 -0
  452. package/esm/src/providers/google/thinking.d.ts +3 -0
  453. package/esm/src/providers/google/thinking.js +8 -0
  454. package/esm/src/providers/google/urls.d.ts +6 -0
  455. package/esm/src/providers/google/urls.js +6 -0
  456. package/esm/src/providers/google/usage.d.ts +7 -0
  457. package/esm/src/providers/google/usage.js +49 -0
  458. package/esm/src/providers/local/local.d.ts +12 -0
  459. package/esm/src/providers/local/local.js +186 -0
  460. package/esm/src/providers/local/mod.d.ts +8 -0
  461. package/esm/src/providers/local/mod.js +8 -0
  462. package/esm/src/providers/mod.d.ts +11 -0
  463. package/esm/src/providers/mod.js +7 -0
  464. package/esm/src/providers/openrouter/cache-control.d.ts +5 -0
  465. package/esm/src/providers/openrouter/cache-control.js +4 -0
  466. package/esm/src/providers/openrouter/chat.d.ts +64 -0
  467. package/esm/src/providers/openrouter/chat.js +490 -0
  468. package/esm/src/providers/openrouter/image.d.ts +24 -0
  469. package/esm/src/providers/openrouter/image.js +187 -0
  470. package/esm/src/providers/openrouter/openai/compat.d.ts +19 -0
  471. package/esm/src/providers/openrouter/openai/compat.js +162 -0
  472. package/esm/src/providers/openrouter/openai/image-payload.d.ts +9 -0
  473. package/esm/src/providers/openrouter/openai/image-payload.js +115 -0
  474. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +10 -0
  475. package/esm/src/providers/openrouter/openai/sdk-messages.js +99 -0
  476. package/esm/src/providers/openrouter/openai/usage.d.ts +8 -0
  477. package/esm/src/providers/openrouter/openai/usage.js +56 -0
  478. package/esm/src/providers/openrouter/resolve-api-key.d.ts +3 -0
  479. package/esm/src/providers/openrouter/resolve-api-key.js +8 -0
  480. package/esm/src/providers/openrouter/speech.d.ts +18 -0
  481. package/esm/src/providers/openrouter/speech.js +109 -0
  482. package/esm/src/providers/probe.d.ts +5 -0
  483. package/esm/src/providers/probe.js +15 -0
  484. package/esm/src/providers/shared/builtin-wire.d.ts +4 -0
  485. package/esm/src/providers/shared/builtin-wire.js +9 -0
  486. package/esm/src/providers/shared/pcm.d.ts +14 -0
  487. package/esm/src/providers/shared/pcm.js +78 -0
  488. package/esm/src/providers/shared/response-identity.d.ts +11 -0
  489. package/esm/src/providers/shared/response-identity.js +12 -0
  490. package/esm/src/providers/shared/sse.d.ts +10 -0
  491. package/esm/src/providers/shared/sse.js +79 -0
  492. package/esm/src/providers/shared/structured-output.d.ts +4 -0
  493. package/esm/src/providers/shared/structured-output.js +11 -0
  494. package/esm/src/providers/shared/tool-args.d.ts +25 -0
  495. package/esm/src/providers/shared/tool-args.js +84 -0
  496. package/esm/src/providers/shared/upstream-tap.d.ts +8 -0
  497. package/esm/src/providers/shared/upstream-tap.js +85 -0
  498. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  499. package/esm/src/providers/shared/upstream-tape.js +88 -0
  500. package/esm/src/providers/shared/vault.d.ts +13 -0
  501. package/esm/src/providers/shared/vault.js +41 -0
  502. package/esm/src/providers/types.d.ts +19 -0
  503. package/esm/src/providers/types.js +1 -0
  504. package/esm/src/surface/formats.d.ts +28 -0
  505. package/esm/src/surface/formats.js +139 -0
  506. package/esm/src/surface/mod.d.ts +14 -0
  507. package/esm/src/surface/mod.js +10 -0
  508. package/esm/src/surface/runtime.d.ts +40 -0
  509. package/esm/src/surface/runtime.js +439 -0
  510. package/esm/src/surface/tools.d.ts +15 -0
  511. package/esm/src/surface/tools.js +80 -0
  512. package/esm/src/surface/types.d.ts +104 -0
  513. package/esm/src/surface/types.js +4 -0
  514. package/package.json +108 -4
@@ -0,0 +1,998 @@
1
+ import { errorKind } from '../../guardrails/error.js';
2
+ import { lexiconText } from '../../guardrails/lexicon.js';
3
+ import { fetchGuarded } from '../../guardrails/network.js';
4
+ import { refreshOAuthToken, tokenAudienceCovers } from '../auth/oauth.js';
5
+ import { mapStrings } from '../engine/tree.js';
6
+ import { guardToolTarget, messageOf, networkBlocked, networkBlockedEvent, requestChecks, startToolExecution, toolEvent, toolNetworkPolicy, } from './events.js';
7
+ import { createMcpSessionCache, isMcpSessionId, mcpSessionKey, } from './mcp-sessions.js';
8
+ import { checkPermission } from './permission.js';
9
+ import { assertFixedEndpointOrigin } from './schema.js';
10
+ import { runPreToolPipeline } from './stage-run.js';
11
+ function authHeaderPair(authConfig, value, defaults) {
12
+ const headerName = authConfig.headerName ?? defaults.headerName;
13
+ const headerPrefix = authConfig.headerPrefix ?? defaults.headerPrefix;
14
+ return { [headerName]: `${headerPrefix}${value}` };
15
+ }
16
+ function buildAuthGate(toolName, authConfig, message, extras) {
17
+ return {
18
+ kind: 'auth',
19
+ tool: toolName,
20
+ authChallenge: {
21
+ slot: authConfig.slot,
22
+ authType: authConfig.type,
23
+ service: authConfig.service,
24
+ message,
25
+ issuer: extras?.issuer ?? authConfig.preResolved?.issuer,
26
+ resource: extras?.resource ?? authConfig.preResolved?.resource,
27
+ requiredScopes: authConfig.scopes,
28
+ },
29
+ };
30
+ }
31
+ function unauthenticatedResult(toolName, authConfig, message, policy, extras) {
32
+ if (policy === 'gate') {
33
+ // The caller emits the gate through emitGateSettlement, so `pre_tool` runs first.
34
+ const gate = buildAuthGate(toolName, authConfig, message, extras);
35
+ return { headers: {}, unauthenticated: true, gate };
36
+ }
37
+ return { headers: {}, unauthenticated: true, modelMessage: message };
38
+ }
39
+ function resolveStaticCredential(credential, authConfig) {
40
+ if (credential.type === 'bearer') {
41
+ return {
42
+ headers: authHeaderPair(authConfig, credential.token, {
43
+ headerName: 'Authorization',
44
+ headerPrefix: 'Bearer ',
45
+ }),
46
+ secret: credential.token,
47
+ };
48
+ }
49
+ if (credential.type === 'api_key') {
50
+ const headerName = credential.headerName ?? authConfig.headerName ?? 'Authorization';
51
+ const headerPrefix = credential.headerPrefix ?? authConfig.headerPrefix ?? '';
52
+ return {
53
+ headers: { [headerName]: `${headerPrefix}${credential.key}` },
54
+ secret: credential.key,
55
+ };
56
+ }
57
+ return undefined;
58
+ }
59
+ /**
60
+ * Refreshes in flight, by grant. Calls that find the same expired token share
61
+ * one refresh: with rotating refresh tokens a second request would present a
62
+ * spent token, which a server may treat as theft and revoke the whole grant.
63
+ */
64
+ const refreshesInFlight = new Map();
65
+ function refreshOnce(credential, refreshToken, transport, clientSecret) {
66
+ const grant = `${credential.tokenEndpoint}\n${refreshToken}`;
67
+ const inFlight = refreshesInFlight.get(grant);
68
+ if (inFlight)
69
+ return inFlight;
70
+ const refresh = refreshOAuthToken({
71
+ refreshToken,
72
+ tokenEndpoint: credential.tokenEndpoint,
73
+ clientId: credential.clientId,
74
+ resource: credential.resource,
75
+ scope: credential.scope,
76
+ issuer: credential.issuer,
77
+ ...(clientSecret === undefined ? {} : { clientSecret }),
78
+ ...transport,
79
+ }).then((result) => result.credential);
80
+ refreshesInFlight.set(grant, refresh);
81
+ const settle = () => refreshesInFlight.delete(grant);
82
+ refresh.then(settle, settle);
83
+ return refresh;
84
+ }
85
+ async function* resolveOAuth2Credential(toolName, authConfig, credential, source, ctx, base, policy) {
86
+ const slot = authConfig.slot;
87
+ const bound = { issuer: credential.issuer, resource: credential.resource };
88
+ // A token goes only to the resource it was issued for; one that names none
89
+ // has nowhere it may go, so the user signs in again.
90
+ if (typeof credential.resource !== 'string' || credential.resource.length === 0) {
91
+ const message = `OAuth credential for '${toolName}' (slot: '${slot}') does not name the resource it was issued for.`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
92
+ return unauthenticatedResult(toolName, authConfig, message, policy, bound);
93
+ }
94
+ let active = credential;
95
+ const isExpired = credential.expiresAt !== undefined && credential.expiresAt <= Date.now() + 30000;
96
+ if (isExpired && credential.refreshToken) {
97
+ try {
98
+ active = await refreshOnce(credential, credential.refreshToken, { network: toolNetworkPolicy(ctx), resolveHost: ctx.resolveHost }, await source.clientSecret?.(credential.clientId));
99
+ }
100
+ catch (err) {
101
+ // The server's own words are untrusted text: they go to the host as
102
+ // `errorInternal`, never to the model or the client.
103
+ yield {
104
+ ...toolEvent(base, {
105
+ phase: 'progress',
106
+ data: { kind: 'auth_token_refresh_failed', slot },
107
+ }),
108
+ errorInternal: messageOf(err),
109
+ };
110
+ const message = `Failed to refresh OAuth token for '${toolName}' (slot: '${slot}').`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
111
+ return unauthenticatedResult(toolName, authConfig, message, policy, bound);
112
+ }
113
+ // The host persists the refreshed credential before the call goes on; the
114
+ // event only names the slot, so no token rides the stream.
115
+ await source.set(slot, active);
116
+ yield toolEvent(base, {
117
+ phase: 'progress',
118
+ data: { kind: 'auth_token_refreshed', slot },
119
+ });
120
+ }
121
+ else if (isExpired) {
122
+ const message = `OAuth token expired for '${toolName}' (slot: '${slot}') and no refresh token is available.`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
123
+ return unauthenticatedResult(toolName, authConfig, message, policy, bound);
124
+ }
125
+ return {
126
+ headers: authHeaderPair(authConfig, active.accessToken, {
127
+ headerName: 'Authorization',
128
+ headerPrefix: 'Bearer ',
129
+ }),
130
+ secret: active.accessToken,
131
+ audience: credential.resource,
132
+ };
133
+ }
134
+ export async function* resolveToolAuth(toolName, authConfig, ctx, base) {
135
+ if (!authConfig) {
136
+ return { headers: {} };
137
+ }
138
+ const source = ctx.credentials;
139
+ const credential = source ? await source.get(authConfig.slot) : undefined;
140
+ const policy = authConfig.onUnauthenticated ?? 'gate';
141
+ if (!source || !credential) {
142
+ const message = `Authentication required for '${toolName}' (auth slot: '${authConfig.slot}').`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
143
+ return unauthenticatedResult(toolName, authConfig, message, policy);
144
+ }
145
+ const staticResolved = resolveStaticCredential(credential, authConfig);
146
+ if (staticResolved) {
147
+ return staticResolved;
148
+ }
149
+ if (credential.type === 'oauth2') {
150
+ return yield* resolveOAuth2Credential(toolName, authConfig, credential, source, ctx, base, policy);
151
+ }
152
+ return { headers: {} };
153
+ }
154
+ /**
155
+ * One path parameter, encoded. `encodeURIComponent` leaves `.` and `..` as they
156
+ * are and a URL resolves them, walking the endpoint's path; they are refused.
157
+ */
158
+ function pathSegment(param, value) {
159
+ if (value === '.' || value === '..') {
160
+ throw new Error(`Path parameter "${param}" cannot be "${value}"`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
161
+ }
162
+ return encodeURIComponent(value);
163
+ }
164
+ export function buildHttpToolTarget(endpoint, method, input, mapping) {
165
+ assertFixedEndpointOrigin(endpoint);
166
+ let urlStr = endpoint;
167
+ const pathParams = mapping?.pathParams ?? [];
168
+ for (const param of pathParams) {
169
+ const val = input[param];
170
+ if (val !== undefined) {
171
+ urlStr = urlStr.replaceAll(`{${param}}`, pathSegment(param, String(val)));
172
+ }
173
+ }
174
+ const unreplacedMatch = urlStr.match(/\{([a-zA-Z0-9_-]+)\}/);
175
+ if (unreplacedMatch) {
176
+ throw new Error(`Missing required path parameter "${unreplacedMatch[1]}" for endpoint "${endpoint}"`);
177
+ }
178
+ const targetUrl = new URL(urlStr);
179
+ const queryParams = mapping?.queryParams ?? [];
180
+ for (const param of queryParams) {
181
+ const val = input[param];
182
+ if (val !== undefined) {
183
+ targetUrl.searchParams.set(param, String(val));
184
+ }
185
+ }
186
+ if (method === 'GET') {
187
+ return { url: targetUrl.toString() };
188
+ }
189
+ if (mapping?.bodyParam) {
190
+ return { url: targetUrl.toString(), body: JSON.stringify(input[mapping.bodyParam]) };
191
+ }
192
+ const bodyObj = {};
193
+ const excluded = new Set([...pathParams, ...queryParams]);
194
+ for (const [k, v] of Object.entries(input)) {
195
+ if (!excluded.has(k)) {
196
+ bodyObj[k] = v;
197
+ }
198
+ }
199
+ return { url: targetUrl.toString(), body: JSON.stringify(bodyObj) };
200
+ }
201
+ function parseHttpResponseData(contentType, text, status) {
202
+ if (contentType.includes('application/json') && text.trim().length > 0) {
203
+ try {
204
+ return JSON.parse(text);
205
+ }
206
+ catch {
207
+ return text;
208
+ }
209
+ }
210
+ if (text.trim().length === 0 && status === 204) {
211
+ return null;
212
+ }
213
+ return text;
214
+ }
215
+ export function parseToolOutput(schema, responseData) {
216
+ let checked = schema.safeParse(responseData);
217
+ if (!checked.success && typeof responseData === 'string' && responseData.trim().length > 0) {
218
+ try {
219
+ const jsonParsed = JSON.parse(responseData);
220
+ const retryChecked = schema.safeParse(jsonParsed);
221
+ if (retryChecked.success) {
222
+ checked = retryChecked;
223
+ }
224
+ }
225
+ catch {
226
+ // Not JSON: the schema's first result stands.
227
+ }
228
+ }
229
+ return checked;
230
+ }
231
+ /** Not repeated as `data`, which `composeToolText` would append a second time. */
232
+ export function modelResultFromOutput(data) {
233
+ return { finding: typeof data === 'string' ? data : JSON.stringify(data) };
234
+ }
235
+ /** The result with the media a tool returned beside its value, when it returned any. */
236
+ export function withMedia(result, parts) {
237
+ return parts?.length ? { ...result, parts } : result;
238
+ }
239
+ function failureOutcome(failure, callNotStarted) {
240
+ return { kind: 'failed', failure, callNotStarted };
241
+ }
242
+ async function* runRemotePreBodyStages(args) {
243
+ const pre = yield* runPreToolPipeline(args);
244
+ if (pre.ok)
245
+ return pre;
246
+ if (pre.kind === 'aborted') {
247
+ return { kind: 'aborted', aborted: pre.aborted };
248
+ }
249
+ if (pre.kind === 'gated') {
250
+ return { kind: 'gated', gate: pre.gate };
251
+ }
252
+ return { ...failureOutcome(pre.failure, true), ...(pre.denied ? { denied: true } : {}) };
253
+ }
254
+ async function* remoteParseAndPermit(tool, rawInput, ctx, base) {
255
+ const started = yield* startToolExecution(tool, rawInput, ctx, base);
256
+ if (!started.ok) {
257
+ return {
258
+ ok: false,
259
+ outcome: failureOutcome({
260
+ code: 'invalid_input',
261
+ kind: 'bad_response',
262
+ message: lexiconText('tool.input_invalid', {}, ctx.profile.lexicon),
263
+ }, true),
264
+ };
265
+ }
266
+ const permissionGate = checkPermission(tool.name, tool.permission, ctx.sessionPermissions, ctx.resume);
267
+ if (permissionGate) {
268
+ return { ok: false, outcome: { kind: 'gated', gate: permissionGate } };
269
+ }
270
+ return { ok: true, input: started.data };
271
+ }
272
+ export function outcomeFromUnauth(authRes) {
273
+ if (!authRes.unauthenticated)
274
+ return undefined;
275
+ if (authRes.gate)
276
+ return { kind: 'gated', gate: authRes.gate };
277
+ if (authRes.modelMessage) {
278
+ return {
279
+ kind: 'ok',
280
+ modelResult: { finding: authRes.modelMessage },
281
+ outputRaw: { unauthenticated: true, message: authRes.modelMessage },
282
+ };
283
+ }
284
+ return failureOutcome({ code: 'not_authorized', kind: 'auth', message: 'Tool authentication required' }, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
285
+ true);
286
+ }
287
+ export function credentialRefusal(response) {
288
+ return response.status === 401 || response.status === 403
289
+ ? { status: response.status, challenge: response.headers.get('www-authenticate') }
290
+ : undefined;
291
+ }
292
+ /** RFC 6750 §3: the characters a scope token may hold. */
293
+ const SCOPE_TOKEN = /^[\x21\x23-\x5B\x5D-\x7E]+$/;
294
+ /** A `Bearer` challenge's `error` and well-formed `scope` tokens (RFC 6750 §3). */
295
+ function bearerChallenge(header) {
296
+ const param = (name) => header?.match(new RegExp(`(?:^|[\\s,])${name}="([^"]*)"`, 'i'))?.[1];
297
+ const scopes = (param('scope') ?? '').split(' ').filter((scope) => SCOPE_TOKEN.test(scope));
298
+ const error = param('error');
299
+ return error === undefined ? { scopes } : { error, scopes };
300
+ }
301
+ /** A refusal that asks for a sign-in: 401, or 403 `insufficient_scope`. Any other 403 is the call's own failure. */
302
+ export function refusalAsksForSignIn(refusal) {
303
+ return (refusal.status === 401 || bearerChallenge(refusal.challenge).error === 'insufficient_scope');
304
+ }
305
+ /**
306
+ * The service refused the credential it was sent. A 401 says it no longer works:
307
+ * the person signs in again. A 403 `insufficient_scope` asks for more access: a
308
+ * sign-in can grant scopes the tool declares, and nothing else, so a request
309
+ * outside them fails with `sign_in.out_of_scope` and the trace records what was
310
+ * asked against what is declared. Any other refusal is the call's failure.
311
+ */
312
+ export function* refusedCredentialOutcome(tool, refusal, authHeaders, ctx, base) {
313
+ const auth = tool.auth;
314
+ if (!auth || !refusal || Object.keys(authHeaders).length === 0)
315
+ return undefined;
316
+ const policy = auth.onUnauthenticated ?? 'gate';
317
+ if (refusal.status === 401) {
318
+ const message = `The credential for '${tool.name}' (slot: '${auth.slot}') was refused.`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
319
+ return outcomeFromUnauth(unauthenticatedResult(tool.name, auth, message, policy));
320
+ }
321
+ const challenge = bearerChallenge(refusal.challenge);
322
+ if (challenge.error !== 'insufficient_scope')
323
+ return undefined;
324
+ const declared = auth.scopes ?? [];
325
+ const requested = challenge.scopes;
326
+ if (requested.length > 0 && requested.every((scope) => declared.includes(scope))) {
327
+ const message = `'${tool.name}' needs scopes its credential (slot: '${auth.slot}') does not carry.`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
328
+ return outcomeFromUnauth(unauthenticatedResult(tool.name, auth, message, policy));
329
+ }
330
+ const refused = {
331
+ kind: 'auth_scope_refused',
332
+ slot: auth.slot,
333
+ requested,
334
+ declared,
335
+ };
336
+ yield toolEvent(base, { phase: 'progress', data: refused });
337
+ return failureOutcome({
338
+ code: 'out_of_scope',
339
+ kind: 'auth',
340
+ message: lexiconText('sign_in.out_of_scope', { service: auth.service }, ctx.profile.lexicon),
341
+ }, false);
342
+ }
343
+ async function* remoteAuthAndPreBody(args) {
344
+ const authRes = yield* resolveToolAuth(args.tool.name, args.tool.auth, args.ctx, args.base);
345
+ const unauth = outcomeFromUnauth(authRes);
346
+ if (unauth)
347
+ return unauth;
348
+ const preBody = yield* runRemotePreBodyStages({
349
+ tool: args.tool,
350
+ input: args.input,
351
+ ctx: args.ctx,
352
+ base: args.base,
353
+ stages: args.stages,
354
+ });
355
+ if (!('ok' in preBody))
356
+ return preBody;
357
+ return {
358
+ ok: true,
359
+ input: preBody.input,
360
+ authHeaders: authRes.headers ?? {},
361
+ ...(authRes.secret ? { secret: authRes.secret } : {}),
362
+ ...(authRes.audience ? { audience: authRes.audience } : {}),
363
+ };
364
+ }
365
+ function* thrownOutcome(err, checks) {
366
+ if (errorKind(err) === 'blocked') {
367
+ const blocked = networkBlockedEvent();
368
+ checks.record(blocked);
369
+ return failureOutcome(yield* networkBlocked(err, blocked), false);
370
+ }
371
+ return failureOutcome({ code: 'network_error', kind: 'network', message: messageOf(err) }, false);
372
+ }
373
+ const OMIT_CREDENTIAL = '[omitted - credential]';
374
+ /** The most of a tool's response body the kernel reads; past it the call fails instead of exhausting the host's memory. */
375
+ export const MAX_TOOL_RESPONSE_BYTES = 4 * 1024 * 1024;
376
+ /** The most of an error or unparseable body quoted back in a failure message. */
377
+ const MAX_QUOTED_BODY_CHARS = 2000;
378
+ /** A body, read up to `maxBytes`; `undefined` when it runs past, with the rest left unread. */
379
+ async function readCappedText(response, maxBytes = MAX_TOOL_RESPONSE_BYTES) {
380
+ const declared = Number(response.headers.get('content-length'));
381
+ if (Number.isFinite(declared) && declared > maxBytes) {
382
+ await response.body?.cancel().catch(() => { });
383
+ return undefined;
384
+ }
385
+ if (!response.body)
386
+ return await response.text();
387
+ const reader = response.body.getReader();
388
+ const decoder = new TextDecoder();
389
+ let bytes = 0;
390
+ let text = '';
391
+ for (;;) {
392
+ const { done, value } = await reader.read();
393
+ if (done)
394
+ break;
395
+ bytes += value.byteLength;
396
+ if (bytes > maxBytes) {
397
+ await reader.cancel().catch(() => { });
398
+ return undefined;
399
+ }
400
+ text += decoder.decode(value, { stream: true });
401
+ }
402
+ return text + decoder.decode();
403
+ }
404
+ /** A body quoted in a failure message, cut short when long. */
405
+ function quotedBody(text) {
406
+ return text.length > MAX_QUOTED_BODY_CHARS ? `${text.slice(0, MAX_QUOTED_BODY_CHARS)}…` : text;
407
+ }
408
+ /** The failure for a body past MAX_TOOL_RESPONSE_BYTES. */
409
+ function tooLargeFailure(host) {
410
+ return {
411
+ code: 'response_too_large',
412
+ kind: 'bad_response',
413
+ message: `Response from ${host} is larger than ${MAX_TOOL_RESPONSE_BYTES} bytes`, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
414
+ };
415
+ }
416
+ /** The text with the credential replaced. */
417
+ function omitSecretText(text, secret) {
418
+ return text.replaceAll(secret, OMIT_CREDENTIAL);
419
+ }
420
+ /** Every string in `value` with the credential replaced. */
421
+ export function omitSecret(value, secret) {
422
+ return mapStrings(value, (text) => omitSecretText(text, secret));
423
+ }
424
+ /** A failure's message and details with the credential replaced. */
425
+ export function failureWithoutSecret(failure, secret) {
426
+ return {
427
+ ...failure,
428
+ message: omitSecretText(failure.message, secret),
429
+ ...(failure.details === undefined ? {} : { details: omitSecret(failure.details, secret) }),
430
+ };
431
+ }
432
+ /** An echo endpoint or debug page can repeat the credential; it must never reach the model, trace or client. */
433
+ function withoutSecret(outcome, secret) {
434
+ if (!secret)
435
+ return outcome;
436
+ if (outcome.kind === 'ok') {
437
+ const outputRaw = omitSecret(outcome.outputRaw, secret);
438
+ return {
439
+ kind: 'ok',
440
+ outputRaw,
441
+ modelResult: withMedia(modelResultFromOutput(outputRaw), outcome.modelResult.parts),
442
+ };
443
+ }
444
+ if (outcome.kind === 'failed') {
445
+ return { ...outcome, failure: failureWithoutSecret(outcome.failure, secret) };
446
+ }
447
+ return outcome;
448
+ }
449
+ /** Why an OAuth token issued for `audience` may not go to `url` (RFC 8707), or `undefined` when it may. */
450
+ export function audienceMismatch(audience, url) {
451
+ if (!audience || tokenAudienceCovers(audience, url))
452
+ return undefined;
453
+ return `The OAuth token for "${audience}" cannot be sent to "${url.origin}${url.pathname}"`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
454
+ }
455
+ /** An OAuth token goes only to the resource it was issued for (RFC 8707); any other target gets no request. */
456
+ async function* sendWithCredential(prepared, url, send) {
457
+ const mismatch = audienceMismatch(prepared.audience, url);
458
+ if (mismatch) {
459
+ return failureOutcome({ code: 'credential_audience_mismatch', kind: 'config', message: mismatch }, true);
460
+ }
461
+ return withoutSecret(yield* send(), prepared.secret);
462
+ }
463
+ /** Order: schema → permission → auth → preTool/host stages → body. */
464
+ export async function* executeHttpTool(tool, rawInput, ctx, base, stages) {
465
+ const parsed = yield* remoteParseAndPermit(tool, rawInput, ctx, base);
466
+ if (!parsed.ok)
467
+ return parsed.outcome;
468
+ let input = parsed.input;
469
+ const prepared = yield* remoteAuthAndPreBody({ tool, input, ctx, base, stages });
470
+ if (!('ok' in prepared))
471
+ return prepared;
472
+ input = prepared.input;
473
+ let target;
474
+ try {
475
+ target = buildHttpToolTarget(tool.endpoint, tool.method, input, tool.mapping);
476
+ }
477
+ catch (err) {
478
+ const failure = {
479
+ code: 'invalid_input',
480
+ kind: 'bad_response',
481
+ message: messageOf(err),
482
+ };
483
+ return failureOutcome(failure, true);
484
+ }
485
+ const guarded = yield* guardToolTarget(target.url, ctx, stages?.span);
486
+ if (!guarded.ok)
487
+ return failureOutcome(guarded.failure, true);
488
+ return yield* sendWithCredential(prepared, guarded.url, () => sendHttpRequest(tool, guarded.url, target.body, prepared.authHeaders, ctx, base, stages?.span));
489
+ }
490
+ async function* sendHttpRequest(tool, targetUrl, body, authHeaders, ctx, base, span) {
491
+ const checks = requestChecks(span);
492
+ const headers = { Accept: 'application/json' };
493
+ if (tool.method !== 'GET') {
494
+ headers['Content-Type'] = 'application/json';
495
+ }
496
+ try {
497
+ const response = await fetchGuarded(targetUrl.href, { method: tool.method, headers, body, signal: ctx.signal }, {
498
+ policy: toolNetworkPolicy(ctx),
499
+ followRedirects: true,
500
+ originBoundHeaders: { ...tool.headers, ...authHeaders },
501
+ resolveHost: ctx.resolveHost,
502
+ onCheck: checks.onCheck,
503
+ });
504
+ const text = await readCappedText(response);
505
+ if (text === undefined)
506
+ return failureOutcome(tooLargeFailure(targetUrl.hostname), false);
507
+ if (!response.ok) {
508
+ const refused = yield* refusedCredentialOutcome(tool, credentialRefusal(response), authHeaders, ctx, base);
509
+ if (refused)
510
+ return refused;
511
+ const failure = {
512
+ code: `http_${response.status}`,
513
+ kind: kindOfToolHttpStatus(response.status),
514
+ message: `HTTP ${response.status} from ${targetUrl.hostname}: ${quotedBody(text)}`,
515
+ };
516
+ return failureOutcome(failure, false);
517
+ }
518
+ const responseData = parseHttpResponseData(response.headers.get('content-type') ?? '', text, response.status);
519
+ const checked = parseToolOutput(tool.output, responseData);
520
+ if (!checked.success) {
521
+ const failure = {
522
+ code: 'invalid_output',
523
+ kind: 'bad_response',
524
+ message: 'HTTP response did not match tool output schema', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
525
+ details: checked.error.flatten(),
526
+ };
527
+ return failureOutcome(failure, false);
528
+ }
529
+ return {
530
+ kind: 'ok',
531
+ modelResult: modelResultFromOutput(checked.data),
532
+ outputRaw: checked.data,
533
+ };
534
+ }
535
+ catch (err) {
536
+ return yield* thrownOutcome(err, checks);
537
+ }
538
+ finally {
539
+ checks.record();
540
+ }
541
+ }
542
+ /** Preferred first. */
543
+ export const MCP_PROTOCOL_VERSIONS = [
544
+ '2026-07-28',
545
+ '2025-11-25',
546
+ '2025-06-18',
547
+ '2025-03-26',
548
+ ];
549
+ /** Accepts a JSON body or an SSE (`data:`) stream. */
550
+ export function parseMcpRpcResponse(text) {
551
+ const trimmed = text.trim();
552
+ if (trimmed.startsWith('{')) {
553
+ return JSON.parse(trimmed);
554
+ }
555
+ const messages = [];
556
+ for (const line of text.split('\n')) {
557
+ if (!line.startsWith('data: '))
558
+ continue;
559
+ const payload = line.slice(6).trim();
560
+ if (!payload || payload === '[DONE]')
561
+ continue;
562
+ try {
563
+ messages.push(JSON.parse(payload));
564
+ }
565
+ catch {
566
+ // Pings and other non-JSON payloads.
567
+ }
568
+ }
569
+ for (let i = messages.length - 1; i >= 0; i--) {
570
+ const msg = messages[i];
571
+ if (msg.result !== undefined && msg.method === undefined)
572
+ return msg;
573
+ }
574
+ const last = messages.at(-1);
575
+ if (!last) {
576
+ throw new Error(`MCP server returned non-JSON response: ${text}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
577
+ }
578
+ return last;
579
+ }
580
+ export function isUnsupportedMcpProtocolError(error) {
581
+ if (!error)
582
+ return false;
583
+ const message = error.message.toLowerCase();
584
+ return (message.includes('unsupported protocol version') || // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
585
+ message.includes('inconsistent mcp protocol version') // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
586
+ );
587
+ }
588
+ function unsupportedProtocolFromHttpBody(text) {
589
+ try {
590
+ const rpcResponse = parseMcpRpcResponse(text);
591
+ if (rpcResponse.error && isUnsupportedMcpProtocolError(rpcResponse.error)) {
592
+ return rpcResponse.error;
593
+ }
594
+ }
595
+ catch {
596
+ // A non-JSON error page falls through to the raw-text check.
597
+ }
598
+ // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
599
+ if (text.toLowerCase().includes('unsupported protocol version')) {
600
+ return { code: -32600, message: text };
601
+ }
602
+ return undefined;
603
+ }
604
+ async function fetchMcpProtocolAttempt(transport, rpcId, mcpToolName, input, protocolVersion) {
605
+ const jsonRpcPayload = {
606
+ jsonrpc: '2.0',
607
+ id: rpcId,
608
+ method: 'tools/call',
609
+ params: {
610
+ name: mcpToolName,
611
+ arguments: input,
612
+ // Stateless servers read the client's capabilities from each call, not from an initialize.
613
+ _meta: {
614
+ 'io.modelcontextprotocol/protocolVersion': protocolVersion,
615
+ 'io.modelcontextprotocol/clientCapabilities': {},
616
+ },
617
+ },
618
+ };
619
+ const response = await fetchGuarded(transport.url, {
620
+ method: 'POST',
621
+ headers: {
622
+ ...transport.headers,
623
+ 'MCP-Protocol-Version': protocolVersion,
624
+ 'Mcp-Method': jsonRpcPayload.method,
625
+ 'Mcp-Name': mcpToolName,
626
+ },
627
+ body: JSON.stringify(jsonRpcPayload),
628
+ signal: transport.signal,
629
+ }, {
630
+ policy: transport.policy,
631
+ followRedirects: true,
632
+ originBoundHeaders: transport.originBoundHeaders,
633
+ resolveHost: transport.resolveHost,
634
+ onCheck: transport.onCheck,
635
+ });
636
+ const text = await readCappedText(response);
637
+ if (text === undefined) {
638
+ return { kind: 'failure', failure: tooLargeFailure(new URL(transport.url).hostname) };
639
+ }
640
+ if (!response.ok) {
641
+ const sessionSent = SESSION_HEADER in transport.originBoundHeaders;
642
+ if (sessionSent && response.status === 404)
643
+ return { kind: 'session_expired' };
644
+ if (!sessionSent && response.status === 400 && /mcp-session-id/i.test(text)) {
645
+ return { kind: 'session_required' };
646
+ }
647
+ const acceptRejected = response.status === 406 &&
648
+ text.toLowerCase().includes('accept') &&
649
+ protocolVersion !== MCP_PROTOCOL_VERSIONS.at(-1);
650
+ if (acceptRejected)
651
+ return { kind: 'retry' };
652
+ const protocolError = unsupportedProtocolFromHttpBody(text);
653
+ if (protocolError)
654
+ return { kind: 'protocol_retry', error: protocolError };
655
+ const refusal = credentialRefusal(response);
656
+ return {
657
+ kind: 'failure',
658
+ ...(refusal ? { refusal } : {}),
659
+ failure: {
660
+ code: `mcp_http_${response.status}`,
661
+ kind: kindOfToolHttpStatus(response.status),
662
+ message: `MCP server error HTTP ${response.status}: ${quotedBody(text)}`, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
663
+ },
664
+ };
665
+ }
666
+ try {
667
+ const rpcResponse = parseMcpRpcResponse(text);
668
+ if (rpcResponse.error && isUnsupportedMcpProtocolError(rpcResponse.error)) {
669
+ return { kind: 'protocol_retry', error: rpcResponse.error };
670
+ }
671
+ return { kind: 'rpc', response: rpcResponse };
672
+ }
673
+ catch {
674
+ return {
675
+ kind: 'failure',
676
+ failure: {
677
+ code: 'invalid_response',
678
+ kind: 'bad_response',
679
+ message: `MCP server returned non-JSON response: ${quotedBody(text)}`, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
680
+ },
681
+ };
682
+ }
683
+ }
684
+ async function negotiateMcpRpc(transport, rpcId, mcpToolName, input) {
685
+ let lastProtocolError;
686
+ for (const protocolVersion of MCP_PROTOCOL_VERSIONS) {
687
+ const outcome = await fetchMcpProtocolAttempt(transport, rpcId, mcpToolName, input, protocolVersion);
688
+ if (outcome.kind === 'retry' || outcome.kind === 'protocol_retry') {
689
+ if (outcome.kind === 'protocol_retry')
690
+ lastProtocolError = outcome.error;
691
+ continue;
692
+ }
693
+ if (outcome.kind === 'failure') {
694
+ return { failure: outcome.failure, ...(outcome.refusal ? { refusal: outcome.refusal } : {}) };
695
+ }
696
+ if (outcome.kind !== 'rpc')
697
+ return { sessionRequired: true };
698
+ return { rpc: outcome.response };
699
+ }
700
+ return { lastProtocolError };
701
+ }
702
+ const SESSION_HEADER = 'Mcp-Session-Id';
703
+ /** The newest revision that has sessions; 2026-07-28 is stateless. */
704
+ const SESSION_PROTOCOL_VERSION = '2025-11-25';
705
+ class McpSessionFailure extends Error {
706
+ failure;
707
+ constructor(failure) {
708
+ super(failure.message);
709
+ this.failure = failure;
710
+ }
711
+ }
712
+ function sessionFailure(code, message) {
713
+ return new McpSessionFailure({ code, kind: 'failed', message });
714
+ }
715
+ /**
716
+ * Opens a session: initialize, then the initialized notification. Only the session ID and version
717
+ * are read back; the server's `instructions` would be a prompt-injection channel. Redirects are not
718
+ * followed, so a session can only come from the configured server.
719
+ */
720
+ async function openMcpSession(transport) {
721
+ const post = (body, headers, originBound = {}) => fetchGuarded(transport.url, {
722
+ method: 'POST',
723
+ headers: { ...transport.headers, ...headers },
724
+ body: JSON.stringify(body),
725
+ signal: transport.signal,
726
+ }, {
727
+ policy: transport.policy,
728
+ followRedirects: false,
729
+ originBoundHeaders: { ...transport.originBoundHeaders, ...originBound },
730
+ resolveHost: transport.resolveHost,
731
+ onCheck: transport.onCheck,
732
+ });
733
+ const response = await post({
734
+ jsonrpc: '2.0',
735
+ id: 'initialize',
736
+ method: 'initialize',
737
+ params: {
738
+ protocolVersion: SESSION_PROTOCOL_VERSION,
739
+ capabilities: {},
740
+ clientInfo: { name: 'theorem', version: '1' },
741
+ },
742
+ }, { 'Mcp-Method': 'initialize' });
743
+ const text = await readCappedText(response);
744
+ if (text === undefined) {
745
+ throw new McpSessionFailure(tooLargeFailure(new URL(transport.url).hostname));
746
+ }
747
+ if (!response.ok) {
748
+ throw sessionFailure(`mcp_session_http_${response.status}`, `MCP server refused to open a session: HTTP ${response.status}: ${quotedBody(text)}`);
749
+ }
750
+ const id = response.headers.get(SESSION_HEADER);
751
+ if (!isMcpSessionId(id)) {
752
+ throw sessionFailure('mcp_session_invalid', 'MCP server required a session but sent no usable session ID');
753
+ }
754
+ let version;
755
+ try {
756
+ version = parseMcpRpcResponse(text).result?.protocolVersion;
757
+ }
758
+ catch {
759
+ version = undefined;
760
+ }
761
+ if (!MCP_PROTOCOL_VERSIONS.some((known) => known === version)) {
762
+ throw sessionFailure('mcp_protocol_error', 'MCP server opened a session on a protocol version Theorem does not speak');
763
+ }
764
+ const protocolVersion = String(version);
765
+ const initialized = await post({ jsonrpc: '2.0', method: 'notifications/initialized' }, { 'MCP-Protocol-Version': protocolVersion, 'Mcp-Method': 'notifications/initialized' }, { [SESSION_HEADER]: id });
766
+ await initialized.body?.cancel().catch(() => { });
767
+ return { id, protocolVersion };
768
+ }
769
+ /** The call in a session: an expired one is reopened once, so a server can't loop us. */
770
+ async function negotiateInSession(transport, rpcId, mcpToolName, input, sessions) {
771
+ let session = sessions.cached;
772
+ for (let attempt = 0; attempt < 2; attempt++) {
773
+ if (!session) {
774
+ try {
775
+ session = await sessions.cache.open(sessions.key, () => openMcpSession(transport));
776
+ }
777
+ catch (err) {
778
+ if (err instanceof McpSessionFailure)
779
+ return { failure: err.failure };
780
+ throw err;
781
+ }
782
+ }
783
+ const outcome = await fetchMcpProtocolAttempt({
784
+ ...transport,
785
+ originBoundHeaders: { ...transport.originBoundHeaders, [SESSION_HEADER]: session.id },
786
+ }, rpcId, mcpToolName, input, session.protocolVersion);
787
+ if (outcome.kind === 'session_expired') {
788
+ sessions.cache.drop(sessions.key, session.id);
789
+ session = undefined;
790
+ continue;
791
+ }
792
+ if (outcome.kind === 'rpc')
793
+ return { rpc: outcome.response };
794
+ if (outcome.kind === 'failure') {
795
+ // A server can echo the session ID in its error; it goes no further than the server.
796
+ const failure = {
797
+ ...outcome.failure,
798
+ message: outcome.failure.message.replaceAll(session.id, '[session]'),
799
+ };
800
+ return { failure, ...(outcome.refusal ? { refusal: outcome.refusal } : {}) };
801
+ }
802
+ return {
803
+ lastProtocolError: outcome.kind === 'protocol_retry' ? outcome.error : undefined,
804
+ };
805
+ }
806
+ return {
807
+ failure: {
808
+ code: 'mcp_session_expired',
809
+ kind: 'failed',
810
+ message: 'MCP server ended the session again right after it was reopened', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
811
+ },
812
+ };
813
+ }
814
+ function kindOfToolHttpStatus(status) {
815
+ return status === 401 || status === 403 ? 'auth' : 'failed';
816
+ }
817
+ /** A content block as text: its text, an embedded resource's text, or a linked resource's URI. */
818
+ function mcpContentText(block) {
819
+ if (block.type === 'text')
820
+ return block.text;
821
+ const resource = block.resource;
822
+ if (block.type === 'resource' && typeof resource?.text === 'string')
823
+ return resource.text;
824
+ if (block.type === 'resource_link' && typeof block.uri === 'string')
825
+ return block.uri;
826
+ return undefined;
827
+ }
828
+ /** An image or audio block as media; the spec gives both as base64 `data` and a `mimeType`. */
829
+ function mcpMedia(block) {
830
+ if (block.type !== 'image' && block.type !== 'audio')
831
+ return undefined;
832
+ const { data, mimeType } = block;
833
+ if (typeof data !== 'string' || !data || typeof mimeType !== 'string' || !mimeType.trim()) {
834
+ return undefined;
835
+ }
836
+ return { type: block.type, mimeType, data };
837
+ }
838
+ /**
839
+ * A tool result's value, read the way the spec layers it: the structured
840
+ * content when the server sends one and it fits the tool's declared output,
841
+ * else the text blocks. Images and audio come back as media, not flattened
842
+ * away.
843
+ */
844
+ function extractMcpOutput(tool, result) {
845
+ const content = Array.isArray(result?.content) ? result.content : undefined;
846
+ const media = content?.flatMap((block) => mcpMedia(block) ?? []);
847
+ const withParts = media?.length ? { parts: media } : {};
848
+ let warning;
849
+ if (result?.structuredContent !== undefined) {
850
+ const structured = tool.output.safeParse(result.structuredContent);
851
+ if (structured.success)
852
+ return { checked: structured, ...withParts };
853
+ warning = structuredMismatch(structured.error.issues);
854
+ }
855
+ const text = content
856
+ ? content.flatMap((block) => mcpContentText(block) ?? []).join('\n')
857
+ : result;
858
+ return {
859
+ checked: parseToolOutput(tool.output, text),
860
+ ...withParts,
861
+ ...(warning ? { warning } : {}),
862
+ };
863
+ }
864
+ /** Otherwise the text fallback hides why the tool's declared fields never arrive. */
865
+ function structuredMismatch(issues) {
866
+ const shown = issues
867
+ .slice(0, 3)
868
+ .map((issue) => `${issue.path.map(String).join('.') || '(root)'}: ${issue.message}`);
869
+ const more = issues.length > 3 ? ` (+${String(issues.length - 3)} more)` : '';
870
+ return {
871
+ code: 'mcp_structured_mismatch',
872
+ severity: 'warning',
873
+ message: `structuredContent does not match the output schema, so the text content was used. ${shown.join('; ')}${more}`, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
874
+ };
875
+ }
876
+ function mcpResultFailure(rpcResponse) {
877
+ if (rpcResponse.error) {
878
+ return {
879
+ code: `mcp_rpc_error_${rpcResponse.error.code}`,
880
+ kind: 'failed',
881
+ message: rpcResponse.error.message,
882
+ details: rpcResponse.error.data,
883
+ };
884
+ }
885
+ const result = rpcResponse.result;
886
+ if (result?.isError) {
887
+ return {
888
+ code: 'mcp_tool_execution_failed',
889
+ kind: 'failed',
890
+ message: result.content?.map((c) => c.text ?? '').join('\n') ?? 'MCP Tool execution error', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
891
+ };
892
+ }
893
+ return undefined;
894
+ }
895
+ function interpretMcpRpc(tool, negotiated) {
896
+ if (negotiated.failure) {
897
+ return { ok: false, failure: negotiated.failure };
898
+ }
899
+ const rpcResponse = negotiated.rpc;
900
+ if (!rpcResponse) {
901
+ return {
902
+ ok: false,
903
+ failure: {
904
+ code: 'mcp_protocol_error',
905
+ kind: 'failed',
906
+ message: negotiated.lastProtocolError?.message ?? 'MCP protocol negotiation failed', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
907
+ details: negotiated.lastProtocolError?.data,
908
+ },
909
+ };
910
+ }
911
+ const resultFailure = mcpResultFailure(rpcResponse);
912
+ if (resultFailure) {
913
+ return { ok: false, failure: resultFailure };
914
+ }
915
+ const { checked, parts, warning } = extractMcpOutput(tool, rpcResponse.result);
916
+ const withWarning = warning ? { warning } : {};
917
+ if (!checked.success) {
918
+ return {
919
+ ok: false,
920
+ failure: {
921
+ code: 'invalid_output',
922
+ kind: 'bad_response',
923
+ message: 'MCP output schema validation failed', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
924
+ details: checked.error.flatten(),
925
+ },
926
+ ...withWarning,
927
+ };
928
+ }
929
+ return { ok: true, data: checked.data, ...(parts ? { parts } : {}), ...withWarning };
930
+ }
931
+ /** Streamable HTTP, spec revision 2026-07-28. Order: schema → permission → auth → preTool/host stages → body. */
932
+ export async function* executeMcpTool(tool, rawInput, ctx, base, stages,
933
+ /** The scope's sessions; without them a session lasts one call. */
934
+ sessions = createMcpSessionCache()) {
935
+ const permitted = yield* remoteParseAndPermit(tool, rawInput, ctx, base);
936
+ if (!permitted.ok)
937
+ return permitted.outcome;
938
+ const guarded = yield* guardToolTarget(tool.serverUrl, ctx, stages?.span);
939
+ if (!guarded.ok)
940
+ return failureOutcome(guarded.failure, true);
941
+ const prepared = yield* remoteAuthAndPreBody({ tool, input: permitted.input, ctx, base, stages });
942
+ if (!('ok' in prepared))
943
+ return prepared;
944
+ return yield* sendWithCredential(prepared, guarded.url, () => sendMcpRequest(tool, guarded.url, prepared.input, prepared.authHeaders, ctx, base, stages?.span, sessions));
945
+ }
946
+ async function* sendMcpRequest(tool, url, input, authHeaders, ctx, base, span, sessions) {
947
+ const checks = requestChecks(span);
948
+ try {
949
+ const transport = {
950
+ url: url.href,
951
+ headers: {
952
+ 'Content-Type': 'application/json',
953
+ Accept: 'application/json, text/event-stream',
954
+ },
955
+ originBoundHeaders: { ...tool.headers, ...authHeaders },
956
+ policy: toolNetworkPolicy(ctx),
957
+ resolveHost: ctx.resolveHost,
958
+ signal: ctx.signal,
959
+ onCheck: checks.onCheck,
960
+ };
961
+ const key = await mcpSessionKey(url.href, transport.originBoundHeaders);
962
+ const cached = sessions.get(key);
963
+ const first = cached
964
+ ? await negotiateInSession(transport, base.callId, tool.mcpToolName, input, {
965
+ cache: sessions,
966
+ key,
967
+ cached,
968
+ })
969
+ : await negotiateMcpRpc(transport, base.callId, tool.mcpToolName, input);
970
+ const negotiated = first.sessionRequired
971
+ ? await negotiateInSession(transport, base.callId, tool.mcpToolName, input, {
972
+ cache: sessions,
973
+ key,
974
+ })
975
+ : first;
976
+ const refused = yield* refusedCredentialOutcome(tool, negotiated.refusal, authHeaders, ctx, base);
977
+ if (refused)
978
+ return refused;
979
+ const interpreted = interpretMcpRpc(tool, negotiated);
980
+ if (interpreted.warning) {
981
+ yield toolEvent(base, { phase: 'warning', warning: interpreted.warning });
982
+ }
983
+ if (!interpreted.ok) {
984
+ return failureOutcome(interpreted.failure, false);
985
+ }
986
+ return {
987
+ kind: 'ok',
988
+ modelResult: withMedia(modelResultFromOutput(interpreted.data), interpreted.parts),
989
+ outputRaw: interpreted.data,
990
+ };
991
+ }
992
+ catch (err) {
993
+ return yield* thrownOutcome(err, checks);
994
+ }
995
+ finally {
996
+ checks.record();
997
+ }
998
+ }