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
@@ -1,29 +1,57 @@
1
1
  import { throwIfAborted } from '../../../guardrails/error.js';
2
+ import { resolveGuardrailPolicy } from '../../../guardrails/policy.js';
3
+ import { recordTaint } from '../../../guardrails/tool-result.js';
4
+ import { wireInteractionPart } from '../../interaction-parts.js';
5
+ import { profileTurnOutputs } from '../../registry/profile-outputs.js';
6
+ import { injectWouldExceedMaxSteps } from '../../stages.js';
7
+ import { profileAllowsInject } from '../../stop.js';
8
+ import { executeRegisteredTool, formatToolFailureForModel, formatToolResult, newCallId, } from '../../tools/execute.js';
9
+ import { applyStageInjects } from './stages.js';
2
10
  import { recordStepEvent } from './state.js';
3
11
  import { yieldProviderEvents } from './stream.js';
4
- import { executeDynamicDeclaration, findDynamicDeclaration, formatToolFinding, isActionableDynamicDeclaration, } from './tools.js';
5
12
  function isStepLimitReached(step, maxSteps) {
6
- if (maxSteps <= 0) {
13
+ if (maxSteps === undefined || maxSteps <= 0) {
7
14
  return false;
8
15
  }
9
16
  return step >= maxSteps;
10
17
  }
18
+ function generationForProviderStep(generation, state) {
19
+ if (state.interactionsContinuation) {
20
+ const { previousInteractionId, input } = state.interactionsContinuation;
21
+ state.interactionsContinuation = undefined;
22
+ return {
23
+ ...generation,
24
+ history: [],
25
+ input: [],
26
+ previousInteractionId,
27
+ interactionOnlyInput: input,
28
+ };
29
+ }
30
+ return { ...generation, history: state.currentHistory };
31
+ }
32
+ function captureInteractionId(event, state) {
33
+ if (event.interactionId) {
34
+ state.lastInteractionId = event.interactionId;
35
+ }
36
+ }
11
37
  async function* executeAutonomousStep(args, state, buffer = {
12
38
  holdLate: false,
13
- holdUserVisible: false,
14
39
  }) {
15
- const { profile, generation, system, provider, gemini, signal } = args;
16
- const genForStep = { ...generation, history: state.currentHistory };
40
+ const { generation, system, provider, upstream, signal } = args;
41
+ const genForStep = generationForProviderStep(generation, state);
17
42
  const pendingTools = [];
18
43
  let latestStructured;
44
+ const control = { withholdVisible: false };
19
45
  for await (const event of yieldProviderEvents({
20
- profile,
46
+ profile: args.profile,
21
47
  generation: genForStep,
22
48
  system,
23
49
  provider,
24
- gemini,
50
+ upstream,
25
51
  signal,
52
+ control,
26
53
  })) {
54
+ captureInteractionId(event, state);
27
55
  if (event.type === 'structured') {
28
56
  latestStructured = event.structured;
29
57
  }
@@ -38,22 +66,41 @@ async function* executeAutonomousStep(args, state, buffer = {
38
66
  continue;
39
67
  }
40
68
  recordStepEvent(event, state);
41
- const isUserVisible = event.type === 'thought' || event.type === 'text';
42
- // Egress must not stream user-visible text before the gate runs.
43
- // Validation-only may stream thought/text live and only hold structured.
44
- const streamNow = !buffer.holdLate || event.type === 'tokens' || (isUserVisible && !buffer.holdUserVisible);
69
+ const isUserVisible = event.type === 'thought' || event.type === 'text' || event.type === 'media';
70
+ if (control.withholdVisible && isUserVisible) {
71
+ // Progressive-yield blocked this attempt — keep events for egress/repair only.
72
+ // Record the decision so the attempt gate knows nothing reached the host.
73
+ state.withheldVisible = true;
74
+ continue;
75
+ }
76
+ // Progressive-yield streams text/thought live under egress; holdLate only
77
+ // buffers non-visible events (e.g. structured) for validation.
78
+ const streamNow = !buffer.holdLate || event.type === 'tokens' || isUserVisible;
45
79
  if (streamNow) {
46
80
  yield event;
47
81
  }
48
82
  }
49
83
  return { pendingTools, latestStructured };
50
84
  }
51
- function appendToolTurnToHistory(history, toolEv, res) {
85
+ function appendInteractionsToolResultToHistory(history, toolEv, result) {
52
86
  const tool = toolEv.tool;
53
87
  if (!tool) {
54
88
  return;
55
89
  }
56
- const callId = tool.id ?? `call_${tool.name}_${String(Date.now())}`;
90
+ history.push({
91
+ role: 'tool',
92
+ tool_call_id: tool.id ?? tool.callId ?? `call_${tool.name}`,
93
+ name: tool.name,
94
+ content: formatToolResult(result),
95
+ ...(result.parts && result.parts.length > 0 ? { parts: result.parts } : {}),
96
+ });
97
+ }
98
+ function appendToolTurnToHistory(history, toolEv, result) {
99
+ const tool = toolEv.tool;
100
+ if (!tool) {
101
+ return;
102
+ }
103
+ const callId = tool.id ?? tool.callId ?? newCallId(tool.name);
57
104
  history.push({
58
105
  role: 'assistant',
59
106
  tool_calls: [
@@ -71,51 +118,205 @@ function appendToolTurnToHistory(history, toolEv, res) {
71
118
  role: 'tool',
72
119
  tool_call_id: callId,
73
120
  name: tool.name,
74
- content: formatToolFinding(res),
121
+ content: formatToolResult(result),
122
+ ...(result.parts && result.parts.length > 0 ? { parts: result.parts } : {}),
75
123
  });
76
124
  }
77
- async function* handlePendingDynamicTools(pendingTools, generation, profile, state) {
78
- let hasRunnableHandler = false;
125
+ function queueInteractionsToolContinuation(state, toolEv, result, fallbackInteractionId) {
126
+ const tool = toolEv.tool;
127
+ if (!tool) {
128
+ return;
129
+ }
130
+ const previousInteractionId = state.lastInteractionId ?? fallbackInteractionId;
131
+ if (!previousInteractionId) {
132
+ return;
133
+ }
134
+ const step = {
135
+ type: 'function_result',
136
+ name: tool.name,
137
+ call_id: tool.id ?? tool.callId ?? `call_${tool.name}`,
138
+ result: result.parts && result.parts.length > 0
139
+ ? result.parts.map(wireInteractionPart)
140
+ : [{ type: 'text', text: formatToolResult(result) }],
141
+ };
142
+ if (state.interactionsContinuation &&
143
+ state.interactionsContinuation.previousInteractionId === previousInteractionId) {
144
+ state.interactionsContinuation.input.push(step);
145
+ return;
146
+ }
147
+ state.interactionsContinuation = {
148
+ previousInteractionId,
149
+ input: [step],
150
+ };
151
+ }
152
+ function recordToolModelResult(state, toolEv, modelResult, generation, useInteractionsContinuation) {
153
+ if (useInteractionsContinuation) {
154
+ queueInteractionsToolContinuation(state, toolEv, modelResult, generation.previousInteractionId);
155
+ if (!state.interactionsContinuation) {
156
+ appendInteractionsToolResultToHistory(state.currentHistory, toolEv, modelResult);
157
+ }
158
+ return;
159
+ }
160
+ appendToolTurnToHistory(state.currentHistory, toolEv, modelResult);
161
+ }
162
+ function enrichToolEvent(tool, callId, patch) {
163
+ return {
164
+ type: 'tool',
165
+ tool: {
166
+ ...tool,
167
+ ...patch,
168
+ callId,
169
+ id: tool.id ?? callId,
170
+ },
171
+ };
172
+ }
173
+ async function* drainToolExecEvents(exec, tool, callId, state) {
174
+ let next = await exec.next();
175
+ let sawGate = false;
176
+ while (!next.done) {
177
+ const event = next.value;
178
+ if (event.type === 'tool') {
179
+ const enriched = enrichToolEvent(tool, callId, event.tool);
180
+ state.allEmittedEvents.push(enriched);
181
+ yield enriched;
182
+ if (event.tool?.phase === 'gate')
183
+ sawGate = true;
184
+ }
185
+ else {
186
+ state.allEmittedEvents.push(event);
187
+ yield event;
188
+ }
189
+ next = await exec.next();
190
+ }
191
+ return { settlement: next.value, sawGate };
192
+ }
193
+ function recordProviderToolFailure(state, toolEv, tool, callId, failure, generation, useInteractionsContinuation, patch) {
194
+ const enriched = enrichToolEvent(tool, callId, {
195
+ phase: 'error',
196
+ failure,
197
+ ...patch,
198
+ });
199
+ state.allEmittedEvents.push(enriched);
200
+ recordToolModelResult(state, toolEv, formatToolFailureForModel(failure), generation, useInteractionsContinuation);
201
+ return enriched;
202
+ }
203
+ function applyToolSettlement(settlement, state, toolEv, generation, useInteractionsContinuation) {
204
+ if (settlement.aborted) {
205
+ state.lastStop = {
206
+ kind: 'cancelled',
207
+ ...(typeof settlement.aborted === 'object' && settlement.aborted.reason
208
+ ? { native: settlement.aborted.reason }
209
+ : {}),
210
+ };
211
+ return 'stop_cancelled';
212
+ }
213
+ if (settlement.gated) {
214
+ state.toolSnapshot = generation.tools;
215
+ return 'gated';
216
+ }
217
+ if (settlement.modelResult?.provenance) {
218
+ state.taint = recordTaint(state.taint, settlement.modelResult.provenance, settlement.modelResult.suspicious);
219
+ }
220
+ if (!settlement.modelResult)
221
+ return 'continue';
222
+ recordToolModelResult(state, toolEv, settlement.modelResult, generation, useInteractionsContinuation);
223
+ if (settlement.pendingInject?.length) {
224
+ applyStageInjects(state, settlement.pendingInject);
225
+ }
226
+ return 'continue';
227
+ }
228
+ async function* handlePendingTools(pendingTools, generation, profile, state, safe) {
229
+ let executed = false;
230
+ let sawGate = false;
231
+ const useInteractionsContinuation = generation.transport === 'interactions';
79
232
  for (const toolEv of pendingTools) {
233
+ if (sawGate)
234
+ break;
80
235
  const tool = toolEv.tool;
81
- if (!tool) {
236
+ if (!tool)
237
+ continue;
238
+ executed = true;
239
+ const callId = tool.id ?? tool.callId ?? newCallId(tool.name || 'unknown');
240
+ if (tool.phase === 'cancel') {
241
+ const enriched = enrichToolEvent(tool, callId);
242
+ state.allEmittedEvents.push(enriched);
243
+ yield enriched;
82
244
  continue;
83
245
  }
84
- const decl = findDynamicDeclaration(generation.dynamicTools, tool.name);
85
- if (!isActionableDynamicDeclaration(decl)) {
86
- state.allEmittedEvents.push(toolEv);
87
- yield toolEv;
246
+ if (tool.phase === 'error' && tool.failure) {
247
+ const enriched = enrichToolEvent(tool, callId);
248
+ state.allEmittedEvents.push(enriched);
249
+ yield enriched;
250
+ recordToolModelResult(state, toolEv, formatToolFailureForModel(tool.failure), generation, useInteractionsContinuation);
88
251
  continue;
89
252
  }
90
- hasRunnableHandler = true;
91
- const finalResult = await executeDynamicDeclaration({
92
- decl,
93
- toolArgs: tool.arguments ?? {},
253
+ if (!tool.name) {
254
+ yield recordProviderToolFailure(state, toolEv, tool, callId, {
255
+ code: 'malformed_arguments',
256
+ message: 'Provider tool call is missing a function name', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
257
+ }, generation, useInteractionsContinuation, { name: '' });
258
+ continue;
259
+ }
260
+ const stages = {
261
+ handlers: safe?.onStage ? [safe.onStage] : [],
94
262
  profile,
95
- generation,
96
- });
97
- const enrichedEvent = {
98
- type: 'tool',
99
- tool: { ...tool, result: finalResult },
263
+ step: state.stepCount,
264
+ history: () => state.currentHistory,
265
+ injectAllowed: profileAllowsInject(profile),
266
+ injectWouldExceedMaxSteps: injectWouldExceedMaxSteps(state.stepCount, generation.maxSteps),
267
+ host: generation.host,
268
+ signal: safe?.signal,
100
269
  };
101
- state.allEmittedEvents.push(enrichedEvent);
102
- yield enrichedEvent;
103
- appendToolTurnToHistory(state.currentHistory, toolEv, finalResult);
270
+ const drained = yield* drainToolExecEvents(executeRegisteredTool({
271
+ profile,
272
+ name: tool.name,
273
+ input: tool.arguments ?? {},
274
+ callId,
275
+ ctx: {
276
+ sessionPermissions: generation.sessionPermissions,
277
+ path: generation.tools.path,
278
+ turn: { step: state.stepCount, taint: state.taint },
279
+ credentials: safe?.credentials,
280
+ host: generation.host,
281
+ resume: undefined,
282
+ signal: safe?.signal,
283
+ },
284
+ snapshot: generation.tools,
285
+ stages,
286
+ }), tool, callId, state);
287
+ if (drained.sawGate)
288
+ sawGate = true;
289
+ const outcome = applyToolSettlement(drained.settlement, state, toolEv, generation, useInteractionsContinuation);
290
+ if (outcome === 'stop_cancelled')
291
+ return false;
292
+ if (outcome === 'gated' || sawGate) {
293
+ sawGate = true;
294
+ }
295
+ }
296
+ if (sawGate) {
297
+ state.lastStop = { kind: 'gate' };
298
+ return false;
104
299
  }
105
- return hasRunnableHandler;
300
+ return executed;
106
301
  }
107
302
  async function* executeAttempt(args) {
108
- const { profile, generation, system, provider, gemini, state } = args;
303
+ const { profile, generation, system, provider, upstream, state } = args;
109
304
  let latestStructured;
110
305
  let pendingTools = [];
111
- let stepInAttempt = 0;
112
- const holdUserVisible = Boolean(profile.guardrails.egress?.enforce);
113
- const holdLate = Boolean(profile.outputs.validation) || holdUserVisible;
114
- while (!isStepLimitReached(stepInAttempt, generation.maxSteps)) {
306
+ // Text/thought stream via progressive-yield under egress; validation and egress
307
+ // both hold non-visible events (structured) until the attempt gate, so a policy
308
+ // sees the structured payload before any of it reaches the host.
309
+ const holdLate = Boolean(profileTurnOutputs(profile)?.validation ||
310
+ resolveGuardrailPolicy(profile.guardrails).egress?.enforce);
311
+ // Ceiling is cumulative `state.stepCount` across before_end inject re-entries
312
+ // within this attempt. Validation/egress repair resets stepCount at the start
313
+ // of each attempt cycle (see gates.ts).
314
+ while (!isStepLimitReached(state.stepCount, generation.maxSteps ?? 0)) {
115
315
  throwIfAborted(args.safe.signal);
116
- stepInAttempt++;
117
316
  state.stepCount++;
118
- const stepResult = yield* executeAutonomousStep({ profile, generation, system, provider, gemini, signal: args.safe.signal }, state, { holdLate, holdUserVisible });
317
+ // Mid-loop inject lives on `post_tool` (after tools). Opening inject is `pre_turn`
318
+ // outside this loop.
319
+ const stepResult = yield* executeAutonomousStep({ profile, generation, system, provider, upstream, signal: args.safe.signal }, state, { holdLate });
119
320
  if (stepResult.latestStructured !== undefined) {
120
321
  latestStructured = stepResult.latestStructured;
121
322
  }
@@ -123,8 +324,8 @@ async function* executeAttempt(args) {
123
324
  if (pendingTools.length === 0) {
124
325
  break;
125
326
  }
126
- const hasRunnableHandler = yield* handlePendingDynamicTools(pendingTools, generation, profile, state);
127
- if (!hasRunnableHandler) {
327
+ const executed = yield* handlePendingTools(pendingTools, generation, profile, state, args.safe);
328
+ if (!executed) {
128
329
  break;
129
330
  }
130
331
  }
@@ -1,12 +1,18 @@
1
1
  import type { ModelProvider, Profile, ResolvedGeneration, TurnEvent } from '../../types.js';
2
- declare function systemFromProfile(profile: Profile, role: string): string;
2
+ /** Mutable control flags shared with the step runner during one provider stream. */
3
+ interface OutboundStreamControl {
4
+ /** Stop releasing text/thought/media to the host; keep recording for egress. */
5
+ withholdVisible: boolean;
6
+ }
3
7
  declare function shouldSkipStreamEvent(event: TurnEvent, profile: Profile): boolean;
4
8
  declare function yieldProviderEvents(args: {
5
9
  profile: Profile;
6
10
  generation: ResolvedGeneration;
7
11
  system: string;
8
12
  provider: ModelProvider;
9
- gemini: Record<string, unknown>[];
13
+ upstream: Record<string, unknown>[];
10
14
  signal?: AbortSignal;
15
+ control?: OutboundStreamControl;
11
16
  }): AsyncGenerator<TurnEvent>;
12
- export { shouldSkipStreamEvent, systemFromProfile, yieldProviderEvents };
17
+ export type { OutboundStreamControl };
18
+ export { shouldSkipStreamEvent, yieldProviderEvents };
@@ -1,44 +1,16 @@
1
+ import { eventHasCanary, isStreamedCanaryEvent, redactCanary } from '../../../guardrails/canary.js';
2
+ import { EGRESS_RULES } from '../../../guardrails/egress.js';
1
3
  import { publicError, throwIfAborted, toErrorEvent } from '../../../guardrails/error.js';
4
+ import { guardrailFromHits } from '../../../guardrails/events.js';
5
+ import { resolveGuardrailPolicy } from '../../../guardrails/policy.js';
6
+ import { createOutboundProgressiveGate, } from '../../../guardrails/progressive-yield.js';
7
+ import { profileTurnOutputs } from '../../registry/profile-outputs.js';
2
8
  import { providerCompleteRequest } from '../../registry/provider-request.js';
3
- import { eventHasCanary, redactCanary } from '../boundary.js';
4
- import { dispatchModelTool } from './tools.js';
5
- function systemFromProfile(profile, role) {
6
- const { identity } = profile;
7
- const { systemByRole, system } = identity;
8
- if (systemByRole) {
9
- const byRole = systemByRole[role];
10
- if (byRole) {
11
- return byRole;
12
- }
13
- }
14
- if (system) {
15
- return system;
16
- }
17
- return '';
18
- }
19
- function* interceptProviderTool(event, profile, generation) {
20
- const tool = event.tool;
21
- if (!tool) {
22
- return;
23
- }
24
- const isDynamic = generation.dynamicTools?.some((d) => d.name === tool.name);
25
- if (isDynamic) {
26
- yield event;
27
- }
28
- else {
29
- for (const item of dispatchModelTool(profile, event, generation.custom)) {
30
- yield item;
31
- }
32
- }
33
- }
34
9
  function shouldSkipStreamEvent(event, profile) {
35
- return event.type === 'thought' && profile.outputs.streaming?.streamThoughts === false;
10
+ return (event.type === 'thought' && profileTurnOutputs(profile)?.streaming?.streamThoughts === false);
36
11
  }
37
- function* processNormalEvent(event, profile, generation) {
38
- if (event.type === 'tool') {
39
- yield* interceptProviderTool(event, profile, generation);
40
- }
41
- else if (event.type === 'error') {
12
+ function* processNormalEvent(event) {
13
+ if (event.type === 'error') {
42
14
  const internal = event.errorInternal ?? event.error ?? '';
43
15
  yield {
44
16
  type: 'error',
@@ -50,24 +22,148 @@ function* processNormalEvent(event, profile, generation) {
50
22
  yield event;
51
23
  }
52
24
  }
25
+ function* yieldCanaryLeak(canary, event) {
26
+ const guardrail = guardrailFromHits('output_delta', 'untrusted', [{ rule: EGRESS_RULES.canary, severity: 'high', match: '[canary]' }], 'block');
27
+ if (guardrail) {
28
+ yield guardrail;
29
+ }
30
+ yield redactCanary(event, canary);
31
+ yield toErrorEvent('canary leaked');
32
+ }
33
+ function* yieldDeltaBlock(hits) {
34
+ const guardrail = guardrailFromHits('output_delta', 'untrusted', hits, 'block');
35
+ if (guardrail) {
36
+ yield guardrail;
37
+ }
38
+ }
39
+ function isHostVisible(event) {
40
+ return event.type === 'text' || event.type === 'thought' || event.type === 'media';
41
+ }
42
+ function canaryOnlyImmediateStop(policy) {
43
+ return !policy.egress?.enforce;
44
+ }
45
+ function hasCanaryHit(hits) {
46
+ return hits.some((hit) => hit.rule === EGRESS_RULES.canary);
47
+ }
53
48
  async function* yieldProviderEvents(args) {
54
- const { profile, generation, system, provider, gemini, signal } = args;
49
+ const { profile, generation, system, provider, upstream, signal, control } = args;
55
50
  const { canary } = generation;
51
+ const policy = resolveGuardrailPolicy(profile.guardrails);
52
+ const context = {
53
+ stage: 'output_final',
54
+ trust: 'untrusted',
55
+ profileId: profile.id,
56
+ ...(canary ? { canary } : {}),
57
+ };
58
+ const gate = createOutboundProgressiveGate(policy, context);
59
+ let lastStreamType;
60
+ let withholdVisible = false;
61
+ function armWithhold() {
62
+ withholdVisible = true;
63
+ if (control)
64
+ control.withholdVisible = true;
65
+ }
66
+ function withholding() {
67
+ return withholdVisible;
68
+ }
69
+ async function* drainBlockedDelta(hits) {
70
+ yield* yieldDeltaBlock(hits);
71
+ // Arm withhold before recording the unreleased tail so the step runner
72
+ // does not forward that text to the host.
73
+ armWithhold();
74
+ const tail = gate?.drainUnreleased();
75
+ if (tail)
76
+ yield { type: 'text', text: tail };
77
+ }
78
+ async function* flushGate() {
79
+ if (!gate || !lastStreamType) {
80
+ return 'pass';
81
+ }
82
+ const emitType = lastStreamType;
83
+ const result = await gate.flush();
84
+ lastStreamType = undefined;
85
+ if (result.blocked) {
86
+ if (hasCanaryHit(result.hits) && canary && canaryOnlyImmediateStop(policy)) {
87
+ yield* yieldCanaryLeak(canary, { type: emitType, text: gate.accumulated() });
88
+ return 'stop';
89
+ }
90
+ yield* drainBlockedDelta(result.hits);
91
+ return 'pass';
92
+ }
93
+ if (result.emit) {
94
+ yield* processNormalEvent({ type: emitType, text: result.emit });
95
+ }
96
+ return 'pass';
97
+ }
98
+ async function* gateStreamEvent(event) {
99
+ if (!gate) {
100
+ yield* processNormalEvent(event);
101
+ return 'continue';
102
+ }
103
+ if (withholding()) {
104
+ // Keep recording ungated fragments for egress context; host will not see them.
105
+ yield { type: event.type, text: event.text ?? '' };
106
+ return 'continue';
107
+ }
108
+ if (lastStreamType && lastStreamType !== event.type) {
109
+ const flushed = yield* flushGate();
110
+ if (flushed === 'stop') {
111
+ return 'stop';
112
+ }
113
+ }
114
+ lastStreamType = event.type;
115
+ const result = await gate.process(event.text ?? '');
116
+ if (result.blocked) {
117
+ if (hasCanaryHit(result.hits) && canary && canaryOnlyImmediateStop(policy)) {
118
+ yield* yieldCanaryLeak(canary, event);
119
+ return 'stop';
120
+ }
121
+ yield* drainBlockedDelta(result.hits);
122
+ return 'continue';
123
+ }
124
+ if (result.emit) {
125
+ yield* processNormalEvent({ ...event, text: result.emit });
126
+ }
127
+ return 'continue';
128
+ }
56
129
  throwIfAborted(signal);
57
130
  for await (const event of provider.complete({
58
131
  ...providerCompleteRequest(generation, system),
59
132
  signal,
60
- tapGemini: (row) => {
61
- gemini.push(row);
133
+ tapUpstream: (row) => {
134
+ upstream.push(row);
62
135
  },
63
136
  })) {
64
137
  throwIfAborted(signal);
138
+ if (isStreamedCanaryEvent(event)) {
139
+ const status = yield* gateStreamEvent(event);
140
+ if (status === 'stop') {
141
+ return;
142
+ }
143
+ continue;
144
+ }
145
+ if (gate && lastStreamType) {
146
+ const flushed = yield* flushGate();
147
+ if (flushed === 'stop') {
148
+ return;
149
+ }
150
+ }
65
151
  if (canary && eventHasCanary(event, canary)) {
66
- yield redactCanary(event, canary);
67
- yield toErrorEvent('canary leaked');
152
+ yield* yieldCanaryLeak(canary, event);
153
+ return;
154
+ }
155
+ if (withholding() && isHostVisible(event)) {
156
+ // Record for attempt egress / repair; step runner withholds from host.
157
+ yield event;
158
+ continue;
159
+ }
160
+ yield* processNormalEvent(event);
161
+ }
162
+ if (gate && lastStreamType) {
163
+ const flushed = yield* flushGate();
164
+ if (flushed === 'stop') {
68
165
  return;
69
166
  }
70
- yield* processNormalEvent(event, profile, generation);
71
167
  }
72
168
  }
73
- export { shouldSkipStreamEvent, systemFromProfile, yieldProviderEvents };
169
+ export { shouldSkipStreamEvent, yieldProviderEvents };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `runSession` — long-lived live profile execution.
3
+ *
4
+ * Shares resolve / tools / canary / system / outbound gate with `runTurn`.
5
+ * Does not use `ModelProvider.complete()` — live is a session, not one turn.
6
+ *
7
+ * Stages: `docs/contracts/stages.md` (cycle map + `executeTool`).
8
+ *
9
+ * @module
10
+ */
11
+ import type { GeminiTransport } from '../../../providers/google/keys.js';
12
+ import type { LiveSession, SessionRequest } from '../../types.js';
13
+ export type { LiveSession, SessionRequest };
14
+ export interface RunSessionOptions {
15
+ gemini: GeminiTransport;
16
+ /** Override socket open (Cloudflare fetch-upgrade, tests). Default: `new WebSocket(url)`. */
17
+ openWebSocket?: (url: string) => Promise<WebSocket>;
18
+ }
19
+ /**
20
+ * Open a gated Gemini Live session for a `type: 'live'` profile.
21
+ *
22
+ * Hosts bridge browser sockets and tool dispatch; THEORUM owns Gemini WS,
23
+ * framing, inbound prep, outbound canary/egress gates, and live stages.
24
+ */
25
+ export declare function runSession(req: SessionRequest, options: RunSessionOptions): Promise<LiveSession>;