theorum 1.0.0 → 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 (204) hide show
  1. package/README.md +81 -25
  2. package/esm/mod.d.ts +48 -29
  3. package/esm/mod.js +37 -24
  4. package/esm/src/cli/commands/bench.js +13 -16
  5. package/esm/src/cli/commands/fuzz-canary.js +25 -29
  6. package/esm/src/cli/commands/guardrails-eval.d.ts +14 -0
  7. package/esm/src/cli/commands/guardrails-eval.js +15 -0
  8. package/esm/src/cli/commands/profile.js +12 -3
  9. package/esm/src/cli/commands/run.js +8 -9
  10. package/esm/src/cli/commands/test.js +8 -8
  11. package/esm/src/cli/event-log.d.ts +1 -1
  12. package/esm/src/cli/index.js +12 -0
  13. package/esm/src/cli/matrix/synthesizer.d.ts +6 -6
  14. package/esm/src/cli/matrix/synthesizer.js +23 -22
  15. package/esm/src/guardrails/canary-gate.d.ts +2 -1
  16. package/esm/src/guardrails/canary-gate.js +2 -1
  17. package/esm/src/guardrails/canary.d.ts +9 -1
  18. package/esm/src/guardrails/canary.js +21 -10
  19. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +1 -0
  20. package/esm/src/guardrails/corpus/fuzz-inbound.js +12 -15
  21. package/esm/src/guardrails/corpus/inbound-payloads.js +1 -0
  22. package/esm/src/guardrails/corpus/live-attacks.js +0 -1
  23. package/esm/src/guardrails/corpus/mod.d.ts +1 -0
  24. package/esm/src/guardrails/corpus/secrets.d.ts +1 -0
  25. package/esm/src/guardrails/corpus/secrets.js +1 -0
  26. package/esm/src/guardrails/corpus/strings.d.ts +1 -0
  27. package/esm/src/guardrails/corpus/strings.js +1 -0
  28. package/esm/src/guardrails/corpus/types.d.ts +1 -0
  29. package/esm/src/guardrails/egress.d.ts +26 -3
  30. package/esm/src/guardrails/egress.js +69 -18
  31. package/esm/src/guardrails/error.d.ts +14 -23
  32. package/esm/src/guardrails/error.js +86 -77
  33. package/esm/src/guardrails/eval/corpus.d.ts +108 -0
  34. package/esm/src/guardrails/eval/corpus.js +978 -0
  35. package/esm/src/guardrails/eval/mod.d.ts +51 -0
  36. package/esm/src/guardrails/eval/mod.js +133 -0
  37. package/esm/src/guardrails/eval/score.d.ts +66 -0
  38. package/esm/src/guardrails/eval/score.js +114 -0
  39. package/esm/src/guardrails/events.d.ts +25 -0
  40. package/esm/src/guardrails/events.js +56 -0
  41. package/esm/src/guardrails/hits.d.ts +24 -0
  42. package/esm/src/guardrails/hits.js +45 -0
  43. package/esm/src/guardrails/lexicon.d.ts +39 -0
  44. package/esm/src/guardrails/lexicon.js +200 -0
  45. package/esm/src/guardrails/live-outbound-gate.d.ts +15 -13
  46. package/esm/src/guardrails/live-outbound-gate.js +131 -89
  47. package/esm/src/guardrails/mod.d.ts +19 -4
  48. package/esm/src/guardrails/mod.js +12 -3
  49. package/esm/src/guardrails/network.d.ts +19 -0
  50. package/esm/src/guardrails/network.js +234 -0
  51. package/esm/src/guardrails/policy.d.ts +35 -0
  52. package/esm/src/guardrails/policy.js +50 -0
  53. package/esm/src/guardrails/progressive-yield.d.ts +51 -0
  54. package/esm/src/guardrails/progressive-yield.js +98 -0
  55. package/esm/src/guardrails/quota.d.ts +17 -3
  56. package/esm/src/guardrails/quota.js +18 -4
  57. package/esm/src/guardrails/sanitize.d.ts +36 -6
  58. package/esm/src/guardrails/sanitize.js +152 -60
  59. package/esm/src/guardrails/serialize.d.ts +35 -0
  60. package/esm/src/guardrails/serialize.js +58 -0
  61. package/esm/src/guardrails/testing.d.ts +7 -0
  62. package/esm/src/guardrails/testing.js +4 -0
  63. package/esm/src/guardrails/theorum-error.d.ts +12 -0
  64. package/esm/src/guardrails/theorum-error.js +15 -0
  65. package/esm/src/guardrails/tool-directives.d.ts +48 -0
  66. package/esm/src/guardrails/tool-directives.js +124 -0
  67. package/esm/src/guardrails/tool-result.d.ts +93 -0
  68. package/esm/src/guardrails/tool-result.js +276 -0
  69. package/esm/src/guardrails/types.d.ts +291 -0
  70. package/esm/src/guardrails/types.js +72 -0
  71. package/esm/src/host/client-turn.js +4 -0
  72. package/esm/src/host/mint-trace.d.ts +1 -1
  73. package/esm/src/kernel/auth/crypto.d.ts +42 -0
  74. package/esm/src/kernel/auth/crypto.js +106 -0
  75. package/esm/src/kernel/auth/mod.d.ts +11 -0
  76. package/esm/src/kernel/auth/mod.js +11 -0
  77. package/esm/src/kernel/auth/oauth.d.ts +47 -0
  78. package/esm/src/kernel/auth/oauth.js +278 -0
  79. package/esm/src/kernel/auth/types.d.ts +133 -0
  80. package/esm/src/kernel/auth/types.js +13 -0
  81. package/esm/src/kernel/engine/delta.js +43 -2
  82. package/esm/src/kernel/engine/live-inbound.d.ts +15 -3
  83. package/esm/src/kernel/engine/live-inbound.js +22 -8
  84. package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
  85. package/esm/src/kernel/engine/live-ingress.js +47 -0
  86. package/esm/src/kernel/engine/repair.js +13 -12
  87. package/esm/src/kernel/engine/runner/gates.js +127 -40
  88. package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
  89. package/esm/src/kernel/engine/runner/mod.js +182 -43
  90. package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
  91. package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
  92. package/esm/src/kernel/engine/runner/stages.js +89 -0
  93. package/esm/src/kernel/engine/runner/state.d.ts +24 -0
  94. package/esm/src/kernel/engine/runner/steps.js +128 -61
  95. package/esm/src/kernel/engine/runner/stream.d.ts +9 -2
  96. package/esm/src/kernel/engine/runner/stream.js +114 -55
  97. package/esm/src/kernel/engine/session/mod.d.ts +3 -1
  98. package/esm/src/kernel/engine/session/mod.js +386 -34
  99. package/esm/src/kernel/interaction-parts.d.ts +14 -0
  100. package/esm/src/kernel/interaction-parts.js +23 -0
  101. package/esm/src/kernel/mod.d.ts +19 -7
  102. package/esm/src/kernel/mod.js +10 -5
  103. package/esm/src/kernel/profile-graph.d.ts +159 -0
  104. package/esm/src/kernel/profile-graph.js +156 -0
  105. package/esm/src/kernel/registry/attachments.d.ts +10 -10
  106. package/esm/src/kernel/registry/attachments.js +29 -26
  107. package/esm/src/kernel/registry/catalog.d.ts +25 -11
  108. package/esm/src/kernel/registry/catalog.js +59 -24
  109. package/esm/src/kernel/registry/ingress.js +34 -22
  110. package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
  111. package/esm/src/kernel/registry/profile-outputs.js +8 -0
  112. package/esm/src/kernel/registry/profiles.d.ts +34 -14
  113. package/esm/src/kernel/registry/profiles.js +306 -59
  114. package/esm/src/kernel/registry/provider-request.js +2 -0
  115. package/esm/src/kernel/registry/resolve.d.ts +7 -6
  116. package/esm/src/kernel/registry/resolve.js +102 -117
  117. package/esm/src/kernel/registry/schemas.js +1 -1
  118. package/esm/src/kernel/registry/sole-model.d.ts +8 -0
  119. package/esm/src/kernel/registry/sole-model.js +10 -0
  120. package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
  121. package/esm/src/kernel/registry/system-prompt.js +40 -0
  122. package/esm/src/kernel/registry/system-role.d.ts +8 -0
  123. package/esm/src/kernel/registry/system-role.js +14 -0
  124. package/esm/src/kernel/registry/vault.d.ts +2 -2
  125. package/esm/src/kernel/registry/vault.js +4 -4
  126. package/esm/src/kernel/schema.d.ts +97 -11
  127. package/esm/src/kernel/schema.js +256 -111
  128. package/esm/src/kernel/stages.d.ts +175 -0
  129. package/esm/src/kernel/stages.js +476 -0
  130. package/esm/src/kernel/stop.d.ts +65 -11
  131. package/esm/src/kernel/stop.js +46 -11
  132. package/esm/src/kernel/tools/events.d.ts +41 -0
  133. package/esm/src/kernel/tools/events.js +71 -0
  134. package/esm/src/kernel/tools/execute.d.ts +63 -24
  135. package/esm/src/kernel/tools/execute.js +475 -224
  136. package/esm/src/kernel/tools/harness.js +16 -14
  137. package/esm/src/kernel/tools/invoke.js +26 -8
  138. package/esm/src/kernel/tools/mod.d.ts +3 -1
  139. package/esm/src/kernel/tools/mod.js +2 -1
  140. package/esm/src/kernel/tools/permission.d.ts +15 -0
  141. package/esm/src/kernel/tools/permission.js +47 -0
  142. package/esm/src/kernel/tools/project.js +5 -3
  143. package/esm/src/kernel/tools/registry.js +20 -10
  144. package/esm/src/kernel/tools/remote.d.ts +94 -0
  145. package/esm/src/kernel/tools/remote.js +577 -0
  146. package/esm/src/kernel/tools/resolve.d.ts +17 -7
  147. package/esm/src/kernel/tools/resolve.js +38 -17
  148. package/esm/src/kernel/tools/schema.d.ts +2 -0
  149. package/esm/src/kernel/tools/schema.js +23 -6
  150. package/esm/src/kernel/tools/stage-run.d.ts +105 -0
  151. package/esm/src/kernel/tools/stage-run.js +155 -0
  152. package/esm/src/kernel/tools/types.d.ts +198 -20
  153. package/esm/src/kernel/types.d.ts +253 -114
  154. package/esm/src/kernel/util/find-last.d.ts +2 -0
  155. package/esm/src/kernel/util/find-last.js +10 -0
  156. package/esm/src/observability/destinations.d.ts +31 -0
  157. package/esm/src/observability/destinations.js +67 -0
  158. package/esm/src/observability/mod.d.ts +10 -3
  159. package/esm/src/observability/mod.js +6 -2
  160. package/esm/src/observability/policy.d.ts +27 -0
  161. package/esm/src/observability/policy.js +80 -0
  162. package/esm/src/observability/resolve-policy.d.ts +16 -0
  163. package/esm/src/observability/resolve-policy.js +64 -0
  164. package/esm/src/observability/trace-attach.d.ts +3 -1
  165. package/esm/src/observability/trace-attach.js +17 -10
  166. package/esm/src/observability/trace-record.d.ts +12 -4
  167. package/esm/src/observability/trace-record.js +63 -19
  168. package/esm/src/observability/trace-sink.d.ts +19 -0
  169. package/esm/src/observability/trace-sink.js +10 -0
  170. package/esm/src/observability/trace.d.ts +14 -11
  171. package/esm/src/observability/trace.js +23 -14
  172. package/esm/src/observability/types.d.ts +113 -0
  173. package/esm/src/observability/types.js +11 -0
  174. package/esm/src/providers/create-provider.d.ts +7 -4
  175. package/esm/src/providers/create-provider.js +21 -7
  176. package/esm/src/providers/google/interactions/framing.d.ts +8 -0
  177. package/esm/src/providers/google/interactions/framing.js +74 -13
  178. package/esm/src/providers/google/live/framing.d.ts +5 -2
  179. package/esm/src/providers/google/live/framing.js +49 -12
  180. package/esm/src/providers/google/live/openapi-schema.js +3 -2
  181. package/esm/src/providers/google/live/stream.d.ts +9 -0
  182. package/esm/src/providers/google/live/stream.js +16 -3
  183. package/esm/src/providers/openrouter/cache-control.d.ts +24 -0
  184. package/esm/src/providers/openrouter/cache-control.js +23 -0
  185. package/esm/src/providers/openrouter/chat.d.ts +21 -1
  186. package/esm/src/providers/openrouter/chat.js +51 -6
  187. package/esm/src/providers/openrouter/image.js +8 -19
  188. package/esm/src/providers/openrouter/openai/chat-payload.js +23 -1
  189. package/esm/src/providers/openrouter/openai/compat.d.ts +7 -1
  190. package/esm/src/providers/openrouter/openai/compat.js +23 -4
  191. package/esm/src/providers/openrouter/openai/image-payload.js +8 -2
  192. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +1 -1
  193. package/esm/src/providers/openrouter/openai/sdk-messages.js +32 -5
  194. package/package.json +1 -1
  195. package/esm/src/interface/blocks.d.ts +0 -20
  196. package/esm/src/interface/blocks.js +0 -180
  197. package/esm/src/interface/from-profile.d.ts +0 -14
  198. package/esm/src/interface/from-profile.js +0 -104
  199. package/esm/src/interface/inputs.d.ts +0 -8
  200. package/esm/src/interface/inputs.js +0 -127
  201. package/esm/src/interface/mod.d.ts +0 -10
  202. package/esm/src/interface/mod.js +0 -9
  203. package/esm/src/interface/types.d.ts +0 -153
  204. package/esm/src/interface/types.js +0 -13
