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.
- package/README.md +241 -98
- package/esm/mod.d.ts +57 -28
- package/esm/mod.js +43 -23
- package/esm/src/cli/commands/bench.js +18 -18
- package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
- package/esm/src/cli/commands/fuzz-canary.js +191 -0
- package/esm/src/cli/commands/fuzz-guardrails.d.ts +3 -5
- package/esm/src/cli/commands/fuzz-guardrails.js +4 -581
- package/esm/src/cli/commands/guardrails-eval.d.ts +14 -0
- package/esm/src/cli/commands/guardrails-eval.js +15 -0
- package/esm/src/cli/commands/profile.js +35 -15
- package/esm/src/cli/commands/run.d.ts +3 -0
- package/esm/src/cli/commands/run.js +23 -32
- package/esm/src/cli/commands/test.d.ts +10 -1
- package/esm/src/cli/commands/test.js +34 -34
- package/esm/src/cli/event-log.d.ts +19 -0
- package/esm/src/cli/event-log.js +147 -0
- package/esm/src/cli/index.js +57 -11
- package/esm/src/cli/matrix/synthesizer.d.ts +10 -12
- package/esm/src/cli/matrix/synthesizer.js +45 -118
- package/esm/src/guardrails/canary-gate.d.ts +21 -0
- package/esm/src/guardrails/canary-gate.js +32 -0
- package/esm/src/guardrails/canary.d.ts +34 -0
- package/esm/src/guardrails/canary.js +150 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +17 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +213 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +125 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
- package/esm/src/guardrails/corpus/live-attacks.js +231 -0
- package/esm/src/guardrails/corpus/mod.d.ts +14 -0
- package/esm/src/guardrails/corpus/mod.js +11 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +17 -0
- package/esm/src/guardrails/corpus/secrets.js +17 -0
- package/esm/src/guardrails/corpus/strings.d.ts +28 -0
- package/esm/src/guardrails/corpus/strings.js +34 -0
- package/esm/src/guardrails/corpus/types.d.ts +38 -0
- package/esm/src/guardrails/corpus/types.js +6 -0
- package/esm/src/guardrails/egress.d.ts +32 -0
- package/esm/src/guardrails/egress.js +87 -0
- package/esm/src/guardrails/error.d.ts +14 -23
- package/esm/src/guardrails/error.js +87 -76
- package/esm/src/guardrails/eval/corpus.d.ts +108 -0
- package/esm/src/guardrails/eval/corpus.js +978 -0
- package/esm/src/guardrails/eval/mod.d.ts +51 -0
- package/esm/src/guardrails/eval/mod.js +133 -0
- package/esm/src/guardrails/eval/score.d.ts +66 -0
- package/esm/src/guardrails/eval/score.js +114 -0
- package/esm/src/guardrails/events.d.ts +25 -0
- package/esm/src/guardrails/events.js +56 -0
- package/esm/src/guardrails/hits.d.ts +24 -0
- package/esm/src/guardrails/hits.js +45 -0
- package/esm/src/guardrails/injection.js +28 -5
- package/esm/src/guardrails/lexicon.d.ts +39 -0
- package/esm/src/guardrails/lexicon.js +200 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +41 -0
- package/esm/src/guardrails/live-outbound-gate.js +222 -0
- package/esm/src/guardrails/mod.d.ts +30 -6
- package/esm/src/guardrails/mod.js +20 -5
- package/esm/src/guardrails/network.d.ts +19 -0
- package/esm/src/guardrails/network.js +234 -0
- package/esm/src/guardrails/policy.d.ts +35 -0
- package/esm/src/guardrails/policy.js +50 -0
- package/esm/src/guardrails/progressive-yield.d.ts +51 -0
- package/esm/src/guardrails/progressive-yield.js +98 -0
- package/esm/src/guardrails/quota.d.ts +17 -3
- package/esm/src/guardrails/quota.js +18 -4
- package/esm/src/guardrails/sanitize.d.ts +45 -19
- package/esm/src/guardrails/sanitize.js +177 -94
- package/esm/src/guardrails/sensitive.js +2 -1
- package/esm/src/guardrails/serialize.d.ts +35 -0
- package/esm/src/guardrails/serialize.js +58 -0
- package/esm/src/guardrails/testing.d.ts +17 -0
- package/esm/src/guardrails/testing.js +13 -0
- package/esm/src/guardrails/theorum-error.d.ts +12 -0
- package/esm/src/guardrails/theorum-error.js +15 -0
- package/esm/src/guardrails/tool-directives.d.ts +48 -0
- package/esm/src/guardrails/tool-directives.js +124 -0
- package/esm/src/guardrails/tool-result.d.ts +93 -0
- package/esm/src/guardrails/tool-result.js +276 -0
- package/esm/src/guardrails/types.d.ts +291 -0
- package/esm/src/guardrails/types.js +72 -0
- package/esm/src/host/client-turn.d.ts +19 -0
- package/esm/src/host/client-turn.js +36 -0
- package/esm/src/host/mint-trace.d.ts +1 -1
- package/esm/src/host/mod.d.ts +5 -3
- package/esm/src/host/mod.js +4 -3
- package/esm/src/kernel/auth/crypto.d.ts +42 -0
- package/esm/src/kernel/auth/crypto.js +106 -0
- package/esm/src/kernel/auth/mod.d.ts +11 -0
- package/esm/src/kernel/auth/mod.js +11 -0
- package/esm/src/kernel/auth/oauth.d.ts +47 -0
- package/esm/src/kernel/auth/oauth.js +278 -0
- package/esm/src/kernel/auth/types.d.ts +133 -0
- package/esm/src/kernel/auth/types.js +13 -0
- package/esm/src/kernel/engine/delta.d.ts +24 -2
- package/esm/src/kernel/engine/delta.js +478 -39
- package/esm/src/kernel/engine/live-inbound.d.ts +21 -0
- package/esm/src/kernel/engine/live-inbound.js +31 -0
- package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
- package/esm/src/kernel/engine/live-ingress.js +47 -0
- package/esm/src/kernel/engine/repair.js +13 -12
- package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
- package/esm/src/kernel/engine/runner/gates.js +130 -43
- package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
- package/esm/src/kernel/engine/runner/mod.js +192 -53
- package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
- package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
- package/esm/src/kernel/engine/runner/stages.js +89 -0
- package/esm/src/kernel/engine/runner/state.d.ts +31 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
- package/esm/src/kernel/engine/runner/steps.js +244 -43
- package/esm/src/kernel/engine/runner/stream.d.ts +9 -3
- package/esm/src/kernel/engine/runner/stream.js +140 -44
- package/esm/src/kernel/engine/session/mod.d.ts +25 -0
- package/esm/src/kernel/engine/session/mod.js +557 -0
- package/esm/src/kernel/interaction-parts.d.ts +14 -0
- package/esm/src/kernel/interaction-parts.js +23 -0
- package/esm/src/kernel/mod.d.ts +21 -10
- package/esm/src/kernel/mod.js +11 -8
- package/esm/src/kernel/profile-graph.d.ts +159 -0
- package/esm/src/kernel/profile-graph.js +156 -0
- package/esm/src/kernel/registry/attachments.d.ts +12 -10
- package/esm/src/kernel/registry/attachments.js +33 -27
- package/esm/src/kernel/registry/catalog.d.ts +25 -24
- package/esm/src/kernel/registry/catalog.js +60 -101
- package/esm/src/kernel/registry/ingress.d.ts +9 -4
- package/esm/src/kernel/registry/ingress.js +97 -75
- package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
- package/esm/src/kernel/registry/profile-outputs.js +8 -0
- package/esm/src/kernel/registry/profiles.d.ts +55 -12
- package/esm/src/kernel/registry/profiles.js +413 -73
- package/esm/src/kernel/registry/provider-request.js +13 -7
- package/esm/src/kernel/registry/resolve.d.ts +8 -8
- package/esm/src/kernel/registry/resolve.js +169 -154
- package/esm/src/kernel/registry/schemas.js +1 -1
- package/esm/src/kernel/registry/sole-model.d.ts +8 -0
- package/esm/src/kernel/registry/sole-model.js +10 -0
- package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
- package/esm/src/kernel/registry/system-prompt.js +40 -0
- package/esm/src/kernel/registry/system-role.d.ts +8 -0
- package/esm/src/kernel/registry/system-role.js +14 -0
- package/esm/src/kernel/registry/vault.d.ts +12 -7
- package/esm/src/kernel/registry/vault.js +32 -10
- package/esm/src/kernel/schema.d.ts +231 -0
- package/esm/src/kernel/schema.js +607 -0
- package/esm/src/kernel/stages.d.ts +175 -0
- package/esm/src/kernel/stages.js +476 -0
- package/esm/src/kernel/stop.d.ts +78 -19
- package/esm/src/kernel/stop.js +51 -16
- package/esm/src/kernel/tools/events.d.ts +41 -0
- package/esm/src/kernel/tools/events.js +71 -0
- package/esm/src/kernel/tools/execute.d.ts +84 -0
- package/esm/src/kernel/tools/execute.js +614 -0
- package/esm/src/kernel/tools/harness.d.ts +8 -0
- package/esm/src/kernel/tools/harness.js +46 -0
- package/esm/src/kernel/tools/invoke.d.ts +10 -0
- package/esm/src/kernel/tools/invoke.js +101 -0
- package/esm/src/kernel/tools/mod.d.ts +13 -0
- package/esm/src/kernel/tools/mod.js +11 -0
- package/esm/src/kernel/tools/permission.d.ts +15 -0
- package/esm/src/kernel/tools/permission.js +47 -0
- package/esm/src/kernel/tools/project.d.ts +12 -0
- package/esm/src/kernel/tools/project.js +36 -0
- package/esm/src/kernel/tools/registry.d.ts +23 -0
- package/esm/src/kernel/tools/registry.js +81 -0
- package/esm/src/kernel/tools/remote.d.ts +94 -0
- package/esm/src/kernel/tools/remote.js +577 -0
- package/esm/src/kernel/tools/resolve.d.ts +39 -0
- package/esm/src/kernel/tools/resolve.js +283 -0
- package/esm/src/kernel/tools/schema.d.ts +15 -0
- package/esm/src/kernel/tools/schema.js +176 -0
- package/esm/src/kernel/tools/stage-run.d.ts +105 -0
- package/esm/src/kernel/tools/stage-run.js +155 -0
- package/esm/src/kernel/tools/types.d.ts +394 -0
- package/esm/src/kernel/tools/types.js +9 -0
- package/esm/src/kernel/types.d.ts +540 -256
- package/esm/src/kernel/util/find-last.d.ts +2 -0
- package/esm/src/kernel/util/find-last.js +10 -0
- package/esm/src/observability/destinations.d.ts +31 -0
- package/esm/src/observability/destinations.js +67 -0
- package/esm/src/observability/mod.d.ts +10 -3
- package/esm/src/observability/mod.js +6 -2
- package/esm/src/observability/policy.d.ts +27 -0
- package/esm/src/observability/policy.js +80 -0
- package/esm/src/observability/resolve-policy.d.ts +16 -0
- package/esm/src/observability/resolve-policy.js +64 -0
- package/esm/src/observability/trace-attach.d.ts +8 -4
- package/esm/src/observability/trace-attach.js +50 -29
- package/esm/src/observability/trace-record.d.ts +23 -13
- package/esm/src/observability/trace-record.js +96 -39
- package/esm/src/observability/trace-sink.d.ts +19 -0
- package/esm/src/observability/trace-sink.js +10 -0
- package/esm/src/observability/trace-usage.d.ts +10 -3
- package/esm/src/observability/trace-usage.js +70 -17
- package/esm/src/observability/trace.d.ts +18 -7
- package/esm/src/observability/trace.js +34 -17
- package/esm/src/observability/types.d.ts +113 -0
- package/esm/src/observability/types.js +11 -0
- package/esm/src/presets/google/speech-voices.d.ts +11 -0
- package/esm/src/presets/google/speech-voices.js +41 -0
- package/esm/src/presets/google.d.ts +36 -24
- package/esm/src/presets/google.js +50 -63
- package/esm/src/presets/mod.d.ts +2 -2
- package/esm/src/presets/mod.js +1 -1
- package/esm/src/providers/create-provider.d.ts +20 -17
- package/esm/src/providers/create-provider.js +72 -26
- package/esm/src/providers/google/interactions/framing.d.ts +23 -0
- package/esm/src/providers/google/interactions/framing.js +269 -0
- package/esm/src/providers/google/interactions/mod.d.ts +7 -0
- package/esm/src/providers/google/interactions/mod.js +7 -0
- package/esm/src/providers/google/interactions/stream.d.ts +83 -0
- package/esm/src/providers/google/interactions/stream.js +588 -0
- package/esm/src/providers/google/keys.d.ts +26 -0
- package/esm/src/providers/{keys.js → google/keys.js} +19 -31
- package/esm/src/providers/google/live/framing.d.ts +49 -0
- package/esm/src/providers/google/live/framing.js +552 -0
- package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
- package/esm/src/providers/google/live/openapi-schema.js +46 -0
- package/esm/src/providers/google/live/session.d.ts +25 -0
- package/esm/src/providers/google/live/session.js +134 -0
- package/esm/src/providers/google/live/stream.d.ts +45 -0
- package/esm/src/providers/google/live/stream.js +214 -0
- package/esm/src/providers/google/urls.d.ts +6 -0
- package/esm/src/providers/google/urls.js +6 -0
- package/esm/src/providers/local/local.d.ts +30 -0
- package/esm/src/providers/{local.js → local/local.js} +66 -126
- package/esm/src/providers/local/mod.d.ts +9 -0
- package/esm/src/providers/local/mod.js +9 -0
- package/esm/src/providers/mod.d.ts +6 -3
- package/esm/src/providers/mod.js +3 -1
- package/esm/src/providers/openrouter/cache-control.d.ts +24 -0
- package/esm/src/providers/openrouter/cache-control.js +23 -0
- package/esm/src/providers/openrouter/chat.d.ts +107 -0
- package/esm/src/providers/{openrouter.js → openrouter/chat.js} +117 -231
- package/esm/src/providers/openrouter/image.d.ts +34 -0
- package/esm/src/providers/openrouter/image.js +275 -0
- package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
- package/esm/src/providers/openrouter/openai/chat-payload.js +82 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +53 -0
- package/esm/src/providers/openrouter/openai/compat.js +213 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +90 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.js +122 -0
- package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
- package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
- package/esm/src/providers/openrouter/speech.d.ts +23 -0
- package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
- package/esm/src/providers/probe.d.ts +1 -0
- package/esm/src/providers/probe.js +22 -0
- package/esm/src/providers/shared/pcm.d.ts +12 -0
- package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
- package/esm/src/providers/shared/sse.d.ts +18 -0
- package/esm/src/providers/shared/sse.js +87 -0
- package/esm/src/providers/shared/tool-args.d.ts +17 -0
- package/esm/src/providers/shared/tool-args.js +45 -0
- package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
- package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
- package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
- package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
- package/esm/src/providers/types.d.ts +27 -0
- package/esm/src/providers/types.js +1 -0
- package/package.json +11 -7
- package/docs/cli.md +0 -97
- package/docs/guardrails.md +0 -178
- package/docs/host.md +0 -97
- package/docs/kernel.md +0 -404
- package/docs/observability.md +0 -105
- package/docs/openrouter.md +0 -125
- package/docs/presets-google.md +0 -91
- package/docs/presets.md +0 -88
- package/docs/providers.md +0 -202
- package/docs/streaming.md +0 -96
- package/esm/src/kernel/engine/boundary.d.ts +0 -10
- package/esm/src/kernel/engine/boundary.js +0 -55
- package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
- package/esm/src/kernel/engine/runner/tools.js +0 -198
- package/esm/src/kernel/registry/tools.d.ts +0 -12
- package/esm/src/kernel/registry/tools.js +0 -36
- package/esm/src/providers/expose-for-tests.d.ts +0 -1
- package/esm/src/providers/expose-for-tests.js +0 -25
- package/esm/src/providers/gemini-tape.d.ts +0 -2
- package/esm/src/providers/google-tap.d.ts +0 -3
- package/esm/src/providers/interactions.d.ts +0 -5
- package/esm/src/providers/interactions.js +0 -169
- package/esm/src/providers/keys.d.ts +0 -19
- package/esm/src/providers/local.d.ts +0 -29
- package/esm/src/providers/openrouter-mod.d.ts +0 -13
- package/esm/src/providers/openrouter-mod.js +0 -12
- package/esm/src/providers/openrouter-payload.d.ts +0 -39
- package/esm/src/providers/openrouter-payload.js +0 -195
- package/esm/src/providers/openrouter.d.ts +0 -15
- package/esm/src/providers/pcm.d.ts +0 -7
- package/esm/src/providers/provider.d.ts +0 -15
- package/esm/src/providers/provider.js +0 -202
- package/esm/src/providers/speech.d.ts +0 -23
- package/esm/src/providers/sse.d.ts +0 -7
- package/esm/src/providers/sse.js +0 -55
- package/esm/src/streaming/mod.d.ts +0 -9
- package/esm/src/streaming/mod.js +0 -8
- /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
- /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
|
@@ -1,106 +1,352 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime profile registry for host-owned THEORUM profiles.
|
|
3
3
|
*
|
|
4
|
-
* THEORUM ships profile types
|
|
5
|
-
*
|
|
4
|
+
* THEORUM ships profile types — not application profiles and not invented defaults.
|
|
5
|
+
* Hosts must pass required fields explicitly (`type`, `models`, …).
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { TheorumError } from '../../guardrails/error.js';
|
|
10
|
+
import { HOST_GUARDRAIL_FIELDS, } from '../../guardrails/types.js';
|
|
11
|
+
import { resolveObservabilityPolicy } from '../../observability/resolve-policy.js';
|
|
12
|
+
import { assertLiveIngressConfigured } from '../engine/live-ingress.js';
|
|
13
|
+
import { CACHE_MODES, CACHE_TTLS, isValidPair, isValidProfileProtocol, protocolsForProfileType, } from '../schema.js';
|
|
14
|
+
import { isContinueStopKind } from '../stop.js';
|
|
15
|
+
import { getTool } from '../tools/registry.js';
|
|
16
|
+
import { soleModelId } from './sole-model.js';
|
|
10
17
|
const profiles = new Map();
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
/** Fields a `host` profile must not carry — rejected when supplied. */
|
|
19
|
+
const HOST_ABSENT_FIELDS = [
|
|
20
|
+
'models',
|
|
21
|
+
'defaultModel',
|
|
22
|
+
'allowModelSelect',
|
|
23
|
+
'identity',
|
|
24
|
+
'inputs',
|
|
25
|
+
'outputs',
|
|
26
|
+
'turnBehaviour',
|
|
27
|
+
'key',
|
|
28
|
+
'maxSteps',
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Guardrails that only a model turn can run, so a host profile must not declare
|
|
32
|
+
* them: `egress` gates user-visible model text in the turn runner, `canary` is
|
|
33
|
+
* minted into a system prompt, and `quota` counts turns. None is reachable from
|
|
34
|
+
* `invokeTool`, so accepting them would register config that guards nothing.
|
|
35
|
+
*/
|
|
36
|
+
const HOST_ABSENT_GUARDRAILS = ['quota', 'canary', 'egress'];
|
|
37
|
+
const HOST_GUARDRAIL_REASON = {
|
|
38
|
+
quota: 'quota counts model turns', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
39
|
+
canary: 'canary is minted into a system prompt', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
40
|
+
egress: 'egress gates user-visible model text in the turn runner', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
41
|
+
};
|
|
42
|
+
function assertHostGuardrails(profileId, guardrails) {
|
|
43
|
+
if (!guardrails)
|
|
44
|
+
return;
|
|
45
|
+
const extra = guardrails;
|
|
46
|
+
for (const key of HOST_ABSENT_GUARDRAILS) {
|
|
47
|
+
if (extra[key] !== undefined) {
|
|
48
|
+
throw new TheorumError(`Profile ${profileId}: type 'host' must not set guardrails.${key} — a host profile runs no model and ${HOST_GUARDRAIL_REASON[key]}. Host profiles accept ${HOST_GUARDRAIL_FIELDS.join(', ')}.`);
|
|
15
49
|
}
|
|
16
50
|
}
|
|
17
51
|
}
|
|
18
|
-
function
|
|
52
|
+
function defineHostProfile(input) {
|
|
53
|
+
const extra = input;
|
|
54
|
+
for (const key of HOST_ABSENT_FIELDS) {
|
|
55
|
+
if (extra[key] !== undefined) {
|
|
56
|
+
throw new TheorumError(`Profile ${input.id}: type 'host' must not set ${key}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
assertHostGuardrails(input.id, input.guardrails);
|
|
60
|
+
assertHostTools(input.id, input.tools);
|
|
61
|
+
assertObservability(input.id, input.observability);
|
|
19
62
|
return {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
63
|
+
type: 'host',
|
|
64
|
+
id: input.id,
|
|
65
|
+
tools: { allow: input.tools.allow },
|
|
66
|
+
guardrails: input.guardrails,
|
|
67
|
+
observability: input.observability,
|
|
24
68
|
};
|
|
25
69
|
}
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
controls: model.controls ?? [],
|
|
35
|
-
maxSteps: model.maxSteps ?? 1,
|
|
36
|
-
key: model.key ?? 'freeA',
|
|
37
|
-
select: model.select,
|
|
38
|
-
};
|
|
70
|
+
function assertHostTools(profileId, tools) {
|
|
71
|
+
if (!Array.isArray(tools?.allow)) {
|
|
72
|
+
throw new TheorumError(`Profile ${profileId}: type 'host' must set tools.allow`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
73
|
+
}
|
|
74
|
+
const extra = tools;
|
|
75
|
+
if (extra.t1Policy !== undefined || extra.t2Loader !== undefined) {
|
|
76
|
+
throw new TheorumError(`Profile ${profileId}: tools.t1Policy / tools.t2Loader are not supported on type 'host' — every allowed tool is executable`);
|
|
77
|
+
}
|
|
39
78
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
voice: inputs?.voice,
|
|
45
|
-
maxFiles: inputs?.maxFiles,
|
|
46
|
-
maxBytes: inputs?.maxBytes,
|
|
47
|
-
maxTurnBytes: inputs?.maxTurnBytes,
|
|
48
|
-
limitsByMime: inputs?.limitsByMime,
|
|
49
|
-
slots: inputs?.slots,
|
|
50
|
-
};
|
|
79
|
+
function assertModelsNonEmpty(profileId, models) {
|
|
80
|
+
if (Object.keys(models).length === 0) {
|
|
81
|
+
throw new TheorumError(`Profile ${profileId} must declare at least one model`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
82
|
+
}
|
|
51
83
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
84
|
+
function assertDefaultModel(profileId, input) {
|
|
85
|
+
const ids = Object.keys(input.models);
|
|
86
|
+
const inferred = input.defaultModel ?? soleModelId(input.models);
|
|
87
|
+
if (!inferred) {
|
|
88
|
+
throw new TheorumError(`Profile ${profileId} must set defaultModel when more than one model is declared`);
|
|
89
|
+
}
|
|
90
|
+
if (!input.models[inferred]) {
|
|
91
|
+
throw new TheorumError(`Profile ${profileId} defaultModel '${inferred}' is not declared`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
92
|
+
}
|
|
93
|
+
if (input.allowModelSelect && ids.length < 2) {
|
|
94
|
+
throw new TheorumError(`Profile ${profileId} allowModelSelect requires at least two models`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function assertModelBinding(profileId, modelId, binding) {
|
|
98
|
+
if (!binding.protocol) {
|
|
99
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}' must set protocol`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
100
|
+
}
|
|
101
|
+
if (!binding.provider) {
|
|
102
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}' must set provider`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
103
|
+
}
|
|
104
|
+
if (!binding.apiId) {
|
|
105
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}' must set apiId`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
106
|
+
}
|
|
107
|
+
if (!isValidPair(binding.protocol, binding.provider)) {
|
|
108
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}': protocol '${binding.protocol}' is not valid for provider '${binding.provider}'`);
|
|
109
|
+
}
|
|
110
|
+
assertModelEfforts(profileId, modelId, binding);
|
|
111
|
+
if (binding.cache) {
|
|
112
|
+
assertCacheSpec(profileId, modelId, binding);
|
|
113
|
+
}
|
|
114
|
+
assertInteractionsPersistence(profileId, modelId, binding);
|
|
60
115
|
}
|
|
61
|
-
function
|
|
116
|
+
function assertModelEfforts(profileId, modelId, binding) {
|
|
117
|
+
const efforts = binding.efforts;
|
|
118
|
+
if (!efforts || Object.keys(efforts).length === 0) {
|
|
119
|
+
if (binding.defaultEffort || binding.allowEffortSelect) {
|
|
120
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}': defaultEffort and allowEffortSelect require efforts`);
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const keys = Object.keys(efforts);
|
|
125
|
+
const defaultAlias = binding.defaultEffort ?? (keys.length === 1 ? keys[0] : undefined);
|
|
126
|
+
if (!defaultAlias) {
|
|
127
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}' must set defaultEffort when more than one effort is declared`);
|
|
128
|
+
}
|
|
129
|
+
if (!efforts[defaultAlias]) {
|
|
130
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}' defaultEffort '${defaultAlias}' is not declared`);
|
|
131
|
+
}
|
|
132
|
+
if (binding.allowEffortSelect && keys.length < 2) {
|
|
133
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}' allowEffortSelect requires at least two efforts`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function assertTypeProtocols(profile) {
|
|
137
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
138
|
+
if (!isValidProfileProtocol(profile.type, binding.protocol)) {
|
|
139
|
+
const valid = protocolsForProfileType(profile.type).join(', ');
|
|
140
|
+
throw new TheorumError(`Profile ${profile.id} model '${modelId}': type '${profile.type}' cannot use protocol '${binding.protocol}'. Supported: ${valid}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function profileModelFields(input) {
|
|
62
145
|
return {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
146
|
+
models: input.models,
|
|
147
|
+
defaultModel: input.defaultModel ?? soleModelId(input.models),
|
|
148
|
+
allowModelSelect: input.allowModelSelect,
|
|
149
|
+
maxSteps: input.maxSteps,
|
|
150
|
+
key: input.key,
|
|
68
151
|
};
|
|
69
152
|
}
|
|
70
|
-
|
|
153
|
+
function assertContinueKindList(profileId, path, kinds) {
|
|
154
|
+
if (!kinds?.length)
|
|
155
|
+
return;
|
|
156
|
+
for (const kind of kinds) {
|
|
157
|
+
if (!isContinueStopKind(kind)) {
|
|
158
|
+
throw new TheorumError(`Profile ${profileId}: turnBehaviour.resumption.${path} may only include ContinueStopKind ` + // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
159
|
+
`(length | stream_incomplete | provider_error); got '${kind}'`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function assertResumption(profileId, resumption) {
|
|
164
|
+
if (!resumption)
|
|
165
|
+
return;
|
|
166
|
+
assertContinueKindList(profileId, 'allowContinue', resumption.allowContinue);
|
|
167
|
+
assertContinueKindList(profileId, 'autoContinue', resumption.autoContinue);
|
|
168
|
+
}
|
|
169
|
+
function assertTurnBehaviour(profileId, input) {
|
|
170
|
+
if (input.type === 'host')
|
|
171
|
+
return;
|
|
172
|
+
if (input.type === 'live') {
|
|
173
|
+
const tb = input.turnBehaviour;
|
|
174
|
+
if (tb?.resumption !== undefined) {
|
|
175
|
+
throw new TheorumError(`Profile ${profileId}: type 'live' uses live.sessionResumption, not turnBehaviour.resumption`);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (input.type !== 'text' && input.turnBehaviour?.allowSteering !== undefined) {
|
|
180
|
+
throw new TheorumError(`Profile ${profileId}: turnBehaviour.allowSteering is only valid on type 'text' or 'live'`);
|
|
181
|
+
}
|
|
182
|
+
assertResumption(profileId, input.turnBehaviour?.resumption);
|
|
183
|
+
}
|
|
184
|
+
function assertObservability(profileId, spec) {
|
|
185
|
+
if (!spec) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const policy = resolveObservabilityPolicy(spec);
|
|
190
|
+
if (policy.retainForDays <= 0 || !Number.isFinite(policy.retainForDays)) {
|
|
191
|
+
throw new TheorumError(`Profile ${profileId}: observability.retainForDays must be a positive number`);
|
|
192
|
+
}
|
|
193
|
+
if (policy.rotateAfterMiB <= 0 || !Number.isFinite(policy.rotateAfterMiB)) {
|
|
194
|
+
throw new TheorumError(`Profile ${profileId}: observability.rotateAfterMiB must be a positive number`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
if (err instanceof TheorumError && err.message.startsWith('Profile ')) {
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
202
|
+
throw new TheorumError(`Profile ${profileId}: ${message}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
71
205
|
function defineProfile(input) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
206
|
+
if (input.type === 'host') {
|
|
207
|
+
return defineHostProfile(input);
|
|
208
|
+
}
|
|
209
|
+
assertModelsNonEmpty(input.id, input.models);
|
|
210
|
+
assertDefaultModel(input.id, input);
|
|
211
|
+
assertTurnBehaviour(input.id, input);
|
|
212
|
+
assertObservability(input.id, input.observability);
|
|
213
|
+
for (const [modelId, binding] of Object.entries(input.models)) {
|
|
214
|
+
assertModelBinding(input.id, modelId, binding);
|
|
215
|
+
}
|
|
216
|
+
const identity = {
|
|
217
|
+
handle: input.identity.handle,
|
|
218
|
+
system: input.identity.system,
|
|
219
|
+
systemByRole: input.identity.systemByRole,
|
|
80
220
|
};
|
|
221
|
+
const guardrails = input.guardrails;
|
|
222
|
+
const observability = input.observability;
|
|
223
|
+
const modelFields = profileModelFields(input);
|
|
224
|
+
let profile;
|
|
225
|
+
switch (input.type) {
|
|
226
|
+
case 'text':
|
|
227
|
+
profile = {
|
|
228
|
+
type: 'text',
|
|
229
|
+
id: input.id,
|
|
230
|
+
identity,
|
|
231
|
+
...modelFields,
|
|
232
|
+
tools: input.tools,
|
|
233
|
+
inputs: input.inputs,
|
|
234
|
+
outputs: input.outputs,
|
|
235
|
+
turnBehaviour: input.turnBehaviour,
|
|
236
|
+
guardrails,
|
|
237
|
+
observability,
|
|
238
|
+
};
|
|
239
|
+
break;
|
|
240
|
+
case 'image':
|
|
241
|
+
profile = {
|
|
242
|
+
type: 'image',
|
|
243
|
+
id: input.id,
|
|
244
|
+
identity,
|
|
245
|
+
...modelFields,
|
|
246
|
+
image: input.image,
|
|
247
|
+
tools: input.tools,
|
|
248
|
+
inputs: input.inputs,
|
|
249
|
+
outputs: input.outputs,
|
|
250
|
+
turnBehaviour: input.turnBehaviour,
|
|
251
|
+
guardrails,
|
|
252
|
+
observability,
|
|
253
|
+
};
|
|
254
|
+
break;
|
|
255
|
+
case 'speech':
|
|
256
|
+
profile = {
|
|
257
|
+
type: 'speech',
|
|
258
|
+
id: input.id,
|
|
259
|
+
identity,
|
|
260
|
+
...modelFields,
|
|
261
|
+
speech: input.speech,
|
|
262
|
+
outputs: input.outputs,
|
|
263
|
+
turnBehaviour: input.turnBehaviour,
|
|
264
|
+
guardrails,
|
|
265
|
+
observability,
|
|
266
|
+
};
|
|
267
|
+
break;
|
|
268
|
+
case 'live': {
|
|
269
|
+
const liveInput = input;
|
|
270
|
+
if (liveInput.inputs !== undefined) {
|
|
271
|
+
throw new TheorumError(`Profile ${input.id}: type 'live' must not set inputs`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
272
|
+
}
|
|
273
|
+
if (liveInput.outputs !== undefined) {
|
|
274
|
+
throw new TheorumError(`Profile ${input.id}: type 'live' must not set outputs`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
275
|
+
}
|
|
276
|
+
profile = {
|
|
277
|
+
type: 'live',
|
|
278
|
+
id: input.id,
|
|
279
|
+
identity,
|
|
280
|
+
...modelFields,
|
|
281
|
+
live: input.live,
|
|
282
|
+
tools: assertLiveTools(input.id, input.tools),
|
|
283
|
+
turnBehaviour: liveInput.turnBehaviour,
|
|
284
|
+
guardrails,
|
|
285
|
+
observability,
|
|
286
|
+
};
|
|
287
|
+
assertLiveIngressConfigured(profile);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
default: {
|
|
291
|
+
const _exhaustive = input;
|
|
292
|
+
throw new TheorumError(`Unknown profile type '${String(_exhaustive)}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
assertTypeProtocols(profile);
|
|
296
|
+
return profile;
|
|
81
297
|
}
|
|
82
298
|
function assertCompactionSpec(profileId, modelId, spec) {
|
|
83
|
-
const tag = `Profile ${profileId} model ${modelId} compaction`;
|
|
299
|
+
const tag = `Profile ${profileId} model ${modelId} compaction`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
84
300
|
assertCompactionBudget(tag, spec);
|
|
85
301
|
assertCompactionRetain(tag, spec);
|
|
86
302
|
if (spec.meter != null && spec.meter !== 'history' && spec.meter !== 'input') {
|
|
87
|
-
throw new TheorumError(`${tag}: meter must be 'history' or 'input'`);
|
|
303
|
+
throw new TheorumError(`${tag}: meter must be 'history' or 'input'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
88
304
|
}
|
|
89
305
|
if (!profiles.has(spec.profile)) {
|
|
90
306
|
throw new TheorumError(`${tag}: compaction profile '${spec.profile}' must be registered before '${profileId}'`);
|
|
91
307
|
}
|
|
92
308
|
}
|
|
309
|
+
function assertCacheSpec(profileId, modelId, binding) {
|
|
310
|
+
const spec = binding.cache;
|
|
311
|
+
if (!spec) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const tag = `Profile ${profileId} model '${modelId}'`;
|
|
315
|
+
if (binding.provider !== 'openrouter' || binding.protocol !== 'openAi') {
|
|
316
|
+
throw new TheorumError(`${tag}: cache is only valid when protocol is 'openAi' and provider is 'openrouter'`);
|
|
317
|
+
}
|
|
318
|
+
if (!CACHE_MODES.includes(spec.mode)) {
|
|
319
|
+
throw new TheorumError(`${tag}: cache.mode must be one of ${CACHE_MODES.join(' | ')}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
320
|
+
}
|
|
321
|
+
if (spec.ttl != null && !CACHE_TTLS.includes(spec.ttl)) {
|
|
322
|
+
throw new TheorumError(`${tag}: cache.ttl must be one of ${CACHE_TTLS.join(' | ')}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function assertInteractionsPersistence(profileId, modelId, binding) {
|
|
326
|
+
if (binding.store === undefined && binding.persistViaInteractionId === undefined) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (binding.protocol === 'geminiInteractions' && binding.provider === 'google') {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const which = binding.store !== undefined && binding.persistViaInteractionId !== undefined
|
|
333
|
+
? 'store and persistViaInteractionId' // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
334
|
+
: binding.store !== undefined
|
|
335
|
+
? 'store'
|
|
336
|
+
: 'persistViaInteractionId';
|
|
337
|
+
throw new TheorumError(`Profile ${profileId} model '${modelId}': ${which} is only valid when protocol is 'geminiInteractions' and provider is 'google'`);
|
|
338
|
+
}
|
|
93
339
|
function assertCompactionBudget(tag, spec) {
|
|
94
340
|
if (spec.maxTokens <= 0) {
|
|
95
|
-
throw new TheorumError(`${tag}: maxTokens must be > 0`);
|
|
341
|
+
throw new TheorumError(`${tag}: maxTokens must be > 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
96
342
|
}
|
|
97
343
|
if (spec.compactAt <= 0 || spec.compactAt >= 1) {
|
|
98
|
-
throw new TheorumError(`${tag}: compactAt must be in (0, 1)`);
|
|
344
|
+
throw new TheorumError(`${tag}: compactAt must be in (0, 1)`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
99
345
|
}
|
|
100
346
|
}
|
|
101
347
|
function assertCompactionRetain(tag, spec) {
|
|
102
348
|
if (spec.previousExchanges < 0) {
|
|
103
|
-
throw new TheorumError(`${tag}: previousExchanges must be >= 0`);
|
|
349
|
+
throw new TheorumError(`${tag}: previousExchanges must be >= 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
104
350
|
}
|
|
105
351
|
if (spec.previousExchanges > 0 && spec.previousExchanges < 1) {
|
|
106
352
|
if (spec.previousExchanges >= spec.compactAt) {
|
|
@@ -108,22 +354,116 @@ function assertCompactionRetain(tag, spec) {
|
|
|
108
354
|
}
|
|
109
355
|
}
|
|
110
356
|
if (spec.previousExchanges >= 1 && !Number.isInteger(spec.previousExchanges)) {
|
|
111
|
-
throw new TheorumError(`${tag}: previousExchanges >= 1 must be an integer`);
|
|
357
|
+
throw new TheorumError(`${tag}: previousExchanges >= 1 must be an integer`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
112
358
|
}
|
|
113
359
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
360
|
+
function profileToolsAllow(profile) {
|
|
361
|
+
if (profile.type === 'speech') {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
return profile.tools.allow;
|
|
365
|
+
}
|
|
366
|
+
function assertCustomToolsOnly(profile) {
|
|
367
|
+
for (const id of profileToolsAllow(profile)) {
|
|
368
|
+
const tool = getTool(id);
|
|
369
|
+
if (tool?.type === 'builtin') {
|
|
370
|
+
throw new TheorumError(profile.type === 'host'
|
|
371
|
+
? `Profile ${profile.id} lists builtin '${id}' in tools.allow — type 'host' never runs a model` // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
372
|
+
: `Profile ${profile.id} lists builtin '${id}' in tools.allow — declare it on models.*.builtInTools instead`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function assertLiveTools(profileId, tools) {
|
|
377
|
+
const extra = tools;
|
|
378
|
+
if (extra.t1Policy !== undefined) {
|
|
379
|
+
throw new TheorumError(`Profile ${profileId}: tools.t1Policy is not supported on type 'live' — wire T0 tools in tools.allow for session setup`);
|
|
380
|
+
}
|
|
381
|
+
if (extra.t2Loader !== undefined) {
|
|
382
|
+
throw new TheorumError(`Profile ${profileId}: tools.t2Loader is not supported on type 'live' — Gemini Live function declarations are fixed at session setup`);
|
|
383
|
+
}
|
|
384
|
+
return { allow: tools.allow };
|
|
385
|
+
}
|
|
386
|
+
function assertProfileToolLoader(profile) {
|
|
387
|
+
if (profile.type === 'speech') {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (profile.type === 'live') {
|
|
391
|
+
assertLiveTools(profile.id, profile.tools);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (profile.type === 'host') {
|
|
395
|
+
assertHostTools(profile.id, profile.tools);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const loaderId = profile.tools.t2Loader;
|
|
399
|
+
if (!loaderId) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (!profile.tools.allow.includes(loaderId)) {
|
|
403
|
+
throw new TheorumError(`Profile ${profile.id} tools.t2Loader '${loaderId}' must also be listed in tools.allow`);
|
|
404
|
+
}
|
|
405
|
+
const tool = getTool(loaderId);
|
|
406
|
+
if (tool?.type !== 'function') {
|
|
407
|
+
throw new TheorumError(`Profile ${profile.id} tools.t2Loader '${loaderId}' must be a registered type: 'function' tool`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** Every builtin id declared across a profile's model bindings. */
|
|
411
|
+
function* modelBuiltinIds(profile) {
|
|
412
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
413
|
+
for (const id of binding.builtInTools ?? []) {
|
|
414
|
+
yield { modelId, id };
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function assertModelBuiltInTools(profile) {
|
|
419
|
+
for (const { modelId, id } of modelBuiltinIds(profile)) {
|
|
420
|
+
const tool = getTool(id);
|
|
421
|
+
if (tool?.type !== 'builtin') {
|
|
422
|
+
throw new TheorumError(`Profile ${profile.id} model '${modelId}' lists '${id}' in builtInTools — not a registered builtin`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function assertCompactionOnlyOnText(profile) {
|
|
427
|
+
if (profile.type === 'text') {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
431
|
+
if (binding.compaction) {
|
|
432
|
+
throw new TheorumError(`Profile ${profile.id} model '${modelId}': compaction is only valid on type 'text'`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function assertMediaLimits(profile) {
|
|
437
|
+
if (profile.type === 'speech' || profile.type === 'live') {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const inputs = profile.inputs;
|
|
441
|
+
if (!inputs) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const { attachments, voice, maxFiles, maxBytes, maxTurnBytes } = inputs;
|
|
119
445
|
if (attachments || voice) {
|
|
120
446
|
if (!(maxFiles && maxBytes && maxTurnBytes)) {
|
|
121
|
-
throw new TheorumError(`Profile ${profile.id} must set maxFiles, maxBytes, and maxTurnBytes`);
|
|
447
|
+
throw new TheorumError(`Profile ${profile.id} must set maxFiles, maxBytes, and maxTurnBytes`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
122
448
|
}
|
|
123
449
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
450
|
+
}
|
|
451
|
+
/** Register one host-owned profile in the process-local registry. */
|
|
452
|
+
function registerProfile(profileInput) {
|
|
453
|
+
const profile = defineProfile(profileInput);
|
|
454
|
+
assertCustomToolsOnly(profile);
|
|
455
|
+
assertProfileToolLoader(profile);
|
|
456
|
+
if (profile.type === 'host') {
|
|
457
|
+
// No models, ingress, media limits, or compaction to validate — the tool ceiling is the whole contract.
|
|
458
|
+
profiles.set(profile.id, profile);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
assertModelBuiltInTools(profile);
|
|
462
|
+
assertCompactionOnlyOnText(profile);
|
|
463
|
+
assertMediaLimits(profile);
|
|
464
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
465
|
+
if (binding.compaction) {
|
|
466
|
+
assertCompactionSpec(profile.id, modelId, binding.compaction);
|
|
127
467
|
}
|
|
128
468
|
}
|
|
129
469
|
profiles.set(profile.id, profile);
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
/** Build the provider request projection shared by execution and tracing. */
|
|
2
2
|
function providerCompleteRequest(generation, system) {
|
|
3
|
+
const isInteractions = generation.transport === 'interactions';
|
|
3
4
|
return {
|
|
4
5
|
model: generation.model,
|
|
5
6
|
apiId: generation.apiId,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
previousInteractionId: isInteractions ? generation.previousInteractionId : undefined,
|
|
8
|
+
store: isInteractions ? generation.store : undefined,
|
|
9
|
+
stream: isInteractions ? generation.stream : undefined,
|
|
9
10
|
thinking: generation.thinking,
|
|
10
|
-
summaries: generation.summaries,
|
|
11
|
+
summaries: isInteractions ? generation.summaries : undefined,
|
|
11
12
|
maxOutputTokens: generation.maxOutputTokens,
|
|
12
13
|
temperature: generation.temperature,
|
|
13
14
|
builtins: generation.builtins,
|
|
15
|
+
googleMapsLocation: isInteractions ? generation.googleMapsLocation : undefined,
|
|
16
|
+
cache: generation.cache,
|
|
17
|
+
sessionId: generation.sessionId,
|
|
14
18
|
system,
|
|
15
19
|
input: generation.input,
|
|
16
20
|
history: generation.history,
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
interactionOnlyInput: isInteractions ? generation.interactionOnlyInput : undefined,
|
|
22
|
+
wireTools: generation.tools.wire,
|
|
19
23
|
structured: generation.structured,
|
|
20
24
|
image: generation.image,
|
|
21
25
|
speech: generation.speech,
|
|
22
|
-
|
|
26
|
+
live: generation.live,
|
|
27
|
+
sessionResumptionHandle: generation.sessionResumptionHandle,
|
|
28
|
+
keySlot: generation.keySlot,
|
|
23
29
|
};
|
|
24
30
|
}
|
|
25
31
|
export { providerCompleteRequest };
|
|
@@ -1,19 +1,19 @@
|
|
|
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
|
*/
|
|
9
|
-
import type { Profile, ProjectedProfile, ResolvedGeneration,
|
|
10
|
-
|
|
6
|
+
import type { ModelId, ModelProfile, Profile, ProjectedProfile, ResolvedGeneration, TurnRequest } from '../types.js';
|
|
7
|
+
/** Narrow to a model-binding profile; `host` never runs a model. */
|
|
8
|
+
declare function requireModelProfile(profile: Profile, door: string): ModelProfile;
|
|
9
|
+
declare function pickModel(profile: ModelProfile, requested?: string): ModelId;
|
|
11
10
|
/** Resolve a host `TurnRequest` into provider-ready generation state. */
|
|
12
11
|
declare function resolveTurn(req: TurnRequest): {
|
|
13
|
-
profile:
|
|
12
|
+
profile: ModelProfile;
|
|
14
13
|
generation: ResolvedGeneration;
|
|
15
14
|
};
|
|
15
|
+
/** Project a profile object into a safe host/UI inspection object. */
|
|
16
|
+
declare function projectProfileObject(input: Profile): ProjectedProfile;
|
|
16
17
|
/** Project a registered profile into a safe host/UI inspection object. */
|
|
17
18
|
declare function projectProfile(id: Profile['id']): ProjectedProfile;
|
|
18
|
-
|
|
19
|
-
export { assertToolAllowed, pickSystemRole, projectProfile, resolveTurn };
|
|
19
|
+
export { pickModel, projectProfile, projectProfileObject, requireModelProfile, resolveTurn };
|