@theoremjs/agents 0.3.0 → 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 (568) hide show
  1. package/README.md +276 -995
  2. package/esm/mod.d.ts +33 -19
  3. package/esm/mod.js +18 -11
  4. package/esm/src/cli/commands/bench.js +36 -14
  5. package/esm/src/cli/commands/detect-compile.d.ts +17 -0
  6. package/esm/src/cli/commands/detect-compile.js +22 -0
  7. package/esm/src/cli/commands/eval.d.ts +3 -3
  8. package/esm/src/cli/commands/fuzz-canary.js +55 -16
  9. package/esm/src/cli/commands/profile.js +4 -4
  10. package/esm/src/cli/commands/run.d.ts +2 -2
  11. package/esm/src/cli/commands/run.js +1 -1
  12. package/esm/src/cli/commands/test.d.ts +4 -3
  13. package/esm/src/cli/commands/test.js +2 -2
  14. package/esm/src/cli/index.js +9 -9
  15. package/esm/src/cli/matrix/fixtures.js +25 -30
  16. package/esm/src/evals/attachments.d.ts +1 -0
  17. package/esm/src/evals/attachments.js +4 -2
  18. package/esm/src/evals/breakdown.d.ts +2 -0
  19. package/esm/src/evals/breakdown.js +1 -0
  20. package/esm/src/evals/graders/answer.d.ts +3 -0
  21. package/esm/src/evals/graders/answer.js +1 -0
  22. package/esm/src/evals/graders/code.d.ts +7 -31
  23. package/esm/src/evals/graders/code.js +4 -25
  24. package/esm/src/evals/graders/judge.d.ts +4 -0
  25. package/esm/src/evals/graders/judge.js +7 -5
  26. package/esm/src/evals/graders/latency.d.ts +1 -0
  27. package/esm/src/evals/graders/latency.js +3 -2
  28. package/esm/src/evals/graders/live.d.ts +3 -0
  29. package/esm/src/evals/graders/live.js +2 -0
  30. package/esm/src/evals/graders/media.js +1 -2
  31. package/esm/src/evals/graders/shared.js +1 -1
  32. package/esm/src/evals/graders/transcript.d.ts +2 -0
  33. package/esm/src/evals/graders/transcript.js +5 -3
  34. package/esm/src/evals/mod.d.ts +1 -9
  35. package/esm/src/evals/mod.js +1 -9
  36. package/esm/src/evals/record.d.ts +6 -0
  37. package/esm/src/evals/record.js +4 -2
  38. package/esm/src/evals/rubrics/mod.d.ts +1 -1
  39. package/esm/src/evals/rubrics/mod.js +1 -1
  40. package/esm/src/evals/rubrics/mustache.js +1 -1
  41. package/esm/src/evals/rubrics/types.js +1 -1
  42. package/esm/src/evals/run.d.ts +9 -52
  43. package/esm/src/evals/run.js +12 -46
  44. package/esm/src/evals/suite.d.ts +12 -3
  45. package/esm/src/evals/suite.js +9 -4
  46. package/esm/src/evals/summary.d.ts +3 -0
  47. package/esm/src/evals/summary.js +1 -0
  48. package/esm/src/evals/trial.d.ts +3 -16
  49. package/esm/src/evals/trial.js +7 -30
  50. package/esm/src/evals/types.d.ts +30 -121
  51. package/esm/src/evals/types.js +3 -16
  52. package/esm/src/evals/verdict.d.ts +5 -0
  53. package/esm/src/evals/verdict.js +4 -1
  54. package/esm/src/guardrails/boundaries.d.ts +41 -0
  55. package/esm/src/guardrails/boundaries.js +103 -0
  56. package/esm/src/guardrails/canary-gate.d.ts +1 -0
  57. package/esm/src/guardrails/canary.d.ts +49 -15
  58. package/esm/src/guardrails/canary.js +103 -30
  59. package/esm/src/guardrails/compile-egress.d.ts +6 -5
  60. package/esm/src/guardrails/compile-egress.js +6 -5
  61. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +3 -1
  62. package/esm/src/guardrails/corpus/canary-egress-attacks.js +4 -2
  63. package/esm/src/guardrails/corpus/fuzz-inbound.js +14 -17
  64. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +2 -0
  65. package/esm/src/guardrails/corpus/inbound-payloads.js +4 -2
  66. package/esm/src/guardrails/corpus/live-attacks.d.ts +2 -0
  67. package/esm/src/guardrails/corpus/live-attacks.js +6 -1
  68. package/esm/src/guardrails/corpus/mod.d.ts +1 -1
  69. package/esm/src/guardrails/corpus/secrets.d.ts +3 -3
  70. package/esm/src/guardrails/corpus/secrets.js +2 -2
  71. package/esm/src/guardrails/corpus/strings.d.ts +1 -1
  72. package/esm/src/guardrails/corpus/strings.js +1 -1
  73. package/esm/src/guardrails/corpus/types.d.ts +5 -1
  74. package/esm/src/guardrails/credential-rules.d.ts +36 -0
  75. package/esm/src/guardrails/credential-rules.js +3001 -0
  76. package/esm/src/guardrails/credential-scan.d.ts +45 -0
  77. package/esm/src/guardrails/credential-scan.js +172 -0
  78. package/esm/src/guardrails/destinations.d.ts +45 -0
  79. package/esm/src/guardrails/destinations.js +234 -0
  80. package/esm/src/guardrails/detect-at.d.ts +107 -0
  81. package/esm/src/guardrails/detect-at.js +434 -0
  82. package/esm/src/guardrails/detect-reply.d.ts +61 -0
  83. package/esm/src/guardrails/detect-reply.js +95 -0
  84. package/esm/src/guardrails/detect-stream.d.ts +33 -0
  85. package/esm/src/guardrails/detect-stream.js +77 -0
  86. package/esm/src/guardrails/detectors.d.ts +216 -0
  87. package/esm/src/guardrails/detectors.js +503 -0
  88. package/esm/src/guardrails/egress-automata.d.ts +4 -6
  89. package/esm/src/guardrails/egress-automata.js +24095 -4572
  90. package/esm/src/guardrails/egress-compiler.d.ts +38 -16
  91. package/esm/src/guardrails/egress-compiler.js +254 -60
  92. package/esm/src/guardrails/egress-patterns.d.ts +4 -2
  93. package/esm/src/guardrails/egress-patterns.js +6 -2
  94. package/esm/src/guardrails/egress-stream.d.ts +54 -36
  95. package/esm/src/guardrails/egress-stream.js +253 -124
  96. package/esm/src/guardrails/egress-urls.d.ts +6 -3
  97. package/esm/src/guardrails/egress-urls.js +14 -22
  98. package/esm/src/guardrails/egress.d.ts +26 -86
  99. package/esm/src/guardrails/egress.js +59 -309
  100. package/esm/src/guardrails/error.d.ts +13 -8
  101. package/esm/src/guardrails/error.js +23 -18
  102. package/esm/src/guardrails/event-schemas.d.ts +46 -3
  103. package/esm/src/guardrails/event-schemas.js +12 -5
  104. package/esm/src/guardrails/events.d.ts +5 -1
  105. package/esm/src/guardrails/events.js +4 -1
  106. package/esm/src/guardrails/hints.d.ts +14 -0
  107. package/esm/src/guardrails/hints.js +57 -0
  108. package/esm/src/guardrails/hits.d.ts +7 -1
  109. package/esm/src/guardrails/hits.js +12 -1
  110. package/esm/src/guardrails/host-patterns.d.ts +85 -0
  111. package/esm/src/guardrails/host-patterns.js +173 -0
  112. package/esm/src/guardrails/injection-patterns.d.ts +6 -1
  113. package/esm/src/guardrails/injection-patterns.js +33 -1
  114. package/esm/src/guardrails/injection-reversed.d.ts +9 -0
  115. package/esm/src/guardrails/injection-reversed.js +49 -0
  116. package/esm/src/guardrails/injection.d.ts +17 -1
  117. package/esm/src/guardrails/injection.js +259 -36
  118. package/esm/src/guardrails/labels.d.ts +40 -0
  119. package/esm/src/guardrails/labels.js +874 -0
  120. package/esm/src/guardrails/live-outbound-gate.d.ts +9 -8
  121. package/esm/src/guardrails/live-outbound-gate.js +100 -107
  122. package/esm/src/guardrails/mod.d.ts +29 -21
  123. package/esm/src/guardrails/mod.js +18 -13
  124. package/esm/src/guardrails/network.d.ts +4 -0
  125. package/esm/src/guardrails/network.js +21 -19
  126. package/esm/src/guardrails/normalize.d.ts +15 -1
  127. package/esm/src/guardrails/normalize.js +34 -1
  128. package/esm/src/guardrails/policy.d.ts +2 -17
  129. package/esm/src/guardrails/policy.js +12 -41
  130. package/esm/src/guardrails/progressive-yield.d.ts +65 -24
  131. package/esm/src/guardrails/progressive-yield.js +191 -147
  132. package/esm/src/guardrails/prompt-echo.d.ts +4 -2
  133. package/esm/src/guardrails/prompt-echo.js +87 -6
  134. package/esm/src/guardrails/quota.d.ts +1 -0
  135. package/esm/src/guardrails/quota.js +1 -1
  136. package/esm/src/guardrails/rules.d.ts +20 -29
  137. package/esm/src/guardrails/rules.js +23 -30
  138. package/esm/src/guardrails/sanitize.d.ts +32 -15
  139. package/esm/src/guardrails/sanitize.js +100 -124
  140. package/esm/src/guardrails/sensitive-groups.d.ts +21 -0
  141. package/esm/src/guardrails/sensitive-groups.js +12 -0
  142. package/esm/src/guardrails/sensitive.d.ts +16 -23
  143. package/esm/src/guardrails/sensitive.js +168 -47
  144. package/esm/src/guardrails/serialize.d.ts +1 -0
  145. package/esm/src/guardrails/testing.d.ts +4 -2
  146. package/esm/src/guardrails/testing.js +4 -2
  147. package/esm/src/guardrails/theorem-error.d.ts +4 -1
  148. package/esm/src/guardrails/theorem-error.js +3 -2
  149. package/esm/src/guardrails/thought-guard.d.ts +34 -13
  150. package/esm/src/guardrails/thought-guard.js +264 -64
  151. package/esm/src/guardrails/tool-directives.d.ts +29 -10
  152. package/esm/src/guardrails/tool-directives.js +133 -47
  153. package/esm/src/guardrails/tool-leak.d.ts +26 -0
  154. package/esm/src/guardrails/tool-leak.js +45 -0
  155. package/esm/src/guardrails/tool-result.d.ts +45 -14
  156. package/esm/src/guardrails/tool-result.js +94 -70
  157. package/esm/src/guardrails/types.d.ts +79 -71
  158. package/esm/src/guardrails/types.js +11 -7
  159. package/esm/src/host/client-turn.d.ts +29 -2
  160. package/esm/src/host/client-turn.js +46 -4
  161. package/esm/src/host/mint-trace.d.ts +1 -0
  162. package/esm/src/host/mint-trace.js +3 -2
  163. package/esm/src/host/mod.d.ts +2 -2
  164. package/esm/src/host/mod.js +1 -1
  165. package/esm/src/host/readStreamingJsonStringField.js +2 -43
  166. package/esm/src/host/reply.d.ts +6 -0
  167. package/esm/src/host/reply.js +7 -1
  168. package/esm/src/interface/blocks.d.ts +3 -0
  169. package/esm/src/interface/blocks.js +84 -14
  170. package/esm/src/interface/composer-actions.d.ts +4 -0
  171. package/esm/src/interface/composer-actions.js +5 -3
  172. package/esm/src/interface/draft.d.ts +6 -0
  173. package/esm/src/interface/draft.js +22 -11
  174. package/esm/src/interface/from-profile.d.ts +13 -1
  175. package/esm/src/interface/from-profile.js +66 -31
  176. package/esm/src/interface/history.d.ts +9 -3
  177. package/esm/src/interface/history.js +33 -24
  178. package/esm/src/interface/inputs.d.ts +1 -0
  179. package/esm/src/interface/inputs.js +3 -0
  180. package/esm/src/interface/mod.d.ts +4 -2
  181. package/esm/src/interface/mod.js +3 -2
  182. package/esm/src/interface/models.d.ts +7 -0
  183. package/esm/src/interface/models.js +5 -0
  184. package/esm/src/interface/pending.d.ts +9 -0
  185. package/esm/src/interface/pending.js +9 -3
  186. package/esm/src/interface/profile-interface.js +54 -40
  187. package/esm/src/interface/session.d.ts +12 -5
  188. package/esm/src/interface/session.js +17 -10
  189. package/esm/src/interface/stop.d.ts +13 -0
  190. package/esm/src/interface/stop.js +28 -0
  191. package/esm/src/interface/tool-calls.d.ts +14 -2
  192. package/esm/src/interface/tool-calls.js +64 -10
  193. package/esm/src/interface/tool-invoke.d.ts +2 -0
  194. package/esm/src/interface/tool-invoke.js +3 -1
  195. package/esm/src/interface/tool-media.d.ts +2 -0
  196. package/esm/src/interface/tool-media.js +2 -1
  197. package/esm/src/interface/types.d.ts +105 -40
  198. package/esm/src/interface/types.js +1 -0
  199. package/esm/src/kernel/api-catalog.d.ts +85 -0
  200. package/esm/src/kernel/api-catalog.js +229 -0
  201. package/esm/src/kernel/auth/crypto.d.ts +7 -2
  202. package/esm/src/kernel/auth/crypto.js +12 -10
  203. package/esm/src/kernel/auth/oauth.d.ts +2 -1
  204. package/esm/src/kernel/auth/oauth.js +23 -22
  205. package/esm/src/kernel/auth/typed-secret.js +2 -2
  206. package/esm/src/kernel/auth/types.d.ts +13 -0
  207. package/esm/src/kernel/checkpoint.d.ts +43 -0
  208. package/esm/src/kernel/checkpoint.js +58 -0
  209. package/esm/src/kernel/default-scope.d.ts +43 -5
  210. package/esm/src/kernel/default-scope.js +54 -5
  211. package/esm/src/kernel/engine/call-trace.d.ts +19 -0
  212. package/esm/src/kernel/engine/call-trace.js +29 -0
  213. package/esm/src/kernel/engine/compaction.d.ts +6 -5
  214. package/esm/src/kernel/engine/compaction.js +12 -11
  215. package/esm/src/kernel/engine/decision.d.ts +10 -8
  216. package/esm/src/kernel/engine/decision.js +173 -146
  217. package/esm/src/kernel/engine/embedding.d.ts +8 -0
  218. package/esm/src/kernel/engine/embedding.js +118 -0
  219. package/esm/src/kernel/engine/live-inbound.d.ts +14 -4
  220. package/esm/src/kernel/engine/live-inbound.js +36 -11
  221. package/esm/src/kernel/engine/live-ingress.d.ts +6 -0
  222. package/esm/src/kernel/engine/live-ingress.js +6 -1
  223. package/esm/src/kernel/engine/repair.js +12 -12
  224. package/esm/src/kernel/engine/runner/gates.d.ts +2 -2
  225. package/esm/src/kernel/engine/runner/gates.js +287 -101
  226. package/esm/src/kernel/engine/runner/mod.d.ts +19 -5
  227. package/esm/src/kernel/engine/runner/mod.js +107 -56
  228. package/esm/src/kernel/engine/runner/nested.d.ts +32 -0
  229. package/esm/src/kernel/engine/runner/nested.js +134 -0
  230. package/esm/src/kernel/engine/runner/schema-validation.d.ts +24 -3
  231. package/esm/src/kernel/engine/runner/schema-validation.js +78 -4
  232. package/esm/src/kernel/engine/runner/stages.d.ts +1 -1
  233. package/esm/src/kernel/engine/runner/stages.js +1 -8
  234. package/esm/src/kernel/engine/runner/state.d.ts +33 -12
  235. package/esm/src/kernel/engine/runner/state.js +11 -1
  236. package/esm/src/kernel/engine/runner/step-order.d.ts +27 -0
  237. package/esm/src/kernel/engine/runner/step-order.js +165 -0
  238. package/esm/src/kernel/engine/runner/steps.d.ts +2 -2
  239. package/esm/src/kernel/engine/runner/steps.js +328 -169
  240. package/esm/src/kernel/engine/runner/stream.d.ts +42 -11
  241. package/esm/src/kernel/engine/runner/stream.js +197 -46
  242. package/esm/src/kernel/engine/runner/usage.d.ts +5 -5
  243. package/esm/src/kernel/engine/runner/usage.js +12 -14
  244. package/esm/src/kernel/engine/session/mod.d.ts +8 -6
  245. package/esm/src/kernel/engine/session/mod.js +360 -129
  246. package/esm/src/kernel/engine/session/session-trace.d.ts +10 -10
  247. package/esm/src/kernel/engine/session/session-trace.js +41 -30
  248. package/esm/src/kernel/engine/token-estimate.d.ts +10 -64
  249. package/esm/src/kernel/engine/token-estimate.js +14 -194
  250. package/esm/src/kernel/engine/tool-trace.d.ts +3 -3
  251. package/esm/src/kernel/engine/tool-trace.js +2 -1
  252. package/esm/src/kernel/engine/turn-trace.d.ts +8 -7
  253. package/esm/src/kernel/engine/turn-trace.js +28 -36
  254. package/esm/src/kernel/engine/usage.d.ts +6 -0
  255. package/esm/src/kernel/engine/usage.js +17 -0
  256. package/esm/src/kernel/interaction-parts.d.ts +3 -0
  257. package/esm/src/kernel/interaction-parts.js +18 -0
  258. package/esm/src/kernel/json.d.ts +10 -0
  259. package/esm/src/kernel/json.js +3 -0
  260. package/esm/src/kernel/mod.d.ts +38 -11
  261. package/esm/src/kernel/mod.js +22 -7
  262. package/esm/src/kernel/profile-graph.d.ts +11 -6
  263. package/esm/src/kernel/profile-graph.js +14 -7
  264. package/esm/src/kernel/profile-presence.d.ts +10 -1
  265. package/esm/src/kernel/profile-presence.js +90 -75
  266. package/esm/src/kernel/profile-scope.d.ts +7 -3
  267. package/esm/src/kernel/profile-scope.js +67 -55
  268. package/esm/src/kernel/protocols/binding.d.ts +65 -0
  269. package/esm/src/kernel/protocols/binding.js +125 -0
  270. package/esm/src/kernel/protocols/limits.d.ts +15 -0
  271. package/esm/src/kernel/protocols/limits.js +28 -0
  272. package/esm/src/kernel/protocols/mod.d.ts +5 -0
  273. package/esm/src/kernel/protocols/mod.js +5 -0
  274. package/esm/src/kernel/protocols/prices.d.ts +36 -0
  275. package/esm/src/kernel/protocols/prices.js +37 -0
  276. package/esm/src/kernel/protocols/protocol.d.ts +253 -0
  277. package/esm/src/kernel/protocols/protocol.js +253 -0
  278. package/esm/src/kernel/protocols/provider.d.ts +108 -0
  279. package/esm/src/kernel/protocols/provider.js +143 -0
  280. package/esm/src/kernel/protocols/runtime.d.ts +55 -0
  281. package/esm/src/kernel/protocols/runtime.js +534 -0
  282. package/esm/src/kernel/provider-budget.d.ts +32 -0
  283. package/esm/src/kernel/provider-budget.js +262 -0
  284. package/esm/src/kernel/provider-dispatch.d.ts +13 -0
  285. package/esm/src/kernel/provider-dispatch.js +36 -0
  286. package/esm/src/kernel/provider-live.d.ts +7 -0
  287. package/esm/src/kernel/provider-live.js +189 -0
  288. package/esm/src/kernel/provider-runtime.d.ts +57 -0
  289. package/esm/src/kernel/provider-runtime.js +524 -0
  290. package/esm/src/kernel/registry/attachments.d.ts +15 -4
  291. package/esm/src/kernel/registry/attachments.js +31 -22
  292. package/esm/src/kernel/registry/catalog.d.ts +6 -0
  293. package/esm/src/kernel/registry/catalog.js +9 -2
  294. package/esm/src/kernel/registry/embedding-rules.d.ts +28 -0
  295. package/esm/src/kernel/registry/embedding-rules.js +66 -0
  296. package/esm/src/kernel/registry/ingress.d.ts +6 -2
  297. package/esm/src/kernel/registry/ingress.js +55 -10
  298. package/esm/src/kernel/registry/kernel-registry.d.ts +3 -0
  299. package/esm/src/kernel/registry/kernel-registry.js +9 -2
  300. package/esm/src/kernel/registry/profile-outputs.js +4 -1
  301. package/esm/src/kernel/registry/profiles.d.ts +38 -14
  302. package/esm/src/kernel/registry/profiles.js +280 -205
  303. package/esm/src/kernel/registry/provider-request.d.ts +3 -3
  304. package/esm/src/kernel/registry/provider-request.js +5 -13
  305. package/esm/src/kernel/registry/resolve.d.ts +4 -1
  306. package/esm/src/kernel/registry/resolve.js +59 -58
  307. package/esm/src/kernel/registry/schemas.d.ts +3 -0
  308. package/esm/src/kernel/registry/schemas.js +3 -1
  309. package/esm/src/kernel/registry/slot-fill.d.ts +7 -0
  310. package/esm/src/kernel/registry/slot-fill.js +22 -0
  311. package/esm/src/kernel/registry/system-prompt.js +7 -8
  312. package/esm/src/kernel/registry/vault.d.ts +14 -13
  313. package/esm/src/kernel/registry/vault.js +1 -14
  314. package/esm/src/kernel/schema.d.ts +100 -46
  315. package/esm/src/kernel/schema.js +499 -257
  316. package/esm/src/kernel/scope.d.ts +12 -5
  317. package/esm/src/kernel/scope.js +23 -3
  318. package/esm/src/kernel/stages.d.ts +38 -3
  319. package/esm/src/kernel/stages.js +82 -39
  320. package/esm/src/kernel/stop.d.ts +29 -6
  321. package/esm/src/kernel/stop.js +16 -4
  322. package/esm/src/kernel/system-parts.js +6 -6
  323. package/esm/src/kernel/tools/activity-label.d.ts +12 -6
  324. package/esm/src/kernel/tools/activity-label.js +258 -32
  325. package/esm/src/kernel/tools/agent.d.ts +66 -0
  326. package/esm/src/kernel/tools/agent.js +212 -0
  327. package/esm/src/kernel/tools/coordinate.d.ts +4 -0
  328. package/esm/src/kernel/tools/coordinate.js +165 -0
  329. package/esm/src/kernel/tools/events.d.ts +21 -6
  330. package/esm/src/kernel/tools/events.js +53 -18
  331. package/esm/src/kernel/tools/execute.d.ts +47 -11
  332. package/esm/src/kernel/tools/execute.js +508 -136
  333. package/esm/src/kernel/tools/gate-answer.d.ts +12 -2
  334. package/esm/src/kernel/tools/gate-answer.js +17 -6
  335. package/esm/src/kernel/tools/handler-events.d.ts +45 -0
  336. package/esm/src/kernel/tools/handler-events.js +106 -0
  337. package/esm/src/kernel/tools/harness.d.ts +1 -0
  338. package/esm/src/kernel/tools/harness.js +2 -1
  339. package/esm/src/kernel/tools/http-limit.d.ts +27 -0
  340. package/esm/src/kernel/tools/http-limit.js +85 -0
  341. package/esm/src/kernel/tools/invoke.d.ts +27 -5
  342. package/esm/src/kernel/tools/invoke.js +223 -70
  343. package/esm/src/kernel/tools/mcp-sessions.d.ts +4 -1
  344. package/esm/src/kernel/tools/mcp-sessions.js +13 -8
  345. package/esm/src/kernel/tools/mod.d.ts +4 -3
  346. package/esm/src/kernel/tools/mod.js +3 -2
  347. package/esm/src/kernel/tools/model-text.d.ts +10 -5
  348. package/esm/src/kernel/tools/model-text.js +18 -12
  349. package/esm/src/kernel/tools/operation.d.ts +76 -0
  350. package/esm/src/kernel/tools/operation.js +100 -0
  351. package/esm/src/kernel/tools/page-answer.d.ts +8 -0
  352. package/esm/src/kernel/tools/page-answer.js +19 -0
  353. package/esm/src/kernel/tools/permission.d.ts +11 -2
  354. package/esm/src/kernel/tools/permission.js +12 -12
  355. package/esm/src/kernel/tools/project.d.ts +4 -1
  356. package/esm/src/kernel/tools/project.js +32 -2
  357. package/esm/src/kernel/tools/registry.d.ts +9 -2
  358. package/esm/src/kernel/tools/registry.js +69 -10
  359. package/esm/src/kernel/tools/remote.d.ts +40 -6
  360. package/esm/src/kernel/tools/remote.js +219 -92
  361. package/esm/src/kernel/tools/resolve.d.ts +3 -0
  362. package/esm/src/kernel/tools/resolve.js +12 -4
  363. package/esm/src/kernel/tools/run.d.ts +28 -0
  364. package/esm/src/kernel/tools/run.js +86 -0
  365. package/esm/src/kernel/tools/schema.d.ts +3 -3
  366. package/esm/src/kernel/tools/schema.js +21 -100
  367. package/esm/src/kernel/tools/signed-in-fetch.d.ts +2 -1
  368. package/esm/src/kernel/tools/signed-in-fetch.js +3 -3
  369. package/esm/src/kernel/tools/stage-run.d.ts +27 -4
  370. package/esm/src/kernel/tools/stage-run.js +75 -17
  371. package/esm/src/kernel/tools/types.d.ts +235 -25
  372. package/esm/src/kernel/tools/unchecked-output.d.ts +14 -0
  373. package/esm/src/kernel/tools/unchecked-output.js +18 -0
  374. package/esm/src/kernel/turn-events.d.ts +208 -31
  375. package/esm/src/kernel/turn-events.js +127 -9
  376. package/esm/src/kernel/types.d.ts +302 -161
  377. package/esm/src/kernel/util/base64.d.ts +2 -0
  378. package/esm/src/kernel/util/base64.js +2 -0
  379. package/esm/src/kernel/util/partial-json.d.ts +23 -0
  380. package/esm/src/kernel/util/partial-json.js +128 -0
  381. package/esm/src/kernel/util/record.d.ts +1 -0
  382. package/esm/src/kernel/util/record.js +1 -0
  383. package/esm/src/observability/destinations.d.ts +5 -0
  384. package/esm/src/observability/destinations.js +7 -2
  385. package/esm/src/observability/jsonl.d.ts +2 -2
  386. package/esm/src/observability/jsonl.js +15 -22
  387. package/esm/src/observability/mod.d.ts +3 -3
  388. package/esm/src/observability/mod.js +2 -2
  389. package/esm/src/observability/openinference.d.ts +1 -1
  390. package/esm/src/observability/openinference.js +3 -3
  391. package/esm/src/observability/otlp.d.ts +3 -0
  392. package/esm/src/observability/otlp.js +3 -3
  393. package/esm/src/observability/phoenix.d.ts +1 -0
  394. package/esm/src/observability/policy.d.ts +3 -0
  395. package/esm/src/observability/policy.js +5 -1
  396. package/esm/src/observability/resolve-policy.js +11 -5
  397. package/esm/src/observability/spans.d.ts +8 -2
  398. package/esm/src/observability/spans.js +21 -5
  399. package/esm/src/observability/trace-catalog.d.ts +19 -2
  400. package/esm/src/observability/trace-catalog.js +128 -87
  401. package/esm/src/observability/trace-record.js +22 -17
  402. package/esm/src/observability/trace-schema.d.ts +3 -0
  403. package/esm/src/observability/trace-schema.js +1 -1
  404. package/esm/src/observability/trace-sink.d.ts +1 -0
  405. package/esm/src/observability/trace-sink.js +1 -1
  406. package/esm/src/observability/trace-span.d.ts +7 -1
  407. package/esm/src/observability/trace-span.js +3 -1
  408. package/esm/src/observability/trace.d.ts +2 -0
  409. package/esm/src/observability/trace.js +4 -2
  410. package/esm/src/observability/types.d.ts +35 -11
  411. package/esm/src/observability/types.js +1 -1
  412. package/esm/src/presets/google/speech-voices.d.ts +1 -0
  413. package/esm/src/presets/google-limits.d.ts +1 -0
  414. package/esm/src/presets/google.d.ts +27 -12
  415. package/esm/src/presets/google.js +34 -25
  416. package/esm/src/presets/local.d.ts +26 -0
  417. package/esm/src/presets/local.js +30 -0
  418. package/esm/src/presets/mod.d.ts +5 -4
  419. package/esm/src/presets/mod.js +4 -4
  420. package/esm/src/presets/openrouter.d.ts +14 -1
  421. package/esm/src/presets/openrouter.js +19 -1
  422. package/esm/src/providers/embedding-limits.d.ts +7 -0
  423. package/esm/src/providers/embedding-limits.js +12 -0
  424. package/esm/src/providers/gemini/embedding-protocol.d.ts +8 -0
  425. package/esm/src/providers/gemini/embedding-protocol.js +125 -0
  426. package/esm/src/providers/gemini/interactions-protocol.d.ts +8 -0
  427. package/esm/src/providers/gemini/interactions-protocol.js +105 -0
  428. package/esm/src/providers/gemini/live-protocol.d.ts +3 -0
  429. package/esm/src/providers/gemini/live-protocol.js +191 -0
  430. package/esm/src/providers/gemini/prices.d.ts +3 -0
  431. package/esm/src/providers/gemini/prices.js +63 -0
  432. package/esm/src/providers/gemini/provider.d.ts +7 -0
  433. package/esm/src/providers/gemini/provider.js +39 -0
  434. package/esm/src/providers/google/builtins.d.ts +14 -0
  435. package/esm/src/providers/google/builtins.js +57 -0
  436. package/esm/src/providers/google/grounding.d.ts +4 -4
  437. package/esm/src/providers/google/interactions/framing.d.ts +20 -8
  438. package/esm/src/providers/google/interactions/framing.js +85 -44
  439. package/esm/src/providers/google/interactions/steps.d.ts +5 -5
  440. package/esm/src/providers/google/interactions/steps.js +4 -4
  441. package/esm/src/providers/google/interactions/stream.d.ts +10 -11
  442. package/esm/src/providers/google/interactions/stream.js +33 -100
  443. package/esm/src/providers/google/live/framing.d.ts +15 -5
  444. package/esm/src/providers/google/live/framing.js +25 -25
  445. package/esm/src/providers/google/live/stream.d.ts +6 -44
  446. package/esm/src/providers/google/live/stream.js +3 -209
  447. package/esm/src/providers/google/urls.d.ts +1 -3
  448. package/esm/src/providers/google/urls.js +1 -3
  449. package/esm/src/providers/google/usage.js +2 -2
  450. package/esm/src/providers/live.d.ts +6 -0
  451. package/esm/src/providers/live.js +6 -0
  452. package/esm/src/providers/local/embedding-protocol.d.ts +7 -0
  453. package/esm/src/providers/local/embedding-protocol.js +60 -0
  454. package/esm/src/providers/local/provider.d.ts +7 -0
  455. package/esm/src/providers/local/provider.js +28 -0
  456. package/esm/src/providers/mod.d.ts +28 -7
  457. package/esm/src/providers/mod.js +19 -3
  458. package/esm/src/providers/open-ai/protocol.d.ts +3 -0
  459. package/esm/src/providers/open-ai/protocol.js +189 -0
  460. package/esm/src/providers/openrouter/embedding-protocol.d.ts +6 -0
  461. package/esm/src/providers/openrouter/embedding-protocol.js +86 -0
  462. package/esm/src/providers/openrouter/image.d.ts +0 -9
  463. package/esm/src/providers/openrouter/image.js +0 -153
  464. package/esm/src/providers/openrouter/images-protocol.d.ts +3 -0
  465. package/esm/src/providers/openrouter/images-protocol.js +82 -0
  466. package/esm/src/providers/openrouter/openai/compat.d.ts +2 -2
  467. package/esm/src/providers/openrouter/openai/compat.js +6 -6
  468. package/esm/src/providers/openrouter/openai/image-payload.d.ts +2 -2
  469. package/esm/src/providers/openrouter/openai/reasoning-state.d.ts +2 -0
  470. package/esm/src/providers/openrouter/openai/reasoning-state.js +31 -0
  471. package/esm/src/providers/openrouter/protocol.d.ts +8 -0
  472. package/esm/src/providers/openrouter/protocol.js +171 -0
  473. package/esm/src/providers/openrouter/provider.d.ts +10 -0
  474. package/esm/src/providers/openrouter/provider.js +50 -0
  475. package/esm/src/providers/shared/carried-tool-files.d.ts +7 -0
  476. package/esm/src/providers/shared/carried-tool-files.js +43 -0
  477. package/esm/src/providers/shared/pcm.js +2 -2
  478. package/esm/src/providers/shared/protocol-events.d.ts +8 -0
  479. package/esm/src/providers/shared/protocol-events.js +20 -0
  480. package/esm/src/providers/shared/response-identity.d.ts +2 -2
  481. package/esm/src/providers/shared/retry.d.ts +17 -0
  482. package/esm/src/providers/shared/retry.js +90 -0
  483. package/esm/src/providers/shared/sse.d.ts +0 -4
  484. package/esm/src/providers/shared/sse.js +50 -36
  485. package/esm/src/providers/shared/tool-args.d.ts +6 -7
  486. package/esm/src/providers/shared/tool-args.js +10 -6
  487. package/esm/src/providers/shared/tool-draft.d.ts +16 -0
  488. package/esm/src/providers/shared/tool-draft.js +14 -0
  489. package/esm/src/providers/shared/upstream-tap.d.ts +2 -2
  490. package/esm/src/providers/shared/upstream-tape.js +1 -1
  491. package/esm/src/providers/system-one/protocol.d.ts +8 -0
  492. package/esm/src/providers/system-one/protocol.js +93 -0
  493. package/esm/src/providers/types.d.ts +48 -16
  494. package/esm/src/providers/types.js +2 -1
  495. package/esm/src/providers/typesafe/provider.d.ts +7 -0
  496. package/esm/src/providers/typesafe/provider.js +24 -0
  497. package/esm/src/surface/formats.d.ts +19 -1
  498. package/esm/src/surface/formats.js +68 -29
  499. package/esm/src/surface/mod.d.ts +4 -2
  500. package/esm/src/surface/mod.js +4 -2
  501. package/esm/src/surface/runtime.d.ts +4 -0
  502. package/esm/src/surface/runtime.js +2 -0
  503. package/esm/src/surface/tools.d.ts +2 -8
  504. package/esm/src/surface/tools.js +4 -22
  505. package/esm/src/surface/types.d.ts +6 -0
  506. package/package.json +5 -13
  507. package/esm/src/cli/commands/egress-compile.d.ts +0 -17
  508. package/esm/src/cli/commands/egress-compile.js +0 -25
  509. package/esm/src/guardrails/egress-policy.d.ts +0 -27
  510. package/esm/src/guardrails/egress-policy.js +0 -76
  511. package/esm/src/guardrails/egress-rules.d.ts +0 -54
  512. package/esm/src/guardrails/egress-rules.js +0 -49
  513. package/esm/src/guardrails/lexicon.d.ts +0 -30
  514. package/esm/src/guardrails/lexicon.js +0 -478
  515. package/esm/src/kernel/engine/media-probe/audio.d.ts +0 -7
  516. package/esm/src/kernel/engine/media-probe/audio.js +0 -342
  517. package/esm/src/kernel/engine/media-probe/bytes.d.ts +0 -6
  518. package/esm/src/kernel/engine/media-probe/bytes.js +0 -26
  519. package/esm/src/kernel/engine/media-probe/image.d.ts +0 -5
  520. package/esm/src/kernel/engine/media-probe/image.js +0 -171
  521. package/esm/src/kernel/engine/media-probe/iso-bmff.d.ts +0 -19
  522. package/esm/src/kernel/engine/media-probe/iso-bmff.js +0 -48
  523. package/esm/src/kernel/engine/media-probe/matroska.d.ts +0 -35
  524. package/esm/src/kernel/engine/media-probe/matroska.js +0 -226
  525. package/esm/src/kernel/engine/media-probe/mp4.d.ts +0 -22
  526. package/esm/src/kernel/engine/media-probe/mp4.js +0 -279
  527. package/esm/src/kernel/engine/media-probe/opus.d.ts +0 -4
  528. package/esm/src/kernel/engine/media-probe/opus.js +0 -23
  529. package/esm/src/kernel/engine/media-probe/pdf.d.ts +0 -5
  530. package/esm/src/kernel/engine/media-probe/pdf.js +0 -117
  531. package/esm/src/kernel/engine/media-probe/video.d.ts +0 -15
  532. package/esm/src/kernel/engine/media-probe/video.js +0 -49
  533. package/esm/src/presets/typesafe.d.ts +0 -2
  534. package/esm/src/presets/typesafe.js +0 -2
  535. package/esm/src/providers/create-provider.d.ts +0 -21
  536. package/esm/src/providers/create-provider.js +0 -90
  537. package/esm/src/providers/decision/usage.d.ts +0 -3
  538. package/esm/src/providers/decision/usage.js +0 -25
  539. package/esm/src/providers/google/interactions/mod.d.ts +0 -2
  540. package/esm/src/providers/google/interactions/mod.js +0 -2
  541. package/esm/src/providers/google/keys.d.ts +0 -20
  542. package/esm/src/providers/google/keys.js +0 -87
  543. package/esm/src/providers/google/live/mod.d.ts +0 -7
  544. package/esm/src/providers/google/live/mod.js +0 -7
  545. package/esm/src/providers/google/live/openapi-schema.d.ts +0 -1
  546. package/esm/src/providers/google/live/openapi-schema.js +0 -42
  547. package/esm/src/providers/google/live/session.d.ts +0 -14
  548. package/esm/src/providers/google/live/session.js +0 -150
  549. package/esm/src/providers/local/local.d.ts +0 -12
  550. package/esm/src/providers/local/local.js +0 -186
  551. package/esm/src/providers/local/mod.d.ts +0 -8
  552. package/esm/src/providers/local/mod.js +0 -8
  553. package/esm/src/providers/openrouter/cache-control.d.ts +0 -5
  554. package/esm/src/providers/openrouter/cache-control.js +0 -4
  555. package/esm/src/providers/openrouter/chat.d.ts +0 -64
  556. package/esm/src/providers/openrouter/chat.js +0 -490
  557. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +0 -10
  558. package/esm/src/providers/openrouter/openai/sdk-messages.js +0 -99
  559. package/esm/src/providers/openrouter/resolve-api-key.d.ts +0 -3
  560. package/esm/src/providers/openrouter/resolve-api-key.js +0 -8
  561. package/esm/src/providers/openrouter/speech.d.ts +0 -18
  562. package/esm/src/providers/openrouter/speech.js +0 -109
  563. package/esm/src/providers/probe.d.ts +0 -5
  564. package/esm/src/providers/probe.js +0 -15
  565. package/esm/src/providers/shared/builtin-wire.d.ts +0 -4
  566. package/esm/src/providers/shared/builtin-wire.js +0 -9
  567. package/esm/src/providers/shared/vault.d.ts +0 -13
  568. package/esm/src/providers/shared/vault.js +0 -41
