theorum 0.1.15 → 1.1.3

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 (305) hide show
  1. package/README.md +241 -98
  2. package/esm/mod.d.ts +57 -28
  3. package/esm/mod.js +43 -23
  4. package/esm/src/cli/commands/bench.js +18 -18
  5. package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
  6. package/esm/src/cli/commands/fuzz-canary.js +191 -0
  7. package/esm/src/cli/commands/fuzz-guardrails.d.ts +3 -5
  8. package/esm/src/cli/commands/fuzz-guardrails.js +4 -581
  9. package/esm/src/cli/commands/guardrails-eval.d.ts +14 -0
  10. package/esm/src/cli/commands/guardrails-eval.js +15 -0
  11. package/esm/src/cli/commands/profile.js +35 -15
  12. package/esm/src/cli/commands/run.d.ts +3 -0
  13. package/esm/src/cli/commands/run.js +23 -32
  14. package/esm/src/cli/commands/test.d.ts +10 -1
  15. package/esm/src/cli/commands/test.js +34 -34
  16. package/esm/src/cli/event-log.d.ts +19 -0
  17. package/esm/src/cli/event-log.js +147 -0
  18. package/esm/src/cli/index.js +57 -11
  19. package/esm/src/cli/matrix/synthesizer.d.ts +10 -12
  20. package/esm/src/cli/matrix/synthesizer.js +45 -118
  21. package/esm/src/guardrails/canary-gate.d.ts +21 -0
  22. package/esm/src/guardrails/canary-gate.js +32 -0
  23. package/esm/src/guardrails/canary.d.ts +34 -0
  24. package/esm/src/guardrails/canary.js +150 -0
  25. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +17 -0
  26. package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
  27. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
  28. package/esm/src/guardrails/corpus/fuzz-inbound.js +213 -0
  29. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
  30. package/esm/src/guardrails/corpus/inbound-payloads.js +125 -0
  31. package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
  32. package/esm/src/guardrails/corpus/live-attacks.js +231 -0
  33. package/esm/src/guardrails/corpus/mod.d.ts +14 -0
  34. package/esm/src/guardrails/corpus/mod.js +11 -0
  35. package/esm/src/guardrails/corpus/secrets.d.ts +17 -0
  36. package/esm/src/guardrails/corpus/secrets.js +17 -0
  37. package/esm/src/guardrails/corpus/strings.d.ts +28 -0
  38. package/esm/src/guardrails/corpus/strings.js +34 -0
  39. package/esm/src/guardrails/corpus/types.d.ts +38 -0
  40. package/esm/src/guardrails/corpus/types.js +6 -0
  41. package/esm/src/guardrails/egress.d.ts +32 -0
  42. package/esm/src/guardrails/egress.js +87 -0
  43. package/esm/src/guardrails/error.d.ts +14 -23
  44. package/esm/src/guardrails/error.js +87 -76
  45. package/esm/src/guardrails/eval/corpus.d.ts +108 -0
  46. package/esm/src/guardrails/eval/corpus.js +978 -0
  47. package/esm/src/guardrails/eval/mod.d.ts +51 -0
  48. package/esm/src/guardrails/eval/mod.js +133 -0
  49. package/esm/src/guardrails/eval/score.d.ts +66 -0
  50. package/esm/src/guardrails/eval/score.js +114 -0
  51. package/esm/src/guardrails/events.d.ts +25 -0
  52. package/esm/src/guardrails/events.js +56 -0
  53. package/esm/src/guardrails/hits.d.ts +24 -0
  54. package/esm/src/guardrails/hits.js +45 -0
  55. package/esm/src/guardrails/injection.js +28 -5
  56. package/esm/src/guardrails/lexicon.d.ts +39 -0
  57. package/esm/src/guardrails/lexicon.js +200 -0
  58. package/esm/src/guardrails/live-outbound-gate.d.ts +41 -0
  59. package/esm/src/guardrails/live-outbound-gate.js +222 -0
  60. package/esm/src/guardrails/mod.d.ts +30 -6
  61. package/esm/src/guardrails/mod.js +20 -5
  62. package/esm/src/guardrails/network.d.ts +19 -0
  63. package/esm/src/guardrails/network.js +234 -0
  64. package/esm/src/guardrails/policy.d.ts +35 -0
  65. package/esm/src/guardrails/policy.js +50 -0
  66. package/esm/src/guardrails/progressive-yield.d.ts +51 -0
  67. package/esm/src/guardrails/progressive-yield.js +98 -0
  68. package/esm/src/guardrails/quota.d.ts +17 -3
  69. package/esm/src/guardrails/quota.js +18 -4
  70. package/esm/src/guardrails/sanitize.d.ts +45 -19
  71. package/esm/src/guardrails/sanitize.js +177 -94
  72. package/esm/src/guardrails/sensitive.js +2 -1
  73. package/esm/src/guardrails/serialize.d.ts +35 -0
  74. package/esm/src/guardrails/serialize.js +58 -0
  75. package/esm/src/guardrails/testing.d.ts +17 -0
  76. package/esm/src/guardrails/testing.js +13 -0
  77. package/esm/src/guardrails/theorum-error.d.ts +12 -0
  78. package/esm/src/guardrails/theorum-error.js +15 -0
  79. package/esm/src/guardrails/tool-directives.d.ts +48 -0
  80. package/esm/src/guardrails/tool-directives.js +124 -0
  81. package/esm/src/guardrails/tool-result.d.ts +93 -0
  82. package/esm/src/guardrails/tool-result.js +276 -0
  83. package/esm/src/guardrails/types.d.ts +291 -0
  84. package/esm/src/guardrails/types.js +72 -0
  85. package/esm/src/host/client-turn.d.ts +19 -0
  86. package/esm/src/host/client-turn.js +36 -0
  87. package/esm/src/host/mint-trace.d.ts +1 -1
  88. package/esm/src/host/mod.d.ts +5 -3
  89. package/esm/src/host/mod.js +4 -3
  90. package/esm/src/kernel/auth/crypto.d.ts +42 -0
  91. package/esm/src/kernel/auth/crypto.js +106 -0
  92. package/esm/src/kernel/auth/mod.d.ts +11 -0
  93. package/esm/src/kernel/auth/mod.js +11 -0
  94. package/esm/src/kernel/auth/oauth.d.ts +47 -0
  95. package/esm/src/kernel/auth/oauth.js +278 -0
  96. package/esm/src/kernel/auth/types.d.ts +133 -0
  97. package/esm/src/kernel/auth/types.js +13 -0
  98. package/esm/src/kernel/engine/delta.d.ts +24 -2
  99. package/esm/src/kernel/engine/delta.js +478 -39
  100. package/esm/src/kernel/engine/live-inbound.d.ts +21 -0
  101. package/esm/src/kernel/engine/live-inbound.js +31 -0
  102. package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
  103. package/esm/src/kernel/engine/live-ingress.js +47 -0
  104. package/esm/src/kernel/engine/repair.js +13 -12
  105. package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
  106. package/esm/src/kernel/engine/runner/gates.js +130 -43
  107. package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
  108. package/esm/src/kernel/engine/runner/mod.js +192 -53
  109. package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
  110. package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
  111. package/esm/src/kernel/engine/runner/stages.js +89 -0
  112. package/esm/src/kernel/engine/runner/state.d.ts +31 -0
  113. package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
  114. package/esm/src/kernel/engine/runner/steps.js +244 -43
  115. package/esm/src/kernel/engine/runner/stream.d.ts +9 -3
  116. package/esm/src/kernel/engine/runner/stream.js +140 -44
  117. package/esm/src/kernel/engine/session/mod.d.ts +25 -0
  118. package/esm/src/kernel/engine/session/mod.js +557 -0
  119. package/esm/src/kernel/interaction-parts.d.ts +14 -0
  120. package/esm/src/kernel/interaction-parts.js +23 -0
  121. package/esm/src/kernel/mod.d.ts +21 -10
  122. package/esm/src/kernel/mod.js +11 -8
  123. package/esm/src/kernel/profile-graph.d.ts +159 -0
  124. package/esm/src/kernel/profile-graph.js +156 -0
  125. package/esm/src/kernel/registry/attachments.d.ts +12 -10
  126. package/esm/src/kernel/registry/attachments.js +33 -27
  127. package/esm/src/kernel/registry/catalog.d.ts +25 -24
  128. package/esm/src/kernel/registry/catalog.js +60 -101
  129. package/esm/src/kernel/registry/ingress.d.ts +9 -4
  130. package/esm/src/kernel/registry/ingress.js +97 -75
  131. package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
  132. package/esm/src/kernel/registry/profile-outputs.js +8 -0
  133. package/esm/src/kernel/registry/profiles.d.ts +55 -12
  134. package/esm/src/kernel/registry/profiles.js +413 -73
  135. package/esm/src/kernel/registry/provider-request.js +13 -7
  136. package/esm/src/kernel/registry/resolve.d.ts +8 -8
  137. package/esm/src/kernel/registry/resolve.js +169 -154
  138. package/esm/src/kernel/registry/schemas.js +1 -1
  139. package/esm/src/kernel/registry/sole-model.d.ts +8 -0
  140. package/esm/src/kernel/registry/sole-model.js +10 -0
  141. package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
  142. package/esm/src/kernel/registry/system-prompt.js +40 -0
  143. package/esm/src/kernel/registry/system-role.d.ts +8 -0
  144. package/esm/src/kernel/registry/system-role.js +14 -0
  145. package/esm/src/kernel/registry/vault.d.ts +12 -7
  146. package/esm/src/kernel/registry/vault.js +32 -10
  147. package/esm/src/kernel/schema.d.ts +231 -0
  148. package/esm/src/kernel/schema.js +607 -0
  149. package/esm/src/kernel/stages.d.ts +175 -0
  150. package/esm/src/kernel/stages.js +476 -0
  151. package/esm/src/kernel/stop.d.ts +78 -19
  152. package/esm/src/kernel/stop.js +51 -16
  153. package/esm/src/kernel/tools/events.d.ts +41 -0
  154. package/esm/src/kernel/tools/events.js +71 -0
  155. package/esm/src/kernel/tools/execute.d.ts +84 -0
  156. package/esm/src/kernel/tools/execute.js +614 -0
  157. package/esm/src/kernel/tools/harness.d.ts +8 -0
  158. package/esm/src/kernel/tools/harness.js +46 -0
  159. package/esm/src/kernel/tools/invoke.d.ts +10 -0
  160. package/esm/src/kernel/tools/invoke.js +101 -0
  161. package/esm/src/kernel/tools/mod.d.ts +13 -0
  162. package/esm/src/kernel/tools/mod.js +11 -0
  163. package/esm/src/kernel/tools/permission.d.ts +15 -0
  164. package/esm/src/kernel/tools/permission.js +47 -0
  165. package/esm/src/kernel/tools/project.d.ts +12 -0
  166. package/esm/src/kernel/tools/project.js +36 -0
  167. package/esm/src/kernel/tools/registry.d.ts +23 -0
  168. package/esm/src/kernel/tools/registry.js +81 -0
  169. package/esm/src/kernel/tools/remote.d.ts +94 -0
  170. package/esm/src/kernel/tools/remote.js +577 -0
  171. package/esm/src/kernel/tools/resolve.d.ts +39 -0
  172. package/esm/src/kernel/tools/resolve.js +283 -0
  173. package/esm/src/kernel/tools/schema.d.ts +15 -0
  174. package/esm/src/kernel/tools/schema.js +176 -0
  175. package/esm/src/kernel/tools/stage-run.d.ts +105 -0
  176. package/esm/src/kernel/tools/stage-run.js +155 -0
  177. package/esm/src/kernel/tools/types.d.ts +394 -0
  178. package/esm/src/kernel/tools/types.js +9 -0
  179. package/esm/src/kernel/types.d.ts +540 -256
  180. package/esm/src/kernel/util/find-last.d.ts +2 -0
  181. package/esm/src/kernel/util/find-last.js +10 -0
  182. package/esm/src/observability/destinations.d.ts +31 -0
  183. package/esm/src/observability/destinations.js +67 -0
  184. package/esm/src/observability/mod.d.ts +10 -3
  185. package/esm/src/observability/mod.js +6 -2
  186. package/esm/src/observability/policy.d.ts +27 -0
  187. package/esm/src/observability/policy.js +80 -0
  188. package/esm/src/observability/resolve-policy.d.ts +16 -0
  189. package/esm/src/observability/resolve-policy.js +64 -0
  190. package/esm/src/observability/trace-attach.d.ts +8 -4
  191. package/esm/src/observability/trace-attach.js +50 -29
  192. package/esm/src/observability/trace-record.d.ts +23 -13
  193. package/esm/src/observability/trace-record.js +96 -39
  194. package/esm/src/observability/trace-sink.d.ts +19 -0
  195. package/esm/src/observability/trace-sink.js +10 -0
  196. package/esm/src/observability/trace-usage.d.ts +10 -3
  197. package/esm/src/observability/trace-usage.js +70 -17
  198. package/esm/src/observability/trace.d.ts +18 -7
  199. package/esm/src/observability/trace.js +34 -17
  200. package/esm/src/observability/types.d.ts +113 -0
  201. package/esm/src/observability/types.js +11 -0
  202. package/esm/src/presets/google/speech-voices.d.ts +11 -0
  203. package/esm/src/presets/google/speech-voices.js +41 -0
  204. package/esm/src/presets/google.d.ts +36 -24
  205. package/esm/src/presets/google.js +50 -63
  206. package/esm/src/presets/mod.d.ts +2 -2
  207. package/esm/src/presets/mod.js +1 -1
  208. package/esm/src/providers/create-provider.d.ts +20 -17
  209. package/esm/src/providers/create-provider.js +72 -26
  210. package/esm/src/providers/google/interactions/framing.d.ts +23 -0
  211. package/esm/src/providers/google/interactions/framing.js +269 -0
  212. package/esm/src/providers/google/interactions/mod.d.ts +7 -0
  213. package/esm/src/providers/google/interactions/mod.js +7 -0
  214. package/esm/src/providers/google/interactions/stream.d.ts +83 -0
  215. package/esm/src/providers/google/interactions/stream.js +588 -0
  216. package/esm/src/providers/google/keys.d.ts +26 -0
  217. package/esm/src/providers/{keys.js → google/keys.js} +19 -31
  218. package/esm/src/providers/google/live/framing.d.ts +49 -0
  219. package/esm/src/providers/google/live/framing.js +552 -0
  220. package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
  221. package/esm/src/providers/google/live/openapi-schema.js +46 -0
  222. package/esm/src/providers/google/live/session.d.ts +25 -0
  223. package/esm/src/providers/google/live/session.js +134 -0
  224. package/esm/src/providers/google/live/stream.d.ts +45 -0
  225. package/esm/src/providers/google/live/stream.js +214 -0
  226. package/esm/src/providers/google/urls.d.ts +6 -0
  227. package/esm/src/providers/google/urls.js +6 -0
  228. package/esm/src/providers/local/local.d.ts +30 -0
  229. package/esm/src/providers/{local.js → local/local.js} +66 -126
  230. package/esm/src/providers/local/mod.d.ts +9 -0
  231. package/esm/src/providers/local/mod.js +9 -0
  232. package/esm/src/providers/mod.d.ts +6 -3
  233. package/esm/src/providers/mod.js +3 -1
  234. package/esm/src/providers/openrouter/cache-control.d.ts +24 -0
  235. package/esm/src/providers/openrouter/cache-control.js +23 -0
  236. package/esm/src/providers/openrouter/chat.d.ts +107 -0
  237. package/esm/src/providers/{openrouter.js → openrouter/chat.js} +117 -231
  238. package/esm/src/providers/openrouter/image.d.ts +34 -0
  239. package/esm/src/providers/openrouter/image.js +275 -0
  240. package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
  241. package/esm/src/providers/openrouter/openai/chat-payload.js +82 -0
  242. package/esm/src/providers/openrouter/openai/compat.d.ts +53 -0
  243. package/esm/src/providers/openrouter/openai/compat.js +213 -0
  244. package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
  245. package/esm/src/providers/openrouter/openai/image-payload.js +90 -0
  246. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
  247. package/esm/src/providers/openrouter/openai/sdk-messages.js +122 -0
  248. package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
  249. package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
  250. package/esm/src/providers/openrouter/speech.d.ts +23 -0
  251. package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
  252. package/esm/src/providers/probe.d.ts +1 -0
  253. package/esm/src/providers/probe.js +22 -0
  254. package/esm/src/providers/shared/pcm.d.ts +12 -0
  255. package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
  256. package/esm/src/providers/shared/sse.d.ts +18 -0
  257. package/esm/src/providers/shared/sse.js +87 -0
  258. package/esm/src/providers/shared/tool-args.d.ts +17 -0
  259. package/esm/src/providers/shared/tool-args.js +45 -0
  260. package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
  261. package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
  262. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  263. package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
  264. package/esm/src/providers/types.d.ts +27 -0
  265. package/esm/src/providers/types.js +1 -0
  266. package/package.json +11 -7
  267. package/docs/cli.md +0 -97
  268. package/docs/guardrails.md +0 -178
  269. package/docs/host.md +0 -97
  270. package/docs/kernel.md +0 -404
  271. package/docs/observability.md +0 -105
  272. package/docs/openrouter.md +0 -125
  273. package/docs/presets-google.md +0 -91
  274. package/docs/presets.md +0 -88
  275. package/docs/providers.md +0 -202
  276. package/docs/streaming.md +0 -96
  277. package/esm/src/kernel/engine/boundary.d.ts +0 -10
  278. package/esm/src/kernel/engine/boundary.js +0 -55
  279. package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
  280. package/esm/src/kernel/engine/runner/tools.js +0 -198
  281. package/esm/src/kernel/registry/tools.d.ts +0 -12
  282. package/esm/src/kernel/registry/tools.js +0 -36
  283. package/esm/src/providers/expose-for-tests.d.ts +0 -1
  284. package/esm/src/providers/expose-for-tests.js +0 -25
  285. package/esm/src/providers/gemini-tape.d.ts +0 -2
  286. package/esm/src/providers/google-tap.d.ts +0 -3
  287. package/esm/src/providers/interactions.d.ts +0 -5
  288. package/esm/src/providers/interactions.js +0 -169
  289. package/esm/src/providers/keys.d.ts +0 -19
  290. package/esm/src/providers/local.d.ts +0 -29
  291. package/esm/src/providers/openrouter-mod.d.ts +0 -13
  292. package/esm/src/providers/openrouter-mod.js +0 -12
  293. package/esm/src/providers/openrouter-payload.d.ts +0 -39
  294. package/esm/src/providers/openrouter-payload.js +0 -195
  295. package/esm/src/providers/openrouter.d.ts +0 -15
  296. package/esm/src/providers/pcm.d.ts +0 -7
  297. package/esm/src/providers/provider.d.ts +0 -15
  298. package/esm/src/providers/provider.js +0 -202
  299. package/esm/src/providers/speech.d.ts +0 -23
  300. package/esm/src/providers/sse.d.ts +0 -7
  301. package/esm/src/providers/sse.js +0 -55
  302. package/esm/src/streaming/mod.d.ts +0 -9
  303. package/esm/src/streaming/mod.js +0 -8
  304. /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
  305. /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Live realtime inbound text — same sanitize + user_data fence as runTurn ingress.
