@theoremjs/agents 0.3.0 → 0.3.2

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 (570) hide show
  1. package/README.md +277 -995
  2. package/esm/mod.d.ts +34 -20
  3. package/esm/mod.js +19 -12
  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 +25 -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 +881 -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 +30 -22
  123. package/esm/src/guardrails/mod.js +19 -14
  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 +4 -1
  135. package/esm/src/guardrails/quota.js +7 -2
  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 +5 -2
  148. package/esm/src/guardrails/theorem-error.js +6 -3
  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 +8 -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 +174 -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 +329 -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 +130 -88
  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 +102 -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/api-error.d.ts +2 -0
  435. package/esm/src/providers/google/api-error.js +26 -18
  436. package/esm/src/providers/google/builtins.d.ts +14 -0
  437. package/esm/src/providers/google/builtins.js +57 -0
  438. package/esm/src/providers/google/grounding.d.ts +4 -4
  439. package/esm/src/providers/google/interactions/framing.d.ts +20 -8
  440. package/esm/src/providers/google/interactions/framing.js +85 -44
  441. package/esm/src/providers/google/interactions/steps.d.ts +5 -5
  442. package/esm/src/providers/google/interactions/steps.js +4 -4
  443. package/esm/src/providers/google/interactions/stream.d.ts +10 -11
  444. package/esm/src/providers/google/interactions/stream.js +33 -100
  445. package/esm/src/providers/google/live/framing.d.ts +15 -5
  446. package/esm/src/providers/google/live/framing.js +25 -25
  447. package/esm/src/providers/google/live/stream.d.ts +6 -44
  448. package/esm/src/providers/google/live/stream.js +4 -210
  449. package/esm/src/providers/google/urls.d.ts +1 -3
  450. package/esm/src/providers/google/urls.js +1 -3
  451. package/esm/src/providers/google/usage.js +2 -2
  452. package/esm/src/providers/live.d.ts +6 -0
  453. package/esm/src/providers/live.js +6 -0
  454. package/esm/src/providers/local/embedding-protocol.d.ts +7 -0
  455. package/esm/src/providers/local/embedding-protocol.js +60 -0
  456. package/esm/src/providers/local/provider.d.ts +7 -0
  457. package/esm/src/providers/local/provider.js +28 -0
  458. package/esm/src/providers/mod.d.ts +28 -7
  459. package/esm/src/providers/mod.js +19 -3
  460. package/esm/src/providers/open-ai/protocol.d.ts +3 -0
  461. package/esm/src/providers/open-ai/protocol.js +189 -0
  462. package/esm/src/providers/openrouter/embedding-protocol.d.ts +6 -0
  463. package/esm/src/providers/openrouter/embedding-protocol.js +86 -0
  464. package/esm/src/providers/openrouter/image.d.ts +0 -9
  465. package/esm/src/providers/openrouter/image.js +0 -153
  466. package/esm/src/providers/openrouter/images-protocol.d.ts +3 -0
  467. package/esm/src/providers/openrouter/images-protocol.js +82 -0
  468. package/esm/src/providers/openrouter/openai/compat.d.ts +2 -2
  469. package/esm/src/providers/openrouter/openai/compat.js +6 -6
  470. package/esm/src/providers/openrouter/openai/image-payload.d.ts +2 -2
  471. package/esm/src/providers/openrouter/openai/reasoning-state.d.ts +2 -0
  472. package/esm/src/providers/openrouter/openai/reasoning-state.js +31 -0
  473. package/esm/src/providers/openrouter/protocol.d.ts +8 -0
  474. package/esm/src/providers/openrouter/protocol.js +171 -0
  475. package/esm/src/providers/openrouter/provider.d.ts +10 -0
  476. package/esm/src/providers/openrouter/provider.js +50 -0
  477. package/esm/src/providers/shared/carried-tool-files.d.ts +7 -0
  478. package/esm/src/providers/shared/carried-tool-files.js +43 -0
  479. package/esm/src/providers/shared/pcm.js +2 -2
  480. package/esm/src/providers/shared/protocol-events.d.ts +8 -0
  481. package/esm/src/providers/shared/protocol-events.js +20 -0
  482. package/esm/src/providers/shared/response-identity.d.ts +2 -2
  483. package/esm/src/providers/shared/retry.d.ts +18 -0
  484. package/esm/src/providers/shared/retry.js +89 -0
  485. package/esm/src/providers/shared/sse.d.ts +0 -4
  486. package/esm/src/providers/shared/sse.js +50 -36
  487. package/esm/src/providers/shared/tool-args.d.ts +6 -7
  488. package/esm/src/providers/shared/tool-args.js +10 -6
  489. package/esm/src/providers/shared/tool-draft.d.ts +16 -0
  490. package/esm/src/providers/shared/tool-draft.js +14 -0
  491. package/esm/src/providers/shared/upstream-tap.d.ts +2 -2
  492. package/esm/src/providers/shared/upstream-tape.js +1 -1
  493. package/esm/src/providers/system-one/protocol.d.ts +8 -0
  494. package/esm/src/providers/system-one/protocol.js +93 -0
  495. package/esm/src/providers/types.d.ts +48 -16
  496. package/esm/src/providers/types.js +2 -1
  497. package/esm/src/providers/typesafe/provider.d.ts +7 -0
  498. package/esm/src/providers/typesafe/provider.js +24 -0
  499. package/esm/src/surface/formats.d.ts +19 -1
  500. package/esm/src/surface/formats.js +68 -29
  501. package/esm/src/surface/mod.d.ts +4 -2
  502. package/esm/src/surface/mod.js +4 -2
  503. package/esm/src/surface/runtime.d.ts +4 -0
  504. package/esm/src/surface/runtime.js +2 -0
  505. package/esm/src/surface/tools.d.ts +2 -8
  506. package/esm/src/surface/tools.js +4 -22
  507. package/esm/src/surface/types.d.ts +6 -0
  508. package/package.json +5 -13
  509. package/esm/src/cli/commands/egress-compile.d.ts +0 -17
  510. package/esm/src/cli/commands/egress-compile.js +0 -25
  511. package/esm/src/guardrails/egress-policy.d.ts +0 -27
  512. package/esm/src/guardrails/egress-policy.js +0 -76
  513. package/esm/src/guardrails/egress-rules.d.ts +0 -54
  514. package/esm/src/guardrails/egress-rules.js +0 -49
  515. package/esm/src/guardrails/lexicon.d.ts +0 -30
  516. package/esm/src/guardrails/lexicon.js +0 -478
  517. package/esm/src/kernel/engine/media-probe/audio.d.ts +0 -7
  518. package/esm/src/kernel/engine/media-probe/audio.js +0 -342
  519. package/esm/src/kernel/engine/media-probe/bytes.d.ts +0 -6
  520. package/esm/src/kernel/engine/media-probe/bytes.js +0 -26
  521. package/esm/src/kernel/engine/media-probe/image.d.ts +0 -5
  522. package/esm/src/kernel/engine/media-probe/image.js +0 -171
  523. package/esm/src/kernel/engine/media-probe/iso-bmff.d.ts +0 -19
  524. package/esm/src/kernel/engine/media-probe/iso-bmff.js +0 -48
  525. package/esm/src/kernel/engine/media-probe/matroska.d.ts +0 -35
  526. package/esm/src/kernel/engine/media-probe/matroska.js +0 -226
  527. package/esm/src/kernel/engine/media-probe/mp4.d.ts +0 -22
  528. package/esm/src/kernel/engine/media-probe/mp4.js +0 -279
  529. package/esm/src/kernel/engine/media-probe/opus.d.ts +0 -4
  530. package/esm/src/kernel/engine/media-probe/opus.js +0 -23
  531. package/esm/src/kernel/engine/media-probe/pdf.d.ts +0 -5
  532. package/esm/src/kernel/engine/media-probe/pdf.js +0 -117
  533. package/esm/src/kernel/engine/media-probe/video.d.ts +0 -15
  534. package/esm/src/kernel/engine/media-probe/video.js +0 -49
  535. package/esm/src/presets/typesafe.d.ts +0 -2
  536. package/esm/src/presets/typesafe.js +0 -2
  537. package/esm/src/providers/create-provider.d.ts +0 -21
  538. package/esm/src/providers/create-provider.js +0 -90
  539. package/esm/src/providers/decision/usage.d.ts +0 -3
  540. package/esm/src/providers/decision/usage.js +0 -25
  541. package/esm/src/providers/google/interactions/mod.d.ts +0 -2
  542. package/esm/src/providers/google/interactions/mod.js +0 -2
  543. package/esm/src/providers/google/keys.d.ts +0 -20
  544. package/esm/src/providers/google/keys.js +0 -87
  545. package/esm/src/providers/google/live/mod.d.ts +0 -7
  546. package/esm/src/providers/google/live/mod.js +0 -7
  547. package/esm/src/providers/google/live/openapi-schema.d.ts +0 -1
  548. package/esm/src/providers/google/live/openapi-schema.js +0 -42
  549. package/esm/src/providers/google/live/session.d.ts +0 -14
  550. package/esm/src/providers/google/live/session.js +0 -150
  551. package/esm/src/providers/local/local.d.ts +0 -12
  552. package/esm/src/providers/local/local.js +0 -186
  553. package/esm/src/providers/local/mod.d.ts +0 -8
  554. package/esm/src/providers/local/mod.js +0 -8
  555. package/esm/src/providers/openrouter/cache-control.d.ts +0 -5
  556. package/esm/src/providers/openrouter/cache-control.js +0 -4
  557. package/esm/src/providers/openrouter/chat.d.ts +0 -64
  558. package/esm/src/providers/openrouter/chat.js +0 -490
  559. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +0 -10
  560. package/esm/src/providers/openrouter/openai/sdk-messages.js +0 -99
  561. package/esm/src/providers/openrouter/resolve-api-key.d.ts +0 -3
  562. package/esm/src/providers/openrouter/resolve-api-key.js +0 -8
  563. package/esm/src/providers/openrouter/speech.d.ts +0 -18
  564. package/esm/src/providers/openrouter/speech.js +0 -109
  565. package/esm/src/providers/probe.d.ts +0 -5
  566. package/esm/src/providers/probe.js +0 -15
  567. package/esm/src/providers/shared/builtin-wire.d.ts +0 -4
  568. package/esm/src/providers/shared/builtin-wire.js +0 -9
  569. package/esm/src/providers/shared/vault.d.ts +0 -13
  570. package/esm/src/providers/shared/vault.js +0 -41
