@theoremjs/agents 0.0.0-stage → 0.3.1

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