3
+ *
4
+ * @module
5
+ */
6
+ import { wrapUserData } from '../../guardrails/canary.js';
7
+ import { guardrailFromHits, projectGuardrailTurnEvent } from '../../guardrails/events.js';
8
+ import { detectionForTrust, resolveGuardrailPolicy } from '../../guardrails/policy.js';
9
+ import { detectText } from '../../guardrails/sanitize.js';
10
+ import { resolveObservabilityPolicy } from '../../observability/resolve-policy.js';
11
+ /**
12
+ * Sanitize profile-controlled inbound text and wrap with user_data fencing.
13
+ *
14
+ * Resolves through the shared policy so Live ingress and the turn engine cannot
15
+ * drift apart on what an unset switch means. Returns an optional guardrail event
16
+ * for the session stream when redaction ran. Match previews follow
17
+ * `observability.include.guardrailMatchPreview`.
18
+ */
19
+ function prepareLiveInboundText(profile, text) {
20
+ const policy = resolveGuardrailPolicy(profile.guardrails);
21
+ const detected = detectText(text, detectionForTrust(policy, 'untrusted'));
22
+ const raw = guardrailFromHits('live_inbound', 'untrusted', detected.hits, 'redact');
23
+ const includeMatch = resolveObservabilityPolicy(profile.observability).include
24
+ .guardrailMatchPreview;
25
+ const guardrail = raw ? projectGuardrailTurnEvent(raw, includeMatch) : undefined;
26
+ return {
27
+ text: wrapUserData(detected.text),
28
+ ...(guardrail ? { guardrail } : {}),
29
+ };
30
+ }
31
+ export { prepareLiveInboundText };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Live realtime ingress — profile gates for sendAudio / sendVideo / sendText.
3
+ *
4
+ * @module
5
+ */
6
+ import type { LiveIngressSpec, Profile } from '../types.js';
7
+ export type LiveIngressChannel = keyof LiveIngressSpec;
8
+ /** Default when `live.ingress.<channel>` is omitted — mic and camera on, typed text off. */
9
+ export declare function liveIngressChannelDefault(channel: LiveIngressChannel): boolean;
10
+ /** Resolve one channel from an ingress spec object (no profile wrapper). */
11
+ export declare function liveIngressEnabledFromSpec(ingress: LiveIngressSpec | undefined, channel: LiveIngressChannel): boolean;
12
+ /** Whether a realtime ingress channel is enabled on the profile. */
13
+ export declare function liveIngressEnabled(profile: Profile, channel: LiveIngressChannel): boolean;
14
+ /** True when at least one realtime ingress channel is enabled. */
15
+ export declare function hasAnyLiveIngress(profile: Profile): boolean;
16
+ /** Reject profiles with every ingress channel disabled. */
17
+ export declare function assertLiveIngressConfigured(profile: Profile): void;
18
+ /** Reject send* calls when the profile disabled that ingress channel. */
19
+ export declare function assertLiveIngress(profile: Profile, channel: LiveIngressChannel): void;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Live realtime ingress — profile gates for sendAudio / sendVideo / sendText.
3
+ *
4
+ * @module
5
+ */
6
+ import { TheorumError } from '../../guardrails/error.js';
7
+ const LIVE_INGRESS_CHANNELS = ['audio', 'video', 'text'];
8
+ function assertLiveProfile(profile) {
9
+ if (profile.type !== 'live') {
10
+ throw new TheorumError(`Profile '${profile.id}' is not type 'live'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
11
+ }
12
+ return profile;
13
+ }
14
+ /** Default when `live.ingress.<channel>` is omitted — mic and camera on, typed text off. */
15
+ export function liveIngressChannelDefault(channel) {
16
+ return channel !== 'text';
17
+ }
18
+ /** Resolve one channel from an ingress spec object (no profile wrapper). */
19
+ export function liveIngressEnabledFromSpec(ingress, channel) {
20
+ const value = ingress?.[channel];
21
+ if (value === undefined)
22
+ return liveIngressChannelDefault(channel);
23
+ return value;
24
+ }
25
+ /** Whether a realtime ingress channel is enabled on the profile. */
26
+ export function liveIngressEnabled(profile, channel) {
27
+ const live = assertLiveProfile(profile);
28
+ return liveIngressEnabledFromSpec(live.live.ingress, channel);
29
+ }
30
+ /** True when at least one realtime ingress channel is enabled. */
31
+ export function hasAnyLiveIngress(profile) {
32
+ const live = assertLiveProfile(profile);
33
+ return LIVE_INGRESS_CHANNELS.some((channel) => liveIngressEnabledFromSpec(live.live.ingress, channel));
34
+ }
35
+ /** Reject profiles with every ingress channel disabled. */
36
+ export function assertLiveIngressConfigured(profile) {
37
+ const live = assertLiveProfile(profile);
38
+ if (hasAnyLiveIngress(live))
39
+ return;
40
+ throw new TheorumError(`Profile '${live.id}': at least one live.ingress channel (audio, video, text) must be enabled`);
41
+ }
42
+ /** Reject send* calls when the profile disabled that ingress channel. */
43
+ export function assertLiveIngress(profile, channel) {
44
+ if (liveIngressEnabled(profile, channel))
45
+ return;
46
+ throw new TheorumError(`Profile '${profile.id}' live.ingress.${channel} is disabled — cannot send on this channel`);
47
+ }
@@ -1,3 +1,5 @@
1
+ import { lexiconText } from '../../guardrails/lexicon.js';
2
+ import { profileTurnOutputs } from '../registry/profile-outputs.js';
1
3
  const MAX_REPAIR_HISTORY_EXCHANGES = 2;
2
4
  const MAX_REPAIR_HISTORY_MESSAGES = MAX_REPAIR_HISTORY_EXCHANGES * 2;
3
5
  function scopeHistory(history) {
@@ -11,28 +13,27 @@ function formatHistoryBlock(messages) {
11
13
  return '';
12
14
  }
13
15
  const lines = messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`);
14
- return `### RECENT CONVERSATION CONTEXT (LAST ${messages.length} TURNS)\n${lines.join('\n')}\n\n`;
16
+ const heading = lexiconText('repair.history_heading', { count: messages.length });
17
+ return `${heading}\n${lines.join('\n')}\n\n`;
15
18
  }
16
19
  function synthesizeRepairPrompt(args) {
17
20
  const { profile, repair, history } = args;
18
21
  const guidance = repair.guidance ||
19
- profile.outputs.validation?.repairGuidance ||
20
- 'Revise the previous output so it satisfies the validator rejection. Preserve the intended user-facing substance unless the guidance says otherwise.';
22
+ profileTurnOutputs(profile)?.validation?.repairGuidance ||
23
+ lexiconText('repair.default_guidance');
21
24
  const historyBlock = formatHistoryBlock(scopeHistory(history));
22
- let prompt = `## OUTPUT REPAIR REQUEST\n\n`;
23
- prompt += `The previous assistant output was rejected by a host validator and must be revised.\n\n`;
24
- prompt += `### PREVIOUS OUTPUT\n\`\`\`\n${repair.previousOutput.trim()}\n\`\`\`\n\n`;
25
- prompt += `### VALIDATOR REJECTION\n${repair.rejection.trim()}\n\n`;
25
+ let prompt = `${lexiconText('repair.prompt_header')}\n\n`;
26
+ prompt += `${lexiconText('repair.prompt_intro')}\n\n`;
27
+ prompt += `${lexiconText('repair.section_previous_output')}\n\`\`\`\n${repair.previousOutput.trim()}\n\`\`\`\n\n`;
28
+ prompt += `${lexiconText('repair.section_validator_rejection')}\n${repair.rejection.trim()}\n\n`;
26
29
  if (guidance.trim()) {
27
- prompt += `### REPAIR GUIDANCE\n${guidance.trim()}\n\n`;
30
+ prompt += `${lexiconText('repair.section_repair_guidance')}\n${guidance.trim()}\n\n`;
28
31
  }
29
32
  if (historyBlock) {
30
33
  prompt += historyBlock;
31
34
  }
32
- prompt += `### INSTRUCTIONS\n`;
33
- prompt += `1. Inspect the previous output and validator rejection.\n`;
34
- prompt += `2. Apply the repair guidance without inventing unsupported facts.\n`;
35
- prompt += `3. Return only the corrected assistant output required by the active profile.`;
35
+ prompt += `${lexiconText('repair.section_instructions')}\n`;
36
+ prompt += lexiconText('repair.prompt_instructions');
36
37
  return prompt;
37
38
  }
38
39
  export { synthesizeRepairPrompt };
@@ -1,4 +1,4 @@
1
1
  import type { ModelProvider, Profile, ResolvedGeneration, TurnEvent, TurnRequest } from '../../types.js';
2
2
  import type { StepExecutionState } from './state.js';
3
- declare function runAttemptsWithValidation(safe: TurnRequest, profile: Profile, generation: ResolvedGeneration, system: string, provider: ModelProvider, gemini: Record<string, unknown>[], state: StepExecutionState): AsyncGenerator<TurnEvent>;
3
+ declare function runAttemptsWithValidation(safe: TurnRequest, profile: Profile, generation: ResolvedGeneration, system: string, provider: ModelProvider, upstream: Record<string, unknown>[], state: StepExecutionState): AsyncGenerator<TurnEvent>;
4
4
  export { runAttemptsWithValidation };
@@ -1,14 +1,40 @@
1
+ import { runEnforcer } from '../../../guardrails/egress.js';
1
2
  import { TheorumError, throwIfAborted, toErrorEvent } from '../../../guardrails/error.js';
3
+ import { guardrailFromVerdict } from '../../../guardrails/events.js';
4
+ import { lexiconText } from '../../../guardrails/lexicon.js';
5
+ import { resolveGuardrailPolicy } from '../../../guardrails/policy.js';
2
6
  import { sanitizeTurnRequest } from '../../../guardrails/sanitize.js';
7
+ import { profileTurnOutputs } from '../../registry/profile-outputs.js';
3
8
  import { resolveTurn } from '../../registry/resolve.js';
4
9
  import { getStructured } from '../../registry/schemas.js';
10
+ import { injectWouldExceedMaxSteps } from '../../stages.js';
11
+ import { findLast } from '../../util/find-last.js';
5
12
  import { collectValidationFailures, formatValidationFailures } from './schema-validation.js';
13
+ import { applyTurnStage } from './stages.js';
6
14
  import { executeAttempt } from './steps.js';
15
+ /** Internal reason recorded when a turn is withheld; mapped to public copy on emit. */
16
+ const WITHHELD = 'Turn withheld: egress disclosure violation'; // lexicon-exempt: internal marker mapped by publicError
7
17
  function collectAttemptText(events) {
8
- return events
9
- .filter((e) => e.type === 'text' && e.text)
10
- .map((e) => e.text)
11
- .join('');
18
+ const parts = [];
19
+ for (const event of events) {
20
+ if (event.type === 'text' && event.text) {
21
+ parts.push(event.text);
22
+ }
23
+ }
24
+ return parts.join('');
25
+ }
26
+ /**
27
+ * Project attempt events into the egress payload.
28
+ *
29
+ * Structured output travels alongside text so a profile with `outputs.structured`
30
+ * is covered by its own egress policy rather than passing unexamined.
31
+ */
32
+ function projectOutbound(events) {
33
+ const structured = findLast(events, (e) => e.type === 'structured')?.structured;
34
+ return {
35
+ text: collectAttemptText(events),
36
+ ...(structured !== undefined ? { structured } : {}),
37
+ };
12
38
  }
13
39
  function buildRepairRequest(safe, previousOutput, rejection, repairGuidance) {
14
40
  return {
@@ -25,31 +51,45 @@ function buildRepairRequest(safe, previousOutput, rejection, repairGuidance) {
25
51
  }
26
52
  async function evaluateEgressOutcome(args) {
27
53
  const { egress, attemptEvents, generation, request, profile, canRetry } = args;
28
- const attemptText = collectAttemptText(attemptEvents);
29
- const result = await egress.enforce({
30
- text: attemptText,
31
- canary: generation.canary,
32
- slots: request.input?.slots,
33
- profile,
34
- role: request.input?.role,
35
- });
36
- if (!result.blocked) {
37
- return { action: 'pass' };
54
+ const payload = projectOutbound(attemptEvents);
55
+ const context = {
56
+ stage: 'output_final',
57
+ trust: 'untrusted',
58
+ profileId: profile.id,
59
+ ...(generation.canary ? { canary: generation.canary } : {}),
60
+ ...(request.input?.slots ? { slots: request.input.slots } : {}),
61
+ ...(request.input?.role ? { role: request.input.role } : {}),
62
+ };
63
+ const verdict = await runEnforcer(egress.enforce, payload, context);
64
+ const guardrail = guardrailFromVerdict('output_final', 'untrusted', verdict);
65
+ // `flag` is advisory: the hit is recorded, the turn still releases.
66
+ if (verdict.action === 'allow' || verdict.action === 'flag') {
67
+ return { outcome: { action: 'pass' }, guardrail };
68
+ }
69
+ // The policy supplied safe replacement prose — release that instead.
70
+ if (verdict.action === 'redact') {
71
+ return {
72
+ outcome: { action: 'refusal', event: { type: 'text', text: verdict.text } },
73
+ guardrail,
74
+ };
38
75
  }
39
76
  if (egress.onBlock === 'refuse_to_user') {
40
- return { action: 'refusal', event: { type: 'text', text: result.text } };
77
+ // Only emit a text turn when the policy supplied copy. Without it the kernel
78
+ // has nothing to say — an empty text event reads as a successful empty reply —
79
+ // so fall back to the same withheld error the exhausted-retry path uses.
80
+ return {
81
+ outcome: verdict.refusal
82
+ ? { action: 'refusal', event: { type: 'text', text: verdict.refusal } }
83
+ : { action: 'withhold', event: toErrorEvent(WITHHELD) },
84
+ guardrail,
85
+ };
41
86
  }
42
87
  if (canRetry) {
43
- const rejectionMsg = result.rejectionMessage || 'Egress disclosure violation detected.';
44
- const repairGuidance = egress.repairGuidance ||
45
- 'Rewrite the message as corrected user-visible prose only. Keep the same helpful substance; scrub all internal tool names, leak phrases, and disclosure markers.';
46
- const nextRequest = buildRepairRequest(request, attemptText, rejectionMsg, repairGuidance);
47
- return { action: 'retry', nextRequest };
88
+ const repairGuidance = egress.repairGuidance || lexiconText('egress.default_repair_guidance');
89
+ const nextRequest = buildRepairRequest(request, payload.text, verdict.rejection, repairGuidance);
90
+ return { outcome: { action: 'retry', nextRequest }, guardrail };
48
91
  }
49
- return {
50
- action: 'withhold',
51
- event: toErrorEvent('Turn withheld: egress disclosure violation'),
52
- };
92
+ return { outcome: { action: 'withhold', event: toErrorEvent(WITHHELD) }, guardrail };
53
93
  }
54
94
  async function evaluateValidationOutcome(args) {
55
95
  const { validation, generation, latestStructured, request, canRetry } = args;
@@ -97,7 +137,7 @@ function updateFlowForRetry(flow, nextReq) {
97
137
  }
98
138
  async function* handleEgressGate(egress, flow, state, profile, maxRetries) {
99
139
  const canRetry = flow.currentAttempt < maxRetries;
100
- const outcome = await evaluateEgressOutcome({
140
+ const { outcome, guardrail } = await evaluateEgressOutcome({
101
141
  egress,
102
142
  attemptEvents: state.attemptEvents,
103
143
  generation: flow.currentGen,
@@ -105,6 +145,10 @@ async function* handleEgressGate(egress, flow, state, profile, maxRetries) {
105
145
  profile,
106
146
  canRetry,
107
147
  });
148
+ if (guardrail) {
149
+ state.allEmittedEvents.push(guardrail);
150
+ yield guardrail;
151
+ }
108
152
  if (outcome.action === 'refusal') {
109
153
  state.allEmittedEvents.push(outcome.event);
110
154
  yield outcome.event;
@@ -150,19 +194,60 @@ function gateStatusToAction(status, terminalStatus = 'terminal') {
150
194
  return null;
151
195
  }
152
196
  async function* executeSingleAttemptCycle(args) {
153
- const { flow, state, profile, system, provider, gemini, maxRetries } = args;
154
- const validation = profile.outputs.validation;
155
- const egress = profile.guardrails.egress;
156
- state.attemptEvents = [];
157
- const { latestStructured } = yield* executeAttempt({
158
- safe: flow.currentReq,
159
- profile,
160
- generation: flow.currentGen,
161
- system,
162
- provider,
163
- gemini,
164
- state,
165
- });
197
+ const { flow, state, profile, system, provider, upstream, maxRetries } = args;
198
+ const validation = profileTurnOutputs(profile)?.validation;
199
+ const egress = resolveGuardrailPolicy(profile.guardrails).egress;
200
+ // Fresh maxSteps budget per validation/egress attempt. before_end inject
201
+ // re-entry inside this cycle still accumulates stepCount (do not reset there).
202
+ state.stepCount = 0;
203
+ let latestStructured;
204
+ // before_end may inject and re-enter the step loop under maxSteps.
205
+ for (;;) {
206
+ state.attemptEvents = [];
207
+ state.withheldVisible = false;
208
+ const attempt = yield* executeAttempt({
209
+ safe: flow.currentReq,
210
+ profile,
211
+ generation: flow.currentGen,
212
+ system,
213
+ provider,
214
+ upstream,
215
+ state,
216
+ });
217
+ latestStructured = attempt.latestStructured;
218
+ // Tool / gate suspension — do not before_end; finalize with that stop.
219
+ if (state.lastStop?.kind === 'tool' || state.lastStop?.kind === 'gate') {
220
+ break;
221
+ }
222
+ if (state.lastStop?.kind === 'cancelled') {
223
+ return { status: 'terminal' };
224
+ }
225
+ const beforeEnd = yield* applyTurnStage({
226
+ profile,
227
+ generation: flow.currentGen,
228
+ state,
229
+ stage: 'before_end',
230
+ step: Math.max(state.stepCount, 1),
231
+ onStage: flow.currentReq.onStage,
232
+ signal: flow.currentReq.signal,
233
+ host: flow.currentGen.host,
234
+ injectWouldExceedMaxSteps: injectWouldExceedMaxSteps(state.stepCount, flow.currentGen.maxSteps),
235
+ });
236
+ if (beforeEnd.abort) {
237
+ state.lastStop = {
238
+ kind: 'cancelled',
239
+ ...(typeof beforeEnd.abort === 'object' && beforeEnd.abort.reason
240
+ ? { native: beforeEnd.abort.reason }
241
+ : {}),
242
+ };
243
+ return { status: 'terminal' };
244
+ }
245
+ if (beforeEnd.injectCount > 0) {
246
+ // Host extended the turn — another provider step under maxSteps.
247
+ continue;
248
+ }
249
+ break;
250
+ }
166
251
  if (egress?.enforce) {
167
252
  const status = yield* handleEgressGate(egress, flow, state, profile, maxRetries);
168
253
  const action = gateStatusToAction(status, 'terminal');
@@ -178,13 +263,15 @@ async function* executeSingleAttemptCycle(args) {
178
263
  }
179
264
  }
180
265
  if (validation || egress?.enforce) {
181
- const alreadyStreamedUserVisible = !egress?.enforce;
182
- yield* yieldBufferedAttemptEvents(state.attemptEvents, alreadyStreamedUserVisible);
266
+ // Progressive-yield already released text/thought live under egress — unless it
267
+ // withheld them mid-stream. A passing final verdict on the full text supersedes
268
+ // that partial-window decision, so the buffer is released instead of dropped.
269
+ yield* yieldBufferedAttemptEvents(state.attemptEvents, !state.withheldVisible);
183
270
  }
184
271
  return { status: 'success' };
185
272
  }
186
- async function* runAttemptsWithValidation(safe, profile, generation, system, provider, gemini, state) {
187
- const maxRetries = Math.max(profile.outputs.validation?.maxRetries ?? 0, profile.guardrails.egress?.maxRetries ?? 2);
273
+ async function* runAttemptsWithValidation(safe, profile, generation, system, provider, upstream, state) {
274
+ const maxRetries = Math.max(profileTurnOutputs(profile)?.validation?.maxRetries ?? 0, resolveGuardrailPolicy(profile.guardrails).egress?.maxRetries ?? 0);
188
275
  const flow = {
189
276
  currentAttempt: 0,
190
277
  currentGen: generation,
@@ -198,7 +285,7 @@ async function* runAttemptsWithValidation(safe, profile, generation, system, pro
198
285
  profile,
199
286
  system,
200
287
  provider,
201
- gemini,
288
+ upstream,
202
289
  maxRetries,
203
290
  });
204
291
  if (step.status === 'terminal' || step.status === 'success') {
@@ -7,8 +7,10 @@
7
7
  *
8
8
  * @module
9
9
  */
10
- import { type TraceSink } from '../../../observability/trace.js';
11
- import type { ModelProvider, TurnEvent, TurnRequest } from '../../types.js';
10
+ import type { TraceSink } from '../../../observability/trace-sink.js';
11
+ import type { ModelProvider, TurnEvent, TurnHistoryMessage, TurnRequest } from '../../types.js';
12
+ /** Fold one history message to a compaction transcript line (media → markers). */
13
+ declare function compactionTranscriptLine(m: TurnHistoryMessage): string;
12
14
  /** Execute one host turn against a provider adapter. */
13
- declare function runTurn(req: TurnRequest, provider: ModelProvider, sink?: TraceSink): AsyncGenerator<TurnEvent>;
14
- export { runTurn };
15
+ declare function runTurn(req: TurnRequest, provider: ModelProvider, sinkOverride?: TraceSink): AsyncGenerator<TurnEvent>;
16
+ export { compactionTranscriptLine, runTurn };