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.
- package/README.md +81 -25
- package/esm/mod.d.ts +48 -29
- package/esm/mod.js +37 -24
- package/esm/src/cli/commands/bench.js +13 -16
- package/esm/src/cli/commands/fuzz-canary.js +25 -29
- 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 +12 -3
- package/esm/src/cli/commands/run.js +8 -9
- package/esm/src/cli/commands/test.js +8 -8
- package/esm/src/cli/event-log.d.ts +1 -1
- package/esm/src/cli/index.js +12 -0
- package/esm/src/cli/matrix/synthesizer.d.ts +6 -6
- package/esm/src/cli/matrix/synthesizer.js +23 -22
- package/esm/src/guardrails/canary-gate.d.ts +2 -1
- package/esm/src/guardrails/canary-gate.js +2 -1
- package/esm/src/guardrails/canary.d.ts +9 -1
- package/esm/src/guardrails/canary.js +21 -10
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +1 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +12 -15
- package/esm/src/guardrails/corpus/inbound-payloads.js +1 -0
- package/esm/src/guardrails/corpus/live-attacks.js +0 -1
- package/esm/src/guardrails/corpus/mod.d.ts +1 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +1 -0
- package/esm/src/guardrails/corpus/secrets.js +1 -0
- package/esm/src/guardrails/corpus/strings.d.ts +1 -0
- package/esm/src/guardrails/corpus/strings.js +1 -0
- package/esm/src/guardrails/corpus/types.d.ts +1 -0
- package/esm/src/guardrails/egress.d.ts +26 -3
- package/esm/src/guardrails/egress.js +69 -18
- package/esm/src/guardrails/error.d.ts +14 -23
- package/esm/src/guardrails/error.js +86 -77
- 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/lexicon.d.ts +39 -0
- package/esm/src/guardrails/lexicon.js +200 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +15 -13
- package/esm/src/guardrails/live-outbound-gate.js +131 -89
- package/esm/src/guardrails/mod.d.ts +19 -4
- package/esm/src/guardrails/mod.js +12 -3
- 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 +36 -6
- package/esm/src/guardrails/sanitize.js +152 -60
- package/esm/src/guardrails/serialize.d.ts +35 -0
- package/esm/src/guardrails/serialize.js +58 -0
- package/esm/src/guardrails/testing.d.ts +7 -0
- package/esm/src/guardrails/testing.js +4 -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.js +4 -0
- package/esm/src/host/mint-trace.d.ts +1 -1
- 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.js +43 -2
- package/esm/src/kernel/engine/live-inbound.d.ts +15 -3
- package/esm/src/kernel/engine/live-inbound.js +22 -8
- 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.js +127 -40
- package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
- package/esm/src/kernel/engine/runner/mod.js +182 -43
- 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 +24 -0
- package/esm/src/kernel/engine/runner/steps.js +128 -61
- package/esm/src/kernel/engine/runner/stream.d.ts +9 -2
- package/esm/src/kernel/engine/runner/stream.js +114 -55
- package/esm/src/kernel/engine/session/mod.d.ts +3 -1
- package/esm/src/kernel/engine/session/mod.js +386 -34
- 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 +19 -7
- package/esm/src/kernel/mod.js +10 -5
- 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 +10 -10
- package/esm/src/kernel/registry/attachments.js +29 -26
- package/esm/src/kernel/registry/catalog.d.ts +25 -11
- package/esm/src/kernel/registry/catalog.js +59 -24
- package/esm/src/kernel/registry/ingress.js +34 -22
- 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 +34 -14
- package/esm/src/kernel/registry/profiles.js +306 -59
- package/esm/src/kernel/registry/provider-request.js +2 -0
- package/esm/src/kernel/registry/resolve.d.ts +7 -6
- package/esm/src/kernel/registry/resolve.js +102 -117
- 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 +2 -2
- package/esm/src/kernel/registry/vault.js +4 -4
- package/esm/src/kernel/schema.d.ts +97 -11
- package/esm/src/kernel/schema.js +256 -111
- package/esm/src/kernel/stages.d.ts +175 -0
- package/esm/src/kernel/stages.js +476 -0
- package/esm/src/kernel/stop.d.ts +65 -11
- package/esm/src/kernel/stop.js +46 -11
- 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 +63 -24
- package/esm/src/kernel/tools/execute.js +475 -224
- package/esm/src/kernel/tools/harness.js +16 -14
- package/esm/src/kernel/tools/invoke.js +26 -8
- package/esm/src/kernel/tools/mod.d.ts +3 -1
- package/esm/src/kernel/tools/mod.js +2 -1
- 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.js +5 -3
- package/esm/src/kernel/tools/registry.js +20 -10
- 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 +17 -7
- package/esm/src/kernel/tools/resolve.js +38 -17
- package/esm/src/kernel/tools/schema.d.ts +2 -0
- package/esm/src/kernel/tools/schema.js +23 -6
- 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 +198 -20
- package/esm/src/kernel/types.d.ts +253 -114
- 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 +3 -1
- package/esm/src/observability/trace-attach.js +17 -10
- package/esm/src/observability/trace-record.d.ts +12 -4
- package/esm/src/observability/trace-record.js +63 -19
- package/esm/src/observability/trace-sink.d.ts +19 -0
- package/esm/src/observability/trace-sink.js +10 -0
- package/esm/src/observability/trace.d.ts +14 -11
- package/esm/src/observability/trace.js +23 -14
- package/esm/src/observability/types.d.ts +113 -0
- package/esm/src/observability/types.js +11 -0
- package/esm/src/providers/create-provider.d.ts +7 -4
- package/esm/src/providers/create-provider.js +21 -7
- package/esm/src/providers/google/interactions/framing.d.ts +8 -0
- package/esm/src/providers/google/interactions/framing.js +74 -13
- package/esm/src/providers/google/live/framing.d.ts +5 -2
- package/esm/src/providers/google/live/framing.js +49 -12
- package/esm/src/providers/google/live/openapi-schema.js +3 -2
- package/esm/src/providers/google/live/stream.d.ts +9 -0
- package/esm/src/providers/google/live/stream.js +16 -3
- 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 +21 -1
- package/esm/src/providers/openrouter/chat.js +51 -6
- package/esm/src/providers/openrouter/image.js +8 -19
- package/esm/src/providers/openrouter/openai/chat-payload.js +23 -1
- package/esm/src/providers/openrouter/openai/compat.d.ts +7 -1
- package/esm/src/providers/openrouter/openai/compat.js +23 -4
- package/esm/src/providers/openrouter/openai/image-payload.js +8 -2
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +1 -1
- package/esm/src/providers/openrouter/openai/sdk-messages.js +32 -5
- package/package.json +1 -1
- package/esm/src/interface/blocks.d.ts +0 -20
- package/esm/src/interface/blocks.js +0 -180
- package/esm/src/interface/from-profile.d.ts +0 -14
- package/esm/src/interface/from-profile.js +0 -104
- package/esm/src/interface/inputs.d.ts +0 -8
- package/esm/src/interface/inputs.js +0 -127
- package/esm/src/interface/mod.d.ts +0 -10
- package/esm/src/interface/mod.js +0 -9
- package/esm/src/interface/types.d.ts +0 -153
- package/esm/src/interface/types.js +0 -13
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile graph — playground / authoring topology projected from PROFILE_FIELDS.
|
|
3
|
+
*
|
|
4
|
+
* This is not runtime policy. It declares which profile sections are graph
|
|
5
|
+
* facets, for which ProfileType, whether they may be omitted, and whether the
|
|
6
|
+
* host UI uses a schema form or a structural editor. The playground must import
|
|
7
|
+
* this catalog; it must not invent FacetKind unions.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type { ProfileType } from './schema.js';
|
|
12
|
+
/** How the playground (or other host UI) should edit this facet. */
|
|
13
|
+
export type ProfileGraphEditor = 'schema' | 'structural';
|
|
14
|
+
/** Where the facet sits in the authoring graph. */
|
|
15
|
+
export type ProfileGraphRole = 'root' | 'spine' | 'branch';
|
|
16
|
+
/**
|
|
17
|
+
* Authoring-graph catalog. Adding a profile section? Add PROFILE_FIELDS and a row
|
|
18
|
+
* here — never a FacetKind in the frontend.
|
|
19
|
+
*
|
|
20
|
+
* `ProfileGraphFacetId` is derived from this array; do not maintain a union by hand.
|
|
21
|
+
*/
|
|
22
|
+
declare const PROFILE_GRAPH_DEF: readonly [{
|
|
23
|
+
readonly id: "identity";
|
|
24
|
+
readonly profilePath: "identity";
|
|
25
|
+
readonly role: "root";
|
|
26
|
+
readonly profileTypes: readonly ("text" | "image" | "speech" | "live" | "host")[];
|
|
27
|
+
readonly optional: false;
|
|
28
|
+
readonly editor: "structural";
|
|
29
|
+
readonly label: "Identity";
|
|
30
|
+
readonly ownsFields: readonly ["id", "type"];
|
|
31
|
+
}, {
|
|
32
|
+
readonly id: "models";
|
|
33
|
+
readonly profilePath: "models";
|
|
34
|
+
readonly role: "spine";
|
|
35
|
+
readonly profileTypes: readonly ("text" | "image" | "speech" | "live" | "host")[];
|
|
36
|
+
readonly optional: false;
|
|
37
|
+
readonly editor: "structural";
|
|
38
|
+
readonly label: "Models";
|
|
39
|
+
readonly ownsFields: readonly ["defaultModel", "allowModelSelect", "maxSteps", "key"];
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: "modelBinding";
|
|
42
|
+
readonly profilePath: "models.*";
|
|
43
|
+
readonly role: "branch";
|
|
44
|
+
readonly parent: "models";
|
|
45
|
+
readonly profileTypes: readonly ("text" | "image" | "speech" | "live" | "host")[];
|
|
46
|
+
readonly optional: false;
|
|
47
|
+
readonly editor: "structural";
|
|
48
|
+
readonly label: "Model binding";
|
|
49
|
+
}, {
|
|
50
|
+
readonly id: "image";
|
|
51
|
+
readonly profilePath: "image";
|
|
52
|
+
readonly role: "spine";
|
|
53
|
+
readonly profileTypes: readonly ["image"];
|
|
54
|
+
readonly optional: false;
|
|
55
|
+
readonly editor: "structural";
|
|
56
|
+
readonly label: "Image";
|
|
57
|
+
}, {
|
|
58
|
+
readonly id: "speech";
|
|
59
|
+
readonly profilePath: "speech";
|
|
60
|
+
readonly role: "spine";
|
|
61
|
+
readonly profileTypes: readonly ["speech"];
|
|
62
|
+
readonly optional: false;
|
|
63
|
+
readonly editor: "structural";
|
|
64
|
+
readonly label: "Speech";
|
|
65
|
+
}, {
|
|
66
|
+
readonly id: "live";
|
|
67
|
+
readonly profilePath: "live";
|
|
68
|
+
readonly role: "spine";
|
|
69
|
+
readonly profileTypes: readonly ["live"];
|
|
70
|
+
readonly optional: false;
|
|
71
|
+
readonly editor: "structural";
|
|
72
|
+
readonly label: "Live";
|
|
73
|
+
}, {
|
|
74
|
+
readonly id: "tools";
|
|
75
|
+
readonly profilePath: "tools";
|
|
76
|
+
readonly role: "spine";
|
|
77
|
+
readonly profileTypes: readonly ["text", "image", "live", "host"];
|
|
78
|
+
readonly optional: false;
|
|
79
|
+
readonly editor: "structural";
|
|
80
|
+
readonly label: "Tools";
|
|
81
|
+
}, {
|
|
82
|
+
readonly id: "toolSpec";
|
|
83
|
+
readonly profilePath: "tools.allow";
|
|
84
|
+
readonly role: "branch";
|
|
85
|
+
readonly parent: "tools";
|
|
86
|
+
readonly profileTypes: readonly ["text", "image", "live", "host"];
|
|
87
|
+
readonly optional: true;
|
|
88
|
+
readonly editor: "structural";
|
|
89
|
+
readonly label: "Tool";
|
|
90
|
+
}, {
|
|
91
|
+
readonly id: "inputs";
|
|
92
|
+
readonly profilePath: "inputs";
|
|
93
|
+
readonly role: "spine";
|
|
94
|
+
readonly profileTypes: readonly ["text", "image"];
|
|
95
|
+
readonly optional: false;
|
|
96
|
+
readonly editor: "structural";
|
|
97
|
+
readonly label: "Inputs";
|
|
98
|
+
}, {
|
|
99
|
+
readonly id: "outputs";
|
|
100
|
+
readonly profilePath: "outputs";
|
|
101
|
+
readonly role: "spine";
|
|
102
|
+
readonly profileTypes: readonly ["text", "image", "speech"];
|
|
103
|
+
readonly optional: true;
|
|
104
|
+
readonly editor: "structural";
|
|
105
|
+
readonly label: "Outputs";
|
|
106
|
+
}, {
|
|
107
|
+
readonly id: "turnBehaviour";
|
|
108
|
+
readonly profilePath: "turnBehaviour";
|
|
109
|
+
readonly role: "spine";
|
|
110
|
+
readonly profileTypes: readonly ["text", "image", "speech", "live"];
|
|
111
|
+
readonly optional: true;
|
|
112
|
+
readonly editor: "structural";
|
|
113
|
+
readonly label: "Turn behaviour";
|
|
114
|
+
}, {
|
|
115
|
+
readonly id: "guardrails";
|
|
116
|
+
readonly profilePath: "guardrails";
|
|
117
|
+
readonly role: "spine";
|
|
118
|
+
readonly profileTypes: readonly ("text" | "image" | "speech" | "live" | "host")[];
|
|
119
|
+
readonly optional: true;
|
|
120
|
+
readonly editor: "structural";
|
|
121
|
+
readonly label: "Guardrails";
|
|
122
|
+
}, {
|
|
123
|
+
readonly id: "observability";
|
|
124
|
+
readonly profilePath: "observability";
|
|
125
|
+
readonly role: "spine";
|
|
126
|
+
readonly profileTypes: readonly ("text" | "image" | "speech" | "live" | "host")[];
|
|
127
|
+
readonly optional: true;
|
|
128
|
+
readonly editor: "structural";
|
|
129
|
+
readonly label: "Observability";
|
|
130
|
+
}];
|
|
131
|
+
/** Stable facet ids — derived from PROFILE_GRAPH; never hand-maintained. */
|
|
132
|
+
export type ProfileGraphFacetId = (typeof PROFILE_GRAPH_DEF)[number]['id'];
|
|
133
|
+
/**
|
|
134
|
+
* One node kind on the profile authoring graph.
|
|
135
|
+
*
|
|
136
|
+
* `profilePath` is a PROFILE_FIELDS key (section root) or a dynamic path
|
|
137
|
+
* (`models.*`). Branch facets nest under `parent`.
|
|
138
|
+
*/
|
|
139
|
+
export interface ProfileGraphFacet {
|
|
140
|
+
id: ProfileGraphFacetId;
|
|
141
|
+
profilePath: string;
|
|
142
|
+
role: ProfileGraphRole;
|
|
143
|
+
parent?: ProfileGraphFacetId;
|
|
144
|
+
profileTypes: readonly ProfileType[];
|
|
145
|
+
optional: boolean;
|
|
146
|
+
editor: ProfileGraphEditor;
|
|
147
|
+
label: string;
|
|
148
|
+
/**
|
|
149
|
+
* Extra PROFILE_FIELDS top-level keys owned by this facet (not 1:1 with id).
|
|
150
|
+
* Used by the drift gate so `defaultModel` / `id` / `type` are not orphaned.
|
|
151
|
+
*/
|
|
152
|
+
ownsFields?: readonly string[];
|
|
153
|
+
}
|
|
154
|
+
export declare const PROFILE_GRAPH: readonly ProfileGraphFacet[];
|
|
155
|
+
/** Spine (and root) facets visible for a profile type, in catalog order. */
|
|
156
|
+
declare function spineFacetsForProfileType(type: ProfileType): ProfileGraphFacet[];
|
|
157
|
+
/** Look up a graph facet by id. */
|
|
158
|
+
declare function profileGraphFacet(id: ProfileGraphFacetId): ProfileGraphFacet | undefined;
|
|
159
|
+
export { profileGraphFacet, spineFacetsForProfileType };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile graph — playground / authoring topology projected from PROFILE_FIELDS.
|
|
3
|
+
*
|
|
4
|
+
* This is not runtime policy. It declares which profile sections are graph
|
|
5
|
+
* facets, for which ProfileType, whether they may be omitted, and whether the
|
|
6
|
+
* host UI uses a schema form or a structural editor. The playground must import
|
|
7
|
+
* this catalog; it must not invent FacetKind unions.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Mirrors PROFILE_TYPES — value import would cycle through schema re-exports.
|
|
13
|
+
* Drift is gated by tests/kernel/profile-graph.test.ts.
|
|
14
|
+
*/
|
|
15
|
+
const ALL = ['text', 'image', 'speech', 'live', 'host'];
|
|
16
|
+
/** Types that bind models — `host` never runs a model. */
|
|
17
|
+
const MODEL_TYPES = ['text', 'image', 'speech', 'live'];
|
|
18
|
+
/**
|
|
19
|
+
* Authoring-graph catalog. Adding a profile section? Add PROFILE_FIELDS and a row
|
|
20
|
+
* here — never a FacetKind in the frontend.
|
|
21
|
+
*
|
|
22
|
+
* `ProfileGraphFacetId` is derived from this array; do not maintain a union by hand.
|
|
23
|
+
*/
|
|
24
|
+
const PROFILE_GRAPH_DEF = [
|
|
25
|
+
{
|
|
26
|
+
id: 'identity',
|
|
27
|
+
profilePath: 'identity',
|
|
28
|
+
role: 'root',
|
|
29
|
+
profileTypes: ALL,
|
|
30
|
+
optional: false,
|
|
31
|
+
editor: 'structural',
|
|
32
|
+
label: 'Identity',
|
|
33
|
+
ownsFields: ['id', 'type'],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'models',
|
|
37
|
+
profilePath: 'models',
|
|
38
|
+
role: 'spine',
|
|
39
|
+
profileTypes: MODEL_TYPES,
|
|
40
|
+
optional: false,
|
|
41
|
+
editor: 'structural',
|
|
42
|
+
label: 'Models',
|
|
43
|
+
ownsFields: ['defaultModel', 'allowModelSelect', 'maxSteps', 'key'],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'modelBinding',
|
|
47
|
+
profilePath: 'models.*',
|
|
48
|
+
role: 'branch',
|
|
49
|
+
parent: 'models',
|
|
50
|
+
profileTypes: MODEL_TYPES,
|
|
51
|
+
optional: false,
|
|
52
|
+
editor: 'structural',
|
|
53
|
+
label: 'Model binding',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'image',
|
|
57
|
+
profilePath: 'image',
|
|
58
|
+
role: 'spine',
|
|
59
|
+
profileTypes: ['image'],
|
|
60
|
+
optional: false,
|
|
61
|
+
editor: 'structural',
|
|
62
|
+
label: 'Image',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'speech',
|
|
66
|
+
profilePath: 'speech',
|
|
67
|
+
role: 'spine',
|
|
68
|
+
profileTypes: ['speech'],
|
|
69
|
+
optional: false,
|
|
70
|
+
editor: 'structural',
|
|
71
|
+
label: 'Speech',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'live',
|
|
75
|
+
profilePath: 'live',
|
|
76
|
+
role: 'spine',
|
|
77
|
+
profileTypes: ['live'],
|
|
78
|
+
optional: false,
|
|
79
|
+
editor: 'structural',
|
|
80
|
+
label: 'Live',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'tools',
|
|
84
|
+
profilePath: 'tools',
|
|
85
|
+
role: 'spine',
|
|
86
|
+
profileTypes: ['text', 'image', 'live', 'host'],
|
|
87
|
+
optional: false,
|
|
88
|
+
editor: 'structural',
|
|
89
|
+
label: 'Tools',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'toolSpec',
|
|
93
|
+
profilePath: 'tools.allow',
|
|
94
|
+
role: 'branch',
|
|
95
|
+
parent: 'tools',
|
|
96
|
+
profileTypes: ['text', 'image', 'live', 'host'],
|
|
97
|
+
optional: true,
|
|
98
|
+
editor: 'structural',
|
|
99
|
+
label: 'Tool',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'inputs',
|
|
103
|
+
profilePath: 'inputs',
|
|
104
|
+
role: 'spine',
|
|
105
|
+
profileTypes: ['text', 'image'],
|
|
106
|
+
optional: false,
|
|
107
|
+
editor: 'structural',
|
|
108
|
+
label: 'Inputs',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'outputs',
|
|
112
|
+
profilePath: 'outputs',
|
|
113
|
+
role: 'spine',
|
|
114
|
+
profileTypes: ['text', 'image', 'speech'],
|
|
115
|
+
optional: true,
|
|
116
|
+
editor: 'structural',
|
|
117
|
+
label: 'Outputs',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'turnBehaviour',
|
|
121
|
+
profilePath: 'turnBehaviour',
|
|
122
|
+
role: 'spine',
|
|
123
|
+
profileTypes: ['text', 'image', 'speech', 'live'],
|
|
124
|
+
optional: true,
|
|
125
|
+
editor: 'structural',
|
|
126
|
+
label: 'Turn behaviour',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'guardrails',
|
|
130
|
+
profilePath: 'guardrails',
|
|
131
|
+
role: 'spine',
|
|
132
|
+
profileTypes: ALL,
|
|
133
|
+
optional: true,
|
|
134
|
+
editor: 'structural',
|
|
135
|
+
label: 'Guardrails',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'observability',
|
|
139
|
+
profilePath: 'observability',
|
|
140
|
+
role: 'spine',
|
|
141
|
+
profileTypes: ALL,
|
|
142
|
+
optional: true,
|
|
143
|
+
editor: 'structural',
|
|
144
|
+
label: 'Observability',
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
export const PROFILE_GRAPH = PROFILE_GRAPH_DEF;
|
|
148
|
+
/** Spine (and root) facets visible for a profile type, in catalog order. */
|
|
149
|
+
function spineFacetsForProfileType(type) {
|
|
150
|
+
return PROFILE_GRAPH.filter((facet) => (facet.role === 'root' || facet.role === 'spine') && facet.profileTypes.includes(type));
|
|
151
|
+
}
|
|
152
|
+
/** Look up a graph facet by id. */
|
|
153
|
+
function profileGraphFacet(id) {
|
|
154
|
+
return PROFILE_GRAPH.find((facet) => facet.id === id);
|
|
155
|
+
}
|
|
156
|
+
export { profileGraphFacet, spineFacetsForProfileType };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type { MediaLimits, MimeInputs, Profile, TurnBlob } from '../types.js';
|
|
2
|
-
declare function tooManyFilesMessage(maxFiles: number): string;
|
|
3
|
-
declare function fileTooLargeMessage(maxBytes: number): string;
|
|
4
|
-
declare function turnTooLargeMessage(maxTurnBytes: number): string;
|
|
1
|
+
import type { MediaLimits, MimeInputs, Profile, TurnBlob, TurnMediaRef } from '../types.js';
|
|
5
2
|
declare function resolveMediaLimits(inputs: MimeInputs): MediaLimits | undefined;
|
|
6
3
|
declare function maxBytesForMime(mimeType: string, limits: MediaLimits): number;
|
|
4
|
+
/** Attachment supplied by provider file reference — no bytes to size-check or sanitize. */
|
|
5
|
+
declare function isTurnMediaRef(item: TurnBlob | TurnMediaRef): item is TurnMediaRef;
|
|
7
6
|
declare function requireMediaLimits(profile: Profile): MediaLimits;
|
|
8
7
|
declare function sanitizeCsvText(text: string): string;
|
|
9
|
-
|
|
10
|
-
declare function
|
|
11
|
-
|
|
8
|
+
/** Enforce file count on every attachment; base64 and byte limits only on inline blobs. */
|
|
9
|
+
declare function assertAttachmentLimits(attachments: Array<TurnBlob | TurnMediaRef>, limits: MediaLimits): void;
|
|
10
|
+
declare function sanitizeTurnBlobs(attachments: Array<TurnBlob | TurnMediaRef> | undefined, voice: TurnBlob[] | undefined, limits: MediaLimits | undefined): {
|
|
11
|
+
attachments?: Array<TurnBlob | TurnMediaRef>;
|
|
12
12
|
voice?: TurnBlob[];
|
|
13
13
|
};
|
|
14
|
-
declare function sanitizeTurnBlobsForProfile(profileId: string, attachments: TurnBlob
|
|
15
|
-
attachments?: TurnBlob
|
|
14
|
+
declare function sanitizeTurnBlobsForProfile(profileId: string, attachments: Array<TurnBlob | TurnMediaRef> | undefined, voice: TurnBlob[] | undefined): {
|
|
15
|
+
attachments?: Array<TurnBlob | TurnMediaRef>;
|
|
16
16
|
voice?: TurnBlob[];
|
|
17
17
|
};
|
|
18
|
-
export { assertAttachmentLimits,
|
|
18
|
+
export { assertAttachmentLimits, isTurnMediaRef, maxBytesForMime, requireMediaLimits, resolveMediaLimits, sanitizeCsvText, sanitizeTurnBlobs, sanitizeTurnBlobsForProfile, };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TheorumError } from '../../guardrails/error.js';
|
|
2
2
|
import { injectionSpans } from '../../guardrails/injection.js';
|
|
3
|
+
import { lexiconText } from '../../guardrails/lexicon.js';
|
|
3
4
|
import { sensitiveSpans } from '../../guardrails/sensitive.js';
|
|
4
5
|
import { applySpans } from '../../observability/spans.js';
|
|
5
6
|
import { getProfile } from './profiles.js';
|
|
@@ -9,22 +10,6 @@ const B64_TRIPLET = 3;
|
|
|
9
10
|
const CSV_FORMULA = /(^|,)(\s*)("?)(?:([=@])|([+-])(?![0-9."]))/gm;
|
|
10
11
|
const B64_BODY = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
11
12
|
const TEXT_MIMES = new Set(['text/csv', 'text/plain', 'text/markdown']);
|
|
12
|
-
const BYTES_PER_KIB = 1024;
|
|
13
|
-
function formatMb(bytes) {
|
|
14
|
-
const mb = bytes / (BYTES_PER_KIB * BYTES_PER_KIB);
|
|
15
|
-
return Number.isInteger(mb) ? `${String(mb)} MB` : `${mb.toFixed(1)} MB`;
|
|
16
|
-
}
|
|
17
|
-
function tooManyFilesMessage(maxFiles) {
|
|
18
|
-
return maxFiles === 1
|
|
19
|
-
? 'Only 1 file per message.'
|
|
20
|
-
: `Only ${String(maxFiles)} files per message.`;
|
|
21
|
-
}
|
|
22
|
-
function fileTooLargeMessage(maxBytes) {
|
|
23
|
-
return `Each file must be ${formatMb(maxBytes)} or smaller.`;
|
|
24
|
-
}
|
|
25
|
-
function turnTooLargeMessage(maxTurnBytes) {
|
|
26
|
-
return `Those files together are too large for one message (${formatMb(maxTurnBytes)} max).`;
|
|
27
|
-
}
|
|
28
13
|
function resolveMediaLimits(inputs) {
|
|
29
14
|
const { maxFiles, maxBytes, maxTurnBytes, limitsByMime } = inputs;
|
|
30
15
|
if (maxFiles && maxBytes && maxTurnBytes) {
|
|
@@ -46,13 +31,23 @@ function maxBytesForMime(mimeType, limits) {
|
|
|
46
31
|
}
|
|
47
32
|
return limits.maxBytes;
|
|
48
33
|
}
|
|
34
|
+
/** Attachment supplied by provider file reference — no bytes to size-check or sanitize. */
|
|
35
|
+
function isTurnMediaRef(item) {
|
|
36
|
+
return 'uri' in item;
|
|
37
|
+
}
|
|
49
38
|
function requireMediaLimits(profile) {
|
|
50
39
|
if (profile.type === 'speech') {
|
|
51
|
-
throw new TheorumError(`Profile ${profile.id} (speech) does not accept media input`);
|
|
40
|
+
throw new TheorumError(`Profile ${profile.id} (speech) does not accept media input`); // lexicon-exempt: developer contract error
|
|
41
|
+
}
|
|
42
|
+
if (profile.type === 'live') {
|
|
43
|
+
throw new TheorumError(`Profile ${profile.id} (live) does not accept turn attachment input`); // lexicon-exempt: developer contract error
|
|
44
|
+
}
|
|
45
|
+
if (profile.type === 'host') {
|
|
46
|
+
throw new TheorumError(`Profile ${profile.id} (host) does not accept turn input`); // lexicon-exempt: developer contract error
|
|
52
47
|
}
|
|
53
48
|
const limits = resolveMediaLimits(profile.inputs ?? {});
|
|
54
49
|
if (!limits) {
|
|
55
|
-
throw new TheorumError(`Profile ${profile.id} must set maxFiles, maxBytes, and maxTurnBytes`);
|
|
50
|
+
throw new TheorumError(`Profile ${profile.id} must set maxFiles, maxBytes, and maxTurnBytes`); // lexicon-exempt: developer contract error
|
|
56
51
|
}
|
|
57
52
|
return limits;
|
|
58
53
|
}
|
|
@@ -102,28 +97,36 @@ function sanitizeTextBytes(mime, bytes) {
|
|
|
102
97
|
}
|
|
103
98
|
return new TextEncoder().encode(applySpans(text, [...injectionSpans(text), ...sensitiveSpans(text)]));
|
|
104
99
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
/** Enforce file count on every attachment; base64 and byte limits only on inline blobs. */
|
|
101
|
+
function assertAttachmentLimits(attachments, limits) {
|
|
102
|
+
if (attachments.length > limits.maxFiles) {
|
|
103
|
+
throw new TheorumError(lexiconText('attachments.too_many_files', { maxFiles: limits.maxFiles }));
|
|
108
104
|
}
|
|
109
105
|
let total = 0;
|
|
110
|
-
for (const blob of
|
|
106
|
+
for (const blob of attachments) {
|
|
107
|
+
if (isTurnMediaRef(blob)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
111
110
|
const { data, mimeType } = blob;
|
|
112
111
|
if (!B64_BODY.test(data)) {
|
|
112
|
+
// lexicon-exempt: developer-facing wire-format diagnostic, not product copy
|
|
113
113
|
throw new TheorumError('attachment data must be base64');
|
|
114
114
|
}
|
|
115
115
|
const size = b64DecodedLen(data);
|
|
116
116
|
const maxAllowed = maxBytesForMime(mimeType, limits);
|
|
117
117
|
if (size > maxAllowed) {
|
|
118
|
-
throw new TheorumError(
|
|
118
|
+
throw new TheorumError(lexiconText('attachments.file_too_large', { maxBytes: maxAllowed }));
|
|
119
119
|
}
|
|
120
120
|
total += size;
|
|
121
121
|
}
|
|
122
122
|
if (total > limits.maxTurnBytes) {
|
|
123
|
-
throw new TheorumError(
|
|
123
|
+
throw new TheorumError(lexiconText('attachments.turn_too_large', { maxTurnBytes: limits.maxTurnBytes }));
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
function sanitizeAttachment(blob) {
|
|
127
|
+
if (isTurnMediaRef(blob)) {
|
|
128
|
+
return blob;
|
|
129
|
+
}
|
|
127
130
|
const { mimeType, data } = blob;
|
|
128
131
|
if (!TEXT_MIMES.has(mimeType.split(';')[0]?.trim().toLowerCase() ?? '')) {
|
|
129
132
|
return blob;
|
|
@@ -141,7 +144,7 @@ function sanitizeTurnBlobs(attachments, voice, limits) {
|
|
|
141
144
|
const files = attachments ?? [];
|
|
142
145
|
const clips = voice ?? [];
|
|
143
146
|
if (!limits) {
|
|
144
|
-
throw new TheorumError('
|
|
147
|
+
throw new TheorumError(lexiconText('attachments.not_accepted', { channel: 'file' }));
|
|
145
148
|
}
|
|
146
149
|
assertAttachmentLimits([...files, ...clips], limits);
|
|
147
150
|
return {
|
|
@@ -156,4 +159,4 @@ function sanitizeTurnBlobsForProfile(profileId, attachments, voice) {
|
|
|
156
159
|
const limits = requireMediaLimits(getProfile(profileId));
|
|
157
160
|
return sanitizeTurnBlobs(attachments, voice, limits);
|
|
158
161
|
}
|
|
159
|
-
export { assertAttachmentLimits,
|
|
162
|
+
export { assertAttachmentLimits, isTurnMediaRef, maxBytesForMime, requireMediaLimits, resolveMediaLimits, sanitizeCsvText, sanitizeTurnBlobs, sanitizeTurnBlobsForProfile, };
|
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MIME helpers and model
|
|
2
|
+
* MIME helpers and model binding utilities.
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import type { MediaInputKind, ModelId,
|
|
6
|
+
import type { MediaInputKind, ModelBinding, ModelId, ModelProfile, Profile, ThinkingLevel } from '../types.js';
|
|
7
|
+
/** `TurnInput` field a media file rides in. */
|
|
8
|
+
type MediaInputChannel = 'attachments' | 'voice';
|
|
7
9
|
declare function mimeEssence(mime: string): string;
|
|
8
10
|
declare function mimeAllowed(accept: string[], mime: string): boolean;
|
|
9
11
|
declare function mediaKindForMime(mime: string): MediaInputKind | undefined;
|
|
10
|
-
/**
|
|
11
|
-
declare function
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
/** The `accept` list a profile declares for one input channel, if it declares one. */
|
|
13
|
+
declare function profileAccept(profile: Profile, channel: MediaInputChannel): string[] | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Which `TurnInput` channel of a profile accepts this MIME, or `undefined` when
|
|
16
|
+
* the profile accepts it nowhere (or the kernel cannot classify it at all).
|
|
17
|
+
*
|
|
18
|
+
* The one public answer to "does this profile take this file". Hosts route and
|
|
19
|
+
* filter channel ingress with it instead of keeping their own MIME table: the
|
|
20
|
+
* profile's `accept` lists are the whole declaration.
|
|
21
|
+
*/
|
|
22
|
+
declare function mediaChannelForMime(profile: Profile, mime: string): MediaInputChannel | undefined;
|
|
23
|
+
/** Require a host-declared model binding for a profile model id. */
|
|
24
|
+
declare function requireModelBinding(profile: ModelProfile, modelId: ModelId): ModelBinding;
|
|
25
|
+
/** Clamp a requested thinking level to what the model binding accepts. */
|
|
26
|
+
declare function clampThinkingLevel(binding: ModelBinding, level: ThinkingLevel): ThinkingLevel;
|
|
27
|
+
/** Look up a model binding by provider-native API id within a host map. */
|
|
28
|
+
declare function modelEntryByApiId(bindings: Record<string, ModelBinding>, apiId: string): ModelBinding | undefined;
|
|
29
|
+
/** Clamp thinking level using a provider-native API id within a host map. */
|
|
30
|
+
declare function clampThinkingLevelForApiId(bindings: Record<string, ModelBinding>, apiId: string, level: ThinkingLevel): ThinkingLevel;
|
|
31
|
+
export type { MediaInputChannel };
|
|
32
|
+
export { clampThinkingLevel, clampThinkingLevelForApiId, mediaChannelForMime, mediaKindForMime, mimeAllowed, mimeEssence, modelEntryByApiId, profileAccept, requireModelBinding, };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MIME helpers and model
|
|
2
|
+
* MIME helpers and model binding utilities.
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
@@ -22,38 +22,73 @@ function mimeAllowed(accept, mime) {
|
|
|
22
22
|
function mediaKindForMime(mime) {
|
|
23
23
|
return MEDIA_INPUT_KINDS[mimeEssence(mime)];
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throw new TheorumError(`Profile ${profile.id} has no model spec for '${modelId}'`);
|
|
25
|
+
/** The `accept` list a profile declares for one input channel, if it declares one. */
|
|
26
|
+
function profileAccept(profile, channel) {
|
|
27
|
+
if (profile.type === 'speech' || profile.type === 'live' || profile.type === 'host') {
|
|
28
|
+
return undefined;
|
|
30
29
|
}
|
|
31
|
-
|
|
30
|
+
const inputs = profile.inputs;
|
|
31
|
+
return channel === 'voice' ? inputs?.voice?.accept : inputs?.attachments?.accept;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Which `TurnInput` channel of a profile accepts this MIME, or `undefined` when
|
|
35
|
+
* the profile accepts it nowhere (or the kernel cannot classify it at all).
|
|
36
|
+
*
|
|
37
|
+
* The one public answer to "does this profile take this file". Hosts route and
|
|
38
|
+
* filter channel ingress with it instead of keeping their own MIME table: the
|
|
39
|
+
* profile's `accept` lists are the whole declaration.
|
|
40
|
+
*/
|
|
41
|
+
function mediaChannelForMime(profile, mime) {
|
|
42
|
+
if (!mediaKindForMime(mime)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
for (const channel of ['attachments', 'voice']) {
|
|
46
|
+
const accept = profileAccept(profile, channel);
|
|
47
|
+
if (accept && mimeAllowed(accept, mime)) {
|
|
48
|
+
return channel;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Require a host-declared model binding for a profile model id. */
|
|
54
|
+
function requireModelBinding(profile, modelId) {
|
|
55
|
+
const binding = profile.models[modelId];
|
|
56
|
+
if (!binding) {
|
|
57
|
+
throw new TheorumError(`Profile ${profile.id} has no model binding for '${modelId}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
58
|
+
}
|
|
59
|
+
return binding;
|
|
60
|
+
}
|
|
61
|
+
function effortLevels(binding) {
|
|
62
|
+
if (!binding?.efforts) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
return Object.values(binding.efforts);
|
|
66
|
+
}
|
|
67
|
+
function clampLevels(binding, level) {
|
|
68
|
+
const legal = effortLevels(binding);
|
|
69
|
+
if (legal.length === 0) {
|
|
35
70
|
return level;
|
|
36
71
|
}
|
|
37
|
-
if (
|
|
72
|
+
if (legal.includes(level)) {
|
|
38
73
|
return level;
|
|
39
74
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
75
|
+
const fallbackAlias = binding?.defaultEffort;
|
|
76
|
+
const fallback = fallbackAlias ? binding?.efforts?.[fallbackAlias] : undefined;
|
|
77
|
+
if (fallback && legal.includes(fallback)) {
|
|
42
78
|
return fallback;
|
|
43
79
|
}
|
|
44
|
-
|
|
45
|
-
return first ?? level;
|
|
80
|
+
return legal[0] ?? level;
|
|
46
81
|
}
|
|
47
|
-
/** Clamp a requested thinking level to what the model
|
|
48
|
-
function clampThinkingLevel(
|
|
49
|
-
return clampLevels(
|
|
82
|
+
/** Clamp a requested thinking level to what the model binding accepts. */
|
|
83
|
+
function clampThinkingLevel(binding, level) {
|
|
84
|
+
return clampLevels(binding, level);
|
|
50
85
|
}
|
|
51
|
-
/** Look up a model
|
|
52
|
-
function modelEntryByApiId(
|
|
53
|
-
return Object.values(
|
|
86
|
+
/** Look up a model binding by provider-native API id within a host map. */
|
|
87
|
+
function modelEntryByApiId(bindings, apiId) {
|
|
88
|
+
return Object.values(bindings).find((m) => m.apiId === apiId);
|
|
54
89
|
}
|
|
55
|
-
/** Clamp thinking level using a provider-native API id within a host
|
|
56
|
-
function clampThinkingLevelForApiId(
|
|
57
|
-
return clampLevels(modelEntryByApiId(
|
|
90
|
+
/** Clamp thinking level using a provider-native API id within a host map. */
|
|
91
|
+
function clampThinkingLevelForApiId(bindings, apiId, level) {
|
|
92
|
+
return clampLevels(modelEntryByApiId(bindings, apiId), level);
|
|
58
93
|
}
|
|
59
|
-
export { clampThinkingLevel, clampThinkingLevelForApiId, mediaKindForMime, mimeAllowed, mimeEssence, modelEntryByApiId,
|
|
94
|
+
export { clampThinkingLevel, clampThinkingLevelForApiId, mediaChannelForMime, mediaKindForMime, mimeAllowed, mimeEssence, modelEntryByApiId, profileAccept, requireModelBinding, };
|