@@ -1,38 +1,55 @@
1
- import { egressChecksProblem } from '../../guardrails/egress.js';
2
- import { streamPlanOf } from '../../guardrails/egress-stream.js';
1
+ import { TOOL_BOUNDARIES } from '../../guardrails/boundaries.js';
2
+ import { detectProblem } from '../../guardrails/detectors.js';
3
3
  import { TheoremError } from '../../guardrails/error.js';
4
- import { validateLexiconOverrides } from '../../guardrails/lexicon.js';
5
- import { SENSITIVE_GROUPS } from '../../guardrails/sensitive.js';
4
+ import { labelText, validateLabelOverrides } from '../../guardrails/labels.js';
5
+ import { BLOCKED_REPLY_ON_BLOCK, } from '../../guardrails/types.js';
6
6
  import { resolveObservabilityPolicy } from '../../observability/resolve-policy.js';
7
+ import { providerContinuationSchema } from '../checkpoint.js';
7
8
  import { assertLiveIngressConfigured } from '../engine/live-ingress.js';
8
- import { outOfScopeFields } from '../profile-scope.js';
9
- import { CACHE_MODES, CACHE_TTLS, IMAGE_ATTACHMENT_ACCEPT_MIMES, isKeySlotName, isValidPair, isValidProfileProtocol, PROFILE_FIELDS, PROFILE_TYPES, protocolsForProfileType, THINKING_LEVELS, } from '../schema.js';
9
+ import { itemSchemaAt, schemaReaches } from '../engine/runner/schema-validation.js';
10
+ import { outOfScopeFields, profileKeysOf, profileTypesForField } from '../profile-scope.js';
11
+ import { checkBinding, decisionModelBindingSchema, modelBindingSchema, } from '../protocols/binding.js';
12
+ import { CONTEXT_SENDERS, IMAGE_ATTACHMENT_ACCEPT_MIMES, PROFILE_FIELDS, PROFILE_HANDLE_MAX_CHARS, PROFILE_ID_MAX_CHARS, PROFILE_TYPES, THINKING_LEVELS, } from '../schema.js';
10
13
  import { isContinueStopKind } from '../stop.js';
11
14
  import { assertSystemPrompt } from '../system-parts.js';
12
15
  import { profileToolAllow, profileToolsSpec } from '../tools/resolve.js';
13
16
  import { isTurnMediaRef } from './attachments.js';
14
17
  import { mediaKindForMime, mimeAllowed, profileInputs } from './catalog.js';
15
18
  import { soleModelId } from './sole-model.js';
