theorum 0.1.14 → 1.0.0

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 (229) hide show
  1. package/README.md +178 -89
  2. package/esm/mod.d.ts +21 -11
  3. package/esm/mod.js +15 -8
  4. package/esm/src/cli/commands/bench.js +6 -3
  5. package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
  6. package/esm/src/cli/commands/fuzz-canary.js +195 -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/profile.js +25 -14
  10. package/esm/src/cli/commands/run.d.ts +3 -0
  11. package/esm/src/cli/commands/run.js +22 -30
  12. package/esm/src/cli/commands/test.d.ts +10 -1
  13. package/esm/src/cli/commands/test.js +31 -31
  14. package/esm/src/cli/event-log.d.ts +19 -0
  15. package/esm/src/cli/event-log.js +147 -0
  16. package/esm/src/cli/index.js +45 -11
  17. package/esm/src/cli/matrix/synthesizer.d.ts +7 -9
  18. package/esm/src/cli/matrix/synthesizer.js +32 -106
  19. package/esm/src/guardrails/canary-gate.d.ts +20 -0
  20. package/esm/src/guardrails/canary-gate.js +31 -0
  21. package/esm/src/guardrails/canary.d.ts +26 -0
  22. package/esm/src/guardrails/canary.js +139 -0
  23. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +16 -0
  24. package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
  25. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
  26. package/esm/src/guardrails/corpus/fuzz-inbound.js +216 -0
  27. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
  28. package/esm/src/guardrails/corpus/inbound-payloads.js +124 -0
  29. package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
  30. package/esm/src/guardrails/corpus/live-attacks.js +232 -0
  31. package/esm/src/guardrails/corpus/mod.d.ts +13 -0
  32. package/esm/src/guardrails/corpus/mod.js +11 -0
  33. package/esm/src/guardrails/corpus/secrets.d.ts +16 -0
  34. package/esm/src/guardrails/corpus/secrets.js +16 -0
  35. package/esm/src/guardrails/corpus/strings.d.ts +27 -0
  36. package/esm/src/guardrails/corpus/strings.js +33 -0
  37. package/esm/src/guardrails/corpus/types.d.ts +37 -0
  38. package/esm/src/guardrails/corpus/types.js +6 -0
  39. package/esm/src/guardrails/egress.d.ts +9 -0
  40. package/esm/src/guardrails/egress.js +36 -0
  41. package/esm/src/guardrails/error.js +6 -4
  42. package/esm/src/guardrails/injection.js +28 -5
  43. package/esm/src/guardrails/live-outbound-gate.d.ts +39 -0
  44. package/esm/src/guardrails/live-outbound-gate.js +180 -0
  45. package/esm/src/guardrails/mod.d.ts +13 -4
  46. package/esm/src/guardrails/mod.js +10 -4
  47. package/esm/src/guardrails/quota.js +1 -1
  48. package/esm/src/guardrails/sanitize.d.ts +11 -15
  49. package/esm/src/guardrails/sanitize.js +40 -49
  50. package/esm/src/guardrails/sensitive.js +2 -1
  51. package/esm/src/guardrails/testing.d.ts +10 -0
  52. package/esm/src/guardrails/testing.js +9 -0
  53. package/esm/src/host/client-turn.d.ts +19 -0
  54. package/esm/src/host/client-turn.js +32 -0
  55. package/esm/src/host/mod.d.ts +5 -3
  56. package/esm/src/host/mod.js +4 -3
  57. package/esm/src/interface/blocks.d.ts +20 -0
  58. package/esm/src/interface/blocks.js +180 -0
  59. package/esm/src/interface/from-profile.d.ts +14 -0
  60. package/esm/src/interface/from-profile.js +104 -0
  61. package/esm/src/interface/inputs.d.ts +8 -0
  62. package/esm/src/interface/inputs.js +127 -0
  63. package/esm/src/interface/mod.d.ts +10 -0
  64. package/esm/src/interface/mod.js +9 -0
  65. package/esm/src/interface/types.d.ts +153 -0
  66. package/esm/src/interface/types.js +13 -0
  67. package/esm/src/kernel/engine/delta.d.ts +24 -2
  68. package/esm/src/kernel/engine/delta.js +519 -29
  69. package/esm/src/kernel/engine/live-inbound.d.ts +9 -0
  70. package/esm/src/kernel/engine/live-inbound.js +17 -0
  71. package/esm/src/kernel/engine/repair.js +1 -1
  72. package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
  73. package/esm/src/kernel/engine/runner/gates.js +7 -7
  74. package/esm/src/kernel/engine/runner/mod.js +13 -13
  75. package/esm/src/kernel/engine/runner/state.d.ts +7 -0
  76. package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
  77. package/esm/src/kernel/engine/runner/steps.js +171 -37
  78. package/esm/src/kernel/engine/runner/stream.d.ts +1 -2
  79. package/esm/src/kernel/engine/runner/stream.js +66 -29
  80. package/esm/src/kernel/engine/session/mod.d.ts +23 -0
  81. package/esm/src/kernel/engine/session/mod.js +205 -0
  82. package/esm/src/kernel/mod.d.ts +9 -10
  83. package/esm/src/kernel/mod.js +6 -8
  84. package/esm/src/kernel/registry/attachments.d.ts +4 -2
  85. package/esm/src/kernel/registry/attachments.js +6 -3
  86. package/esm/src/kernel/registry/catalog.d.ts +3 -16
  87. package/esm/src/kernel/registry/catalog.js +5 -81
  88. package/esm/src/kernel/registry/ingress.d.ts +9 -4
  89. package/esm/src/kernel/registry/ingress.js +73 -63
  90. package/esm/src/kernel/registry/profiles.d.ts +35 -12
  91. package/esm/src/kernel/registry/profiles.js +159 -66
  92. package/esm/src/kernel/registry/provider-request.js +11 -7
  93. package/esm/src/kernel/registry/resolve.d.ts +5 -6
  94. package/esm/src/kernel/registry/resolve.js +100 -70
  95. package/esm/src/kernel/registry/vault.d.ts +12 -7
  96. package/esm/src/kernel/registry/vault.js +30 -8
  97. package/esm/src/kernel/schema.d.ts +145 -0
  98. package/esm/src/kernel/schema.js +462 -0
  99. package/esm/src/kernel/stop.d.ts +15 -10
  100. package/esm/src/kernel/stop.js +5 -5
  101. package/esm/src/kernel/tools/execute.d.ts +45 -0
  102. package/esm/src/kernel/tools/execute.js +363 -0
  103. package/esm/src/kernel/tools/harness.d.ts +8 -0
  104. package/esm/src/kernel/tools/harness.js +44 -0
  105. package/esm/src/kernel/tools/invoke.d.ts +10 -0
  106. package/esm/src/kernel/tools/invoke.js +83 -0
  107. package/esm/src/kernel/tools/mod.d.ts +11 -0
  108. package/esm/src/kernel/tools/mod.js +10 -0
  109. package/esm/src/kernel/tools/project.d.ts +12 -0
  110. package/esm/src/kernel/tools/project.js +34 -0
  111. package/esm/src/kernel/tools/registry.d.ts +23 -0
  112. package/esm/src/kernel/tools/registry.js +71 -0
  113. package/esm/src/kernel/tools/resolve.d.ts +29 -0
  114. package/esm/src/kernel/tools/resolve.js +262 -0
  115. package/esm/src/kernel/tools/schema.d.ts +13 -0
  116. package/esm/src/kernel/tools/schema.js +159 -0
  117. package/esm/src/kernel/tools/types.d.ts +216 -0
  118. package/esm/src/kernel/tools/types.js +9 -0
  119. package/esm/src/kernel/types.d.ts +338 -193
  120. package/esm/src/observability/trace-attach.d.ts +6 -4
  121. package/esm/src/observability/trace-attach.js +39 -25
  122. package/esm/src/observability/trace-record.d.ts +11 -9
  123. package/esm/src/observability/trace-record.js +36 -23
  124. package/esm/src/observability/trace-usage.d.ts +10 -3
  125. package/esm/src/observability/trace-usage.js +70 -17
  126. package/esm/src/observability/trace.d.ts +9 -1
  127. package/esm/src/observability/trace.js +11 -3
  128. package/esm/src/presets/google/speech-voices.d.ts +11 -0
  129. package/esm/src/presets/google/speech-voices.js +41 -0
  130. package/esm/src/presets/google.d.ts +36 -24
  131. package/esm/src/presets/google.js +50 -63
  132. package/esm/src/presets/mod.d.ts +2 -2
  133. package/esm/src/presets/mod.js +1 -1
  134. package/esm/src/providers/create-provider.d.ts +14 -14
  135. package/esm/src/providers/create-provider.js +55 -23
  136. package/esm/src/providers/google/interactions/framing.d.ts +15 -0
  137. package/esm/src/providers/google/interactions/framing.js +208 -0
  138. package/esm/src/providers/google/interactions/mod.d.ts +7 -0
  139. package/esm/src/providers/google/interactions/mod.js +7 -0
  140. package/esm/src/providers/google/interactions/stream.d.ts +83 -0
  141. package/esm/src/providers/google/interactions/stream.js +588 -0
  142. package/esm/src/providers/google/keys.d.ts +26 -0
  143. package/esm/src/providers/{keys.js → google/keys.js} +19 -31
  144. package/esm/src/providers/google/live/framing.d.ts +46 -0
  145. package/esm/src/providers/google/live/framing.js +515 -0
  146. package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
  147. package/esm/src/providers/google/live/openapi-schema.js +45 -0
  148. package/esm/src/providers/google/live/session.d.ts +25 -0
  149. package/esm/src/providers/google/live/session.js +134 -0
  150. package/esm/src/providers/google/live/stream.d.ts +36 -0
  151. package/esm/src/providers/google/live/stream.js +201 -0
  152. package/esm/src/providers/google/urls.d.ts +6 -0
  153. package/esm/src/providers/google/urls.js +6 -0
  154. package/esm/src/providers/local/local.d.ts +30 -0
  155. package/esm/src/providers/{local.js → local/local.js} +66 -126
  156. package/esm/src/providers/local/mod.d.ts +9 -0
  157. package/esm/src/providers/local/mod.js +9 -0
  158. package/esm/src/providers/mod.d.ts +6 -3
  159. package/esm/src/providers/mod.js +3 -1
  160. package/esm/src/providers/openrouter/chat.d.ts +87 -0
  161. package/esm/src/providers/{openrouter.js → openrouter/chat.js} +70 -229
  162. package/esm/src/providers/openrouter/image.d.ts +34 -0
  163. package/esm/src/providers/openrouter/image.js +286 -0
  164. package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
  165. package/esm/src/providers/openrouter/openai/chat-payload.js +60 -0
  166. package/esm/src/providers/openrouter/openai/compat.d.ts +47 -0
  167. package/esm/src/providers/openrouter/openai/compat.js +194 -0
  168. package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
  169. package/esm/src/providers/openrouter/openai/image-payload.js +84 -0
  170. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
  171. package/esm/src/providers/openrouter/openai/sdk-messages.js +95 -0
  172. package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
  173. package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
  174. package/esm/src/providers/openrouter/speech.d.ts +23 -0
  175. package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
  176. package/esm/src/providers/probe.d.ts +1 -0
  177. package/esm/src/providers/probe.js +22 -0
  178. package/esm/src/providers/shared/pcm.d.ts +12 -0
  179. package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
  180. package/esm/src/providers/shared/sse.d.ts +18 -0
  181. package/esm/src/providers/shared/sse.js +87 -0
  182. package/esm/src/providers/shared/tool-args.d.ts +17 -0
  183. package/esm/src/providers/shared/tool-args.js +45 -0
  184. package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
  185. package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
  186. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  187. package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
  188. package/esm/src/providers/types.d.ts +27 -0
  189. package/esm/src/providers/types.js +1 -0
  190. package/package.json +11 -7
  191. package/docs/cli.md +0 -97
  192. package/docs/guardrails.md +0 -178
  193. package/docs/host.md +0 -97
  194. package/docs/kernel.md +0 -404
  195. package/docs/observability.md +0 -105
  196. package/docs/openrouter.md +0 -125
  197. package/docs/presets-google.md +0 -91
  198. package/docs/presets.md +0 -88
  199. package/docs/providers.md +0 -201
  200. package/docs/streaming.md +0 -96
  201. package/esm/src/kernel/engine/boundary.d.ts +0 -10
  202. package/esm/src/kernel/engine/boundary.js +0 -55
  203. package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
  204. package/esm/src/kernel/engine/runner/tools.js +0 -198
  205. package/esm/src/kernel/registry/tools.d.ts +0 -12
  206. package/esm/src/kernel/registry/tools.js +0 -36
  207. package/esm/src/providers/expose-for-tests.d.ts +0 -1
  208. package/esm/src/providers/expose-for-tests.js +0 -25
  209. package/esm/src/providers/gemini-tape.d.ts +0 -2
  210. package/esm/src/providers/google-tap.d.ts +0 -3
  211. package/esm/src/providers/interactions.d.ts +0 -5
  212. package/esm/src/providers/interactions.js +0 -169
  213. package/esm/src/providers/keys.d.ts +0 -19
  214. package/esm/src/providers/local.d.ts +0 -29
  215. package/esm/src/providers/openrouter-mod.d.ts +0 -13
  216. package/esm/src/providers/openrouter-mod.js +0 -12
  217. package/esm/src/providers/openrouter-payload.d.ts +0 -39
  218. package/esm/src/providers/openrouter-payload.js +0 -195
  219. package/esm/src/providers/openrouter.d.ts +0 -15
  220. package/esm/src/providers/pcm.d.ts +0 -7
  221. package/esm/src/providers/provider.d.ts +0 -15
  222. package/esm/src/providers/provider.js +0 -176
  223. package/esm/src/providers/speech.d.ts +0 -23
  224. package/esm/src/providers/sse.d.ts +0 -7
  225. package/esm/src/providers/sse.js +0 -55
  226. package/esm/src/streaming/mod.d.ts +0 -9
  227. package/esm/src/streaming/mod.js +0 -8
  228. /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
  229. /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