@@ -1,16 +1,14 @@
1
1
  import type { TraceSink } from '../../../observability/trace-sink.js';
2
2
  import { type SpanHandle, type TraceAttributes, type TraceTree } from '../../../observability/trace-span.js';
3
- import type { ProfileObservabilitySpec, ResolvedObservabilityPolicy } from '../../../observability/types.js';
4
- import type { SessionQueueItem } from '../../../providers/google/live/stream.js';
3
+ import type { ResolvedObservabilityPolicy } from '../../../observability/types.js';
4
+ import { type LiveQueueItem } from '../../../providers/types.js';
5
5
  import type { TurnEventOf } from '../../turn-events.js';
6
- import type { ModelBinding, ProviderCompleteRequest, ResolvedGeneration, SessionRequest, TurnEvent } from '../../types.js';
7
- import type { MediaTokenFamily } from '../token-estimate.js';
6
+ import type { ModelBinding, ModelRequest, Profile, ResolvedGeneration, SessionRequest, TurnEvent } from '../../types.js';
8
7
  type LiveCloser = 'host' | 'provider' | 'theorem';
9
8
  interface LiveTraceBinding {
10
- request: ProviderCompleteRequest;
9
+ request: ModelRequest;
11
10
  generation: ResolvedGeneration;
12
11
  binding: ModelBinding | undefined;
13
- family: MediaTokenFamily | undefined;
14
12
  /** The system prompt as sent, canary bound. */
15
13
  system: string;
16
14
  canary: string;
@@ -41,6 +39,8 @@ declare class LiveTrace {
41
39
  private endedByGoAway;
42
40
  private socketHasClosed;
43
41
  private closing?;
42
+ /** Canaries bound by the turns this session's agent tools ran: scrubbed from every record after. */
43
+ readonly agentCanaries: string[];
44
44
  constructor(tree: TraceTree, out: {
45
45
  sink: TraceSink;
46
46
  policy: ResolvedObservabilityPolicy;
@@ -54,7 +54,7 @@ declare class LiveTrace {
54
54
  private keyFallback;
55
55
  setup(frame: Record<string, unknown>): void;
56
56
  /** One received queue item, before guardrails. */
57
- receive(item: SessionQueueItem): void;
57
+ receive(item: LiveQueueItem): void;
58
58
  /**
59
59
  * One event as the host received it. Output reaches the host only after
60
60
  * `receive` opened its response, so every output part lands on the open one;
@@ -74,7 +74,7 @@ declare class LiveTrace {
74
74
  * session. A provider close carries its warning (`goAway`) and what the code
75
75
  * means as a failure, so a warned close stays fully diagnosable.
76
76
  */
77
- socketClosed(code: number, reason: string, initiator: LiveCloser, provider?: Extract<SessionQueueItem, {
77
+ socketClosed(code: number, reason: string, initiator: LiveCloser, provider?: Extract<LiveQueueItem, {
78
78
  type: 'closed';
79
79
  }>): void;
80
80
  /**
@@ -108,8 +108,8 @@ declare class LiveTrace {
108
108
  }
109
109
  /**
110
110
  * Open a session's trace: its `invoke_agent` span under the host's `traceparent`,
111
- * written under `observability`, the session's profile's block.
111
+ * written under the session's profile's `observability` and cleaned with its detectors.
112
112
  */
113
- declare function startLiveTrace(req: SessionRequest, observability: ProfileObservabilitySpec | undefined, sinkOverride?: TraceSink): LiveTrace;
113
+ declare function startLiveTrace(req: SessionRequest, profile: Pick<Profile, 'observability' | 'guardrails'> | undefined, sinkOverride?: TraceSink): LiveTrace;
114
114
  export type { LiveCloser, LiveTrace, LiveTraceBinding, LiveTraceEnd };
115
115
  export { startLiveTrace };
@@ -3,8 +3,8 @@ import { resolveTraceWriter } from '../../../observability/policy.js';
3
3
  import { writeTrace } from '../../../observability/trace.js';
4
4
  import { buildRecord } from '../../../observability/trace-record.js';
5
5
  import { startTrace, traceJson, } from '../../../observability/trace-span.js';
6
- import { liveFrameInput } from '../../../providers/google/live/framing.js';
7
- import { LIVE_FALLBACK_ROW } from '../../../providers/google/live/session.js';
6
+ import { liveSentInput } from '../../../providers/live.js';
7
+ import { KEY_FALLBACK_ROW } from '../../../providers/types.js';
8
8
  import { base64ToBytes, bytesToBase64 } from '../../util/base64.js';
9
9
  import { asRecord } from '../record.js';
10
10
  import { callTokensEvent, heldAfter, observeCallEvent, startCallUsage, } from '../runner/usage.js';
@@ -33,7 +33,7 @@ function audioRunParts(run) {
33
33
  return [{ type: 'audio', mimeType: run.mimeType, data: bytesToBase64(merged) }];
34
34
  }
35
35
  catch {
36
- // Not base64: keep each chunk as sent, so the record labels what it holds.
36
+ // why: Not base64: keep each chunk as sent, so the record labels what it holds.
37
37
  return run.chunks.map((data) => ({ type: 'audio', mimeType: run.mimeType, data }));
38
38
  }
39
39
  }
@@ -109,7 +109,7 @@ function sessionSignal(event) {
109
109
  }
110
110
  return undefined;
111
111
  }
112
- // Each record is written as soon as it is complete, so a long session never holds its whole trace.
112
+ // why: Each record is written as soon as it is complete, so a long session never holds its whole trace.
113
113
  class LiveTrace {
114
114
  tree;
115
115
  out;
@@ -132,6 +132,8 @@ class LiveTrace {
132
132
  endedByGoAway = false;
133
133
  socketHasClosed = false;
134
134
  closing;
135
+ /** Canaries bound by the turns this session's agent tools ran: scrubbed from every record after. */
136
+ agentCanaries = [];
135
137
  constructor(tree, out, identity) {
136
138
  this.tree = tree;
137
139
  this.out = out;
@@ -144,10 +146,16 @@ class LiveTrace {
144
146
  }
145
147
  /** The adapter's `tapUpstream`: every frame sent, as it is sent. */
146
148
  sent = (row) => {
147
- if (row.eventType === LIVE_FALLBACK_ROW) {
149
+ if (row.eventType === KEY_FALLBACK_ROW) {
148
150
  this.keyFallback(row);
149
151
  return;
150
152
  }
153
+ if (row.direction === 'receive') {
154
+ const body = asRecord(row.body);
155
+ if (body)
156
+ this.row(body);
157
+ return;
158
+ }
151
159
  const frame = asRecord(row.body);
152
160
  if (!frame)
153
161
  return;
@@ -156,22 +164,21 @@ class LiveTrace {
156
164
  this.root.event('theorem.wire.request', { body: traceJson(body) });
157
165
  return;
158
166
  }
159
- this.pendingInput.add(liveFrameInput(frame));
167
+ this.pendingInput.add(liveSentInput(row));
160
168
  this.pendingFrames.push({ timeUnixNano: this.root.nowUnixNano(), body });
161
169
  };
162
170
  /** The pinned key was refused for quota at setup; the session reopens on the fallback slot, which its responses name. */
163
171
  keyFallback(row) {
164
- const to = String(row.keySlot);
165
172
  this.root.event('theorem.session', {
166
173
  kind: 'key_fallback',
167
174
  key_slot: String(row.from),
168
- to_key_slot: to,
175
+ to_key_slot: String(row.keySlot),
169
176
  'error.type': String(row.errorKind),
170
177
  error: String(row.error),
171
178
  });
172
179
  const bound = this.bound;
173
- if (bound)
174
- this.bound = { ...bound, request: { ...bound.request, keySlot: to } };
180
+ if (bound?.binding)
181
+ this.bound = { ...bound, binding: { ...bound.binding, keySlot: String(row.keySlot) } };
175
182
  }
176
183
  setup(frame) {
177
184
  this.root.event('theorem.upstream.row', { row: traceJson(frame) });
@@ -195,7 +202,7 @@ class LiveTrace {
195
202
  return;
196
203
  case 'closed':
197
204
  this.socketClosed(item.code, item.reason, 'provider', item);
198
- // A close the provider warned of ends the session; it is not the session's failure.
205
+ // why: A close the provider warned of ends the session; it is not the session's failure.
199
206
  if (item.goAway)
200
207
  this.endedByGoAway = true;
201
208
  else
@@ -303,7 +310,7 @@ class LiveTrace {
303
310
  if (this.response) {
304
311
  await this.endResponse(thrown === undefined ? { cancelled: true } : { thrown });
305
312
  }
306
- // Sent after the last response: no response read it.
313
+ // why: Sent after the last response: no response read it.
307
314
  for (const frame of this.pendingFrames) {
308
315
  this.root.event('theorem.wire.request', { body: traceJson(frame.body) }, frame.timeUnixNano);
309
316
  }
@@ -319,7 +326,7 @@ class LiveTrace {
319
326
  endThrownSpan(this.root, thrown);
320
327
  }
321
328
  else {
322
- // A warned close is no verdict: neither failed nor known to be fine.
329
+ // why: A warned close is no verdict: neither failed nor known to be fine.
323
330
  this.root.end(aborted || this.endedByGoAway ? { code: 'UNSET' } : { code: 'OK' });
324
331
  }
325
332
  this.write(this.tree.collect());
@@ -340,7 +347,7 @@ class LiveTrace {
340
347
  }
341
348
  const kind = event.type === 'evidence' ? event.evidence.kind : undefined;
342
349
  if (kind === 'input_transcription' && !(this.response && !this.response.answering)) {
343
- // Heard after the model began answering: input for the next response.
350
+ // why: Heard after the model began answering: input for the next response.
344
351
  this.pendingHeard.push(event);
345
352
  return;
346
353
  }
@@ -352,7 +359,7 @@ class LiveTrace {
352
359
  observeCallEvent(response.usage, event);
353
360
  response.call.observe(event);
354
361
  if (!response.answering && (OUTPUT_EVENTS.has(event.type) || kind === 'output_transcription')) {
355
- // The reply's first chunk, from the response's first input frame: what a person waited.
362
+ // why: The reply's first chunk, from the response's first input frame: what a person waited.
356
363
  response.call.span.set({
357
364
  'gen_ai.response.time_to_first_chunk': response.call.span.msSinceStart() / MS_PER_S,
358
365
  });
@@ -361,7 +368,9 @@ class LiveTrace {
361
368
  if (event.type === 'tool' && event.tool.phase === undefined) {
362
369
  this.callParents.set(event.tool.callId, response.call.span.traceparent());
363
370
  }
364
- if (event.type === 'done' && response.stop?.kind !== 'interrupted') {
371
+ if (event.type === 'done' &&
372
+ response.stop?.kind !== 'interrupted' &&
373
+ !(event.stop.kind === 'completed' && response.stop?.kind === 'generation_complete')) {
365
374
  response.stop = event.stop;
366
375
  }
367
376
  }
@@ -374,17 +383,17 @@ class LiveTrace {
374
383
  return this.response;
375
384
  const bound = this.bound;
376
385
  if (!bound) {
377
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
386
+ // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
378
387
  throw new TheoremError('internal', 'Live trace received a response before it was bound');
379
388
  }
380
389
  const usage = startCallUsage(bound.system, { history: [], input: [] }, this.held);
381
- // A response starts at its first input frame, so its duration includes the model's listening time.
390
+ // why: A response starts at its first input frame, so its duration includes the model's listening time.
382
391
  const startTimeUnixNano = this.pendingFrames[0]?.timeUnixNano;
383
392
  let tree;
384
393
  const call = startCallTrace((name, options) => {
385
394
  tree = startTrace(name, {
386
395
  ...options,
387
- // Each record stands alone (the session record may never be written): it names its agent.
396
+ // why: Each record stands alone (the session record may never be written): it names its agent.
388
397
  attributes: { ...this.identity, ...options.attributes },
389
398
  traceparent: this.root.traceparent(),
390
399
  clock: this.tree.clock,
@@ -399,10 +408,10 @@ class LiveTrace {
399
408
  step: this.responses + 1,
400
409
  });
401
410
  if (!tree) {
402
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
411
+ // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
403
412
  throw new TheoremError('internal', 'Live response span was not opened');
404
413
  }
405
- // A Live response streams over the session socket; no HTTP try says so.
414
+ // why: A Live response streams over the session socket; no HTTP try says so.
406
415
  call.span.set({ 'gen_ai.request.stream': true });
407
416
  for (const frame of this.pendingFrames) {
408
417
  call.span.event('theorem.wire.request', { body: traceJson(frame.body) }, frame.timeUnixNano);
@@ -437,12 +446,10 @@ class LiveTrace {
437
446
  this.responses += 1;
438
447
  response.usage.conversation = { history: response.input.messages(), input: [] };
439
448
  const answered = end.thrown === undefined && !end.cancelled;
440
- const tokens = answered
441
- ? await callTokensEvent(response.usage, bound.generation, bound.family)
442
- : undefined;
449
+ const tokens = answered ? await callTokensEvent(response.usage, bound.generation) : undefined;
443
450
  if (answered)
444
- this.held = await heldAfter(response.usage, bound.family);
445
- // Beside `output.messages` (what the model produced): what the host received of it.
451
+ this.held = await heldAfter(response.usage);
452
+ // why: Beside `output.messages` (what the model produced): what the host received of it.
446
453
  response.call.span.set({
447
454
  'theorem.output.delivered': [{ role: 'assistant', parts: response.delivered.parts }],
448
455
  });
@@ -462,7 +469,7 @@ class LiveTrace {
462
469
  const record = buildRecord({
463
470
  spans,
464
471
  policy: this.out.policy,
465
- canaries: this.bound ? [this.bound.canary] : [],
472
+ canaries: [...(this.bound ? [this.bound.canary] : []), ...this.agentCanaries],
466
473
  ...(this.out.metadata ? { metadata: this.out.metadata } : {}),
467
474
  });
468
475
  this.writes = this.writes.then(() => writeTrace(this.out.sink, record, this.out.policy));
@@ -470,10 +477,14 @@ class LiveTrace {
470
477
  }
471
478
  /**
472
479
  * Open a session's trace: its `invoke_agent` span under the host's `traceparent`,
473
- * written under `observability`, the session's profile's block.
480
+ * written under the session's profile's `observability` and cleaned with its detectors.
474
481
  */
475
- function startLiveTrace(req, observability, sinkOverride) {
476
- const { sink, policy } = resolveTraceWriter({ override: sinkOverride, observability });
482
+ function startLiveTrace(req, profile, sinkOverride) {
483
+ const { sink, policy } = resolveTraceWriter({
484
+ override: sinkOverride,
485
+ observability: profile?.observability,
486
+ guardrails: profile?.guardrails,
487
+ });
477
488
  const identity = {
478
489
  'gen_ai.agent.name': req.profile,
479
490
  ...optional('gen_ai.conversation.id', req.conversationId),
@@ -1,77 +1,23 @@
1
1
  /**
2
- * o200k is not every model's tokenizer, so text counts are an estimate for all families. Media is
3
- * counted only by a rule the family's billed usage was measured to follow; a family without one
4
- * reports media as `unknownMedia`, never a borrowed rate.
5
- *
6
- * Gemini 3 (`gemini-3*` flash / pro text models, direct or via OpenRouter).
7
- * Measured 22/09/2026 against the usage Gemini bills — Interactions `usage`
8
- * and `generateContent` `usageMetadata` agree exactly — on gemini-3.8-flash
9
- * at the default media resolution (THEOREM never sets `media_resolution`).
10
- * `countTokens` is not the oracle: it reports 560 per PDF page and 32 tokens
11
- * per audio second, and neither is what is billed.
12
- * - image: a patch grid inside a 1120-token budget that keeps the aspect
13
- * ratio — `⌊√(1120·w/h)⌋ × ⌊√(1120·h/w)⌋` (1024×1024 → 1089, 1920×1080 →
14
- * 1100, 4032×3024 HEIC → 1064). Needs the pixel size from the image header.
15
- * - audio: `⌈seconds × 25⌉`, seconds being decoded samples ÷ rate
16
- * (`audioSeconds`): 2.2 s → 56, 0.5 s → 13. Sample rate, channels and
17
- * silence do not matter. Raw PCM: bare `audio/pcm` is 16 kHz mono 16-bit;
18
- * `audio/L16` needs both `rate` and `channels`. `audio/pcm` with parameters,
19
- * `audio/alaw`, and `audio/mulaw` are refused by Interactions — unknown.
20
- * Mono `audio/L16` is unknown: Gemini converts it first and adds text tokens
21
- * no rule reproduces (about 31–33, plus 2 per second, plus 1 audio token
22
- * away from 16 kHz).
23
- * - video: `frames × ⌊√(70·w/h)⌋ × ⌊√(70·h/w)⌋ + ⌈min(audio, frames) × 25⌉`,
24
- * `frames` = the video track's seconds rounded half up (66 per frame at
25
- * 16:9, 63 at 4:3; fps, codec and container do not matter). Under half a
26
- * second rounds to no frames, which Gemini refuses — unknown. MP4 / MOV /
27
- * 3GP and WebM only (`videoInfo`); other containers are unknown.
28
- * - PDF: 520 tokens per page. Text on the page adds nothing.
29
- * - text documents (plain, Markdown, CSV, JSON, HTML, CSS, XML, RTF,
30
- * JavaScript, Python) as their UTF-8 text. `text/md` and
31
- * `application/x-python` are unknown: Gemini converts them first, adding
32
- * about 30 text tokens no rule reproduces.
33
- * - Provider file references (`uri`): unknown — the bytes are not here.
34
- *
35
- * Known gap: ADTS AAC. Gemini estimates its length from the bitrate; the
36
- * decoded length here runs 2–3 tokens over at 10 s.
37
- *
38
- * Not yet checked against billed usage: video in WebM / MOV / 3GP, anamorphic
39
- * video, and Gemini 3 models other than gemini-3.8-flash. The full probe and
40
- * open decisions: https://github.com/masudl-hub/theoremai/issues/18
2
+ * Text counts use a characters / 4 heuristic for all model families. Media is never estimated: a
3
+ * media part is reported as `unknownMedia`, and the upstream's own count is used when it gives one.
41
4
  */
42
- import { type Provider } from '../schema.js';
43
5
  import type { InteractionPart, TurnHistoryMessage } from '../types.js';
44
- export declare const TOKEN_TEXT_ENCODING = "o200k_base";
45
- /** Model families with a verified media rule. */
46
- export type MediaTokenFamily = 'gemini-3';
47
- export type MediaPayload = {
48
- mimeType: string;
49
- data: string;
50
- } | {
51
- mimeType: string;
52
- uri: string;
53
- };
6
+ /** Text estimation method; retained under this name for API compatibility. */
7
+ export declare const TOKEN_TEXT_ENCODING = "chars/4";
8
+ /** A token count and the number of media parts left out of it. */
54
9
  export interface TokenCount {
55
10
  tokens: number;
56
11
  /** Media parts left out of `tokens` because their count is unknown. */
57
12
  unknownMedia: number;
58
13
  }
59
- /** Loaded estimator. Text counting is synchronous; anything with media is not. */
14
+ /** Text counting is synchronous; anything with media is not. */
60
15
  export interface TokenEstimator {
61
- /** o200k token count of `text`. */
16
+ /** Approximate tokens: UTF-16 code units divided by four, rounded up. */
62
17
  text: (text: string) => number;
63
- /** Token count of one media payload, or `undefined` when unknown. */
64
- media: (payload: MediaPayload, family: MediaTokenFamily | undefined) => Promise<number | undefined>;
65
- parts: (parts: InteractionPart[], family: MediaTokenFamily | undefined) => Promise<TokenCount>;
18
+ parts: (parts: InteractionPart[]) => TokenCount;
66
19
  /** Content, parts, and tool-call arguments across history messages. */
67
- messages: (messages: TurnHistoryMessage[], family: MediaTokenFamily | undefined) => Promise<TokenCount>;
20
+ messages: (messages: TurnHistoryMessage[]) => TokenCount;
68
21
  }
69
- /**
70
- * Media family for a model binding — `undefined` when no verified rule covers
71
- * it. OpenRouter uses the routed model's family (`google/gemini-3…`).
72
- */
73
- export declare function mediaTokenFamily(binding: {
74
- provider: Provider;
75
- apiId: string;
76
- }): MediaTokenFamily | undefined;
22
+ /** Returns the heuristic estimator; async for compatibility with existing callers. */
77
23
  export declare function loadTokenEstimator(): Promise<TokenEstimator>;
@@ -1,206 +1,27 @@
1
1
  /**
2
- * o200k is not every model's tokenizer, so text counts are an estimate for all families. Media is
3
- * counted only by a rule the family's billed usage was measured to follow; a family without one
4
- * reports media as `unknownMedia`, never a borrowed rate.
5
- *
6
- * Gemini 3 (`gemini-3*` flash / pro text models, direct or via OpenRouter).
7
- * Measured 22/09/2026 against the usage Gemini bills — Interactions `usage`
8
- * and `generateContent` `usageMetadata` agree exactly — on gemini-3.8-flash
9
- * at the default media resolution (THEOREM never sets `media_resolution`).
10
- * `countTokens` is not the oracle: it reports 560 per PDF page and 32 tokens
11
- * per audio second, and neither is what is billed.
12
- * - image: a patch grid inside a 1120-token budget that keeps the aspect
13
- * ratio — `⌊√(1120·w/h)⌋ × ⌊√(1120·h/w)⌋` (1024×1024 → 1089, 1920×1080 →
14
- * 1100, 4032×3024 HEIC → 1064). Needs the pixel size from the image header.
15
- * - audio: `⌈seconds × 25⌉`, seconds being decoded samples ÷ rate
16
- * (`audioSeconds`): 2.2 s → 56, 0.5 s → 13. Sample rate, channels and
17
- * silence do not matter. Raw PCM: bare `audio/pcm` is 16 kHz mono 16-bit;
18
- * `audio/L16` needs both `rate` and `channels`. `audio/pcm` with parameters,
19
- * `audio/alaw`, and `audio/mulaw` are refused by Interactions — unknown.
20
- * Mono `audio/L16` is unknown: Gemini converts it first and adds text tokens
21
- * no rule reproduces (about 31–33, plus 2 per second, plus 1 audio token
22
- * away from 16 kHz).
23
- * - video: `frames × ⌊√(70·w/h)⌋ × ⌊√(70·h/w)⌋ + ⌈min(audio, frames) × 25⌉`,
24
- * `frames` = the video track's seconds rounded half up (66 per frame at
25
- * 16:9, 63 at 4:3; fps, codec and container do not matter). Under half a
26
- * second rounds to no frames, which Gemini refuses — unknown. MP4 / MOV /
27
- * 3GP and WebM only (`videoInfo`); other containers are unknown.
28
- * - PDF: 520 tokens per page. Text on the page adds nothing.
29
- * - text documents (plain, Markdown, CSV, JSON, HTML, CSS, XML, RTF,
30
- * JavaScript, Python) as their UTF-8 text. `text/md` and
31
- * `application/x-python` are unknown: Gemini converts them first, adding
32
- * about 30 text tokens no rule reproduces.
33
- * - Provider file references (`uri`): unknown — the bytes are not here.
34
- *
35
- * Known gap: ADTS AAC. Gemini estimates its length from the bitrate; the
36
- * decoded length here runs 2–3 tokens over at 10 s.
37
- *
38
- * Not yet checked against billed usage: video in WebM / MOV / 3GP, anamorphic
39
- * video, and Gemini 3 models other than gemini-3.8-flash. The full probe and
40
- * open decisions: https://github.com/masudl-hub/theoremai/issues/18
2
+ * Text counts use a characters / 4 heuristic for all model families. Media is never estimated: a
3
+ * media part is reported as `unknownMedia`, and the upstream's own count is used when it gives one.
41
4
  */
42
5
  import { historyMessageParts } from '../interaction-parts.js';
43
- import { MEDIA_INPUT_KINDS } from '../schema.js';
44
- import { base64ToBytes } from '../util/base64.js';
45
- import { mimeEssence } from '../util/mime.js';
46
- import { audioSeconds } from './media-probe/audio.js';
47
- import { imageSize } from './media-probe/image.js';
48
- import { pdfPageCount } from './media-probe/pdf.js';
49
- import { videoInfo } from './media-probe/video.js';
50
- export const TOKEN_TEXT_ENCODING = 'o200k_base';
51
- const GEMINI_3_TEXT_MODEL = /^gemini-3(?:\.\d+)?-(?:flash|pro)(?:-lite)?(?:-preview)?$/;
52
- const OPENROUTER_GOOGLE_PREFIX = 'google/';
53
- const GEMINI_3_IMAGE_BUDGET = 1120;
54
- const GEMINI_3_VIDEO_FRAME_BUDGET = 70;
55
- const GEMINI_3_AUDIO_PER_SECOND = 25;
56
- const GEMINI_3_PDF_PER_PAGE = 520;
57
- /** Bare `audio/pcm`: 16-bit mono at 16 kHz. */
58
- const PCM_DEFAULT_RATE = 16_000;
59
- const PCM_BYTES_PER_SAMPLE = 2;
60
- /** Document types Gemini reads as their own text. */
61
- const GEMINI_3_TEXT_DOCUMENTS = new Set([
62
- 'text/plain',
63
- 'text/csv',
64
- 'text/markdown',
65
- 'text/html',
66
- 'text/css',
67
- 'text/xml',
68
- 'text/rtf',
69
- 'text/javascript',
70
- 'application/x-javascript',
71
- 'text/x-python',
72
- 'application/json',
73
- ]);
74
- /**
75
- * Media family for a model binding — `undefined` when no verified rule covers
76
- * it. OpenRouter uses the routed model's family (`google/gemini-3…`).
77
- */
78
- export function mediaTokenFamily(binding) {
79
- const modelId = binding.provider === 'google'
80
- ? binding.apiId
81
- : binding.provider === 'openrouter' && binding.apiId.startsWith(OPENROUTER_GOOGLE_PREFIX)
82
- ? binding.apiId.slice(OPENROUTER_GOOGLE_PREFIX.length)
83
- : undefined;
84
- if (modelId === undefined)
85
- return undefined;
86
- return GEMINI_3_TEXT_MODEL.test(modelId) ? 'gemini-3' : undefined;
87
- }
88
- function mimeParam(mimeType, name) {
89
- for (const piece of mimeType.split(';').slice(1)) {
90
- const [key, value] = piece.split('=').map((s) => s.trim().toLowerCase());
91
- if (key === name) {
92
- const n = Number(value);
93
- return Number.isInteger(n) && n > 0 ? n : undefined;
94
- }
95
- }
96
- return undefined;
97
- }
98
- function hasParams(mimeType) {
99
- return mimeType.includes(';');
100
- }
101
- /** Seconds of raw 16-bit PCM, or `undefined` when Gemini refuses how it is declared. */
102
- function pcmSeconds(mimeType, bytes) {
103
- const essence = mimeEssence(mimeType);
104
- let rate;
105
- let channels;
106
- if (essence === 'audio/pcm') {
107
- if (hasParams(mimeType))
108
- return undefined;
109
- rate = PCM_DEFAULT_RATE;
110
- channels = 1;
111
- }
112
- else {
113
- rate = mimeParam(mimeType, 'rate');
114
- channels = mimeParam(mimeType, 'channels');
115
- // Mono L16 is converted before it is counted.
116
- if (channels === 1)
117
- return undefined;
118
- }
119
- if (!rate || !channels)
120
- return undefined;
121
- return Math.floor(bytes.length / (PCM_BYTES_PER_SAMPLE * channels)) / rate;
122
- }
123
- function gemini3Audio(mimeType, bytes) {
124
- const essence = mimeEssence(mimeType);
125
- const seconds = essence === 'audio/pcm' || essence === 'audio/l16'
126
- ? pcmSeconds(mimeType, bytes)
127
- : essence === 'audio/alaw' || essence === 'audio/mulaw'
128
- ? undefined
129
- : audioSeconds(bytes);
130
- return seconds === undefined ? undefined : Math.ceil(seconds * GEMINI_3_AUDIO_PER_SECOND);
131
- }
132
- function patchGrid(budget, w, h) {
133
- return Math.floor(Math.sqrt((budget * w) / h)) * Math.floor(Math.sqrt((budget * h) / w));
134
- }
135
- function gemini3Video(bytes) {
136
- const info = videoInfo(bytes);
137
- if (!info || info.audioSeconds === undefined)
138
- return undefined;
139
- const frames = Math.floor(info.seconds + 0.5);
140
- if (frames === 0)
141
- return undefined;
142
- return (frames * patchGrid(GEMINI_3_VIDEO_FRAME_BUDGET, info.width, info.height) +
143
- Math.ceil(Math.min(info.audioSeconds, frames) * GEMINI_3_AUDIO_PER_SECOND));
144
- }
145
- function utf8(bytes) {
146
- try {
147
- return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
148
- }
149
- catch {
150
- // Not UTF-8: what Gemini reads from it is unknown.
151
- return undefined;
152
- }
153
- }
154
- async function gemini3Media(payload, text) {
155
- if (!('data' in payload))
156
- return undefined;
157
- const essence = mimeEssence(payload.mimeType);
158
- const bytes = base64ToBytes(payload.data);
159
- switch (MEDIA_INPUT_KINDS[essence]) {
160
- case 'image': {
161
- const size = imageSize(bytes);
162
- return size ? patchGrid(GEMINI_3_IMAGE_BUDGET, size.width, size.height) : undefined;
163
- }
164
- case 'audio':
165
- return gemini3Audio(payload.mimeType, bytes);
166
- case 'video':
167
- return gemini3Video(bytes);
168
- case 'document': {
169
- if (essence === 'application/pdf') {
170
- const pages = await pdfPageCount(bytes);
171
- return pages === undefined ? undefined : pages * GEMINI_3_PDF_PER_PAGE;
172
- }
173
- if (!GEMINI_3_TEXT_DOCUMENTS.has(essence))
174
- return undefined;
175
- const decoded = utf8(bytes);
176
- return decoded === undefined ? undefined : text(decoded);
177
- }
178
- }
179
- return undefined;
180
- }
181
- let encodePromise = null;
182
- function buildEstimator(encode) {
183
- const text = (value) => (value ? encode(value).length : 0);
184
- const media = async (payload, family) => family === 'gemini-3' ? await gemini3Media(payload, text) : undefined;
185
- const parts = async (list, family) => {
6
+ /** Text estimation method; retained under this name for API compatibility. */
7
+ export const TOKEN_TEXT_ENCODING = 'chars/4';
8
+ function buildEstimator() {
9
+ const text = (value) => Math.ceil(value.length / 4);
10
+ const parts = (list) => {
186
11
  const count = { tokens: 0, unknownMedia: 0 };
187
12
  for (const part of list) {
188
13
  if (part.type === 'text') {
189
14
  count.tokens += text(part.text);
190
15
  continue;
191
16
  }
192
- const n = await media(part, family);
193
- if (n === undefined)
194
- count.unknownMedia += 1;
195
- else
196
- count.tokens += n;
17
+ count.unknownMedia += 1;
197
18
  }
198
19
  return count;
199
20
  };
200
- const messages = async (list, family) => {
21
+ const messages = (list) => {
201
22
  const count = { tokens: 0, unknownMedia: 0 };
202
23
  for (const msg of list) {
203
- const fromParts = await parts(historyMessageParts(msg), family);
24
+ const fromParts = parts(historyMessageParts(msg));
204
25
  count.tokens += fromParts.tokens;
205
26
  count.unknownMedia += fromParts.unknownMedia;
206
27
  for (const tc of msg.tool_calls ?? []) {
@@ -209,10 +30,9 @@ function buildEstimator(encode) {
209
30
  }
210
31
  return count;
211
32
  };
212
- return { text, media, parts, messages };
33
+ return { text, parts, messages };
213
34
  }
214
- // The ranks import lazily, so hosts that never estimate never pay for them.
215
- export async function loadTokenEstimator() {
216
- encodePromise ??= import('gpt-tokenizer/encoding/o200k_base').then((m) => m.encode);
217
- return buildEstimator(await encodePromise);
35
+ /** Returns the heuristic estimator; async for compatibility with existing callers. */
36
+ export function loadTokenEstimator() {
37
+ return Promise.resolve(buildEstimator());
218
38
  }
@@ -4,8 +4,8 @@ import type { ToolPermission } from '../schema.js';
4
4
  import type { ToolFailure } from '../tools/types.js';
5
5
  import type { InteractionPart, TurnEvent } from '../types.js';
6
6
  import { type GuardrailCheck, type SentToolCall } from './turn-trace.js';
7
- /** A check at the tool boundary: the call's arguments, the remote-content gate, the result, or the network target. */
8
- type ToolCheck = Extract<GuardrailCheck, 'tool_arguments' | 'taint' | 'tool_result' | 'tool_failure' | 'network' | 'network_request'>;
7
+ /** A check at the tool boundary: the call's arguments, the remote-content gate, its destinations, the result, or the network target. */
8
+ type ToolCheck = Extract<GuardrailCheck, 'tool_arguments' | 'taint' | 'destination' | 'tool_result' | 'tool_failure' | 'network' | 'network_request'>;
9
9
  /**
10
10
  * Records one tool-boundary check on the tool's span with the time it took:
11
11
  * its decision, or a pass (`allow`) when it let the call or result through.
@@ -49,7 +49,7 @@ interface ToolCallTrace {
49
49
  }
50
50
  declare function toolSpanName(name: string): string;
51
51
  /**
52
- * Spans from before `pre_tool` to settlement, so it covers the hooks, the gates and the body.
52
+ * Spans from before `pre_tool` to settlement, so it covers the hooks, the gates, and the body.
53
53
  * Provider-run tools (search, maps, code execution) get no span: Theorem did not run them.
54
54
  * `open` places it: a child of the turn's root, or the root of a host-invoked tool's own record.
55
55
  */
@@ -5,6 +5,7 @@ import { guardrailAttributes, guardrailCheckAttributes, optional, recordExceptio
5
5
  const TOOL_CHECK_STAGE = {
6
6
  tool_arguments: 'tool_call',
7
7
  taint: 'tool_call',
8
+ destination: 'tool_call',
8
9
  tool_result: 'tool_result',
9
10
  tool_failure: 'tool_result',
10
11
  network: 'network',
@@ -73,7 +74,7 @@ const OUTCOME_STATUS = {
73
74
  cancelled: 'UNSET',
74
75
  };
75
76
  /**
76
- * Spans from before `pre_tool` to settlement, so it covers the hooks, the gates and the body.
77
+ * Spans from before `pre_tool` to settlement, so it covers the hooks, the gates, and the body.
77
78
  * Provider-run tools (search, maps, code execution) get no span: Theorem did not run them.
78
79
  * `open` places it: a child of the turn's root, or the root of a host-invoked tool's own record.
79
80
  */