16
- function assertModelRoute(profileId, modelId, binding, type) {
17
- if (type === 'decision' && !binding.apiId.trim()) {
18
- throw new TheoremError('config', `Profile ${profileId} model '${modelId}' must set apiId`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
19
- }
20
- if (type === 'decision' && !isValidProfileProtocol(type, binding.protocol)) {
21
- throw new TheoremError('config',
22
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
23
- `Profile ${profileId} model '${modelId}': type 'decision' cannot use protocol '${binding.protocol}'. Supported: decision`);
24
- }
25
- if (!isValidPair(binding.protocol, binding.provider)) {
26
- throw new TheoremError('config',
27
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
28
- `Profile ${profileId} model '${modelId}': protocol '${binding.protocol}' is not valid for provider '${binding.provider}'`);
29
- }
19
+ function assertModelRoute(_profileId, _modelId, binding, _type) {
20
+ if (!binding.provider?.trim() || !binding.apiId?.trim())
21
+ throw new TheoremError('config', labelText('provider.binding_identity'));
22
+ }
23
+ function defineEmbeddingProfile(input) {
24
+ assertModelsNonEmpty(input.id, input.models);
25
+ assertObservability(input.id, input.observability);
26
+ assertMediaLimits(input.id, input.inputs);
27
+ for (const [modelId, binding] of Object.entries(input.models))
28
+ assertModelBinding(input.id, modelId, binding);
29
+ return {
30
+ type: 'embedding',
31
+ id: input.id,
32
+ identity: { handle: input.identity.handle },
33
+ models: input.models,
34
+ defaultModel: resolveDefaultModel(input.id, input),
35
+ allowModelSelect: input.allowModelSelect,
36
+ inputs: input.inputs,
37
+ guardrails: input.guardrails,
38
+ observability: input.observability,
39
+ labels: input.labels,
40
+ };
41
+ }
42
+ function assertBindingData(profileId, modelId, binding, schema) {
43
+ const parsed = schema.safeParse(binding);
44
+ if (!parsed.success)
45
+ throw new TheoremError('config', `Profile ${profileId} model '${modelId}': ${parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join('; ')}`);
30
46
  }
31
47
  function validateDecisionBinding(profileId, modelId, binding) {
48
+ assertBindingData(profileId, modelId, binding, decisionModelBindingSchema);
32
49
  assertModelRoute(profileId, modelId, binding, 'decision');
33
50
  if ('retry' in binding) {
34
51
  throw new TheoremError('config',
35
- // lexicon-exempt: developer contract / internal diagnostic
52
+ // labels-exempt: developer contract / internal diagnostic
36
53
  `Profile ${profileId} model '${modelId}': decision retry configuration is unsupported; POSTs are never retried`);
37
54
  }
38
55
  if (binding.timeoutMs !== undefined &&
@@ -49,53 +66,49 @@ function validateDecisionModel(input) {
49
66
  }
50
67
  function validateDecisionConfig(input) {
51
68
  if (input.inputs.state !== 'json') {
52
- throw new TheoremError('config', `Profile ${input.id}: decision inputs.state must be 'json'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
69
+ throw new TheoremError('config', `Profile ${input.id}: decision inputs.state must be 'json'`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
53
70
  }
54
71
  const cap = input.inputs.maxStateBytes;
55
72
  if (cap !== undefined && !(Number.isInteger(cap) && cap > 0)) {
56
73
  throw new TheoremError('config', `Profile ${input.id}: decision inputs.maxStateBytes must be a positive integer`);
57
74
  }
58
75
  if (!input.decision.contract.trim()) {
59
- throw new TheoremError('config', `Profile ${input.id}: decision.contract must be non-empty`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
76
+ throw new TheoremError('config', `Profile ${input.id}: decision.contract must be non-empty`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
60
77
  }
61
78
  }
62
79
  function defineDecisionProfile(input) {
63
80
  validateDecisionModel(input);
64
81
  validateDecisionConfig(input);
65
- assertSlotName(input.id, 'key', input.key);
66
- for (const [modelId, binding] of Object.entries(input.models)) {
67
- assertSlotName(input.id, `models.${modelId}.key`, binding.key);
68
- if (!binding.key && !input.key) {
69
- throw new TheoremError('config', `Profile ${input.id} model '${modelId}': a decision model needs models.*.key or the profile key`);
70
- }
71
- }
72
82
  assertObservability(input.id, input.observability);
73
83
  return { ...input, identity: { handle: input.identity.handle } };
74
84
  }
85
+ /** `tools.parallel` carried over only when the definition sets it. */
86
+ function optionalParallel(parallel) {
87
+ return parallel === undefined ? {} : { parallel };
88
+ }
75
89
  function defineHostProfile(input) {
76
90
  assertHostTools(input.id, input.tools);
77
91
  assertObservability(input.id, input.observability);
78
92
  return {
79
93
  type: 'host',
80
94
  id: input.id,
81
- tools: { allow: input.tools.allow },
95
+ tools: { allow: input.tools.allow, ...optionalParallel(input.tools.parallel) },
82
96
  guardrails: input.guardrails,
83
97
  observability: input.observability,
84
- lexicon: input.lexicon,
98
+ labels: input.labels,
85
99
  };
86
100
  }
87
101
  function assertHostTools(profileId, tools) {
88
102
  if (!Array.isArray(tools.allow)) {
89
- throw new TheoremError('config', `Profile ${profileId}: type 'host' must set tools.allow`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
103
+ throw new TheoremError('config', `Profile ${profileId}: type 'host' must set tools.allow`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
90
104
  }
91
105
  }
92
106
  function assertModelsNonEmpty(profileId, models) {
93
107
  if (Object.keys(models).length === 0) {
94
- throw new TheoremError('config', `Profile ${profileId} must declare at least one model`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
108
+ throw new TheoremError('config', `Profile ${profileId} must declare at least one model`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
95
109
  }
96
110
  }
97
111
  function resolveDefaultModel(profileId, input) {
98
- const ids = Object.keys(input.models);
99
112
  const inferred = input.defaultModel ?? soleModelId(input.models);
100
113
  if (!inferred) {
101
114
  throw new TheoremError('config', `Profile ${profileId} must set defaultModel when more than one model is declared`);
@@ -103,51 +116,12 @@ function resolveDefaultModel(profileId, input) {
103
116
  if (!input.models[inferred]) {
104
117
  throw new TheoremError('config', `Profile ${profileId} defaultModel '${inferred}' is not declared`);
105
118
  }
106
- if (input.allowModelSelect && ids.length < 2) {
107
- throw new TheoremError('config', `Profile ${profileId} allowModelSelect requires at least two models`);
108
- }
109
119
  return inferred;
110
120
  }
111
121
  function assertModelBinding(profileId, modelId, binding) {
112
- assertModelRoute(profileId, modelId, binding);
113
122
  assertModelEfforts(profileId, modelId, binding);
114
- if (binding.cache) {
115
- assertCacheSpec(profileId, modelId, binding, binding.cache);
116
- }
117
- assertInteractionsPersistence(profileId, modelId, binding);
118
- assertLocalServer(profileId, modelId, binding);
119
- }
120
- function assertSlotName(profileId, path, slot) {
121
- if (slot !== undefined && !isKeySlotName(slot)) {
122
- throw new TheoremError('config', `Profile ${profileId}: ${path} '${slot}' is not a key slot name; use letters, digits, '-' and '_', up to 32 characters`);
123
- }
124
- }
125
- /** A model's key and fallback, each its own or the profile's. A fallback is never implied. */
126
- function assertKeySlot(profileId, modelId, binding, profile) {
127
- assertSlotName(profileId, `models.${modelId}.key`, binding.key);
128
- assertSlotName(profileId, `models.${modelId}.fallbackKey`, binding.fallbackKey);
129
- const key = binding.key ?? profile.key;
130
- const fallback = binding.fallbackKey ?? profile.fallbackKey;
131
- if (binding.provider !== 'local' && !key) {
132
- throw new TheoremError('config', `Profile ${profileId} model '${modelId}': a ${binding.provider} model needs models.*.key or the profile key`);
133
- }
134
- if (fallback === undefined)
135
- return;
136
- if (fallback === key) {
137
- throw new TheoremError('config', `Profile ${profileId} model '${modelId}': fallbackKey '${fallback}' is the same slot as its key`);
138
- }
139
- }
140
- function assertLocalServer(profileId, modelId, binding) {
141
- if (binding.server === undefined) {
142
- return;
143
- }
144
- const tag = `Profile ${profileId} model '${modelId}'`;
145
- if (binding.provider !== 'local') {
146
- throw new TheoremError('config', `${tag}: server is only valid when provider is 'local'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
147
- }
148
- if (typeof binding.server !== 'string' || binding.server.trim() === '') {
149
- throw new TheoremError('config', `${tag}: server must be a non-empty string`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
150
- }
123
+ assertBindingData(profileId, modelId, binding, modelBindingSchema);
124
+ assertModelEfforts(profileId, modelId, binding);
151
125
  }
152
126
  function assertModelEfforts(profileId, modelId, binding) {
153
127
  const efforts = binding.efforts;
@@ -174,22 +148,15 @@ function assertModelEfforts(profileId, modelId, binding) {
174
148
  throw new TheoremError('config', `Profile ${profileId} model '${modelId}' allowEffortSelect requires at least two efforts`);
175
149
  }
176
150
  }
177
- function assertTypeProtocols(profile) {
178
- for (const [modelId, binding] of Object.entries(profile.models)) {
179
- if (!isValidProfileProtocol(profile.type, binding.protocol)) {
180
- const valid = protocolsForProfileType(profile.type).join(', ');
181
- throw new TheoremError('config', `Profile ${profile.id} model '${modelId}': type '${profile.type}' cannot use protocol '${binding.protocol}'. Supported: ${valid}`);
182
- }
183
- }
184
- }
185
151
  function profileModelFields(input) {
186
152
  return {
153
+ providerContinuation: input.providerContinuation
154
+ ? providerContinuationSchema.parse(input.providerContinuation)
155
+ : {},
187
156
  models: input.models,
188
157
  defaultModel: resolveDefaultModel(input.id, input),
189
158
  allowModelSelect: input.allowModelSelect,
190
159
  maxSteps: input.maxSteps,
191
- key: input.key,
192
- fallbackKey: input.fallbackKey,
193
160
  };
194
161
  }
195
162
  function assertContinueKindList(profileId, path, kinds) {
@@ -197,7 +164,7 @@ function assertContinueKindList(profileId, path, kinds) {
197
164
  return;
198
165
  for (const kind of kinds) {
199
166
  if (!isContinueStopKind(kind)) {
200
- throw new TheoremError('config', `Profile ${profileId}: turnBehaviour.resumption.${path} may only include ContinueStopKind ` + // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
167
+ throw new TheoremError('config', `Profile ${profileId}: turnBehaviour.resumption.${path} may only include ContinueStopKind ` + // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
201
168
  `(length | stream_incomplete | provider_error); got '${kind}'`);
202
169
  }
203
170
  }
@@ -227,20 +194,33 @@ const REQUIRED_PATHS = Object.entries(PROFILE_FIELDS)
227
194
  .filter(([, meta]) => meta.required === true)
228
195
  .map(([path, meta]) => [path, meta.profileTypes])
229
196
  .sort(([a], [b]) => a.split('.').length - b.split('.').length);
230
- /** A definition may come over the network (a playground draft), so its shape is checked before anything reads it. */
197
+ /** A definition may come over the network (a studio draft), so its shape is checked before anything reads it. */
231
198
  function assertProfileShape(input) {
232
199
  if (!isRecord(input)) {
233
- throw new TheoremError('config', 'Profile definition must be an object'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
200
+ throw new TheoremError('config', 'Profile definition must be an object'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
234
201
  }
235
202
  const id = typeof input.id === 'string' && input.id.trim() ? input.id : undefined;
236
203
  if (!id) {
237
- throw new TheoremError('config', 'Profile definition must set id'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
204
+ throw new TheoremError('config', 'Profile definition must set id'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
238
205
  }
239
206
  const type = PROFILE_TYPES.find((known) => known === input.type);
240
207
  if (!type) {
241
208
  throw new TheoremError('config', `Profile ${id}: type must be one of ${PROFILE_TYPES.join(', ')}`);
242
209
  }
243
210
  }
211
+ /** A profile's id and its handle each fit their limit. */
212
+ function assertNameLengths(input) {
213
+ const handle = input.identity?.handle;
214
+ const names = [
215
+ ['id', input.id, PROFILE_ID_MAX_CHARS],
216
+ ['identity.handle', typeof handle === 'string' ? handle : '', PROFILE_HANDLE_MAX_CHARS],
217
+ ];
218
+ for (const [path, name, max] of names) {
219
+ if (Array.from(name).length <= max)
220
+ continue;
221
+ throw new TheoremError('config', `Profile ${input.id.slice(0, PROFILE_ID_MAX_CHARS)}: ${path} must be at most ${max} characters`);
222
+ }
223
+ }
244
224
  /** A field under an optional parent the definition leaves out is not required. */
245
225
  function assertRequiredFields(input) {
246
226
  const { id, type } = input;
@@ -248,7 +228,7 @@ function assertRequiredFields(input) {
248
228
  for (const [path, profileTypes] of REQUIRED_PATHS) {
249
229
  if (profileTypes && !profileTypes.includes(type))
250
230
  continue;
251
- const keys = path.split('.');
231
+ const keys = profileKeysOf(path);
252
232
  const parents = valuesAt(root, keys.slice(0, -1));
253
233
  const last = keys[keys.length - 1];
254
234
  const missing = parents.some((parent) => {
@@ -258,10 +238,20 @@ function assertRequiredFields(input) {
258
238
  return value === undefined || value === null || value === '';
259
239
  });
260
240
  if (missing) {
261
- throw new TheoremError('config', `Profile ${id}: type '${type}' must set ${path}`);
241
+ throw new TheoremError('config', `Profile ${id}: type '${type}' must set ${keys.join('.')}`);
262
242
  }
263
243
  }
264
244
  }
245
+ /** `inputs` is optional on a live profile only, where it holds slots and context and nothing else. */
246
+ function assertInputsSet(input) {
247
+ const { id, type } = input;
248
+ if (type === 'live' || !profileTypesForField('inputs').includes(type))
249
+ return;
250
+ const inputs = input.inputs;
251
+ if (inputs === undefined || inputs === null) {
252
+ throw new TheoremError('config', `Profile ${id}: type '${type}' must set inputs`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
253
+ }
254
+ }
265
255
  /** Covers untyped hosts that the definition types can't stop. */
266
256
  function assertFieldScope(input) {
267
257
  const [field] = outOfScopeFields(input);
@@ -275,50 +265,57 @@ function assertTurnBehaviour(profileId, input) {
275
265
  const tb = input.turnBehaviour;
276
266
  assertResumption(profileId, tb?.resumption);
277
267
  }
278
- /**
279
- * The canary lives in the system prompt, and speech has none: Gemini TTS rejects developer
280
- * instructions and OpenAI-compatible `/audio/speech` has no field for one.
281
- */
282
- function speechGuardrails(input) {
283
- return { ...input.guardrails, canary: false };
268
+ function assertValidation(profileId, validation) {
269
+ const fail = (message) => new TheoremError('config', `Profile ${profileId}: ${message}`);
270
+ const retries = validation?.maxRetries;
271
+ if (retries !== undefined && (!Number.isInteger(retries) || retries < 0)) {
272
+ throw fail('outputs.validation.maxRetries must be a non-negative integer'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
273
+ }
284
274
  }
285
- function assertEgress(profileId, guardrails) {
286
- const egress = guardrails?.egress;
275
+ function assertGuardrails(profileId, guardrails) {
287
276
  const fail = (message) => new TheoremError('config', `Profile ${profileId}: ${message}`);
288
- if (egress && (egress.enforce === undefined) === (egress.checks === undefined)) {
277
+ // why: A key this does not read would be a check the builder believes is on.
278
+ const unknown = Object.keys(guardrails ?? {}).find((key) => !GUARDRAIL_KEYS.includes(key));
279
+ if (unknown !== undefined) {
280
+ throw fail(`guardrails.${unknown} is not a setting; it takes ${GUARDRAIL_KEYS.join(', ')}`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
281
+ }
282
+ assertBlockedReply(guardrails?.blockedReply, fail);
283
+ }
284
+ const GUARDRAIL_KEYS = [
285
+ 'quota',
286
+ 'detect',
287
+ 'blockedReply',
288
+ 'network',
289
+ 'taint',
290
+ ];
291
+ const BLOCKED_REPLY_KEYS = ['onBlock', 'maxRetries'];
292
+ function assertBlockedReply(blockedReply, fail) {
293
+ if (blockedReply === undefined)
294
+ return;
295
+ const unknown = Object.keys(blockedReply).find((key) => !BLOCKED_REPLY_KEYS.includes(key));
296
+ if (unknown !== undefined) {
289
297
  throw fail(
290
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
291
- 'guardrails.egress takes enforce (your own check) or checks (the bundled ones), one of the two');
298
+ // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
299
+ `guardrails.blockedReply.${unknown} is not a setting; it takes onBlock and maxRetries`);
292
300
  }
293
- if (egress?.enforce !== undefined && typeof egress.enforce !== 'function') {
294
- throw fail('guardrails.egress.enforce must be a function'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
295
- }
296
- const problem = egress?.checks === undefined
297
- ? undefined
298
- : egressChecksProblem('guardrails.egress.checks', egress.checks);
299
- if (problem !== undefined)
300
- throw fail(problem);
301
- for (const key of ['maxRetries', 'holdback']) {
302
- const value = egress?.[key];
303
- if (value !== undefined && (!Number.isInteger(value) || value < 0)) {
304
- throw new TheoremError('config', `Profile ${profileId}: guardrails.egress.${key} must be a non-negative integer`);
305
- }
301
+ const { onBlock, maxRetries } = blockedReply;
302
+ if (onBlock !== undefined && !BLOCKED_REPLY_ON_BLOCK.includes(onBlock)) {
303
+ throw fail(
304
+ // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
305
+ `guardrails.blockedReply.onBlock must be one of ${BLOCKED_REPLY_ON_BLOCK.join(', ')}`);
306
306
  }
307
- if (egress?.holdback !== undefined &&
308
- (egress.enforce === undefined || streamPlanOf(egress.enforce))) {
309
- throw new TheoremError('config', `Profile ${profileId}: guardrails.egress.holdback applies only to a host egress.enforce; the bundled policy holds exactly what could still become a match`);
307
+ if (maxRetries !== undefined && (!Number.isInteger(maxRetries) || maxRetries < 0)) {
308
+ throw fail('guardrails.blockedReply.maxRetries must be a non-negative integer'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
310
309
  }
311
310
  }
312
- /** A misspelt group would leave the group it meant on, silently. */
313
- function assertRedactSensitive(profileId, guardrails) {
314
- const selection = guardrails?.redactSensitive;
315
- if (selection === undefined || typeof selection === 'boolean')
316
- return;
317
- const groups = new Set(SENSITIVE_GROUPS);
318
- const bad = Object.entries(selection).find(([group, on]) => !groups.has(group) || typeof on !== 'boolean');
319
- if (bad !== undefined) {
320
- throw new TheoremError('config', `Profile ${profileId}: guardrails.redactSensitive.${bad[0]} is not a group (${SENSITIVE_GROUPS.join(', ')}) set to a boolean`);
321
- }
311
+ /** A host profile runs no model turn, so the tool boundaries are the only ones it has. */
312
+ function assertDetect(input) {
313
+ const spec = input.guardrails?.detect;
314
+ const problem = input.type === 'host'
315
+ ? detectProblem('guardrails.detect', spec, TOOL_BOUNDARIES)
316
+ : detectProblem('guardrails.detect', spec);
317
+ if (problem !== undefined)
318
+ throw new TheoremError('config', `Profile ${input.id}: ${problem}`);
322
319
  }
323
320
  function assertObservability(profileId, spec) {
324
321
  try {
@@ -339,7 +336,10 @@ function assertObservability(profileId, spec) {
339
336
  }
340
337
  }
341
338
  function assertIdentitySystem(input) {
342
- if (input.type === 'host' || input.type === 'decision' || input.type === 'speech')
339
+ if (input.type === 'host' ||
340
+ input.type === 'decision' ||
341
+ input.type === 'speech' ||
342
+ input.type === 'embedding')
343
343
  return;
344
344
  const { system, systemByRole } = input.identity;
345
345
  if (system !== undefined)
@@ -348,29 +348,37 @@ function assertIdentitySystem(input) {
348
348
  assertSystemPrompt(prompt, `Profile ${input.id} identity.systemByRole.${role}`);
349
349
  }
350
350
  }
351
+ /** Validate any definition and return the profile. */
351
352
  function defineProfile(input) {
352
353
  assertProfileShape(input);
354
+ for (const field of ['key', 'fallbackKey', 'protocol', 'provider'])
355
+ if (field in input)
356
+ throw new TheoremError('config', labelText('provider.profile_configuration', { profile: input.id, field }));
353
357
  assertFieldScope(input);
358
+ assertInputsSet(input);
354
359
  assertRequiredFields(input);
355
- if (input.lexicon)
356
- validateLexiconOverrides(input.lexicon, `Profile ${input.id}`);
360
+ assertNameLengths(input);
361
+ if (input.labels)
362
+ validateLabelOverrides(input.labels, `Profile ${input.id}`);
357
363
  assertIdentitySystem(input);
358
- assertRedactSensitive(input.id, input.guardrails);
364
+ assertDetect(input);
359
365
  if (input.type === 'host') {
360
366
  return defineHostProfile(input);
361
367
  }
362
368
  if (input.type === 'decision') {
363
369
  return defineDecisionProfile(input);
364
370
  }
371
+ if (input.type === 'embedding') {
372
+ return defineEmbeddingProfile(input);
373
+ }
365
374
  assertModelsNonEmpty(input.id, input.models);
366
375
  assertTurnBehaviour(input.id, input);
367
- assertEgress(input.id, input.guardrails);
376
+ assertGuardrails(input.id, input.guardrails);
377
+ if (input.type === 'text')
378
+ assertValidation(input.id, input.outputs?.validation);
368
379
  assertObservability(input.id, input.observability);
369
- assertSlotName(input.id, 'key', input.key);
370
- assertSlotName(input.id, 'fallbackKey', input.fallbackKey);
371
380
  for (const [modelId, binding] of Object.entries(input.models)) {
372
381
  assertModelBinding(input.id, modelId, binding);
373
- assertKeySlot(input.id, modelId, binding, input);
374
382
  }
375
383
  const identity = input.type === 'speech'
376
384
  ? { handle: input.identity.handle }
@@ -381,7 +389,7 @@ function defineProfile(input) {
381
389
  };
382
390
  const guardrails = input.guardrails;
383
391
  const observability = input.observability;
384
- const lexicon = input.lexicon;
392
+ const labels = input.labels;
385
393
  const modelFields = profileModelFields(input);
386
394
  let profile;
387
395
  switch (input.type) {
@@ -397,7 +405,7 @@ function defineProfile(input) {
397
405
  turnBehaviour: input.turnBehaviour,
398
406
  guardrails,
399
407
  observability,
400
- lexicon,
408
+ labels,
401
409
  };
402
410
  break;
403
411
  case 'image':
@@ -413,7 +421,7 @@ function defineProfile(input) {
413
421
  turnBehaviour: input.turnBehaviour,
414
422
  guardrails,
415
423
  observability,
416
- lexicon,
424
+ labels,
417
425
  };
418
426
  assertImageAccept(profile.id, profile.inputs.attachments?.accept);
419
427
  assertImagePinValues(profile.id, profile.image);
@@ -427,9 +435,9 @@ function defineProfile(input) {
427
435
  speech: input.speech,
428
436
  outputs: input.outputs,
429
437
  turnBehaviour: input.turnBehaviour,
430
- guardrails: speechGuardrails(input),
438
+ guardrails,
431
439
  observability,
432
- lexicon,
440
+ labels,
433
441
  };
434
442
  break;
435
443
  case 'live': {
@@ -439,25 +447,43 @@ function defineProfile(input) {
439
447
  identity,
440
448
  ...modelFields,
441
449
  live: input.live,
442
- tools: { allow: input.tools.allow },
450
+ inputs: input.inputs,
451
+ tools: { allow: input.tools.allow, ...optionalParallel(input.tools.parallel) },
443
452
  turnBehaviour: input.turnBehaviour,
444
453
  guardrails,
445
454
  observability,
446
- lexicon,
455
+ labels,
447
456
  };
448
457
  assertLiveIngressConfigured(profile);
449
458
  assertLiveCompression(profile.id, profile.live.contextCompression);
459
+ assertLiveOpening(profile);
450
460
  break;
451
461
  }
452
462
  default: {
453
463
  const _exhaustive = input;
454
- throw new TheoremError('config', `Unknown profile type '${String(_exhaustive)}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
464
+ throw new TheoremError('config', `Unknown profile type '${String(_exhaustive)}'`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
455
465
  }
456
466
  }
457
467
  assertStructuredSlot(profile);
458
- assertTypeProtocols(profile);
468
+ assertMaxSteps(profile);
469
+ assertAnswerStep(profile);
459
470
  return profile;
460
471
  }
472
+ /** Only a positive whole number is a cap; left out, the turn gets `DEFAULT_MAX_STEPS`. */
473
+ function assertMaxSteps(profile) {
474
+ const { maxSteps } = profile;
475
+ if (maxSteps === undefined || (Number.isInteger(maxSteps) && maxSteps > 0))
476
+ return;
477
+ throw new TheoremError('config', `Profile ${profile.id}: maxSteps must be a whole number of 1 or more; leave it out for the default of 20`);
478
+ }
479
+ /** The answering step takes no tools, so with one step the profile's tools could never run. */
480
+ function assertAnswerStep(profile) {
481
+ if (profile.type !== 'text' || profile.turnBehaviour?.atStepLimit !== 'answer')
482
+ return;
483
+ if (profile.maxSteps !== 1)
484
+ return;
485
+ throw new TheoremError('config', `Profile ${profile.id}: turnBehaviour.atStepLimit 'answer' with maxSteps 1 leaves no step that can call a tool`);
486
+ }
461
487
  /** A turn can pass no value outside the slot's choices, so any other mapped key is dead. */
462
488
  function assertStructuredSlot(profile) {
463
489
  if (profile.type === 'live')
@@ -475,28 +501,28 @@ function assertStructuredSlot(profile) {
475
501
  }
476
502
  }
477
503
  const IMAGE_PIN_RULES = {
478
- n: { ok: (v) => Number.isInteger(v) && v >= 1, rule: 'a whole number of 1 or more' }, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
479
- seed: { ok: Number.isInteger, rule: 'a whole number' }, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
504
+ n: { ok: (v) => Number.isInteger(v) && v >= 1, rule: 'a whole number of 1 or more' }, // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
505
+ seed: { ok: Number.isInteger, rule: 'a whole number' }, // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
480
506
  outputCompression: {
481
507
  ok: (v) => Number.isInteger(v) && v >= 0 && v <= 100,
482
- rule: 'a whole number from 0 to 100', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
508
+ rule: 'a whole number from 0 to 100', // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
483
509
  },
484
510
  };
485
511
  function assertImagePinValues(profileId, image) {
486
512
  for (const [name, { ok, rule }] of Object.entries(IMAGE_PIN_RULES)) {
487
513
  const value = image[name];
488
514
  if (value !== undefined && !ok(value)) {
489
- throw new TheoremError('config', `Profile ${profileId}: image.${name} must be ${rule}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
515
+ throw new TheoremError('config', `Profile ${profileId}: image.${name} must be ${rule}`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
490
516
  }
491
517
  }
492
518
  image.references?.forEach((reference, index) => {
493
519
  const where = `Profile ${profileId}: image.references[${index}]`;
494
520
  if (mediaKindForMime(reference.mimeType) !== 'image') {
495
- throw new TheoremError('config', `${where} must be an image, not '${reference.mimeType}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
521
+ throw new TheoremError('config', `${where} must be an image, not '${reference.mimeType}'`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
496
522
  }
497
523
  const source = isTurnMediaRef(reference) ? reference.uri : reference.data;
498
524
  if (!source) {
499
- throw new TheoremError('config', `${where} needs its bytes or its uri`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
525
+ throw new TheoremError('config', `${where} needs its bytes or its uri`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
500
526
  }
501
527
  });
502
528
  }
@@ -505,35 +531,55 @@ function assertImageAccept(profileId, accept) {
505
531
  if (!outside?.length)
506
532
  return;
507
533
  throw new TheoremError('config',
508
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
509
- `Profile ${profileId}: an image profile's attachments take images, video and PDF only, ` +
534
+ // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
535
+ `Profile ${profileId}: an image profile's attachments take images, video, and PDF only, ` +
510
536
  `not ${outside.join(', ')}`);
511
537
  }
538
+ /** A greeting is a prompt, and a resume prompt needs the handles that make a resume. */
539
+ function assertLiveOpening(profile) {
540
+ const { greeting, resumed, sessionResumption } = profile.live;
541
+ const tag = `Profile ${profile.id} live`; // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
542
+ if (greeting !== undefined && (typeof greeting !== 'string' || !greeting.trim())) {
543
+ throw new TheoremError('config', `${tag}.greeting must be a prompt; leave it out to wait`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
544
+ }
545
+ if (!resumed)
546
+ return;
547
+ if (typeof resumed.prompt !== 'string' || !resumed.prompt.trim()) {
548
+ throw new TheoremError('config', `${tag}.resumed.prompt must be a prompt`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
549
+ }
550
+ const { afterMs } = resumed;
551
+ if (afterMs !== undefined && !(Number.isFinite(afterMs) && afterMs >= 0)) {
552
+ throw new TheoremError('config', `${tag}.resumed.afterMs must be 0 or more milliseconds`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
553
+ }
554
+ if (!sessionResumption) {
555
+ throw new TheoremError('config', `${tag}.resumed needs live.sessionResumption: without it no call resumes`);
556
+ }
557
+ }
512
558
  function assertLiveCompression(profileId, spec) {
513
559
  if (!spec)
514
560
  return;
515
- const tag = `Profile ${profileId} live.contextCompression`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
561
+ const tag = `Profile ${profileId} live.contextCompression`; // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
516
562
  const trigger = spec.triggerTokens;
517
563
  const target = spec.slidingWindow.targetTokens;
518
564
  assertWholeTokens(`${tag}.triggerTokens`, trigger);
519
565
  assertWholeTokens(`${tag}.slidingWindow.targetTokens`, target);
520
566
  if (trigger !== undefined && target !== undefined && target >= trigger) {
521
567
  throw new TheoremError('config',
522
- // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
568
+ // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
523
569
  `${tag}: slidingWindow.targetTokens must be below triggerTokens`);
524
570
  }
525
571
  }
526
572
  function assertWholeTokens(tag, value) {
527
573
  if (value === undefined || (Number.isInteger(value) && value > 0))
528
574
  return;
529
- throw new TheoremError('config', `${tag} must be a whole number above 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
575
+ throw new TheoremError('config', `${tag} must be a whole number above 0`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
530
576
  }
531
577
  function assertCompactionSpec(registered, owner, modelId, spec) {
532
- const tag = `Profile ${owner.id} model ${modelId} compaction`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
578
+ const tag = `Profile ${owner.id} model ${modelId} compaction`; // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
533
579
  assertCompactionBudget(tag, spec);
534
580
  assertCompactionRetain(tag, spec);
535
581
  if (spec.meter != null && spec.meter !== 'history' && spec.meter !== 'input') {
536
- throw new TheoremError('config', `${tag}: meter must be 'history' or 'input'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
582
+ throw new TheoremError('config', `${tag}: meter must be 'history' or 'input'`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
537
583
  }
538
584
  if (spec.profile === undefined) {
539
585
  if (owner.type !== 'text' || owner.inputs.text === false) {
@@ -549,55 +595,23 @@ function assertCompactionSpec(registered, owner, modelId, spec) {
549
595
  throw new TheoremError('config', `${tag}: compaction profile '${spec.profile}' must be a text profile that takes text`);
550
596
  }
551
597
  }
552
- function assertCacheSpec(profileId, modelId, binding, spec) {
553
- const tag = `Profile ${profileId} model '${modelId}'`;
554
- if (binding.provider !== 'openrouter') {
555
- throw new TheoremError('config', `${tag}: cache is only valid when protocol is 'openAi' and provider is 'openrouter'`);
556
- }
557
- if (!CACHE_MODES.includes(spec.mode)) {
558
- throw new TheoremError('config', `${tag}: cache.mode must be one of ${CACHE_MODES.join(' | ')}`);
559
- }
560
- if (spec.ttl != null && !CACHE_TTLS.includes(spec.ttl)) {
561
- throw new TheoremError('config', `${tag}: cache.ttl must be one of ${CACHE_TTLS.join(' | ')}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
562
- }
563
- }
564
- function assertInteractionsPersistence(profileId, modelId, binding) {
565
- if (binding.protocol === 'geminiInteractions') {
566
- if (binding.persistViaInteractionId === undefined) {
567
- throw new TheoremError('config', `Profile ${profileId} model '${modelId}': persistViaInteractionId is required on a 'geminiInteractions' binding — true chains on Google's stored interaction, false sends the host's history plus this turn's steps every call`);
568
- }
569
- if (binding.persistViaInteractionId && binding.store === false) {
570
- throw new TheoremError('config', `Profile ${profileId} model '${modelId}': persistViaInteractionId: true needs store left on — Google chains only from a stored interaction`);
571
- }
572
- return;
573
- }
574
- if (binding.store === undefined && binding.persistViaInteractionId === undefined) {
575
- return;
576
- }
577
- const which = binding.store !== undefined && binding.persistViaInteractionId !== undefined
578
- ? 'store and persistViaInteractionId' // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
579
- : binding.store !== undefined
580
- ? 'store'
581
- : 'persistViaInteractionId';
582
- throw new TheoremError('config', `Profile ${profileId} model '${modelId}': ${which} is only valid when protocol is 'geminiInteractions' and provider is 'google'`);
583
- }
584
598
  function assertCompactionBudget(tag, spec) {
585
599
  if (spec.maxTokens <= 0) {
586
- throw new TheoremError('config', `${tag}: maxTokens must be > 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
600
+ throw new TheoremError('config', `${tag}: maxTokens must be > 0`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
587
601
  }
588
602
  if (spec.compactAt <= 0 || spec.compactAt >= 1) {
589
- throw new TheoremError('config', `${tag}: compactAt must be in (0, 1)`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
603
+ throw new TheoremError('config', `${tag}: compactAt must be in (0, 1)`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
590
604
  }
591
605
  }
592
606
  function assertCompactionRetain(tag, spec) {
593
607
  if (spec.previousExchanges < 0) {
594
- throw new TheoremError('config', `${tag}: previousExchanges must be >= 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
608
+ throw new TheoremError('config', `${tag}: previousExchanges must be >= 0`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
595
609
  }
596
610
  if (spec.previousExchanges < 1 && spec.previousExchanges >= spec.compactAt) {
597
611
  throw new TheoremError('config', `${tag}: previousExchanges as fraction (${spec.previousExchanges}) must be < compactAt (${spec.compactAt})`);
598
612
  }
599
613
  if (spec.previousExchanges > 1 && !Number.isInteger(spec.previousExchanges)) {
600
- throw new TheoremError('config', `${tag}: previousExchanges >= 1 must be an integer`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
614
+ throw new TheoremError('config', `${tag}: previousExchanges >= 1 must be an integer`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
601
615
  }
602
616
  }
603
617
  function assertCustomToolsOnly(tools, profile) {
@@ -605,7 +619,7 @@ function assertCustomToolsOnly(tools, profile) {
605
619
  const tool = tools.get(id);
606
620
  if (tool?.type === 'builtin') {
607
621
  throw new TheoremError('config', profile.type === 'host'
608
- ? `Profile ${profile.id} lists builtin '${id}' in tools.allow — type 'host' never runs a model` // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
622
+ ? `Profile ${profile.id} lists builtin '${id}' in tools.allow — type 'host' never runs a model` // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
609
623
  : `Profile ${profile.id} lists builtin '${id}' in tools.allow — declare it on models.*.builtInTools instead`);
610
624
  }
611
625
  }
@@ -638,35 +652,96 @@ function assertModelBuiltInTools(tools, profile) {
638
652
  }
639
653
  }
640
654
  }
641
- function assertMediaLimits(profile) {
642
- const inputs = profileInputs(profile);
655
+ /** The schema ids `outputs.structured` can pick, by slot and fallback. */
656
+ function structuredIds(profile) {
657
+ const structured = profile.type === 'text' ? profile.outputs?.structured : undefined;
658
+ if (!structured)
659
+ return [];
660
+ if (typeof structured === 'string')
661
+ return [structured];
662
+ return [...new Set([...Object.values(structured.map), structured.fallback])];
663
+ }
664
+ /**
665
+ * Every schema `outputs.structured` names is registered, and every
666
+ * `outputs.validation.fields` path reaches a property of one of them.
667
+ */
668
+ function assertStructuredSchemas(schemas, profile) {
669
+ const specs = structuredIds(profile).map((id) => {
670
+ const spec = schemas.find(id);
671
+ if (!spec) {
672
+ throw new TheoremError('config', `Profile ${profile.id}: outputs.structured names '${id}', which is not a registered schema; register it before the profile`);
673
+ }
674
+ return spec;
675
+ });
676
+ const validation = profile.type === 'text' ? profile.outputs?.validation : undefined;
677
+ const fields = Object.keys(validation?.fields ?? {});
678
+ const lists = Object.keys(validation?.items ?? {});
679
+ if (!fields.length && !lists.length)
680
+ return;
681
+ if (!specs.length) {
682
+ throw new TheoremError('config', `Profile ${profile.id}: outputs.validation checks a structured reply, so it needs outputs.structured`);
683
+ }
684
+ const notLists = lists.filter((path) => !specs.some((spec) => itemSchemaAt(spec.jsonSchema, path)));
685
+ if (notLists.length) {
686
+ throw new TheoremError('config', `Profile ${profile.id}: outputs.validation.items has ${notLists.join(', ')}, which no structured schema reaches as a list with an items schema`);
687
+ }
688
+ const unreached = fields.filter((path) => !specs.some((spec) => schemaReaches(spec.jsonSchema, path)));
689
+ if (unreached.length) {
690
+ throw new TheoremError('config', `Profile ${profile.id}: outputs.validation.fields has ${unreached.join(', ')}, which no structured schema reaches through object properties`);
691
+ }
692
+ }
693
+ function assertContextSpec(profile) {
694
+ const context = profileInputs(profile)?.context;
695
+ if (!context) {
696
+ return;
697
+ }
698
+ const senders = CONTEXT_SENDERS;
699
+ if (context.from.length === 0 || context.from.some((sender) => !senders.includes(sender))) {
700
+ throw new TheoremError('config', `Profile ${profile.id}: inputs.context.from must list ${CONTEXT_SENDERS.join(' or ')}`);
701
+ }
702
+ if (!(Number.isInteger(context.maxChars) && context.maxChars > 0)) {
703
+ throw new TheoremError('config', `Profile ${profile.id}: inputs.context.maxChars must be a positive integer`);
704
+ }
705
+ }
706
+ function assertMediaLimits(profileId, inputs) {
643
707
  if (!inputs) {
644
708
  return;
645
709
  }
646
710
  const { attachments, voice, maxFiles, maxBytes, maxTurnBytes, limitsByMime } = inputs;
647
711
  const limits = { maxFiles, maxBytes, maxTurnBytes };
648
712
  if ((attachments || voice) && Object.values(limits).some((value) => value === undefined)) {
649
- throw new TheoremError('config', `Profile ${profile.id} must set maxFiles, maxBytes, and maxTurnBytes`);
713
+ throw new TheoremError('config', `Profile ${profileId} must set maxFiles, maxBytes, and maxTurnBytes`);
650
714
  }
651
715
  for (const [mime, value] of Object.entries(limitsByMime ?? {})) {
652
716
  limits[`limitsByMime['${mime}']`] = value;
653
717
  }
654
718
  for (const [name, value] of Object.entries(limits)) {
655
719
  if (value !== undefined && !(Number.isInteger(value) && value > 0)) {
656
- throw new TheoremError('config', `Profile ${profile.id}: inputs.${name} must be a positive integer`);
720
+ throw new TheoremError('config', `Profile ${profileId}: inputs.${name} must be a positive integer`);
657
721
  }
658
722
  }
659
723
  }
660
- /** Profiles are checked against `tools`, so register a scope's tools before its profiles. */
661
- function createProfileRegistry(tools) {
724
+ /** Profiles are checked against `tools` and `schemas`, so register a scope's tools and schemas before its profiles. */
725
+ function createProfileRegistry(tools, schemas, providers) {
662
726
  const profiles = new Map();
663
727
  const register = (profileInput) => {
664
- const profile = defineProfile(profileInput);
728
+ const defined = defineProfile(profileInput);
729
+ const profile = providers && defined.type !== 'host'
730
+ ? {
731
+ ...defined,
732
+ models: Object.fromEntries(Object.entries(defined.models).map(([modelId, binding]) => [
733
+ modelId,
734
+ checkBinding(providers.require(binding.provider, binding.apiId), binding, defined.type),
735
+ ])),
736
+ }
737
+ : defined;
665
738
  assertCustomToolsOnly(tools, profile);
666
739
  assertProfileToolLoader(tools, profile);
667
- if (profile.type !== 'host' && profile.type !== 'decision') {
740
+ if (profile.type !== 'host' && profile.type !== 'decision' && profile.type !== 'embedding') {
668
741
  assertModelBuiltInTools(tools, profile);
669
- assertMediaLimits(profile);
742
+ assertMediaLimits(profile.id, profileInputs(profile));
743
+ assertContextSpec(profile);
744
+ assertStructuredSchemas(schemas, profile);
670
745
  for (const [modelId, binding] of Object.entries(profile.models)) {
671
746
  if (binding.compaction) {
672
747
  assertCompactionSpec(profiles, profile, modelId, binding.compaction);