@@ -1,24 +1,17 @@
1
1
  /**
2
2
  * Profile resolution for THEORUM turns.
3
3
  *
4
- * This module validates caller overrides, selects models and tools, normalizes
5
- * input parts, and produces the provider request state consumed by `runTurn`.
6
- *
7
4
  * @module
8
5
  */
6
+ import { mintCanary } from '../../guardrails/canary.js';
9
7
  import { TheorumError } from '../../guardrails/error.js';
10
8
  import { sanitizeTurnRequest } from '../../guardrails/sanitize.js';
11
- import { mintCanary } from '../engine/boundary.js';
12
- import { CATALOG, clampThinkingLevel, getTool, listBuiltinIds, requireModelSpec, } from './catalog.js';
13
- import { assertImageGrounding, assertSpeechRole, resolveImageFormat, resolveInputParts, } from './ingress.js';
9
+ import { projectTools } from '../tools/project.js';
10
+ import { resolveTurnTools } from '../tools/resolve.js';
11
+ import { clampThinkingLevel, requireModelSpec } from './catalog.js';
12
+ import { assertOutputMode, assertSpeechRole, resolveImageFormat, resolveInputParts, } from './ingress.js';
14
13
  import { getProfile } from './profiles.js';
15
- import { resolveGeminiBucket } from './vault.js';
16
- function applyBuiltinMutualExclusions(requested) {
17
- return requested.filter((id) => {
18
- const conflicts = getTool(id)?.conflictsWith ?? [];
19
- return !conflicts.some((other) => requested.includes(other));
20
- });
21
- }
14
+ import { providerUsesKeySlots, resolveKeySlot } from './vault.js';
22
15
  function firstSelectKey(selectMap) {
23
16
  const [key] = Object.keys(selectMap);
24
17
  return key;
@@ -56,6 +49,9 @@ function pickModel(profile, select) {
56
49
  return only;
57
50
  }
58
51
  function thinkingFromControl(spec, thinkingOn) {
52
+ if (!spec.thinking) {
53
+ throw new TheorumError('model.config thinking map is required when controls include thinking');
54
+ }
59
55
  if (thinkingOn) {
60
56
  return spec.thinking.on;
61
57
  }
@@ -92,6 +88,9 @@ function resolveThinking(profile, spec, thinkingOn, select) {
92
88
  return clampThinkingLevel(spec, raw);
93
89
  }
94
90
  function resolveSummaries(profile, spec, thinkingOn) {
91
+ if (!spec.summaries) {
92
+ return undefined;
93
+ }
95
94
  if (profile.model.controls?.includes('thinking')) {
96
95
  if (thinkingOn) {
97
96
  return spec.summaries.on;
@@ -100,27 +99,8 @@ function resolveSummaries(profile, spec, thinkingOn) {
100
99
  }
101
100
  return spec.summaries.on;
102
101
  }
103
- function isGatedOn(requested, id) {
104
- if (!requested) {
105
- return false;
106
- }
107
- return requested[id] === true;
108
- }
109
- function resolveBuiltins(profile, requested) {
110
- const allowed = profile.tools.allow.filter((id) => CATALOG.tools[id]?.kind === 'builtin');
111
- const picked = listBuiltinIds().filter((id) => allowed.includes(id) && isGatedOn(requested, id));
112
- return applyBuiltinMutualExclusions(picked);
113
- }
114
- function resolveCustom(profile, requested) {
115
- return profile.tools.allow.filter((id) => CATALOG.tools[id]?.kind === 'custom' && isGatedOn(requested, id));
116
- }
117
- function assertToolAllowed(profile, name) {
118
- if (!profile.tools.allow.includes(name)) {
119
- throw new TheorumError(`Tool '${name}' is not allowed on ${profile.id}`);
120
- }
121
- }
122
102
  function resolveStructured(profile, slots) {
123
- const { structured } = profile.outputs;
103
+ const structured = profile.outputs?.structured;
124
104
  if (!structured) {
125
105
  return null;
126
106
  }
@@ -136,76 +116,126 @@ function resolveStructured(profile, slots) {
136
116
  }
137
117
  return structured.fallback;
138
118
  }
119
+ /**
120
+ * THEORUM prefers SSE when the host omits `outputs.streaming.mode`.
121
+ * Explicit `'buffered'` opts out; `'sse'` (or omit) yields `stream: true`.
122
+ */
123
+ function resolveStreamFlag(profile) {
124
+ return profile.outputs?.streaming?.mode !== 'buffered';
125
+ }
126
+ function resolveStore(spec, reqStore) {
127
+ if (reqStore !== undefined) {
128
+ return reqStore;
129
+ }
130
+ return spec.store;
131
+ }
132
+ function assertTurnResumption(profile, req) {
133
+ if (!req.continueFrom) {
134
+ return;
135
+ }
136
+ if (profile.type === 'live') {
137
+ throw new TheorumError(`Profile ${profile.id}: type 'live' uses live.sessionResumption, not turnResumption/continueFrom`);
138
+ }
139
+ const policy = profile.turnResumption;
140
+ const max = policy?.maxContinues;
141
+ if (max === undefined) {
142
+ return;
143
+ }
144
+ const attempt = req.continuation;
145
+ if (attempt === undefined) {
146
+ throw new TheorumError(`Profile ${profile.id}: continueFrom requires TurnRequest.continuation when turnResumption.maxContinues is set`);
147
+ }
148
+ if (attempt < 1) {
149
+ throw new TheorumError(`Profile ${profile.id}: continuation must be >= 1`);
150
+ }
151
+ if (attempt > max) {
152
+ throw new TheorumError(`Profile ${profile.id}: continuation ${attempt} exceeds turnResumption.maxContinues (${max})`);
153
+ }
154
+ }
139
155
  /** Resolve a host `TurnRequest` into provider-ready generation state. */
140
156
  function resolveTurn(req) {
141
157
  const safe = sanitizeTurnRequest(req);
142
158
  const input = safe.input ?? {};
143
159
  const profile = getProfile(safe.profile);
160
+ assertTurnResumption(profile, safe);
144
161
  const model = pickModel(profile, safe.select);
145
162
  const spec = requireModelSpec(profile, model);
146
163
  const thinkingOn = safe.thinking === true;
147
- const builtins = resolveBuiltins(profile, safe.tools);
148
- assertImageGrounding(profile, model, builtins);
164
+ const toolSnapshot = resolveTurnTools(profile, safe, model);
165
+ const builtins = toolSnapshot.builtins;
166
+ const structured = resolveStructured(profile, input.slots);
167
+ assertOutputMode(profile, structured);
149
168
  assertSpeechRole(profile);
150
- const geminiBucket = profile.model.provider === 'google'
151
- ? resolveGeminiBucket(profile.model.key ?? 'freeA', spec, builtins)
169
+ const pinnedKey = profile.model.key ?? spec.key;
170
+ const keySlot = providerUsesKeySlots(profile.model.provider)
171
+ ? resolveKeySlot(pinnedKey, spec, builtins, profile.model.provider === 'google')
152
172
  : undefined;
173
+ const transport = profile.type === 'live'
174
+ ? 'geminiLive'
175
+ : profile.model.protocol === 'geminiInteractions' && profile.model.provider === 'google'
176
+ ? 'interactions'
177
+ : 'openAiCompat';
178
+ const previousInteractionId = spec.persistViaInteractionId === false ? undefined : safe.previousInteractionId;
153
179
  return {
154
180
  profile,
155
181
  generation: {
156
182
  model,
157
183
  apiId: spec.apiId,
158
- openRouterId: spec.openRouterId,
159
- previousInteractionId: safe.previousInteractionId,
160
- store: safe.store,
184
+ transport,
185
+ previousInteractionId,
186
+ store: resolveStore(spec, safe.store),
187
+ stream: resolveStreamFlag(profile),
161
188
  thinking: resolveThinking(profile, spec, thinkingOn, safe.select),
162
189
  summaries: resolveSummaries(profile, spec, thinkingOn),
163
190
  maxOutputTokens: spec.maxOutputTokens,
164
191
  temperature: spec.temperature,
165
192
  builtins,
166
- custom: resolveCustom(profile, safe.tools),
167
- dynamicTools: safe.dynamicTools,
168
- dynamicToolLoader: safe.dynamicToolLoader,
193
+ googleMapsLocation: safe.googleMapsLocation,
194
+ tools: toolSnapshot,
169
195
  sessionPermissions: safe.sessionPermissions,
170
196
  history: input.history,
171
- maxSteps: profile.model.maxSteps ?? 1,
172
- structured: resolveStructured(profile, input.slots),
173
- image: resolveImageFormat(profile, model, input.slots),
174
- speech: profile.outputs.speech,
197
+ maxSteps: profile.model.maxSteps,
198
+ structured,
199
+ image: resolveImageFormat(profile),
200
+ speech: profile.type === 'speech' ? profile.speech : undefined,
201
+ live: profile.type === 'live' ? profile.live : undefined,
175
202
  input: resolveInputParts(profile, model, safe),
176
- geminiBucket,
177
- canary: profile.guardrails.canary !== false ? mintCanary() : '',
203
+ keySlot,
204
+ canary: profile.guardrails?.canary === true ? mintCanary() : '',
205
+ sessionResumptionHandle: safe.sessionResumptionHandle ?? input.sessionResumptionHandle,
178
206
  },
179
207
  };
180
208
  }
181
209
  function primaryImageSpec(profile) {
182
- return profile.outputs.image;
210
+ return profile.type === 'image' ? profile.image : null;
183
211
  }
184
- /** Project a registered profile into a safe host/UI inspection object. */
185
- function projectProfile(id) {
186
- const profile = getProfile(id);
187
- const { model, identity, tools, inputs, outputs } = profile;
188
- const { select, allow, maxSteps, controls } = model;
189
- const { handle, chat } = identity;
190
- const { slots } = inputs;
212
+ function profileInputsOrNull(profile) {
213
+ if (profile.type === 'speech') {
214
+ return null;
215
+ }
216
+ return profile.inputs ?? null;
217
+ }
218
+ /** Project a profile object into a safe host/UI inspection object. */
219
+ function projectProfileObject(profile) {
220
+ const { model, identity, outputs } = profile;
221
+ const inputs = profileInputsOrNull(profile);
191
222
  return {
192
223
  id: profile.id,
193
- handle,
194
- chat: chat !== false,
195
- maxSteps: maxSteps ?? 1,
196
- models: allow,
197
- select: select ?? null,
198
- controls: controls ?? [],
199
- tools: tools.allow.map((name) => ({
200
- name,
201
- ...(CATALOG.tools[name] ?? { kind: 'custom', ui: true }),
202
- })),
224
+ type: profile.type,
225
+ handle: identity.handle,
226
+ model,
227
+ tools: projectTools(profile),
203
228
  inputs,
204
- slots: slots ?? {},
205
- outputs,
229
+ outputs: outputs ?? null,
206
230
  image: primaryImageSpec(profile),
231
+ speech: profile.type === 'speech' ? profile.speech : null,
232
+ live: profile.type === 'live' ? profile.live : null,
207
233
  };
208
234
  }
235
+ /** Project a registered profile into a safe host/UI inspection object. */
236
+ function projectProfile(id) {
237
+ return projectProfileObject(getProfile(id));
238
+ }
209
239
  function pickSystemRole(profile, requested) {
210
240
  const { identity } = profile;
211
241
  const { handle, systemByRole } = identity;
@@ -214,4 +244,4 @@ function pickSystemRole(profile, requested) {
214
244
  }
215
245
  return handle;
216
246
  }
217
- export { assertToolAllowed, pickSystemRole, projectProfile, resolveTurn };
247
+ export { pickModel, pickSystemRole, projectProfile, projectProfileObject, resolveTurn };
@@ -1,11 +1,16 @@
1
1
  /**
2
- * Gemini vault slot selection for Google Interactions transport.
3
- *
4
- * Host-owned policy over `ModelSpec.key` / `keyBuiltins`. Pure; no network.
2
+ * Vault key-slot selection for credentialed transports (Google, OpenRouter, …).
5
3
  *
6
4
  * @module
7
5
  */
8
- import type { BuiltinToolId, GeminiBucket, GeminiFreeBucket, ModelSpec } from '../types.js';
9
- /** Pick the vault slot for a turn from profile key, model pin, and builtins. */
10
- declare function resolveGeminiBucket(profileKey: GeminiFreeBucket, spec: ModelSpec, builtins: BuiltinToolId[]): GeminiBucket;
11
- export { resolveGeminiBucket };
6
+ import type { BuiltinToolId, KeySlot, ModelSpec, Provider } from '../types.js';
7
+ /** Providers that resolve a vault `keySlot` on each turn. */
8
+ export declare function providerUsesKeySlots(provider: Provider): boolean;
9
+ /**
10
+ * Pick the key slot for a turn from profile key, model pin, and enabled builtins.
11
+ *
12
+ * When `required` is false and nothing pins a slot, returns `undefined` so hosts
13
+ * can use a single flat `apiKey` (OpenRouter without a vault).
14
+ */
15
+ declare function resolveKeySlot(profileKey: KeySlot | undefined, spec: ModelSpec, builtins: BuiltinToolId[], required: boolean): KeySlot | undefined;
16
+ export { resolveKeySlot };
@@ -1,18 +1,40 @@
1
1
  /**
2
- * Gemini vault slot selection for Google Interactions transport.
3
- *
4
- * Host-owned policy over `ModelSpec.key` / `keyBuiltins`. Pure; no network.
2
+ * Vault key-slot selection for credentialed transports (Google, OpenRouter, …).
5
3
  *
6
4
  * @module
7
5
  */
8
- /** Pick the vault slot for a turn from profile key, model pin, and builtins. */
9
- function resolveGeminiBucket(profileKey, spec, builtins) {
6
+ import { TheorumError } from '../../guardrails/error.js';
7
+ import { getTool } from '../tools/registry.js';
8
+ function builtinForcesPaid(id) {
9
+ const tool = getTool(id);
10
+ if (tool?.type !== 'builtin') {
11
+ return false;
12
+ }
13
+ return tool.forcePaidKey === true;
14
+ }
15
+ /** Providers that resolve a vault `keySlot` on each turn. */
16
+ export function providerUsesKeySlots(provider) {
17
+ return provider === 'google' || provider === 'openrouter';
18
+ }
19
+ /**
20
+ * Pick the key slot for a turn from profile key, model pin, and enabled builtins.
21
+ *
22
+ * When `required` is false and nothing pins a slot, returns `undefined` so hosts
23
+ * can use a single flat `apiKey` (OpenRouter without a vault).
24
+ */
25
+ function resolveKeySlot(profileKey, spec, builtins, required) {
10
26
  if (spec.key) {
11
27
  return spec.key;
12
28
  }
13
- if (builtins.some((id) => !spec.keyBuiltins.includes(id))) {
29
+ if (builtins.some((id) => builtinForcesPaid(id))) {
14
30
  return 'paid';
15
31
  }
16
- return profileKey;
32
+ if (profileKey) {
33
+ return profileKey;
34
+ }
35
+ if (required) {
36
+ throw new TheorumError('Profile must set model.key or model.config.*.key');
37
+ }
38
+ return undefined;
17
39
  }
18
- export { resolveGeminiBucket };
40
+ export { resolveKeySlot };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Runtime vocabulary and field catalog for THEORUM profile types.
3
+ *
4
+ * Closed unions live here as `as const` arrays; TypeScript types are derived
5
+ * from those arrays. Host UIs and docs import this module (no Deno APIs) so
6
+ * dropdowns and hover tips stay in lockstep with the kernel.
7
+ *
8
+ * @module
9
+ */
10
+ /** Primary profile archetype. Discriminated union key for `ProfileDefinition` and `Profile`. */
11
+ export declare const PROFILE_TYPES: readonly ["text", "image", "speech", "live"];
12
+ export type ProfileType = (typeof PROFILE_TYPES)[number];
13
+ /** Model reasoning effort level normalized across provider adapters. */
14
+ export declare const THINKING_LEVELS: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
15
+ export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
16
+ /** Wire protocol for a profile. */
17
+ export declare const PROTOCOLS: readonly ["geminiInteractions", "geminiLive", "openAi"];
18
+ export type Protocol = (typeof PROTOCOLS)[number];
19
+ /** Transport provider for a profile. */
20
+ export declare const PROVIDERS: readonly ["google", "openrouter", "local"];
21
+ export type Provider = (typeof PROVIDERS)[number];
22
+ /**
23
+ * Legal `createProvider` pairs. Keep this table in lockstep with the factory.
24
+ * Keys are protocols; values are the providers that protocol may bind.
25
+ */
26
+ export declare const PROTOCOL_PROVIDERS: {
27
+ readonly geminiInteractions: readonly ["google"];
28
+ readonly geminiLive: readonly ["google"];
29
+ readonly openAi: readonly ["openrouter", "local"];
30
+ };
31
+ /**
32
+ * Legal wire protocols for each profile archetype.
33
+ * 'live' profiles require 'geminiLive'; turn-based archetypes require turn protocols.
34
+ */
35
+ export declare const PROFILE_TYPE_PROTOCOLS: {
36
+ readonly text: readonly ["geminiInteractions", "openAi"];
37
+ readonly image: readonly ["geminiInteractions", "openAi"];
38
+ readonly speech: readonly ["geminiInteractions", "openAi"];
39
+ readonly live: readonly ["geminiLive"];
40
+ };
41
+ export type ProfileTypeProtocol<T extends ProfileType> = (typeof PROFILE_TYPE_PROTOCOLS)[T][number];
42
+ /** Protocols allowed for a profile archetype (`type`). */
43
+ export declare function protocolsForProfileType(type: ProfileType): readonly Protocol[];
44
+ /** True when the protocol is valid for the given profile archetype. */
45
+ export declare function isValidProfileProtocol(type: ProfileType, protocol: Protocol): boolean;
46
+ /** Named vault key slots for host-supplied credentials (provider-neutral). */
47
+ export declare const KEY_SLOTS: readonly ["slotA", "slotB", "slotC", "paid"];
48
+ export type KeySlot = (typeof KEY_SLOTS)[number];
49
+ /** Key slots that may overflow to `paid` after quota backoff. */
50
+ export declare const OVERFLOW_KEY_SLOTS: readonly ["slotA", "slotB", "slotC"];
51
+ export type OverflowKeySlot = (typeof OVERFLOW_KEY_SLOTS)[number];
52
+ /** Host vault: one optional credential string per key slot. */
53
+ export type KeyVault = Record<KeySlot, string | undefined>;
54
+ /** Profile-level control a caller may toggle at turn time. */
55
+ export declare const CONTROL_IDS: readonly ["thinking"];
56
+ export type ControlId = (typeof CONTROL_IDS)[number];
57
+ /** Normalized multimodal part category. */
58
+ export declare const MEDIA_INPUT_KIND_VALUES: readonly ["image", "audio", "video", "document"];
59
+ export type MediaInputKind = (typeof MEDIA_INPUT_KIND_VALUES)[number];
60
+ /** Provider thinking-summary behavior. */
61
+ export declare const SUMMARY_MODES: readonly ["auto", "none"];
62
+ export type SummaryMode = (typeof SUMMARY_MODES)[number];
63
+ /** Stream delivery mode. */
64
+ export declare const STREAM_MODES: readonly ["sse", "buffered"];
65
+ export type StreamMode = (typeof STREAM_MODES)[number];
66
+ /** Audio container for speech generation output. */
67
+ export declare const SPEECH_AUDIO_FORMATS: readonly ["pcm", "mp3"];
68
+ export type SpeechAudioFormat = (typeof SPEECH_AUDIO_FORMATS)[number];
69
+ /** Live session activity handling (barge-in behavior). */
70
+ export declare const LIVE_ACTIVITY_HANDLINGS: readonly ["START_OF_ACTIVITY_INTERRUPTS", "NO_INTERRUPTION"];
71
+ export type LiveActivityHandling = (typeof LIVE_ACTIVITY_HANDLINGS)[number];
72
+ /** Live session voice activity detection sensitivity. */
73
+ export declare const LIVE_SPEECH_SENSITIVITIES: readonly ["START_SENSITIVITY_LOW", "START_SENSITIVITY_HIGH", "END_SENSITIVITY_LOW", "END_SENSITIVITY_HIGH"];
74
+ export type LiveSpeechSensitivity = (typeof LIVE_SPEECH_SENSITIVITIES)[number];
75
+ /** Live session context window compression mode. */
76
+ export declare const LIVE_CONTEXT_COMPRESSIONS: readonly ["slidingWindow", "none"];
77
+ export type LiveContextCompression = (typeof LIVE_CONTEXT_COMPRESSIONS)[number];
78
+ /** Structured-output enforcement mode. */
79
+ export declare const SCHEMA_ENFORCEMENTS: readonly ["responseFormat", "prompt"];
80
+ export type SchemaEnforcement = (typeof SCHEMA_ENFORCEMENTS)[number];
81
+ /** Compaction threshold meter. */
82
+ export declare const COMPACTION_METERS: readonly ["history", "input"];
83
+ export type CompactionMeter = (typeof COMPACTION_METERS)[number];
84
+ /** When compaction runs relative to the primary turn. */
85
+ export declare const COMPACTION_TIMINGS: readonly ["before", "after"];
86
+ export type CompactionTiming = (typeof COMPACTION_TIMINGS)[number];
87
+ /** Egress block handling. */
88
+ export declare const EGRESS_ON_BLOCK: readonly ["reject_to_agent", "refuse_to_user"];
89
+ export type EgressOnBlock = (typeof EGRESS_ON_BLOCK)[number];
90
+ /** Why a turn ended (provider-neutral). */
91
+ export declare const TURN_STOP_KINDS: readonly ["completed", "length", "tool", "filtered", "provider_error", "cancelled", "stream_incomplete", "interrupted", "generation_complete"];
92
+ export type TurnStopKind = (typeof TURN_STOP_KINDS)[number];
93
+ /** Per-tool visibility tier — enforced by the kernel at resolve time. */
94
+ export declare const TOOL_LOAD_TIERS: readonly ["T0", "T1", "T2"];
95
+ export type ToolLoadTier = (typeof TOOL_LOAD_TIERS)[number];
96
+ /** Registered tool discriminant (`registerTool`). */
97
+ export declare const TOOL_TYPES: readonly ["builtin", "function"];
98
+ /** Semantic access level — host policy / UI; not enforced by execute. */
99
+ export declare const TOOL_ACCESS: readonly ["read-only", "read-write", "destructive"];
100
+ export type ToolAccess = (typeof TOOL_ACCESS)[number];
101
+ /** Execution authorization tier for registered tools. */
102
+ export declare const TOOL_PERMISSION: readonly ["auto", "session_consent", "always_confirm"];
103
+ export type ToolPermission = (typeof TOOL_PERMISSION)[number];
104
+ /** MIME essence → normalized media part category (shared ingress map). */
105
+ export declare const MEDIA_INPUT_KINDS: Record<string, MediaInputKind>;
106
+ /** Type-prefix wildcards accepted by `mimeAllowed`. */
107
+ export declare const MEDIA_WILDCARDS: readonly ["image/*", "audio/*", "video/*"];
108
+ /** Attachment `accept` values the kernel can classify (wildcards + known types). */
109
+ export declare const ATTACHMENT_ACCEPT_MIMES: readonly string[];
110
+ /** Voice `accept` values the kernel can classify (wildcard + known audio types). */
111
+ export declare const VOICE_ACCEPT_MIMES: readonly string[];
112
+ /** Providers allowed for a protocol. */
113
+ export declare function providersFor(protocol: Protocol): readonly Provider[];
114
+ /** Protocols allowed for a provider. */
115
+ export declare function protocolsFor(provider: Provider): readonly Protocol[];
116
+ /** True when `createProvider` will accept this pair. */
117
+ export declare function isValidPair(protocol: Protocol, provider: Provider): boolean;
118
+ /** When protocol changes, snap provider to a valid partner. */
119
+ export declare function coerceProvider(protocol: Protocol, provider: Provider): Provider;
120
+ /** When provider changes, snap protocol to a valid partner. */
121
+ export declare function coerceProtocol(protocol: Protocol, provider: Provider): Protocol;
122
+ /** Metadata for one profile (or adjacent) field, used by docs/UI hover. */
123
+ export type FieldMeta = {
124
+ type: string;
125
+ doc: string;
126
+ options?: readonly string[];
127
+ optionDescriptions?: Record<string, string>;
128
+ optionNote?: string;
129
+ };
130
+ /**
131
+ * Parents whose next key is a host-owned map key (model id, slot name, …).
132
+ * The annotator substitutes `*` so `model.config.flash.apiId` → `model.config.*.apiId`.
133
+ */
134
+ export declare const DYNAMIC_FIELD_PARENTS: ReadonlySet<string>;
135
+ /** Resolve a key stack from authored source into a catalog path. */
136
+ export declare function catalogPathFor(keys: readonly string[]): string;
137
+ /**
138
+ * Authoring-surface catalog for `Profile` / `defineProfile`.
139
+ * Hover UIs look up dotted paths. Adding a profile field? Add it here.
140
+ */
141
+ export declare const PROFILE_FIELDS: Record<string, FieldMeta>;
142
+ /** Adjacent tool catalog fields that appear next to profile examples. */
143
+ export declare const EXTRA_FIELDS: Record<string, FieldMeta>;
144
+ /** Look up hover metadata for a dotted path (profile first, then extra). */
145
+ export declare function fieldMeta(path: string): FieldMeta | undefined;