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
@@ -3,25 +3,27 @@
3
3
  *
4
4
  * THEORUM keeps the public `ModelProvider` and `TurnEvent` contract; AI SDK
5
5
  * owns the OpenRouter call, stream parsing, provider compatibility, and tool
6
- * call normalization.
6
+ * call normalization. Message assembly delegates to `openai/sdk-messages.ts`.
7
7
  *
8
8
  * @module
9
9
  */
10
10
  import { createOpenRouter } from '@openrouter/ai-sdk-provider';
11
11
  import { jsonSchema, streamText, tool, } from 'ai';
12
- import { isAbortError, toErrorEvent } from '../guardrails/error.js';
13
- import { tryStructured } from '../kernel/engine/delta.js';
14
- import { getStructured } from '../kernel/registry/schemas.js';
15
- import { turnStopFromOpenRouter } from '../kernel/stop.js';
16
- import { exposeForTests } from './expose-for-tests.js';
17
- import { resolveOpenRouterModel, resolveOpenRouterPlugins, toOpenRouterPayload, } from './openrouter-payload.js';
18
- function trimApiKey(explicitKey) {
12
+ import { isAbortError, TheorumError, toErrorEvent } from '../../guardrails/error.js';
13
+ import { extractUsageTokens, parseStructuredOutput } from '../../kernel/engine/delta.js';
14
+ import { turnStopFromOpenAiFinishReason } from '../../kernel/stop.js';
15
+ import { cacheControlJson } from './cache-control.js';
16
+ import { resolveOpenRouterPlugins } from './openai/chat-payload.js';
17
+ import { openAiGatewayHeaders, resolveResponseFormat } from './openai/compat.js';
18
+ import { buildAiSdkMessages } from './openai/sdk-messages.js';
19
+ import { resolveOpenAiGatewayApiKey } from './resolve-api-key.js';
20
+ export function trimApiKey(explicitKey) {
19
21
  if (explicitKey?.trim()) {
20
22
  return explicitKey.trim();
21
23
  }
22
24
  return undefined;
23
25
  }
24
- function createAccumulator() {
26
+ export function createAccumulator() {
25
27
  return {
26
28
  text: '',
27
29
  evidenceSeen: false,
@@ -29,127 +31,35 @@ function createAccumulator() {
29
31
  errored: false,
30
32
  };
31
33
  }
32
- function mediaPart(part) {
33
- if (part.type === 'text') {
34
- return { type: 'text', text: part.text };
35
- }
36
- if (part.type === 'image') {
37
- return {
38
- type: 'image',
39
- image: `data:${part.mimeType};base64,${part.data}`,
40
- };
41
- }
42
- return { type: 'file', mediaType: part.mimeType, data: part.data };
43
- }
44
- function contentFromParts(parts) {
45
- if (parts.every((part) => part.type === 'text')) {
46
- return parts
47
- .map((part) => (part.type === 'text' ? part.text : ''))
48
- .filter(Boolean)
49
- .join('\n');
50
- }
51
- return parts.map(mediaPart);
52
- }
53
- function parseToolInput(raw) {
54
- try {
55
- return JSON.parse(raw);
56
- }
57
- catch {
58
- return { _raw: raw };
59
- }
60
- }
61
- function stringDefault(value, fallback) {
62
- return value === undefined ? fallback : value;
63
- }
64
- function fallbackToolCallId(name) {
65
- return `call_${stringDefault(name, 'tool')}`;
66
- }
67
- function toolResultContent(msg) {
68
- const toolName = stringDefault(msg.name, 'tool');
69
- return {
70
- role: 'tool',
71
- content: [
72
- {
73
- type: 'tool-result',
74
- toolCallId: stringDefault(msg.tool_call_id, fallbackToolCallId(msg.name)),
75
- toolName,
76
- output: { type: 'text', value: stringDefault(msg.content, '') },
77
- },
78
- ],
79
- };
80
- }
81
- function assistantToolCallContent(msg) {
82
- if (!msg.tool_calls || msg.tool_calls.length === 0) {
83
- return null;
84
- }
85
- return {
86
- role: 'assistant',
87
- content: msg.tool_calls.map((call) => ({
88
- type: 'tool-call',
89
- toolCallId: call.id,
90
- toolName: call.function.name,
91
- input: parseToolInput(call.function.arguments),
92
- })),
93
- };
94
- }
95
- function historyMessage(msg) {
96
- if (msg.role === 'tool') {
97
- return toolResultContent(msg);
98
- }
99
- return assistantToolCallContent(msg) || contentHistoryMessage(msg);
100
- }
101
- function contentFromOptionalParts(parts, text) {
102
- if (!parts || parts.length === 0) {
103
- return stringDefault(text, '');
104
- }
105
- return contentFromParts(parts);
106
- }
107
- function contentHistoryMessage(msg) {
108
- const content = contentFromOptionalParts(msg.parts, msg.content);
109
- return { role: msg.role, content };
110
- }
111
- function sourceEvent(part) {
34
+ export function sourceEvent(part) {
112
35
  if (part.sourceType !== 'url') {
113
36
  return {
114
37
  type: 'evidence',
115
38
  evidence: { provider: 'openrouter', raw: part },
116
39
  };
117
40
  }
118
- const title = part.title ?? part.url;
41
+ const title = part.title ?? part.url ?? '';
119
42
  return {
120
43
  type: 'evidence',
121
44
  evidence: {
122
45
  provider: 'openrouter',
123
46
  raw: part,
124
- citations: [part.url],
125
- sources: [{ title, uri: part.url, type: 'web' }],
47
+ citations: part.url ? [part.url] : [],
48
+ sources: part.url ? [{ title, uri: part.url, type: 'web' }] : [],
126
49
  },
127
50
  };
128
51
  }
129
- function buildMessages(req) {
130
- const messages = [];
131
- for (const msg of req.history ?? []) {
132
- const wired = historyMessage(msg);
133
- if (wired) {
134
- messages.push(wired);
135
- }
136
- }
137
- if (req.input.length > 0) {
138
- messages.push({ role: 'user', content: contentFromParts(req.input) });
139
- }
140
- return messages;
141
- }
142
- function schemaForTool(decl) {
52
+ export function schemaForTool(decl) {
143
53
  return decl.parameters ?? { type: 'object', properties: {}, additionalProperties: true };
144
54
  }
145
- function buildTools(dynamicTools) {
146
- if (!dynamicTools || dynamicTools.length === 0) {
55
+ export function buildTools(wireTools) {
56
+ if (!wireTools || wireTools.length === 0) {
147
57
  return undefined;
148
58
  }
149
59
  const tools = {};
150
- for (const decl of dynamicTools) {
60
+ for (const decl of wireTools) {
151
61
  tools[decl.name] = tool({
152
- description: decl.description ?? '',
62
+ description: decl.description,
153
63
  inputSchema: jsonSchema(schemaForTool(decl)),
154
64
  });
155
65
  }
@@ -167,36 +77,44 @@ function openRouterSettings(req) {
167
77
  settings.web_search_options = {};
168
78
  return settings;
169
79
  }
170
- function tokensFromUsage(usage) {
80
+ export function tokensFromUsage(usage) {
171
81
  const input = usage.inputTokens ?? 0;
172
82
  const output = usage.outputTokens ?? 0;
173
83
  const total = usage.totalTokens ?? input + output;
174
- if (input === 0 && output === 0 && total === 0) {
84
+ const cached = usage.inputTokenDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? undefined;
85
+ const cacheWrite = usage.inputTokenDetails?.cacheWriteTokens ?? undefined;
86
+ if (input === 0 &&
87
+ output === 0 &&
88
+ total === 0 &&
89
+ !(cached && cached > 0) &&
90
+ !(cacheWrite && cacheWrite > 0)) {
175
91
  return undefined;
176
92
  }
177
93
  return {
178
94
  input,
179
95
  output,
180
96
  total,
97
+ ...(cached && cached > 0 ? { cached } : {}),
98
+ ...(cacheWrite && cacheWrite > 0 ? { cacheWrite } : {}),
181
99
  };
182
100
  }
183
- function rawRecord(value) {
101
+ export function rawRecord(value) {
184
102
  if (value && typeof value === 'object' && !Array.isArray(value)) {
185
103
  return value;
186
104
  }
187
105
  return undefined;
188
106
  }
189
- function stringArray(value) {
107
+ export function stringArray(value) {
190
108
  if (!Array.isArray(value)) {
191
109
  return undefined;
192
110
  }
193
111
  const out = value.filter((item) => typeof item === 'string');
194
112
  return out.length > 0 ? out : undefined;
195
113
  }
196
- function metadataRecord(raw, key) {
114
+ export function metadataRecord(raw, key) {
197
115
  return rawRecord(raw[key]);
198
116
  }
199
- function citationCandidates(raw) {
117
+ export function citationCandidates(raw) {
200
118
  const openrouter = metadataRecord(raw, 'openrouter') ?? {};
201
119
  return [
202
120
  raw.citations,
@@ -207,7 +125,7 @@ function citationCandidates(raw) {
207
125
  metadataRecord(openrouter, 'provider_metadata')?.citations,
208
126
  ];
209
127
  }
210
- function nestedCitations(raw) {
128
+ export function nestedCitations(raw) {
211
129
  for (const candidate of citationCandidates(raw)) {
212
130
  const citations = stringArray(candidate);
213
131
  if (citations) {
@@ -216,7 +134,7 @@ function nestedCitations(raw) {
216
134
  }
217
135
  return undefined;
218
136
  }
219
- function metadataAnnotations(raw) {
137
+ export function metadataAnnotations(raw) {
220
138
  const openrouter = metadataRecord(raw, 'openrouter');
221
139
  if (Array.isArray(raw.annotations)) {
222
140
  return raw.annotations;
@@ -226,7 +144,7 @@ function metadataAnnotations(raw) {
226
144
  }
227
145
  return undefined;
228
146
  }
229
- function evidenceFromMetadata(metadata, acc) {
147
+ export function evidenceFromMetadata(metadata, acc) {
230
148
  const raw = rawRecord(metadata);
231
149
  if (!raw || acc.evidenceSeen) {
232
150
  return undefined;
@@ -242,7 +160,7 @@ function evidenceFromMetadata(metadata, acc) {
242
160
  evidence: { provider: 'openrouter', raw, citations, annotations },
243
161
  };
244
162
  }
245
- function toolArguments(input) {
163
+ export function toolArguments(input) {
246
164
  if (input && typeof input === 'object' && !Array.isArray(input)) {
247
165
  return input;
248
166
  }
@@ -251,10 +169,10 @@ function toolArguments(input) {
251
169
  }
252
170
  return { value: input };
253
171
  }
254
- function toolResultData(output) {
172
+ export function toolResultData(output) {
255
173
  return rawRecord(output);
256
174
  }
257
- function rawThoughtEvent(raw) {
175
+ export function rawThoughtEvent(raw) {
258
176
  const choices = raw.choices;
259
177
  if (!Array.isArray(choices)) {
260
178
  return undefined;
@@ -267,7 +185,7 @@ function rawThoughtEvent(raw) {
267
185
  }
268
186
  return undefined;
269
187
  }
270
- function rawChoiceMessageEvidence(raw, acc) {
188
+ export function rawChoiceMessageEvidence(raw, acc) {
271
189
  const choices = raw.choices;
272
190
  if (!Array.isArray(choices)) {
273
191
  return undefined;
@@ -284,7 +202,7 @@ function rawChoiceMessageEvidence(raw, acc) {
284
202
  }
285
203
  return undefined;
286
204
  }
287
- function rawEvents(raw, acc) {
205
+ export function rawEvents(raw, acc) {
288
206
  const record = rawRecord(raw);
289
207
  if (!record) {
290
208
  return [];
@@ -302,6 +220,13 @@ function rawEvents(raw, acc) {
302
220
  if (messageEvidence) {
303
221
  events.push(messageEvidence);
304
222
  }
223
+ if (!acc.emittedTokens) {
224
+ const usage = extractUsageTokens(record.usage);
225
+ if (usage) {
226
+ acc.emittedTokens = true;
227
+ events.push({ type: 'tokens', tokens: usage });
228
+ }
229
+ }
305
230
  const choices = Array.isArray(record.choices) ? record.choices : [];
306
231
  for (const choice of choices) {
307
232
  const row = rawRecord(choice);
@@ -316,7 +241,7 @@ function rawEvents(raw, acc) {
316
241
  }
317
242
  return events;
318
243
  }
319
- function toolCallEvent(part) {
244
+ export function toolCallEvent(part) {
320
245
  return {
321
246
  type: 'tool',
322
247
  tool: {
@@ -326,32 +251,30 @@ function toolCallEvent(part) {
326
251
  },
327
252
  };
328
253
  }
329
- function toolResultEvent(part) {
254
+ export function toolResultEvent(part) {
255
+ const output = typeof part.output === 'string' ? part.output : toolResultData(part.output);
330
256
  return {
331
257
  type: 'tool',
332
258
  tool: {
333
259
  name: part.toolName,
334
260
  arguments: toolArguments(part.input),
335
- result: {
336
- status: 'ok',
337
- data: toolResultData(part.output),
338
- finding: typeof part.output === 'string' ? part.output : undefined,
339
- },
340
261
  id: part.toolCallId,
262
+ phase: 'complete',
263
+ output,
341
264
  },
342
265
  };
343
266
  }
344
- function tokenEvent(part) {
267
+ export function tokenEvent(part) {
345
268
  const tokens = tokensFromUsage(part.totalUsage);
346
269
  return tokens ? { type: 'tokens', tokens } : undefined;
347
270
  }
348
- function providerMetadataEvent(part, acc) {
271
+ export function providerMetadataEvent(part, acc) {
349
272
  if (!('providerMetadata' in part)) {
350
273
  return undefined;
351
274
  }
352
275
  return evidenceFromMetadata(part.providerMetadata, acc);
353
276
  }
354
- function eventFromPart(part, acc) {
277
+ export function eventFromPart(part, acc) {
355
278
  const mapped = primaryEventFromPart(part, acc);
356
279
  if (mapped) {
357
280
  return [mapped];
@@ -359,7 +282,7 @@ function eventFromPart(part, acc) {
359
282
  const evidence = providerMetadataEvent(part, acc);
360
283
  return evidence ? [evidence] : [];
361
284
  }
362
- function primaryEventFromPart(part, acc) {
285
+ export function primaryEventFromPart(part, acc) {
363
286
  switch (part.type) {
364
287
  case 'text-delta':
365
288
  acc.text += part.text;
@@ -385,59 +308,84 @@ function primaryEventFromPart(part, acc) {
385
308
  return undefined;
386
309
  }
387
310
  }
388
- function finishEvent(part, acc) {
389
- if ('finishReason' in part && part.finishReason != null) {
311
+ export function finishEvent(part, acc) {
312
+ if (part.finishReason != null) {
390
313
  acc.finishReason = String(part.finishReason);
391
314
  }
392
315
  if (acc.emittedTokens) {
393
316
  return undefined;
394
317
  }
395
- const event = tokenEvent(part);
318
+ if (!part.totalUsage) {
319
+ return undefined;
320
+ }
321
+ const event = tokenEvent({ totalUsage: part.totalUsage });
396
322
  if (event) {
397
323
  acc.emittedTokens = true;
398
324
  }
399
325
  return event;
400
326
  }
401
- function* finalEvents(req, acc) {
327
+ export function* finalEvents(req, acc) {
402
328
  if (acc.errored) {
403
329
  return;
404
330
  }
405
331
  if (req.structured && acc.text) {
406
- const structured = tryStructured(acc.text);
407
- if (structured) {
408
- yield structured;
332
+ const parsed = parseStructuredOutput(acc.text);
333
+ if (!parsed.ok) {
334
+ yield toErrorEvent(new TheorumError(parsed.error));
335
+ return;
409
336
  }
337
+ yield { type: 'structured', structured: parsed.structured };
410
338
  }
411
339
  yield {
412
340
  type: 'done',
413
- stop: turnStopFromOpenRouter(acc.finishReason, acc.nativeFinishReason),
341
+ stop: turnStopFromOpenAiFinishReason(acc.finishReason, acc.nativeFinishReason),
414
342
  };
415
343
  }
416
344
  function createStreamContext(req, config, apiKey) {
417
345
  const openrouter = createOpenRouter({
418
346
  apiKey,
419
347
  baseURL: config.baseUrl,
420
- headers: openRouterHeaders(config),
348
+ headers: openAiGatewayHeaders(config),
421
349
  fetch: config.fetch,
422
350
  compatibility: 'strict',
423
351
  });
424
352
  return {
425
353
  openrouter,
426
- modelName: resolveOpenRouterModel(req.model, config.modelMap, {
427
- apiId: req.apiId,
428
- openRouterId: req.openRouterId,
429
- }),
354
+ modelName: req.apiId,
430
355
  };
431
356
  }
357
+ /** System via instructions XOR a cache-marked system message — never both. */
358
+ export function systemDelivery(req) {
359
+ if (!req.system) {
360
+ return {};
361
+ }
362
+ if (req.cache?.mode === 'system') {
363
+ return {
364
+ systemMessage: {
365
+ role: 'system',
366
+ content: req.system,
367
+ providerOptions: {
368
+ openrouter: { cacheControl: cacheControlJson(req.cache) },
369
+ },
370
+ },
371
+ };
372
+ }
373
+ return { instructions: req.system };
374
+ }
432
375
  function streamTextOptions(req, context) {
376
+ const delivery = systemDelivery(req);
377
+ const messages = buildAiSdkMessages(req);
378
+ if (delivery.systemMessage) {
379
+ messages.unshift(delivery.systemMessage);
380
+ }
433
381
  return {
434
382
  model: context.openrouter.chat(context.modelName, openRouterSettings(req)),
435
- instructions: req.system,
436
- messages: buildMessages(req),
383
+ instructions: delivery.instructions,
384
+ messages,
437
385
  allowSystemInMessages: true,
438
386
  temperature: req.temperature,
439
387
  maxOutputTokens: req.maxOutputTokens,
440
- tools: buildTools(req.dynamicTools),
388
+ tools: buildTools(req.wireTools),
441
389
  providerOptions: providerOptionsFor(req),
442
390
  include: { rawChunks: true },
443
391
  abortSignal: req.signal,
@@ -448,7 +396,7 @@ async function* yieldAiSdkStream(req, acc, context) {
448
396
  const result = streamText(streamTextOptions(req, context));
449
397
  for await (const part of result.stream) {
450
398
  if (part.type === 'raw') {
451
- req.tapGemini?.(rawRecord(part.rawValue) ?? { rawValue: part.rawValue });
399
+ req.tapUpstream?.(rawRecord(part.rawValue) ?? { rawValue: part.rawValue });
452
400
  for (const event of rawEvents(part.rawValue, acc)) {
453
401
  yield event;
454
402
  }
@@ -459,13 +407,16 @@ async function* yieldAiSdkStream(req, acc, context) {
459
407
  }
460
408
  }
461
409
  }
462
- function missingOpenRouterKey() {
410
+ export function missingOpenRouterKey() {
463
411
  return toErrorEvent('missing OpenRouter API key');
464
412
  }
465
413
  async function* streamOpenRouter(req, config) {
466
- const apiKey = trimApiKey(config.apiKey);
467
- if (!apiKey) {
468
- yield missingOpenRouterKey();
414
+ let apiKey;
415
+ try {
416
+ apiKey = resolveOpenAiGatewayApiKey(config, req.keySlot);
417
+ }
418
+ catch (err) {
419
+ yield toErrorEvent(err);
469
420
  return;
470
421
  }
471
422
  const acc = createAccumulator();
@@ -481,93 +432,28 @@ async function* streamOpenRouter(req, config) {
481
432
  yield toErrorEvent(err);
482
433
  }
483
434
  }
484
- function responseFormatFor(req) {
485
- if (!req.structured)
486
- return undefined;
487
- const spec = getStructured(req.structured);
488
- if (!spec.jsonSchema)
489
- return undefined;
490
- return {
491
- type: 'json_schema',
492
- json_schema: {
493
- name: String(req.structured),
494
- strict: true,
495
- schema: spec.jsonSchema,
496
- },
497
- };
498
- }
499
- function providerOptionsFor(req) {
435
+ export function providerOptionsFor(req) {
500
436
  const openrouter = {};
501
- if (req.thinking !== 'none') {
437
+ if (req.thinking && req.thinking !== 'none') {
502
438
  openrouter.reasoning = { effort: req.thinking };
503
439
  }
504
- const responseFormat = responseFormatFor(req);
440
+ const responseFormat = resolveResponseFormat(req.structured);
505
441
  if (responseFormat) {
506
442
  openrouter.response_format = responseFormat;
507
443
  }
444
+ if (req.cache?.mode === 'automatic') {
445
+ openrouter.cacheControl = cacheControlJson(req.cache);
446
+ }
447
+ if (req.sessionId) {
448
+ openrouter.session_id = req.sessionId;
449
+ }
508
450
  if (Object.keys(openrouter).length === 0)
509
451
  return undefined;
510
452
  return { openrouter };
511
453
  }
512
- function openRouterHeaders(config) {
513
- const headers = {};
514
- if (config.siteUrl) {
515
- headers['HTTP-Referer'] = config.siteUrl;
516
- }
517
- if (config.siteName) {
518
- headers['X-Title'] = config.siteName;
519
- }
520
- return Object.keys(headers).length > 0 ? headers : undefined;
521
- }
522
454
  /** Create a `ModelProvider` backed by OpenRouter through AI SDK Core. */
523
- function createOpenRouterProvider(config = {}) {
455
+ export function createOpenRouterProvider(config = {}) {
524
456
  return {
525
457
  complete: (req) => streamOpenRouter(req, config),
526
458
  };
527
459
  }
528
- export { createOpenRouterProvider, resolveOpenRouterModel, toOpenRouterPayload };
529
- exposeForTests('openrouter', {
530
- trimApiKey,
531
- createAccumulator,
532
- mediaPart,
533
- contentFromParts,
534
- parseToolInput,
535
- stringDefault,
536
- fallbackToolCallId,
537
- toolResultContent,
538
- assistantToolCallContent,
539
- historyMessage,
540
- contentFromOptionalParts,
541
- contentHistoryMessage,
542
- buildMessages,
543
- schemaForTool,
544
- buildTools,
545
- openRouterSettings,
546
- tokensFromUsage,
547
- rawRecord,
548
- stringArray,
549
- metadataRecord,
550
- citationCandidates,
551
- nestedCitations,
552
- metadataAnnotations,
553
- evidenceFromMetadata,
554
- toolArguments,
555
- toolResultData,
556
- rawThoughtEvent,
557
- rawChoiceMessageEvidence,
558
- rawEvents,
559
- toolCallEvent,
560
- toolResultEvent,
561
- tokenEvent,
562
- providerMetadataEvent,
563
- eventFromPart,
564
- primaryEventFromPart,
565
- finishEvent,
566
- sourceEvent,
567
- finalEvents,
568
- responseFormatFor,
569
- providerOptionsFor,
570
- openRouterHeaders,
571
- missingOpenRouterKey,
572
- streamTextOptions,
573
- });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * OpenAI-compatible image generation transport (internal).
3
+ *
4
+ * Hosts use `createProvider(profile, { openAiGateway })` — this module is selected
5
+ * when the profile is an openAi image role on OpenRouter.
6
+ *
7
+ * Image-only turns POST `/images`. When `image.includeText` is set, chat
8
+ * completions carry an OpenRouter image-generation server tool so the model may
9
+ * return interleaved assistant text and images.
10
+ *
11
+ * @module
12
+ */
13
+ import type { ModelProvider, ProviderCompleteRequest, TurnEvent } from '../../kernel/types.js';
14
+ import type { OpenAiGatewayConfig } from '../types.js';
15
+ /** OpenRouter chat server tool for inline image generation. */
16
+ export declare const OPENROUTER_IMAGE_TOOL = "openrouter:image_generation";
17
+ export type ImageProviderConfig = OpenAiGatewayConfig;
18
+ export declare function buildImageHeaders(apiKey: string, config: ImageProviderConfig): Record<string, string>;
19
+ export declare function mediaFromImagesResponse(body: Record<string, unknown>, fallbackMime?: string): {
20
+ mimeType: string;
21
+ data: string;
22
+ } | null;
23
+ export declare function fetchImageAsBase64(url: string, fetchFn: typeof fetch, signal?: AbortSignal): Promise<{
24
+ mimeType: string;
25
+ data: string;
26
+ } | null>;
27
+ export declare function markdownImageUrls(content: string): string[];
28
+ export declare function plainTextFromContent(content: unknown): string;
29
+ export declare function buildInterleavedChatPayload(req: ProviderCompleteRequest): Record<string, unknown>;
30
+ export declare function yieldInterleavedChat(req: ProviderCompleteRequest, config: ImageProviderConfig, apiKey: string): AsyncGenerator<TurnEvent>;
31
+ export declare function yieldImagesEndpoint(req: ProviderCompleteRequest, config: ImageProviderConfig, apiKey: string): AsyncGenerator<TurnEvent>;
32
+ export declare function streamImage(req: ProviderCompleteRequest, config?: ImageProviderConfig): AsyncGenerator<TurnEvent>;
33
+ /** Internal ModelProvider for openAi image roles on OpenRouter. */
34
+ export declare function createImageProvider(config?: ImageProviderConfig): ModelProvider;