@@ -5,101 +5,78 @@
5
5
  */
6
6
  import { mintCanary } from '../../guardrails/canary.js';
7
7
  import { TheorumError } from '../../guardrails/error.js';
8
+ import { resolveGuardrailPolicy } from '../../guardrails/policy.js';
8
9
  import { sanitizeTurnRequest } from '../../guardrails/sanitize.js';
10
+ import { profileTurnResumption } from '../stop.js';
9
11
  import { projectTools } from '../tools/project.js';
10
12
  import { resolveTurnTools } from '../tools/resolve.js';
11
- import { clampThinkingLevel, requireModelSpec } from './catalog.js';
13
+ import { requireModelBinding } from './catalog.js';
12
14
  import { assertOutputMode, assertSpeechRole, resolveImageFormat, resolveInputParts, } from './ingress.js';
13
15
  import { getProfile } from './profiles.js';
16
+ import { soleModelId } from './sole-model.js';
17
+ import { resolveTurnSystemPrompt } from './system-prompt.js';
14
18
  import { providerUsesKeySlots, resolveKeySlot } from './vault.js';
15
- function firstSelectKey(selectMap) {
16
- const [key] = Object.keys(selectMap);
17
- return key;
18
- }
19
- function lookupSelectId(profile, select) {
20
- const { select: selectMap } = profile.model;
21
- if (!selectMap) {
22
- return undefined;
23
- }
24
- let key = select;
25
- if (!key) {
26
- key = firstSelectKey(selectMap);
27
- }
28
- if (!key) {
29
- return undefined;
19
+ /** Narrow to a model-binding profile; `host` never runs a model. */
20
+ function requireModelProfile(profile, door) {
21
+ if (profile.type === 'host') {
22
+ throw new TheorumError(`Profile ${profile.id}: type 'host' never runs a model — ${door} is not supported; execute tools with invokeTool`);
30
23
  }
31
- return selectMap[key];
24
+ return profile;
32
25
  }
33
- function pickModel(profile, select) {
34
- if (profile.model.select) {
35
- const id = lookupSelectId(profile, select);
36
- if (!(id && profile.model.allow.includes(id))) {
37
- let label = '';
38
- if (select) {
39
- label = select;
40
- }
41
- throw new TheorumError(`Unknown model select '${label}' for ${profile.id}`);
26
+ function pickModel(profile, requested) {
27
+ if (requested) {
28
+ if (!profile.allowModelSelect) {
29
+ throw new TheorumError(`Profile ${profile.id} does not allow model selection`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
42
30
  }
43
- return id;
44
- }
45
- const [only] = profile.model.allow;
46
- if (!only) {
47
- throw new TheorumError(`Profile ${profile.id} has no models`);
48
- }
49
- return only;
50
- }
51
- function thinkingFromControl(spec, thinkingOn) {
52
- if (!spec.thinking) {
53
- throw new TheorumError('model.config thinking map is required when controls include thinking');
31
+ if (!profile.models[requested]) {
32
+ throw new TheorumError(`Unknown model '${requested}' for ${profile.id}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
33
+ }
34
+ return requested;
54
35
  }
55
- if (thinkingOn) {
56
- return spec.thinking.on;
36
+ const defaultId = profile.defaultModel ?? soleModelId(profile.models);
37
+ if (!defaultId || !profile.models[defaultId]) {
38
+ throw new TheorumError(`Profile ${profile.id} has no default model`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
57
39
  }
58
- return spec.thinking.off;
40
+ return defaultId;
59
41
  }
60
- function pinnedLevel(pinned, key) {
61
- if (!key) {
42
+ function resolveEffort(profile, binding, modelId, requested) {
43
+ const efforts = binding.efforts;
44
+ if (!efforts || Object.keys(efforts).length === 0) {
45
+ if (requested) {
46
+ throw new TheorumError(`Profile ${profile.id} model '${modelId}' has no selectable efforts`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
47
+ }
62
48
  return undefined;
63
49
  }
64
- return pinned[key];
65
- }
66
- function thinkingFromPin(profile, select) {
67
- const pinned = profile.model.thinking;
68
- if (typeof pinned === 'string') {
69
- return pinned;
70
- }
71
- if (!pinned) {
72
- throw new TheorumError(`Profile ${profile.id} must pin thinking or list it in controls`);
73
- }
74
- const fromSelect = pinnedLevel(pinned, select);
75
- if (fromSelect) {
76
- return fromSelect;
50
+ const keys = Object.keys(efforts);
51
+ if (requested) {
52
+ if (!binding.allowEffortSelect) {
53
+ throw new TheorumError(`Profile ${profile.id} model '${modelId}' does not allow effort selection`);
54
+ }
55
+ const level = efforts[requested];
56
+ if (!level) {
57
+ throw new TheorumError(`Unknown effort '${requested}' for ${profile.id} model '${modelId}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
58
+ }
59
+ return level;
77
60
  }
78
- const fromFirst = pinnedLevel(pinned, firstSelectKey(profile.model.select ?? {}));
79
- if (fromFirst) {
80
- return fromFirst;
61
+ const alias = binding.defaultEffort ?? (keys.length === 1 ? keys[0] : undefined);
62
+ if (!alias) {
63
+ throw new TheorumError(`Profile ${profile.id} model '${modelId}' must set defaultEffort when more than one effort is declared`);
81
64
  }
82
- throw new TheorumError(`Profile ${profile.id} must pin thinking or list it in controls`);
83
- }
84
- function resolveThinking(profile, spec, thinkingOn, select) {
85
- const raw = profile.model.controls?.includes('thinking')
86
- ? thinkingFromControl(spec, thinkingOn)
87
- : thinkingFromPin(profile, select);
88
- return clampThinkingLevel(spec, raw);
65
+ return efforts[alias];
89
66
  }
90
- function resolveSummaries(profile, spec, thinkingOn) {
91
- if (!spec.summaries) {
92
- return undefined;
67
+ function resolveSummaries(binding) {
68
+ if (binding.summaries === true) {
69
+ return 'auto';
93
70
  }
94
- if (profile.model.controls?.includes('thinking')) {
95
- if (thinkingOn) {
96
- return spec.summaries.on;
97
- }
98
- return spec.summaries.off;
71
+ if (binding.summaries === false) {
72
+ return 'none';
99
73
  }
100
- return spec.summaries.on;
74
+ return undefined;
101
75
  }
102
76
  function resolveStructured(profile, slots) {
77
+ if (profile.type === 'live') {
78
+ return null;
79
+ }
103
80
  const structured = profile.outputs?.structured;
104
81
  if (!structured) {
105
82
  return null;
@@ -121,88 +98,98 @@ function resolveStructured(profile, slots) {
121
98
  * Explicit `'buffered'` opts out; `'sse'` (or omit) yields `stream: true`.
122
99
  */
123
100
  function resolveStreamFlag(profile) {
101
+ if (profile.type === 'live') {
102
+ return true;
103
+ }
124
104
  return profile.outputs?.streaming?.mode !== 'buffered';
125
105
  }
126
- function resolveStore(spec, reqStore) {
106
+ function resolveStore(binding, reqStore) {
127
107
  if (reqStore !== undefined) {
128
108
  return reqStore;
129
109
  }
130
- return spec.store;
110
+ return binding.store;
111
+ }
112
+ function resolveTransport(profile, binding) {
113
+ if (profile.type === 'live') {
114
+ return 'geminiLive';
115
+ }
116
+ if (binding.protocol === 'geminiInteractions' && binding.provider === 'google') {
117
+ return 'interactions';
118
+ }
119
+ return 'openAiCompat';
131
120
  }
132
121
  function assertTurnResumption(profile, req) {
133
122
  if (!req.continueFrom) {
134
123
  return;
135
124
  }
136
125
  if (profile.type === 'live') {
137
- throw new TheorumError(`Profile ${profile.id}: type 'live' uses live.sessionResumption, not turnResumption/continueFrom`);
126
+ throw new TheorumError(`Profile ${profile.id}: type 'live' uses live.sessionResumption, not turnBehaviour.resumption/continueFrom`);
138
127
  }
139
- const policy = profile.turnResumption;
128
+ const policy = profileTurnResumption(profile);
140
129
  const max = policy?.maxContinues;
141
130
  if (max === undefined) {
142
131
  return;
143
132
  }
144
133
  const attempt = req.continuation;
145
134
  if (attempt === undefined) {
146
- throw new TheorumError(`Profile ${profile.id}: continueFrom requires TurnRequest.continuation when turnResumption.maxContinues is set`);
135
+ throw new TheorumError(`Profile ${profile.id}: continueFrom requires TurnRequest.continuation when turnBehaviour.resumption.maxContinues is set`);
147
136
  }
148
137
  if (attempt < 1) {
149
- throw new TheorumError(`Profile ${profile.id}: continuation must be >= 1`);
138
+ throw new TheorumError(`Profile ${profile.id}: continuation must be >= 1`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
150
139
  }
151
140
  if (attempt > max) {
152
- throw new TheorumError(`Profile ${profile.id}: continuation ${attempt} exceeds turnResumption.maxContinues (${max})`);
141
+ throw new TheorumError(`Profile ${profile.id}: continuation ${attempt} exceeds turnBehaviour.resumption.maxContinues (${max})`);
153
142
  }
154
143
  }
155
144
  /** Resolve a host `TurnRequest` into provider-ready generation state. */
156
145
  function resolveTurn(req) {
157
146
  const safe = sanitizeTurnRequest(req);
158
147
  const input = safe.input ?? {};
159
- const profile = getProfile(safe.profile);
148
+ const profile = requireModelProfile(getProfile(safe.profile), 'resolveTurn');
160
149
  assertTurnResumption(profile, safe);
161
- const model = pickModel(profile, safe.select);
162
- const spec = requireModelSpec(profile, model);
163
- const thinkingOn = safe.thinking === true;
150
+ const model = pickModel(profile, safe.model);
151
+ const binding = requireModelBinding(profile, model);
164
152
  const toolSnapshot = resolveTurnTools(profile, safe, model);
165
153
  const builtins = toolSnapshot.builtins;
166
154
  const structured = resolveStructured(profile, input.slots);
167
155
  assertOutputMode(profile, structured);
168
156
  assertSpeechRole(profile);
169
- const pinnedKey = profile.model.key ?? spec.key;
170
- const keySlot = providerUsesKeySlots(profile.model.provider)
171
- ? resolveKeySlot(pinnedKey, spec, builtins, profile.model.provider === 'google')
157
+ const pinnedKey = profile.key ?? binding.key;
158
+ const keySlot = providerUsesKeySlots(binding.provider)
159
+ ? resolveKeySlot(pinnedKey, binding, builtins, binding.provider === 'google')
172
160
  : 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;
161
+ const previousInteractionId = binding.persistViaInteractionId === false ? undefined : safe.previousInteractionId;
179
162
  return {
180
163
  profile,
181
164
  generation: {
182
165
  model,
183
- apiId: spec.apiId,
184
- transport,
166
+ apiId: binding.apiId,
167
+ transport: resolveTransport(profile, binding),
185
168
  previousInteractionId,
186
- store: resolveStore(spec, safe.store),
169
+ store: resolveStore(binding, safe.store),
187
170
  stream: resolveStreamFlag(profile),
188
- thinking: resolveThinking(profile, spec, thinkingOn, safe.select),
189
- summaries: resolveSummaries(profile, spec, thinkingOn),
190
- maxOutputTokens: spec.maxOutputTokens,
191
- temperature: spec.temperature,
171
+ thinking: resolveEffort(profile, binding, model, safe.effort),
172
+ summaries: resolveSummaries(binding),
173
+ maxOutputTokens: binding.maxOutputTokens,
174
+ temperature: binding.temperature,
192
175
  builtins,
193
176
  googleMapsLocation: safe.googleMapsLocation,
177
+ cache: binding.cache,
178
+ sessionId: safe.sessionId,
194
179
  tools: toolSnapshot,
195
180
  sessionPermissions: safe.sessionPermissions,
196
181
  history: input.history,
197
- maxSteps: profile.model.maxSteps,
182
+ maxSteps: profile.maxSteps,
198
183
  structured,
199
184
  image: resolveImageFormat(profile),
200
185
  speech: profile.type === 'speech' ? profile.speech : undefined,
201
186
  live: profile.type === 'live' ? profile.live : undefined,
202
187
  input: resolveInputParts(profile, model, safe),
203
188
  keySlot,
204
- canary: profile.guardrails?.canary === true ? mintCanary() : '',
189
+ canary: resolveGuardrailPolicy(profile.guardrails).canary ? mintCanary() : '',
205
190
  sessionResumptionHandle: safe.sessionResumptionHandle ?? input.sessionResumptionHandle,
191
+ resolvedSystem: resolveTurnSystemPrompt(profile, safe),
192
+ host: safe.host,
206
193
  },
207
194
  };
208
195
  }
@@ -210,23 +197,29 @@ function primaryImageSpec(profile) {
210
197
  return profile.type === 'image' ? profile.image : null;
211
198
  }
212
199
  function profileInputsOrNull(profile) {
213
- if (profile.type === 'speech') {
200
+ if (profile.type === 'speech' || profile.type === 'live') {
214
201
  return null;
215
202
  }
216
203
  return profile.inputs ?? null;
217
204
  }
218
205
  /** Project a profile object into a safe host/UI inspection object. */
219
- function projectProfileObject(profile) {
220
- const { model, identity, outputs } = profile;
206
+ function projectProfileObject(input) {
207
+ const profile = requireModelProfile(input, 'projectProfile');
208
+ const { identity } = profile;
221
209
  const inputs = profileInputsOrNull(profile);
210
+ const outputs = profile.type === 'live' ? null : (profile.outputs ?? null);
222
211
  return {
223
212
  id: profile.id,
224
213
  type: profile.type,
225
214
  handle: identity.handle,
226
- model,
215
+ models: profile.models,
216
+ defaultModel: profile.defaultModel,
217
+ allowModelSelect: profile.allowModelSelect,
218
+ maxSteps: profile.maxSteps,
219
+ key: profile.key,
227
220
  tools: projectTools(profile),
228
221
  inputs,
229
- outputs: outputs ?? null,
222
+ outputs,
230
223
  image: primaryImageSpec(profile),
231
224
  speech: profile.type === 'speech' ? profile.speech : null,
232
225
  live: profile.type === 'live' ? profile.live : null,
@@ -236,12 +229,4 @@ function projectProfileObject(profile) {
236
229
  function projectProfile(id) {
237
230
  return projectProfileObject(getProfile(id));
238
231
  }
239
- function pickSystemRole(profile, requested) {
240
- const { identity } = profile;
241
- const { handle, systemByRole } = identity;
242
- if (requested && systemByRole && Object.hasOwn(systemByRole, requested)) {
243
- return requested;
244
- }
245
- return handle;
246
- }
247
- export { pickModel, pickSystemRole, projectProfile, projectProfileObject, resolveTurn };
232
+ export { pickModel, projectProfile, projectProfileObject, requireModelProfile, resolveTurn };
@@ -16,7 +16,7 @@ function registerStructured(id, spec) {
16
16
  function getStructured(id) {
17
17
  const spec = schemas.get(id);
18
18
  if (!spec) {
19
- throw new TheorumError(`Unknown structured schema '${id}'`);
19
+ throw new TheorumError(`Unknown structured schema '${id}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
20
20
  }
21
21
  return spec;
22
22
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Shared model-id helpers for profile registration and resolution.
3
+ *
4
+ * @module
5
+ */
6
+ import type { ModelBinding, ModelId } from '../types.js';
7
+ /** When exactly one model is declared, that id is the implicit default. */
8
+ export declare function soleModelId(models: Record<ModelId, ModelBinding>): ModelId | undefined;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Shared model-id helpers for profile registration and resolution.
3
+ *
4
+ * @module
5
+ */
6
+ /** When exactly one model is declared, that id is the implicit default. */
7
+ export function soleModelId(models) {
8
+ const ids = Object.keys(models);
9
+ return ids.length === 1 ? ids[0] : undefined;
10
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Synchronous system-prompt resolution for a turn or session.
3
+ * Snapshotted in `resolveTurn` before any async work — never mutate registry per request.
4
+ *
5
+ * @module
6
+ */
7
+ import type { ModelProfile, TurnRequest } from '../types.js';
8
+ /** Merge profile + host turn system synchronously at resolve time. */
9
+ declare function resolveTurnSystemPrompt(profile: ModelProfile, req: TurnRequest): string;
10
+ export { resolveTurnSystemPrompt };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Synchronous system-prompt resolution for a turn or session.
3
+ * Snapshotted in `resolveTurn` before any async work — never mutate registry per request.
4
+ *
5
+ * @module
6
+ */
7
+ import { lexiconText } from '../../guardrails/lexicon.js';
8
+ import { detectionForTrust, resolveGuardrailPolicy } from '../../guardrails/policy.js';
9
+ import { sanitizeText } from '../../guardrails/sanitize.js';
10
+ import { profileTurnResumption } from '../stop.js';
11
+ import { pickSystemRole } from './system-role.js';
12
+ /**
13
+ * Author-time system text for a role (handle or `systemByRole`).
14
+ *
15
+ * Routed through the guardrail policy at `trust: 'trusted'` so the exemption is
16
+ * declared at the point it applies rather than implied by never calling the
17
+ * sanitizer. Trusted resolves to no detection, so the text reaches the provider
18
+ * verbatim — `req.system`, which the host assembles per turn, is handled as
19
+ * `assembled` in `sanitizeTurnRequest` and is not exempt.
20
+ */
21
+ function systemFromProfile(profile, role) {
22
+ const { identity } = profile;
23
+ const { systemByRole, system } = identity;
24
+ const text = systemByRole?.[role] || system || '';
25
+ if (!text) {
26
+ return '';
27
+ }
28
+ const policy = resolveGuardrailPolicy(profile.guardrails);
29
+ return sanitizeText(text, detectionForTrust(policy, 'trusted'));
30
+ }
31
+ /** Merge profile + host turn system synchronously at resolve time. */
32
+ function resolveTurnSystemPrompt(profile, req) {
33
+ const role = pickSystemRole(profile, req.input?.role);
34
+ // Profile override wins; otherwise the registered lexicon default.
35
+ const continueSys = req.continueFrom
36
+ ? lexiconText('continue.instruction', {}, profileTurnResumption(profile)?.continueInstruction)
37
+ : '';
38
+ return [systemFromProfile(profile, role), req.system, continueSys].filter(Boolean).join('\n\n');
39
+ }
40
+ export { resolveTurnSystemPrompt };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolve which `systemByRole` key (or handle) applies for a turn.
3
+ *
4
+ * @module
5
+ */
6
+ import type { ModelProfile } from '../types.js';
7
+ declare function pickSystemRole(profile: ModelProfile, requested?: string): string;
8
+ export { pickSystemRole };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve which `systemByRole` key (or handle) applies for a turn.
3
+ *
4
+ * @module
5
+ */
6
+ function pickSystemRole(profile, requested) {
7
+ const { identity } = profile;
8
+ const { handle, systemByRole } = identity;
9
+ if (requested && systemByRole && Object.hasOwn(systemByRole, requested)) {
10
+ return requested;
11
+ }
12
+ return handle;
13
+ }
14
+ export { pickSystemRole };
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { BuiltinToolId, KeySlot, ModelSpec, Provider } from '../types.js';
6
+ import type { BuiltinToolId, KeySlot, ModelBinding, Provider } from '../types.js';
7
7
  /** Providers that resolve a vault `keySlot` on each turn. */
8
8
  export declare function providerUsesKeySlots(provider: Provider): boolean;
9
9
  /**
@@ -12,5 +12,5 @@ export declare function providerUsesKeySlots(provider: Provider): boolean;
12
12
  * When `required` is false and nothing pins a slot, returns `undefined` so hosts
13
13
  * can use a single flat `apiKey` (OpenRouter without a vault).
14
14
  */
15
- declare function resolveKeySlot(profileKey: KeySlot | undefined, spec: ModelSpec, builtins: BuiltinToolId[], required: boolean): KeySlot | undefined;
15
+ declare function resolveKeySlot(profileKey: KeySlot | undefined, binding: ModelBinding, builtins: BuiltinToolId[], required: boolean): KeySlot | undefined;
16
16
  export { resolveKeySlot };
@@ -22,9 +22,9 @@ export function providerUsesKeySlots(provider) {
22
22
  * When `required` is false and nothing pins a slot, returns `undefined` so hosts
23
23
  * can use a single flat `apiKey` (OpenRouter without a vault).
24
24
  */
25
- function resolveKeySlot(profileKey, spec, builtins, required) {
26
- if (spec.key) {
27
- return spec.key;
25
+ function resolveKeySlot(profileKey, binding, builtins, required) {
26
+ if (binding.key) {
27
+ return binding.key;
28
28
  }
29
29
  if (builtins.some((id) => builtinForcesPaid(id))) {
30
30
  return 'paid';
@@ -33,7 +33,7 @@ function resolveKeySlot(profileKey, spec, builtins, required) {
33
33
  return profileKey;
34
34
  }
35
35
  if (required) {
36
- throw new TheorumError('Profile must set model.key or model.config.*.key');
36
+ throw new TheorumError('Profile must set key or models.*.key'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
37
37
  }
38
38
  return undefined;
39
39
  }
@@ -7,8 +7,11 @@
7
7
  *
8
8
  * @module
9
9
  */
10
+ /** lexicon-exempt-file: authoring field-meta / closed unions — not runtime user or model copy (P2) */
11
+ import { EGRESS_ON_BLOCK, type EgressOnBlock } from '../guardrails/types.js';
12
+ export { EGRESS_ON_BLOCK, type EgressOnBlock };
10
13
  /** Primary profile archetype. Discriminated union key for `ProfileDefinition` and `Profile`. */
11
- export declare const PROFILE_TYPES: readonly ["text", "image", "speech", "live"];
14
+ export declare const PROFILE_TYPES: readonly ["text", "image", "speech", "live", "host"];
12
15
  export type ProfileType = (typeof PROFILE_TYPES)[number];
13
16
  /** Model reasoning effort level normalized across provider adapters. */
14
17
  export declare const THINKING_LEVELS: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
@@ -30,13 +33,15 @@ export declare const PROTOCOL_PROVIDERS: {
30
33
  };
31
34
  /**
32
35
  * Legal wire protocols for each profile archetype.
33
- * 'live' profiles require 'geminiLive'; turn-based archetypes require turn protocols.
36
+ * 'live' profiles require 'geminiLive'; turn-based archetypes require turn protocols;
37
+ * 'host' never runs a model and binds no protocol.
34
38
  */
35
39
  export declare const PROFILE_TYPE_PROTOCOLS: {
36
40
  readonly text: readonly ["geminiInteractions", "openAi"];
37
41
  readonly image: readonly ["geminiInteractions", "openAi"];
38
42
  readonly speech: readonly ["geminiInteractions", "openAi"];
39
43
  readonly live: readonly ["geminiLive"];
44
+ readonly host: readonly [];
40
45
  };
41
46
  export type ProfileTypeProtocol<T extends ProfileType> = (typeof PROFILE_TYPE_PROTOCOLS)[T][number];
42
47
  /** Protocols allowed for a profile archetype (`type`). */
@@ -52,8 +57,6 @@ export type OverflowKeySlot = (typeof OVERFLOW_KEY_SLOTS)[number];
52
57
  /** Host vault: one optional credential string per key slot. */
53
58
  export type KeyVault = Record<KeySlot, string | undefined>;
54
59
  /** 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
60
  /** Normalized multimodal part category. */
58
61
  export declare const MEDIA_INPUT_KIND_VALUES: readonly ["image", "audio", "video", "document"];
59
62
  export type MediaInputKind = (typeof MEDIA_INPUT_KIND_VALUES)[number];
@@ -66,6 +69,11 @@ export type StreamMode = (typeof STREAM_MODES)[number];
66
69
  /** Audio container for speech generation output. */
67
70
  export declare const SPEECH_AUDIO_FORMATS: readonly ["pcm", "mp3"];
68
71
  export type SpeechAudioFormat = (typeof SPEECH_AUDIO_FORMATS)[number];
72
+ /** Speech `format` values legal for a wire protocol (`assertSpeechRole` / UI). */
73
+ export declare function speechFormatsForProtocol(protocol: Protocol): readonly SpeechAudioFormat[];
74
+ export declare function isSpeechFormatAllowedForProtocol(protocol: Protocol, format: SpeechAudioFormat): boolean;
75
+ /** Snap an illegal or omitted format to the first legal value for the protocol. */
76
+ export declare function coerceSpeechFormat(protocol: Protocol, format: SpeechAudioFormat | undefined): SpeechAudioFormat;
69
77
  /** Live session activity handling (barge-in behavior). */
70
78
  export declare const LIVE_ACTIVITY_HANDLINGS: readonly ["START_OF_ACTIVITY_INTERRUPTS", "NO_INTERRUPTION"];
71
79
  export type LiveActivityHandling = (typeof LIVE_ACTIVITY_HANDLINGS)[number];
@@ -84,24 +92,100 @@ export type CompactionMeter = (typeof COMPACTION_METERS)[number];
84
92
  /** When compaction runs relative to the primary turn. */
85
93
  export declare const COMPACTION_TIMINGS: readonly ["before", "after"];
86
94
  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];
95
+ /** OpenRouter prompt-cache mode (models.*.cache.mode). */
96
+ export declare const CACHE_MODES: readonly ["automatic", "system"];
97
+ export type CacheMode = (typeof CACHE_MODES)[number];
98
+ /** OpenRouter ephemeral cache TTL (models.*.cache.ttl). */
99
+ export declare const CACHE_TTLS: readonly ["5m", "1h"];
100
+ export type CacheTtl = (typeof CACHE_TTLS)[number];
90
101
  /** 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"];
102
+ export declare const TURN_STOP_KINDS: readonly ["completed", "length", "tool", "gate", "filtered", "provider_error", "cancelled", "stream_incomplete", "interrupted", "generation_complete"];
92
103
  export type TurnStopKind = (typeof TURN_STOP_KINDS)[number];
104
+ /**
105
+ * Stop kinds eligible for `continueFrom` / resumption allowlists.
106
+ * Excludes terminal-success, user abort, tool/gate suspension, filter, and live-only
107
+ * boundaries — those use other host paths (or are not resumeable).
108
+ */
109
+ export declare const CONTINUE_STOP_KINDS: readonly ["length", "stream_incomplete", "provider_error"];
110
+ export type ContinueStopKind = (typeof CONTINUE_STOP_KINDS)[number];
111
+ /**
112
+ * Turn / utterance-cycle timeline stages (`docs/contracts/stages.md`).
113
+ * Replaces the former steer barriers (`pre_llm` / `pre_tool_followup`).
114
+ */
115
+ export declare const TURN_STAGES: readonly ["pre_turn", "pre_tool", "post_tool", "before_end", "post_turn"];
116
+ export type TurnStage = (typeof TURN_STAGES)[number];
117
+ /** True when `value` is a known `TurnStage`. */
118
+ export declare function isTurnStage(value: unknown): value is TurnStage;
119
+ /** Stages where inject is physically meaningful (still requires inject gate). */
120
+ export declare const TURN_INJECT_STAGES: readonly ["pre_turn", "post_tool", "before_end"];
121
+ export type TurnInjectStage = (typeof TURN_INJECT_STAGES)[number];
122
+ /** True when inject is physically meaningful at this stage (gate still required). */
123
+ export declare function isTurnInjectStage(value: unknown): value is TurnInjectStage;
124
+ /** `pre_tool` gate kinds — confirm-to-run / permission / auth. Not awaiting. */
125
+ export declare const TOOL_GATE_KINDS: readonly ["confirmation", "permission", "auth"];
126
+ export type ToolGateKind = (typeof TOOL_GATE_KINDS)[number];
127
+ /** True when `value` is a known tool-gate kind. */
128
+ export declare function isToolGateKind(value: unknown): value is ToolGateKind;
129
+ /** `awaiting_user_input.kind` — harness ask_user / human-as-product completions. */
130
+ export declare const AWAITING_USER_INPUT_KINDS: readonly ["confirm", "choice", "text"];
131
+ export type AwaitingUserInputKind = (typeof AWAITING_USER_INPUT_KINDS)[number];
132
+ /** Discriminator on tool output for awaiting completions. */
133
+ export declare const AWAITING_USER_INPUT_STATUS: "awaiting_user_input";
93
134
  /** Per-tool visibility tier — enforced by the kernel at resolve time. */
94
135
  export declare const TOOL_LOAD_TIERS: readonly ["T0", "T1", "T2"];
95
136
  export type ToolLoadTier = (typeof TOOL_LOAD_TIERS)[number];
137
+ /** HTTP verbs supported by declarative HTTP tools. */
138
+ export declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"];
139
+ export type HttpMethod = (typeof HTTP_METHODS)[number];
140
+ /** When remote tool auth is missing or expired. */
141
+ export declare const AUTH_UNAUTHENTICATED_POLICIES: readonly ["pause", "report_to_model"];
142
+ export type AuthUnauthenticatedPolicy = (typeof AUTH_UNAUTHENTICATED_POLICIES)[number];
96
143
  /** Registered tool discriminant (`registerTool`). */
97
- export declare const TOOL_TYPES: readonly ["builtin", "function"];
144
+ export declare const TOOL_TYPES: readonly ["builtin", "function", "http", "mcp"];
98
145
  /** Semantic access level — host policy / UI; not enforced by execute. */
99
146
  export declare const TOOL_ACCESS: readonly ["read-only", "read-write", "destructive"];
100
147
  export type ToolAccess = (typeof TOOL_ACCESS)[number];
101
148
  /** Execution authorization tier for registered tools. */
102
149
  export declare const TOOL_PERMISSION: readonly ["auto", "session_consent", "always_confirm"];
103
150
  export type ToolPermission = (typeof TOOL_PERMISSION)[number];
104
- /** MIME essence normalized media part category (shared ingress map). */
151
+ /** Credential attachment modes for HTTP and MCP tools (`auth.type`). */
152
+ export declare const TOOL_AUTH_TYPES: readonly ["bearer", "api_key", "oauth2"];
153
+ export type ToolAuthType = (typeof TOOL_AUTH_TYPES)[number];
154
+ /** Playground auth select — includes UI-only `none` (omits auth at compile time). */
155
+ export declare const PLAYGROUND_AUTH_TYPES: readonly ["none", "bearer", "api_key", "oauth2"];
156
+ export type PlaygroundAuthType = (typeof PLAYGROUND_AUTH_TYPES)[number];
157
+ export type ToolType = (typeof TOOL_TYPES)[number];
158
+ /** Custom registerTool discriminants (excludes provider builtins). */
159
+ export type CustomToolType = Exclude<ToolType, 'builtin'>;
160
+ /**
161
+ * MIME essence → normalized media part category.
162
+ *
163
+ * This table is the complete media-input vocabulary of the package: every MIME
164
+ * any supported transport may carry on a turn appears here exactly once, and
165
+ * nothing else is a media input type. `assertMediaMime` (`registry/ingress.ts`)
166
+ * refuses anything absent from it, so hosts declare what they accept in
167
+ * `inputs.attachments.accept` / `inputs.voice.accept` and keep no second table.
168
+ *
169
+ * Contents are the union of the documented provider input lists:
170
+ * - Google Interactions / Live (image, audio, video, document lists verified
171
+ * 2026-09-12) — the widest of the three and therefore the table's shape. The
172
+ * document row is Google's document-understanding list in full: PDF, plain
173
+ * text, HTML, CSS, Markdown (`text/md`), CSV, XML, RTF, JavaScript
174
+ * (`text/javascript`, `application/x-javascript`) and Python
175
+ * (`text/x-python`, `application/x-python`); `application/json` is an
176
+ * established row alongside it. TypeScript, `application/xml` and
177
+ * `application/rtf` are NOT on Google's list and are therefore not rows.
178
+ * - OpenRouter / OpenAI-compat (`providers/openrouter/openai/compat.ts`,
179
+ * `sdk-messages.ts`): the adapters wire every `MediaInputKind` (image →
180
+ * `image_url`/`image`, audio → `input_audio`, video and document → `file`)
181
+ * and forward the part's MIME verbatim, so their accepted set is open-ended
182
+ * and adds no rows. What they cannot carry — a `uri` reference part — is
183
+ * refused at request time with `TheorumError`, not by a second MIME list.
184
+ *
185
+ * Alias essences that providers also emit (`image/jpg`, `video/mov`,
186
+ * `audio/x-wav`, `video/x-ms-wmv`, …) are rows here; `resolveInputParts`
187
+ * canonicalizes `image/jpg` to `image/jpeg` on the wire.
188
+ */
105
189
  export declare const MEDIA_INPUT_KINDS: Record<string, MediaInputKind>;
106
190
  /** Type-prefix wildcards accepted by `mimeAllowed`. */
107
191
  export declare const MEDIA_WILDCARDS: readonly ["image/*", "audio/*", "video/*"];
@@ -129,7 +213,7 @@ export type FieldMeta = {
129
213
  };
130
214
  /**
131
215
  * 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`.
216
+ * The annotator substitutes `*` so `models.flash.apiId` → `models.*.apiId`.
133
217
  */
134
218
  export declare const DYNAMIC_FIELD_PARENTS: ReadonlySet<string>;
135
219
  /** Resolve a key stack from authored source into a catalog path. */
@@ -143,3 +227,5 @@ export declare const PROFILE_FIELDS: Record<string, FieldMeta>;
143
227
  export declare const EXTRA_FIELDS: Record<string, FieldMeta>;
144
228
  /** Look up hover metadata for a dotted path (profile first, then extra). */
145
229
  export declare function fieldMeta(path: string): FieldMeta | undefined;
230
+ export type { ProfileGraphEditor, ProfileGraphFacet, ProfileGraphFacetId, ProfileGraphRole, } from './profile-graph.js';
231
+ export { PROFILE_GRAPH, profileGraphFacet, spineFacetsForProfileType, } from './profile-graph.js';