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,588 @@
1
+ /**
2
+ * Google Interactions provider adapter.
3
+ *
4
+ * This adapter converts THEORUM's provider-neutral request into the Google
5
+ * Interactions wire format and streams normalized `TurnEvent` objects.
6
+ * Speech-role turns use `response_format: audio` + `speech_config` (same
7
+ * transport as chat/image).
8
+ *
9
+ * @module
10
+ */
11
+ import { isAbortError, TheorumError, toErrorEvent } from '../../../guardrails/error.js';
12
+ import { codeExecutionEvidence, codeExecutionStepKey, eventsFromComplete, eventsFromDelta, extractTokenEvent, googleBuiltinEvidence, groundingFromEvent, isCodeExecutionType, isGoogleBuiltinStepType, mergeCodeExecutionPayload, parseStructuredOutput, } from '../../../kernel/engine/delta.js';
13
+ import { asRecord } from '../../../kernel/engine/record.js';
14
+ import { base64ToBytes, bytesToBase64, wrapPcmAsWav } from '../../shared/pcm.js';
15
+ import { readSseChunks } from '../../shared/sse.js';
16
+ import { parseToolArgumentsObject } from '../../shared/tool-args.js';
17
+ import { tapFetch } from '../../shared/upstream-tap.js';
18
+ import { fetchGemini } from '../keys.js';
19
+ import { INTERACTIONS_JSON_URL, INTERACTIONS_URL } from '../urls.js';
20
+ import { toInteractionsBody } from './framing.js';
21
+ const HTTP_OK = 200;
22
+ export function isRawPcmMime(mime) {
23
+ const lower = mime.toLowerCase();
24
+ return (lower.startsWith('audio/pcm') || lower.startsWith('audio/raw') || lower.startsWith('audio/l16'));
25
+ }
26
+ export function normalizeSpeechMedia(event, speech) {
27
+ if (!speech || event.type !== 'media' || !event.media) {
28
+ return event;
29
+ }
30
+ const { media } = event;
31
+ if (!isRawPcmMime(media.mimeType)) {
32
+ return event;
33
+ }
34
+ const pcm = base64ToBytes(media.data);
35
+ const wav = wrapPcmAsWav(pcm, 24000);
36
+ return {
37
+ type: 'media',
38
+ media: { mimeType: 'audio/wav', data: bytesToBase64(wav) },
39
+ };
40
+ }
41
+ export function newStreamFold() {
42
+ return {
43
+ text: '',
44
+ functionCalls: new Map(),
45
+ emittedToolKeys: new Set(),
46
+ emittedEvidenceKeys: new Set(),
47
+ codeSteps: new Map(),
48
+ sawStreamedMedia: false,
49
+ };
50
+ }
51
+ export function eventType(payload) {
52
+ return String(payload.event_type ?? payload.type ?? '');
53
+ }
54
+ export function isDeltaEvent(type) {
55
+ return type === 'content.delta' || type === 'step.delta';
56
+ }
57
+ export function isCompleteEvent(type) {
58
+ return type === 'interaction.complete' || type === 'interaction.completed';
59
+ }
60
+ export function* yieldGrounding(event) {
61
+ const g = groundingFromEvent(event);
62
+ if (g) {
63
+ yield g;
64
+ }
65
+ }
66
+ export function* yieldTokens(event) {
67
+ const tokens = extractTokenEvent(event);
68
+ if (tokens) {
69
+ yield tokens;
70
+ }
71
+ }
72
+ export function functionCallKey(tool) {
73
+ return `${tool.name ?? ''}:${tool.id ?? ''}:${JSON.stringify(tool.arguments ?? {})}`;
74
+ }
75
+ export function* yieldEvidenceStep(step, emittedKeys) {
76
+ const event = googleBuiltinEvidence(step);
77
+ const ev = event.evidence;
78
+ if (!ev)
79
+ return;
80
+ const kind = String(ev.kind ?? ev.raw?.type ?? '');
81
+ const id = (typeof ev.callId === 'string' && ev.callId) ||
82
+ (typeof ev.id === 'string' && ev.id) ||
83
+ (typeof ev.raw?.call_id === 'string' && ev.raw.call_id) ||
84
+ (typeof ev.raw?.id === 'string' && ev.raw.id) ||
85
+ '';
86
+ const hasResult = ev.raw?.result !== undefined;
87
+ // Stub maps/search starts often arrive before `result`; allow a second emit once result lands.
88
+ const key = `${kind}:${id}:${hasResult ? 'result' : 'stub'}:${ev.code ?? ''}`;
89
+ if (emittedKeys.has(key))
90
+ return;
91
+ // Prefer the result-bearing emit: drop the stub key so hosts don't keep an empty payload.
92
+ if (hasResult) {
93
+ emittedKeys.delete(`${kind}:${id}:stub:`);
94
+ }
95
+ emittedKeys.add(key);
96
+ yield event;
97
+ }
98
+ function recordCodeStep(index, delta, steps) {
99
+ const existing = steps.get(index);
100
+ if (!existing) {
101
+ steps.set(index, delta);
102
+ return;
103
+ }
104
+ mergeCodeExecutionPayload(existing, delta);
105
+ }
106
+ export function isCompleteCodeStep(step) {
107
+ return step.arguments !== undefined || step.result !== undefined;
108
+ }
109
+ function* flushCompletedCodeSteps(steps, emittedKeys) {
110
+ for (const [index, step] of steps) {
111
+ if (!isCompleteCodeStep(step)) {
112
+ continue;
113
+ }
114
+ const key = codeExecutionStepKey(step);
115
+ if (!key || emittedKeys.has(key)) {
116
+ continue;
117
+ }
118
+ const event = codeExecutionEvidence(step);
119
+ if (event) {
120
+ emittedKeys.add(key);
121
+ steps.delete(index);
122
+ yield event;
123
+ }
124
+ }
125
+ }
126
+ export function foldStepStart(payload, fold) {
127
+ const step = asRecord(payload.step);
128
+ if (!step)
129
+ return [];
130
+ const type = String(step.type ?? '');
131
+ const index = typeof payload.index === 'number' ? payload.index : 0;
132
+ if (type === 'function_call') {
133
+ // Always buffer until step.stop. Gemini often sends arguments: {} on start, then
134
+ // streams the real JSON via arguments_delta — early emit would run with empty args
135
+ // and leave a nameless pending for the deltas.
136
+ const existing = fold.functionCalls.get(index);
137
+ let argumentsStr = existing?.arguments ?? '';
138
+ if (typeof step.arguments === 'string') {
139
+ argumentsStr = step.arguments;
140
+ }
141
+ else if (step.arguments !== undefined && typeof step.arguments === 'object') {
142
+ argumentsStr = JSON.stringify(step.arguments);
143
+ }
144
+ fold.functionCalls.set(index, {
145
+ id: typeof step.id === 'string' ? step.id : existing?.id,
146
+ name: typeof step.name === 'string' ? step.name : existing?.name,
147
+ arguments: argumentsStr,
148
+ });
149
+ return [];
150
+ }
151
+ if (isCodeExecutionType(type)) {
152
+ recordCodeStep(index, step, fold.codeSteps);
153
+ return Array.from(flushCompletedCodeSteps(fold.codeSteps, fold.emittedEvidenceKeys));
154
+ }
155
+ if (isGoogleBuiltinStepType(type)) {
156
+ return Array.from(yieldEvidenceStep(step, fold.emittedEvidenceKeys));
157
+ }
158
+ return [];
159
+ }
160
+ export function parseArgumentsObject(raw) {
161
+ return parseToolArgumentsObject(raw);
162
+ }
163
+ export function emitUniqueToolEvent(tool, fold) {
164
+ const key = functionCallKey(tool);
165
+ if (fold.emittedToolKeys.has(key))
166
+ return [];
167
+ fold.emittedToolKeys.add(key);
168
+ return [{ type: 'tool', tool }];
169
+ }
170
+ /** Emit a tool call, or a structured tool failure when arguments cannot be parsed. */
171
+ export function emitToolCallFromRawArguments(tool, rawArguments, fold) {
172
+ const name = tool.name.trim();
173
+ if (!name) {
174
+ return emitUniqueToolEvent({
175
+ id: tool.id,
176
+ name: '',
177
+ arguments: {},
178
+ phase: 'error',
179
+ failure: {
180
+ code: 'malformed_arguments',
181
+ message: 'function call is missing a name',
182
+ details: { rawArguments },
183
+ },
184
+ }, fold);
185
+ }
186
+ const parsed = parseArgumentsObject(rawArguments);
187
+ if (!parsed.ok) {
188
+ return emitUniqueToolEvent({
189
+ id: tool.id,
190
+ name,
191
+ arguments: {},
192
+ phase: 'error',
193
+ failure: {
194
+ code: 'malformed_arguments',
195
+ message: parsed.error,
196
+ details: { raw: parsed.raw },
197
+ },
198
+ }, fold);
199
+ }
200
+ return emitUniqueToolEvent({ id: tool.id, name, arguments: parsed.value }, fold);
201
+ }
202
+ export function foldArgumentsDelta(delta, index, fold) {
203
+ const existing = fold.functionCalls.get(index) ?? { arguments: '' };
204
+ const chunk = typeof delta.arguments === 'string' ? delta.arguments : '';
205
+ if (!chunk) {
206
+ fold.functionCalls.set(index, existing);
207
+ return;
208
+ }
209
+ // Replace empty / `{}` placeholder from step.start so streamed JSON is not concatenated onto it.
210
+ if (existing.arguments === '' || existing.arguments === '{}') {
211
+ existing.arguments = chunk;
212
+ }
213
+ else {
214
+ existing.arguments += chunk;
215
+ }
216
+ fold.functionCalls.set(index, existing);
217
+ }
218
+ export function foldFunctionCallDelta(delta, fold) {
219
+ const id = typeof delta.id === 'string' ? delta.id : undefined;
220
+ const name = typeof delta.name === 'string' ? delta.name : '';
221
+ return emitToolCallFromRawArguments({ id, name }, delta.arguments, fold);
222
+ }
223
+ export function foldDeltaPayload(payload, fold) {
224
+ const delta = asRecord(payload.delta);
225
+ if (!delta) {
226
+ return [];
227
+ }
228
+ const stepType = String(delta.type ?? '');
229
+ const index = typeof payload.index === 'number' ? payload.index : 0;
230
+ if (stepType === 'arguments_delta' || stepType === 'arguments') {
231
+ foldArgumentsDelta(delta, index, fold);
232
+ return [];
233
+ }
234
+ if (stepType === 'function_call') {
235
+ return foldFunctionCallDelta(delta, fold);
236
+ }
237
+ if (isCodeExecutionType(stepType)) {
238
+ recordCodeStep(index, delta, fold.codeSteps);
239
+ return Array.from(flushCompletedCodeSteps(fold.codeSteps, fold.emittedEvidenceKeys));
240
+ }
241
+ if (isGoogleBuiltinStepType(stepType)) {
242
+ return Array.from(yieldEvidenceStep(delta, fold.emittedEvidenceKeys));
243
+ }
244
+ const events = [];
245
+ for (const ev of eventsFromDelta(delta)) {
246
+ if (ev.type === 'text' && ev.text) {
247
+ fold.text += ev.text;
248
+ }
249
+ events.push(ev);
250
+ }
251
+ return events;
252
+ }
253
+ export function readData(part) {
254
+ const data = part.data;
255
+ if (typeof data === 'string' && data.length > 0)
256
+ return data;
257
+ return undefined;
258
+ }
259
+ export function readMime(part) {
260
+ const mime = part.mime_type ?? part.mimeType;
261
+ if (typeof mime === 'string' && mime.length > 0)
262
+ return mime;
263
+ return undefined;
264
+ }
265
+ export function* yieldMediaChunk(part) {
266
+ const data = readData(part);
267
+ if (!data)
268
+ return;
269
+ const mime = readMime(part) ?? 'application/octet-stream';
270
+ if (isRawPcmMime(mime)) {
271
+ const wav = wrapPcmAsWav(base64ToBytes(data), 24000);
272
+ yield {
273
+ type: 'media',
274
+ media: { mimeType: 'audio/wav', data: bytesToBase64(wav) },
275
+ };
276
+ return;
277
+ }
278
+ yield { type: 'media', media: { mimeType: mime, data } };
279
+ }
280
+ export function* scanMediaParts(content) {
281
+ if (!Array.isArray(content))
282
+ return;
283
+ for (const item of content) {
284
+ if (!item || typeof item !== 'object')
285
+ continue;
286
+ const part = item;
287
+ const type = typeof part.type === 'string' ? part.type : '';
288
+ if (type === 'image' || type === 'audio' || type === 'media' || type === 'video') {
289
+ yield* yieldMediaChunk(part);
290
+ }
291
+ }
292
+ }
293
+ export function emitPendingFunctionCall(index, fold) {
294
+ const pending = fold.functionCalls.get(index);
295
+ if (!pending)
296
+ return [];
297
+ fold.functionCalls.delete(index);
298
+ return emitToolCallFromRawArguments({ id: pending.id, name: pending.name ?? '' }, pending.arguments || {}, fold);
299
+ }
300
+ export function foldStepStop(payload, fold) {
301
+ const index = typeof payload.index === 'number' ? payload.index : 0;
302
+ const fromFunctionCall = emitPendingFunctionCall(index, fold);
303
+ if (fromFunctionCall.length > 0) {
304
+ return fromFunctionCall;
305
+ }
306
+ const step = asRecord(payload.step);
307
+ if (!step)
308
+ return [];
309
+ const stepType = String(step.type ?? '');
310
+ if (isGoogleBuiltinStepType(stepType)) {
311
+ return Array.from(yieldEvidenceStep(step, fold.emittedEvidenceKeys));
312
+ }
313
+ if (isCodeExecutionType(stepType)) {
314
+ return [codeExecutionEvidence(step)];
315
+ }
316
+ return [];
317
+ }
318
+ export function foldInteractionSteps(interaction, fold) {
319
+ const events = [];
320
+ const steps = interaction.steps;
321
+ if (!Array.isArray(steps))
322
+ return events;
323
+ for (const s of steps) {
324
+ const step = asRecord(s);
325
+ if (!step)
326
+ continue;
327
+ const stepType = String(step.type ?? '');
328
+ if (stepType === 'function_call') {
329
+ const id = typeof step.id === 'string' ? step.id : undefined;
330
+ const name = typeof step.name === 'string' ? step.name : '';
331
+ events.push(...emitToolCallFromRawArguments({ id, name }, step.arguments, fold));
332
+ }
333
+ }
334
+ return events;
335
+ }
336
+ export function foldCompleteEvents(payload, fold) {
337
+ const events = [];
338
+ const fromComp = eventsFromComplete(payload, fold.text.length > 0);
339
+ for (const ev of fromComp) {
340
+ if (ev.type === 'evidence' && ev.evidence?.raw) {
341
+ const key = codeExecutionStepKey(ev.evidence.raw);
342
+ if (fold.emittedEvidenceKeys.has(key))
343
+ continue;
344
+ fold.emittedEvidenceKeys.add(key);
345
+ }
346
+ if (ev.type === 'media') {
347
+ fold.sawStreamedMedia = true;
348
+ }
349
+ events.push(ev);
350
+ }
351
+ return events;
352
+ }
353
+ export function foldPayload(payload, fold) {
354
+ const apiError = readApiErrorMessage(payload);
355
+ if (apiError) {
356
+ return [toErrorEvent(new TheorumError(apiError))];
357
+ }
358
+ const events = [];
359
+ for (const ev of yieldGrounding(payload))
360
+ events.push(ev);
361
+ const type = eventType(payload);
362
+ if (!isCompleteEvent(type)) {
363
+ for (const ev of yieldTokens(payload))
364
+ events.push(ev);
365
+ }
366
+ if (type === 'step.start') {
367
+ events.push(...foldStepStart(payload, fold));
368
+ return events;
369
+ }
370
+ if (type === 'step.stop') {
371
+ events.push(...foldStepStop(payload, fold));
372
+ return events;
373
+ }
374
+ if (isDeltaEvent(type)) {
375
+ events.push(...foldDeltaPayload(payload, fold));
376
+ return events;
377
+ }
378
+ if (type === 'interaction.status_update' || isCompleteEvent(type)) {
379
+ const interaction = asRecord(payload.interaction) ?? payload;
380
+ events.push(...foldInteractionSteps(interaction, fold));
381
+ if (isCompleteEvent(type)) {
382
+ events.push(...foldCompleteEvents(payload, fold));
383
+ }
384
+ return events;
385
+ }
386
+ return events;
387
+ }
388
+ export function* finalizeStructured(req, fold) {
389
+ if (!req.structured || !fold.text) {
390
+ return;
391
+ }
392
+ const parsed = parseStructuredOutput(fold.text);
393
+ if (!parsed.ok) {
394
+ yield toErrorEvent(new TheorumError(parsed.error));
395
+ return;
396
+ }
397
+ yield { type: 'structured', structured: parsed.structured };
398
+ }
399
+ export function* scanInteractionsMedia(json) {
400
+ const steps = json.steps;
401
+ if (!Array.isArray(steps)) {
402
+ return;
403
+ }
404
+ for (const step of steps) {
405
+ if (!step || typeof step !== 'object')
406
+ continue;
407
+ const content = step.content;
408
+ yield* scanMediaParts(content);
409
+ }
410
+ }
411
+ export function isVoiceProfile(req) {
412
+ return Boolean(req.speech?.voice);
413
+ }
414
+ export function shouldReportMissingSpeechAudio(req, fold) {
415
+ // Any speech-role completion without real audio is a failure — including
416
+ // empty turns (no text and no media). Never invent PCM from text.
417
+ return isVoiceProfile(req) && !fold.sawStreamedMedia;
418
+ }
419
+ /** Speech-role turns must receive real audio; never invent PCM from text bytes. */
420
+ export function* missingSpeechAudioError() {
421
+ yield toErrorEvent(new TheorumError('speech audio was not returned by the model'));
422
+ }
423
+ async function* parseInteractionsSse(response, req) {
424
+ if (!response.body) {
425
+ yield toErrorEvent('empty response body');
426
+ return;
427
+ }
428
+ const fold = newStreamFold();
429
+ const speech = Boolean(req.speech);
430
+ let hasError = false;
431
+ for await (const row of readSseChunks(response.body)) {
432
+ req.tapUpstream?.(row);
433
+ if (row.eventType === 'sse_done') {
434
+ break;
435
+ }
436
+ const events = foldPayload(row, fold);
437
+ for (const ev of events) {
438
+ if (ev.type === 'media') {
439
+ fold.sawStreamedMedia = true;
440
+ }
441
+ if (ev.type === 'error') {
442
+ hasError = true;
443
+ }
444
+ yield normalizeSpeechMedia(ev, speech);
445
+ }
446
+ if (hasError) {
447
+ return;
448
+ }
449
+ }
450
+ if (hasError) {
451
+ return;
452
+ }
453
+ yield* flushCompletedCodeSteps(fold.codeSteps, fold.emittedEvidenceKeys);
454
+ yield* finalizeStructured(req, fold);
455
+ if (shouldReportMissingSpeechAudio(req, fold)) {
456
+ yield* missingSpeechAudioError();
457
+ }
458
+ }
459
+ export function readApiErrorMessage(record) {
460
+ const error = record.error;
461
+ if (!error) {
462
+ if (record.sseEvent === 'error' ||
463
+ record.eventType === 'error' ||
464
+ record.event_type === 'error' ||
465
+ record.type === 'error') {
466
+ const msg = typeof record.message === 'string' ? record.message : null;
467
+ return msg || 'Gemini returned an error.';
468
+ }
469
+ return null;
470
+ }
471
+ if (typeof error === 'string' && error.length > 0) {
472
+ return error;
473
+ }
474
+ if (typeof error !== 'object')
475
+ return null;
476
+ const errorRecord = error;
477
+ if (typeof errorRecord.message === 'string' && errorRecord.message.length > 0) {
478
+ const status = typeof errorRecord.status === 'string' ? errorRecord.status : null;
479
+ return status ? `${status}: ${errorRecord.message}` : errorRecord.message;
480
+ }
481
+ return 'Gemini returned an error.';
482
+ }
483
+ export async function readNonOkErrorMessage(response) {
484
+ try {
485
+ const text = await response.text();
486
+ if (!text.trim())
487
+ return `HTTP ${response.status}`;
488
+ try {
489
+ const parsed = JSON.parse(text);
490
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
491
+ const msg = readApiErrorMessage(parsed);
492
+ if (msg)
493
+ return msg;
494
+ }
495
+ }
496
+ catch {
497
+ // JSON parse failed; return raw body below.
498
+ }
499
+ return `Gemini HTTP ${response.status}: ${text}`;
500
+ }
501
+ catch {
502
+ return `HTTP ${response.status}`;
503
+ }
504
+ }
505
+ function prepareInteractionsFetch(req) {
506
+ if (!req.keySlot) {
507
+ throw new TheorumError('Request requires keySlot');
508
+ }
509
+ const body = JSON.stringify(toInteractionsBody(req));
510
+ return {
511
+ slot: req.keySlot,
512
+ init: {
513
+ method: 'POST',
514
+ headers: { 'content-type': 'application/json' },
515
+ body,
516
+ signal: req.signal,
517
+ },
518
+ };
519
+ }
520
+ async function* fetchInteractionsOnce(req, transport) {
521
+ const { slot, init } = prepareInteractionsFetch(req);
522
+ const response = await fetchGemini(INTERACTIONS_JSON_URL, init, slot, transport);
523
+ if (response.status !== HTTP_OK) {
524
+ const reason = await readNonOkErrorMessage(response);
525
+ throw new TheorumError(reason);
526
+ }
527
+ const text = await response.text();
528
+ const parsed = JSON.parse(text);
529
+ const apiError = readApiErrorMessage(parsed);
530
+ if (apiError) {
531
+ throw new TheorumError(apiError);
532
+ }
533
+ req.tapUpstream?.(parsed);
534
+ yield* yieldGrounding(parsed);
535
+ yield* yieldTokens(parsed);
536
+ const fold = newStreamFold();
537
+ for (const ev of scanInteractionsMedia(parsed)) {
538
+ fold.sawStreamedMedia = true;
539
+ yield ev;
540
+ }
541
+ for (const ev of eventsFromComplete(parsed, fold.text.length > 0)) {
542
+ if (ev.type === 'text' && ev.text)
543
+ fold.text += ev.text;
544
+ yield ev;
545
+ }
546
+ yield* finalizeStructured(req, fold);
547
+ if (shouldReportMissingSpeechAudio(req, fold)) {
548
+ yield* missingSpeechAudioError();
549
+ }
550
+ }
551
+ export function withTap(req, transport) {
552
+ if (!req.tapUpstream) {
553
+ return transport;
554
+ }
555
+ const fetchFn = tapFetch(req.tapUpstream, transport.fetch ?? fetch);
556
+ return { ...transport, fetch: fetchFn };
557
+ }
558
+ async function* streamInteractions(req, transport) {
559
+ const { slot, init } = prepareInteractionsFetch(req);
560
+ const customTransport = withTap(req, transport);
561
+ const response = await fetchGemini(INTERACTIONS_URL, init, slot, customTransport);
562
+ if (response.status !== HTTP_OK) {
563
+ const reason = await readNonOkErrorMessage(response);
564
+ throw new TheorumError(reason);
565
+ }
566
+ yield* parseInteractionsSse(response, req);
567
+ }
568
+ /** Create a `ModelProvider` backed by Google Interactions HTTP / SSE. */
569
+ export function createInteractionsProvider(transport) {
570
+ return {
571
+ async *complete(req) {
572
+ try {
573
+ if (req.stream === false) {
574
+ yield* fetchInteractionsOnce(req, transport);
575
+ }
576
+ else {
577
+ yield* streamInteractions(req, transport);
578
+ }
579
+ }
580
+ catch (err) {
581
+ if (isAbortError(err)) {
582
+ throw err;
583
+ }
584
+ yield toErrorEvent(err);
585
+ }
586
+ },
587
+ };
588
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Google transport credentials, quota overflow, and fetch retries.
3
+ *
4
+ * Hosts supply a provider-neutral `KeyVault` via `GeminiTransport`.
5
+ * THEORUM does not read environment variables for these keys.
6
+ *
7
+ * @module
8
+ */
9
+ import type { KeySlot, KeyVault } from '../../kernel/types.js';
10
+ /** Google Interactions / Live transport: shared `KeyVault` + optional fetch/wait. */
11
+ interface GeminiTransport {
12
+ vault: KeyVault;
13
+ wait?: (ms: number) => Promise<void>;
14
+ fetch?: typeof fetch;
15
+ }
16
+ export declare function waitDefault(ms: number): Promise<void>;
17
+ export declare function isQuota(err: unknown): boolean;
18
+ export declare function isTransientHttp(status: number): boolean;
19
+ export declare function isTransientThrown(err: unknown): boolean;
20
+ export declare function requireKey(vault: KeyVault, slot: KeySlot): string;
21
+ export declare function backoffMs(attempt: number): number;
22
+ export declare function canOverflow(slot: KeySlot, vault: KeyVault, primary: string): string | undefined;
23
+ export declare function withGeminiKey<T>(slot: KeySlot, run: (apiKey: string) => Promise<T>, transport: GeminiTransport): Promise<T>;
24
+ export declare function withApiKey(init: RequestInit, apiKey: string): RequestInit;
25
+ export declare function fetchGemini(url: string, init: RequestInit, slot: KeySlot, transport: GeminiTransport): Promise<Response>;
26
+ export type { GeminiTransport };