theorum 0.1.14 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -89
- package/esm/mod.d.ts +21 -11
- package/esm/mod.js +15 -8
- package/esm/src/cli/commands/bench.js +6 -3
- package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
- package/esm/src/cli/commands/fuzz-canary.js +195 -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/profile.js +25 -14
- package/esm/src/cli/commands/run.d.ts +3 -0
- package/esm/src/cli/commands/run.js +22 -30
- package/esm/src/cli/commands/test.d.ts +10 -1
- package/esm/src/cli/commands/test.js +31 -31
- 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 +45 -11
- package/esm/src/cli/matrix/synthesizer.d.ts +7 -9
- package/esm/src/cli/matrix/synthesizer.js +32 -106
- package/esm/src/guardrails/canary-gate.d.ts +20 -0
- package/esm/src/guardrails/canary-gate.js +31 -0
- package/esm/src/guardrails/canary.d.ts +26 -0
- package/esm/src/guardrails/canary.js +139 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +16 -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 +216 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +124 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
- package/esm/src/guardrails/corpus/live-attacks.js +232 -0
- package/esm/src/guardrails/corpus/mod.d.ts +13 -0
- package/esm/src/guardrails/corpus/mod.js +11 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +16 -0
- package/esm/src/guardrails/corpus/secrets.js +16 -0
- package/esm/src/guardrails/corpus/strings.d.ts +27 -0
- package/esm/src/guardrails/corpus/strings.js +33 -0
- package/esm/src/guardrails/corpus/types.d.ts +37 -0
- package/esm/src/guardrails/corpus/types.js +6 -0
- package/esm/src/guardrails/egress.d.ts +9 -0
- package/esm/src/guardrails/egress.js +36 -0
- package/esm/src/guardrails/error.js +6 -4
- package/esm/src/guardrails/injection.js +28 -5
- package/esm/src/guardrails/live-outbound-gate.d.ts +39 -0
- package/esm/src/guardrails/live-outbound-gate.js +180 -0
- package/esm/src/guardrails/mod.d.ts +13 -4
- package/esm/src/guardrails/mod.js +10 -4
- package/esm/src/guardrails/quota.js +1 -1
- package/esm/src/guardrails/sanitize.d.ts +11 -15
- package/esm/src/guardrails/sanitize.js +40 -49
- package/esm/src/guardrails/sensitive.js +2 -1
- package/esm/src/guardrails/testing.d.ts +10 -0
- package/esm/src/guardrails/testing.js +9 -0
- package/esm/src/host/client-turn.d.ts +19 -0
- package/esm/src/host/client-turn.js +32 -0
- package/esm/src/host/mod.d.ts +5 -3
- package/esm/src/host/mod.js +4 -3
- package/esm/src/interface/blocks.d.ts +20 -0
- package/esm/src/interface/blocks.js +180 -0
- package/esm/src/interface/from-profile.d.ts +14 -0
- package/esm/src/interface/from-profile.js +104 -0
- package/esm/src/interface/inputs.d.ts +8 -0
- package/esm/src/interface/inputs.js +127 -0
- package/esm/src/interface/mod.d.ts +10 -0
- package/esm/src/interface/mod.js +9 -0
- package/esm/src/interface/types.d.ts +153 -0
- package/esm/src/interface/types.js +13 -0
- package/esm/src/kernel/engine/delta.d.ts +24 -2
- package/esm/src/kernel/engine/delta.js +519 -29
- package/esm/src/kernel/engine/live-inbound.d.ts +9 -0
- package/esm/src/kernel/engine/live-inbound.js +17 -0
- package/esm/src/kernel/engine/repair.js +1 -1
- package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
- package/esm/src/kernel/engine/runner/gates.js +7 -7
- package/esm/src/kernel/engine/runner/mod.js +13 -13
- package/esm/src/kernel/engine/runner/state.d.ts +7 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
- package/esm/src/kernel/engine/runner/steps.js +171 -37
- package/esm/src/kernel/engine/runner/stream.d.ts +1 -2
- package/esm/src/kernel/engine/runner/stream.js +66 -29
- package/esm/src/kernel/engine/session/mod.d.ts +23 -0
- package/esm/src/kernel/engine/session/mod.js +205 -0
- package/esm/src/kernel/mod.d.ts +9 -10
- package/esm/src/kernel/mod.js +6 -8
- package/esm/src/kernel/registry/attachments.d.ts +4 -2
- package/esm/src/kernel/registry/attachments.js +6 -3
- package/esm/src/kernel/registry/catalog.d.ts +3 -16
- package/esm/src/kernel/registry/catalog.js +5 -81
- package/esm/src/kernel/registry/ingress.d.ts +9 -4
- package/esm/src/kernel/registry/ingress.js +73 -63
- package/esm/src/kernel/registry/profiles.d.ts +35 -12
- package/esm/src/kernel/registry/profiles.js +159 -66
- package/esm/src/kernel/registry/provider-request.js +11 -7
- package/esm/src/kernel/registry/resolve.d.ts +5 -6
- package/esm/src/kernel/registry/resolve.js +100 -70
- package/esm/src/kernel/registry/vault.d.ts +12 -7
- package/esm/src/kernel/registry/vault.js +30 -8
- package/esm/src/kernel/schema.d.ts +145 -0
- package/esm/src/kernel/schema.js +462 -0
- package/esm/src/kernel/stop.d.ts +15 -10
- package/esm/src/kernel/stop.js +5 -5
- package/esm/src/kernel/tools/execute.d.ts +45 -0
- package/esm/src/kernel/tools/execute.js +363 -0
- package/esm/src/kernel/tools/harness.d.ts +8 -0
- package/esm/src/kernel/tools/harness.js +44 -0
- package/esm/src/kernel/tools/invoke.d.ts +10 -0
- package/esm/src/kernel/tools/invoke.js +83 -0
- package/esm/src/kernel/tools/mod.d.ts +11 -0
- package/esm/src/kernel/tools/mod.js +10 -0
- package/esm/src/kernel/tools/project.d.ts +12 -0
- package/esm/src/kernel/tools/project.js +34 -0
- package/esm/src/kernel/tools/registry.d.ts +23 -0
- package/esm/src/kernel/tools/registry.js +71 -0
- package/esm/src/kernel/tools/resolve.d.ts +29 -0
- package/esm/src/kernel/tools/resolve.js +262 -0
- package/esm/src/kernel/tools/schema.d.ts +13 -0
- package/esm/src/kernel/tools/schema.js +159 -0
- package/esm/src/kernel/tools/types.d.ts +216 -0
- package/esm/src/kernel/tools/types.js +9 -0
- package/esm/src/kernel/types.d.ts +338 -193
- package/esm/src/observability/trace-attach.d.ts +6 -4
- package/esm/src/observability/trace-attach.js +39 -25
- package/esm/src/observability/trace-record.d.ts +11 -9
- package/esm/src/observability/trace-record.js +36 -23
- 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 +9 -1
- package/esm/src/observability/trace.js +11 -3
- 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 +14 -14
- package/esm/src/providers/create-provider.js +55 -23
- package/esm/src/providers/google/interactions/framing.d.ts +15 -0
- package/esm/src/providers/google/interactions/framing.js +208 -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 +46 -0
- package/esm/src/providers/google/live/framing.js +515 -0
- package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
- package/esm/src/providers/google/live/openapi-schema.js +45 -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 +36 -0
- package/esm/src/providers/google/live/stream.js +201 -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/chat.d.ts +87 -0
- package/esm/src/providers/{openrouter.js → openrouter/chat.js} +70 -229
- package/esm/src/providers/openrouter/image.d.ts +34 -0
- package/esm/src/providers/openrouter/image.js +286 -0
- package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
- package/esm/src/providers/openrouter/openai/chat-payload.js +60 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +47 -0
- package/esm/src/providers/openrouter/openai/compat.js +194 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +84 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.js +95 -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 -201
- 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 -176
- 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
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime vocabulary and field catalog for THEORUM profile types.
|
|
3
|
+
*
|
|
4
|
+
* Closed unions live here as `as const` arrays; TypeScript types are derived
|
|
5
|
+
* from those arrays. Host UIs and docs import this module (no Deno APIs) so
|
|
6
|
+
* dropdowns and hover tips stay in lockstep with the kernel.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { GOOGLE_SPEECH_VOICES } from '../presets/google/speech-voices.js';
|
|
11
|
+
/** Primary profile archetype. Discriminated union key for `ProfileDefinition` and `Profile`. */
|
|
12
|
+
export const PROFILE_TYPES = ['text', 'image', 'speech', 'live'];
|
|
13
|
+
/** Model reasoning effort level normalized across provider adapters. */
|
|
14
|
+
export const THINKING_LEVELS = [
|
|
15
|
+
'none',
|
|
16
|
+
'minimal',
|
|
17
|
+
'low',
|
|
18
|
+
'medium',
|
|
19
|
+
'high',
|
|
20
|
+
'xhigh',
|
|
21
|
+
'max',
|
|
22
|
+
];
|
|
23
|
+
/** Wire protocol for a profile. */
|
|
24
|
+
export const PROTOCOLS = ['geminiInteractions', 'geminiLive', 'openAi'];
|
|
25
|
+
/** Transport provider for a profile. */
|
|
26
|
+
export const PROVIDERS = ['google', 'openrouter', 'local'];
|
|
27
|
+
/**
|
|
28
|
+
* Legal `createProvider` pairs. Keep this table in lockstep with the factory.
|
|
29
|
+
* Keys are protocols; values are the providers that protocol may bind.
|
|
30
|
+
*/
|
|
31
|
+
export const PROTOCOL_PROVIDERS = {
|
|
32
|
+
geminiInteractions: ['google'],
|
|
33
|
+
geminiLive: ['google'],
|
|
34
|
+
openAi: ['openrouter', 'local'],
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Legal wire protocols for each profile archetype.
|
|
38
|
+
* 'live' profiles require 'geminiLive'; turn-based archetypes require turn protocols.
|
|
39
|
+
*/
|
|
40
|
+
export const PROFILE_TYPE_PROTOCOLS = {
|
|
41
|
+
text: ['geminiInteractions', 'openAi'],
|
|
42
|
+
image: ['geminiInteractions', 'openAi'],
|
|
43
|
+
speech: ['geminiInteractions', 'openAi'],
|
|
44
|
+
live: ['geminiLive'],
|
|
45
|
+
};
|
|
46
|
+
/** Protocols allowed for a profile archetype (`type`). */
|
|
47
|
+
export function protocolsForProfileType(type) {
|
|
48
|
+
return PROFILE_TYPE_PROTOCOLS[type];
|
|
49
|
+
}
|
|
50
|
+
/** True when the protocol is valid for the given profile archetype. */
|
|
51
|
+
export function isValidProfileProtocol(type, protocol) {
|
|
52
|
+
return PROFILE_TYPE_PROTOCOLS[type].includes(protocol);
|
|
53
|
+
}
|
|
54
|
+
/** Named vault key slots for host-supplied credentials (provider-neutral). */
|
|
55
|
+
export const KEY_SLOTS = ['slotA', 'slotB', 'slotC', 'paid'];
|
|
56
|
+
/** Key slots that may overflow to `paid` after quota backoff. */
|
|
57
|
+
export const OVERFLOW_KEY_SLOTS = ['slotA', 'slotB', 'slotC'];
|
|
58
|
+
/** Profile-level control a caller may toggle at turn time. */
|
|
59
|
+
export const CONTROL_IDS = ['thinking'];
|
|
60
|
+
/** Normalized multimodal part category. */
|
|
61
|
+
export const MEDIA_INPUT_KIND_VALUES = ['image', 'audio', 'video', 'document'];
|
|
62
|
+
/** Provider thinking-summary behavior. */
|
|
63
|
+
export const SUMMARY_MODES = ['auto', 'none'];
|
|
64
|
+
/** Stream delivery mode. */
|
|
65
|
+
export const STREAM_MODES = ['sse', 'buffered'];
|
|
66
|
+
/** Audio container for speech generation output. */
|
|
67
|
+
export const SPEECH_AUDIO_FORMATS = ['pcm', 'mp3'];
|
|
68
|
+
/** Live session activity handling (barge-in behavior). */
|
|
69
|
+
export const LIVE_ACTIVITY_HANDLINGS = ['START_OF_ACTIVITY_INTERRUPTS', 'NO_INTERRUPTION'];
|
|
70
|
+
/** Live session voice activity detection sensitivity. */
|
|
71
|
+
export const LIVE_SPEECH_SENSITIVITIES = [
|
|
72
|
+
'START_SENSITIVITY_LOW',
|
|
73
|
+
'START_SENSITIVITY_HIGH',
|
|
74
|
+
'END_SENSITIVITY_LOW',
|
|
75
|
+
'END_SENSITIVITY_HIGH',
|
|
76
|
+
];
|
|
77
|
+
/** Live session context window compression mode. */
|
|
78
|
+
export const LIVE_CONTEXT_COMPRESSIONS = ['slidingWindow', 'none'];
|
|
79
|
+
/** Structured-output enforcement mode. */
|
|
80
|
+
export const SCHEMA_ENFORCEMENTS = ['responseFormat', 'prompt'];
|
|
81
|
+
/** Compaction threshold meter. */
|
|
82
|
+
export const COMPACTION_METERS = ['history', 'input'];
|
|
83
|
+
/** When compaction runs relative to the primary turn. */
|
|
84
|
+
export const COMPACTION_TIMINGS = ['before', 'after'];
|
|
85
|
+
/** Egress block handling. */
|
|
86
|
+
export const EGRESS_ON_BLOCK = ['reject_to_agent', 'refuse_to_user'];
|
|
87
|
+
/** Why a turn ended (provider-neutral). */
|
|
88
|
+
export const TURN_STOP_KINDS = [
|
|
89
|
+
'completed',
|
|
90
|
+
'length',
|
|
91
|
+
'tool',
|
|
92
|
+
'filtered',
|
|
93
|
+
'provider_error',
|
|
94
|
+
'cancelled',
|
|
95
|
+
'stream_incomplete',
|
|
96
|
+
'interrupted',
|
|
97
|
+
/** Live: model finished generating audio/text for this utterance; turn may still be open. */
|
|
98
|
+
'generation_complete',
|
|
99
|
+
];
|
|
100
|
+
/** Per-tool visibility tier — enforced by the kernel at resolve time. */
|
|
101
|
+
export const TOOL_LOAD_TIERS = ['T0', 'T1', 'T2'];
|
|
102
|
+
/** Registered tool discriminant (`registerTool`). */
|
|
103
|
+
export const TOOL_TYPES = ['builtin', 'function'];
|
|
104
|
+
/** Semantic access level — host policy / UI; not enforced by execute. */
|
|
105
|
+
export const TOOL_ACCESS = ['read-only', 'read-write', 'destructive'];
|
|
106
|
+
/** Execution authorization tier for registered tools. */
|
|
107
|
+
export const TOOL_PERMISSION = ['auto', 'session_consent', 'always_confirm'];
|
|
108
|
+
/** MIME essence → normalized media part category (shared ingress map). */
|
|
109
|
+
export const MEDIA_INPUT_KINDS = {
|
|
110
|
+
'image/png': 'image',
|
|
111
|
+
'image/jpeg': 'image',
|
|
112
|
+
'image/jpg': 'image',
|
|
113
|
+
'image/webp': 'image',
|
|
114
|
+
'image/heic': 'image',
|
|
115
|
+
'image/heif': 'image',
|
|
116
|
+
'audio/wav': 'audio',
|
|
117
|
+
'audio/x-wav': 'audio',
|
|
118
|
+
'audio/mpeg': 'audio',
|
|
119
|
+
'audio/mp3': 'audio',
|
|
120
|
+
'audio/aiff': 'audio',
|
|
121
|
+
'audio/aac': 'audio',
|
|
122
|
+
'audio/ogg': 'audio',
|
|
123
|
+
'audio/flac': 'audio',
|
|
124
|
+
'audio/webm': 'audio',
|
|
125
|
+
'audio/mp4': 'audio',
|
|
126
|
+
'audio/pcm': 'audio',
|
|
127
|
+
'video/mp4': 'video',
|
|
128
|
+
'video/mpeg': 'video',
|
|
129
|
+
'video/quicktime': 'video',
|
|
130
|
+
'video/x-msvideo': 'video',
|
|
131
|
+
'video/x-flv': 'video',
|
|
132
|
+
'video/mpg': 'video',
|
|
133
|
+
'video/webm': 'video',
|
|
134
|
+
'video/wmv': 'video',
|
|
135
|
+
'video/x-ms-wmv': 'video',
|
|
136
|
+
'video/3gpp': 'video',
|
|
137
|
+
'application/pdf': 'document',
|
|
138
|
+
'text/plain': 'document',
|
|
139
|
+
'text/csv': 'document',
|
|
140
|
+
'text/markdown': 'document',
|
|
141
|
+
'text/html': 'document',
|
|
142
|
+
'application/json': 'document',
|
|
143
|
+
};
|
|
144
|
+
/** Type-prefix wildcards accepted by `mimeAllowed`. */
|
|
145
|
+
export const MEDIA_WILDCARDS = ['image/*', 'audio/*', 'video/*'];
|
|
146
|
+
function mimesOf(kind) {
|
|
147
|
+
return Object.keys(MEDIA_INPUT_KINDS).filter((mime) => MEDIA_INPUT_KINDS[mime] === kind);
|
|
148
|
+
}
|
|
149
|
+
/** Attachment `accept` values the kernel can classify (wildcards + known types). */
|
|
150
|
+
export const ATTACHMENT_ACCEPT_MIMES = [
|
|
151
|
+
'image/*',
|
|
152
|
+
'video/*',
|
|
153
|
+
...mimesOf('image'),
|
|
154
|
+
...mimesOf('video'),
|
|
155
|
+
...mimesOf('document'),
|
|
156
|
+
];
|
|
157
|
+
/** Voice `accept` values the kernel can classify (wildcard + known audio types). */
|
|
158
|
+
export const VOICE_ACCEPT_MIMES = ['audio/*', ...mimesOf('audio')];
|
|
159
|
+
/** Providers allowed for a protocol. */
|
|
160
|
+
export function providersFor(protocol) {
|
|
161
|
+
return PROTOCOL_PROVIDERS[protocol];
|
|
162
|
+
}
|
|
163
|
+
/** Protocols allowed for a provider. */
|
|
164
|
+
export function protocolsFor(provider) {
|
|
165
|
+
const found = [];
|
|
166
|
+
for (const protocol of PROTOCOLS) {
|
|
167
|
+
if (PROTOCOL_PROVIDERS[protocol].includes(provider)) {
|
|
168
|
+
found.push(protocol);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return found;
|
|
172
|
+
}
|
|
173
|
+
/** True when `createProvider` will accept this pair. */
|
|
174
|
+
export function isValidPair(protocol, provider) {
|
|
175
|
+
return PROTOCOL_PROVIDERS[protocol].includes(provider);
|
|
176
|
+
}
|
|
177
|
+
/** When protocol changes, snap provider to a valid partner. */
|
|
178
|
+
export function coerceProvider(protocol, provider) {
|
|
179
|
+
const allowed = providersFor(protocol);
|
|
180
|
+
const [first] = allowed;
|
|
181
|
+
return allowed.includes(provider) ? provider : first;
|
|
182
|
+
}
|
|
183
|
+
/** When provider changes, snap protocol to a valid partner. */
|
|
184
|
+
export function coerceProtocol(protocol, provider) {
|
|
185
|
+
const allowed = protocolsFor(provider);
|
|
186
|
+
const [first] = allowed;
|
|
187
|
+
return allowed.includes(protocol) ? protocol : first;
|
|
188
|
+
}
|
|
189
|
+
function unionType(values) {
|
|
190
|
+
return values.map((value) => `'${value}'`).join(' | ');
|
|
191
|
+
}
|
|
192
|
+
function field(type, doc, options, optionDescriptionsOrNote, optionNote) {
|
|
193
|
+
if (options) {
|
|
194
|
+
if (typeof optionDescriptionsOrNote === 'object') {
|
|
195
|
+
return optionNote
|
|
196
|
+
? { type, doc, options, optionDescriptions: optionDescriptionsOrNote, optionNote }
|
|
197
|
+
: { type, doc, options, optionDescriptions: optionDescriptionsOrNote };
|
|
198
|
+
}
|
|
199
|
+
if (typeof optionDescriptionsOrNote === 'string') {
|
|
200
|
+
return { type, doc, options, optionNote: optionDescriptionsOrNote };
|
|
201
|
+
}
|
|
202
|
+
return { type, doc, options };
|
|
203
|
+
}
|
|
204
|
+
return { type, doc };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Parents whose next key is a host-owned map key (model id, slot name, …).
|
|
208
|
+
* The annotator substitutes `*` so `model.config.flash.apiId` → `model.config.*.apiId`.
|
|
209
|
+
*/
|
|
210
|
+
export const DYNAMIC_FIELD_PARENTS = new Set([
|
|
211
|
+
'model.config',
|
|
212
|
+
'model.select',
|
|
213
|
+
'model.thinking',
|
|
214
|
+
'identity.systemByRole',
|
|
215
|
+
'inputs.slots',
|
|
216
|
+
'inputs.limitsByMime',
|
|
217
|
+
'outputs.validation.fields',
|
|
218
|
+
]);
|
|
219
|
+
/** Resolve a key stack from authored source into a catalog path. */
|
|
220
|
+
export function catalogPathFor(keys) {
|
|
221
|
+
const resolved = [];
|
|
222
|
+
for (const key of keys) {
|
|
223
|
+
const prefix = resolved.join('.');
|
|
224
|
+
if (DYNAMIC_FIELD_PARENTS.has(prefix)) {
|
|
225
|
+
resolved.push('*');
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
resolved.push(key);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return resolved.join('.');
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Authoring-surface catalog for `Profile` / `defineProfile`.
|
|
235
|
+
* Hover UIs look up dotted paths. Adding a profile field? Add it here.
|
|
236
|
+
*/
|
|
237
|
+
export const PROFILE_FIELDS = {
|
|
238
|
+
id: field('string', 'Host-owned profile identifier.'),
|
|
239
|
+
type: field("'text' | 'image' | 'speech' | 'live'", 'Required profile archetype.'),
|
|
240
|
+
identity: field('{ handle, system?, systemByRole? }', 'Display handle and system instruction the model receives each turn.'),
|
|
241
|
+
'identity.handle': field('string', 'Public handle for this agent.'),
|
|
242
|
+
'identity.system': field('string', 'System instruction merged into every turn.'),
|
|
243
|
+
'identity.systemByRole': field('Record<string, string>', 'Optional system instruction keyed by turn role.'),
|
|
244
|
+
'identity.systemByRole.*': field('string', 'System instruction merged when this turn role is active.'),
|
|
245
|
+
model: field('ProfileModelSpec', 'Protocol, provider, allowlist, wire config, and step bounds.'),
|
|
246
|
+
'model.protocol': field(unionType(PROTOCOLS), 'Wire protocol. Pairs with provider via createProvider.', PROTOCOLS, {
|
|
247
|
+
geminiInteractions: 'Google Gemini Interactions wire protocol (Gemini 2.5 / 3+).',
|
|
248
|
+
openAi: 'OpenAI-compatible chat completions and streaming protocol.',
|
|
249
|
+
}),
|
|
250
|
+
'model.provider': field(unionType(PROVIDERS), 'Transport. Must form a legal pair with protocol.', PROVIDERS, {
|
|
251
|
+
google: 'Direct Google Gemini API transport.',
|
|
252
|
+
openrouter: 'OpenRouter multi-provider API proxy.',
|
|
253
|
+
local: 'Local OpenAI-compatible server (Ollama, llama.cpp, vLLM).',
|
|
254
|
+
}),
|
|
255
|
+
'model.allow': field('ModelId[]', 'Ids this profile may select. Each id must exist in config.'),
|
|
256
|
+
'model.config': field('Record<ModelId, ModelSpec>', 'Host-owned wire config keyed by the same ids used in allow / select.'),
|
|
257
|
+
'model.config.*': field('ModelSpec', 'Host-owned wire config for this model id (apiId, thinking, token limits, compaction).'),
|
|
258
|
+
'model.config.*.apiId': field('string', 'Provider wire model id.'),
|
|
259
|
+
'model.config.*.thinking': field('{ on, off }', 'Thinking levels used when a boolean thinking control is on or off.'),
|
|
260
|
+
'model.config.*.thinking.on': field(unionType(THINKING_LEVELS), 'Level when thinking is on.', THINKING_LEVELS, {
|
|
261
|
+
none: 'Disable reasoning tokens completely.',
|
|
262
|
+
minimal: 'Minimal reasoning tokens for fastest response.',
|
|
263
|
+
low: 'Low reasoning budget for basic structured tasks.',
|
|
264
|
+
medium: 'Balanced reasoning for multi-step agent actions.',
|
|
265
|
+
high: 'Deep reasoning for complex planning and code.',
|
|
266
|
+
xhigh: 'Extended reasoning budget for hard problems.',
|
|
267
|
+
max: 'Maximum reasoning tokens supported by model.',
|
|
268
|
+
}),
|
|
269
|
+
'model.config.*.thinking.off': field(unionType(THINKING_LEVELS), 'Level when thinking is off.', THINKING_LEVELS, {
|
|
270
|
+
none: 'Disable reasoning tokens completely.',
|
|
271
|
+
minimal: 'Minimal reasoning tokens for fastest response.',
|
|
272
|
+
low: 'Low reasoning budget for basic structured tasks.',
|
|
273
|
+
medium: 'Balanced reasoning for multi-step agent actions.',
|
|
274
|
+
high: 'Deep reasoning for complex planning and code.',
|
|
275
|
+
xhigh: 'Extended reasoning budget for hard problems.',
|
|
276
|
+
max: 'Maximum reasoning tokens supported by model.',
|
|
277
|
+
}),
|
|
278
|
+
'model.config.*.thinkingLevels': field('ThinkingLevel[]', 'Levels this model accepts. Illegal values are clamped.', THINKING_LEVELS, {
|
|
279
|
+
none: 'Disable reasoning tokens completely.',
|
|
280
|
+
minimal: 'Minimal reasoning tokens for fastest response.',
|
|
281
|
+
low: 'Low reasoning budget for basic structured tasks.',
|
|
282
|
+
medium: 'Balanced reasoning for multi-step agent actions.',
|
|
283
|
+
high: 'Deep reasoning for complex planning and code.',
|
|
284
|
+
xhigh: 'Extended reasoning budget for hard problems.',
|
|
285
|
+
max: 'Maximum reasoning tokens supported by model.',
|
|
286
|
+
}),
|
|
287
|
+
'model.config.*.summaries': field('{ on, off }', 'Summary behavior for the thinking control.'),
|
|
288
|
+
'model.config.*.summaries.on': field(unionType(SUMMARY_MODES), 'Summaries when thinking is on.', SUMMARY_MODES, {
|
|
289
|
+
auto: 'Emit thinking summaries when available.',
|
|
290
|
+
none: 'Suppress thinking summaries from the stream.',
|
|
291
|
+
}),
|
|
292
|
+
'model.config.*.summaries.off': field(unionType(SUMMARY_MODES), 'Summaries when thinking is off.', SUMMARY_MODES, {
|
|
293
|
+
auto: 'Emit thinking summaries when available.',
|
|
294
|
+
none: 'Suppress thinking summaries from the stream.',
|
|
295
|
+
}),
|
|
296
|
+
'model.config.*.maxOutputTokens': field('number', 'Maximum tokens the model may emit.'),
|
|
297
|
+
'model.config.*.temperature': field('number', 'Sampling temperature.'),
|
|
298
|
+
'model.config.*.builtInTools': field('BuiltinToolId[]', 'Provider-native builtins enabled whenever this model is selected.'),
|
|
299
|
+
'model.config.*.key': field(unionType(KEY_SLOTS), 'Optional vault slot for this model. Overrides profile.model.key.', KEY_SLOTS),
|
|
300
|
+
'model.config.*.compaction': field('CompactionSpec', 'Optional compaction policy for this model.'),
|
|
301
|
+
'model.config.*.compaction.maxTokens': field('number', 'Token budget compared by the trigger (compactAt * maxTokens).'),
|
|
302
|
+
'model.config.*.compaction.compactAt': field('number', 'Fraction of maxTokens at which compaction fires. Must be in (0, 1).'),
|
|
303
|
+
'model.config.*.compaction.previousExchanges': field('number', '≥ 1 = exchange count, (0, 1) = fraction of maxTokens, 0 = compact all.'),
|
|
304
|
+
'model.config.*.compaction.profile': field('ProfileId', 'Compaction agent profile id. Must be registered before the owning profile.'),
|
|
305
|
+
'model.config.*.compaction.timing': field(unionType(COMPACTION_TIMINGS), 'When compaction runs relative to the primary turn.', COMPACTION_TIMINGS, {
|
|
306
|
+
before: 'Compact synchronously before running the turn request.',
|
|
307
|
+
after: 'Signal on done for host async background compaction.',
|
|
308
|
+
}),
|
|
309
|
+
'model.config.*.compaction.meter': field(unionType(COMPACTION_METERS), 'What the threshold meters. Defaults to history.', COMPACTION_METERS, {
|
|
310
|
+
history: 'Meters token count across prior conversation turns.',
|
|
311
|
+
input: 'Meters full turn input token count (system + history + attachments).',
|
|
312
|
+
}),
|
|
313
|
+
'model.select': field('Record<string, ModelId>', 'Named aliases (fast / smart) → allowlisted ids.'),
|
|
314
|
+
'model.select.*': field('ModelId', 'Allowlisted model id for this select key.'),
|
|
315
|
+
'model.thinking': field('ThinkingLevel | Record<string, ThinkingLevel>', 'Default thinking pin, or a map keyed by select labels.', THINKING_LEVELS, {
|
|
316
|
+
none: 'Disable reasoning tokens completely.',
|
|
317
|
+
minimal: 'Minimal reasoning tokens for fastest response.',
|
|
318
|
+
low: 'Low reasoning budget for basic structured tasks.',
|
|
319
|
+
medium: 'Balanced reasoning for multi-step agent actions.',
|
|
320
|
+
high: 'Deep reasoning for complex planning and code.',
|
|
321
|
+
xhigh: 'Extended reasoning budget for hard problems.',
|
|
322
|
+
max: 'Maximum reasoning tokens supported by model.',
|
|
323
|
+
}),
|
|
324
|
+
'model.thinking.*': field(unionType(THINKING_LEVELS), 'Thinking pin for this select key.', THINKING_LEVELS, {
|
|
325
|
+
none: 'Disable reasoning tokens completely.',
|
|
326
|
+
minimal: 'Minimal reasoning tokens for fastest response.',
|
|
327
|
+
low: 'Low reasoning budget for basic structured tasks.',
|
|
328
|
+
medium: 'Balanced reasoning for multi-step agent actions.',
|
|
329
|
+
high: 'Deep reasoning for complex planning and code.',
|
|
330
|
+
xhigh: 'Extended reasoning budget for hard problems.',
|
|
331
|
+
max: 'Maximum reasoning tokens supported by model.',
|
|
332
|
+
}),
|
|
333
|
+
'model.controls': field('ControlId[]', 'Turn-time toggles this profile exposes.', CONTROL_IDS),
|
|
334
|
+
'model.maxSteps': field('number', 'Tool-loop ceiling. <=0 unbounded, 1 one-shot, >1 ceiling. Omit → unbounded.'),
|
|
335
|
+
'model.key': field(unionType(OVERFLOW_KEY_SLOTS), 'Vault key slot (slotA/B/C). Paid is overflow-only via model.config.*.key or forcePaidKey builtins.', OVERFLOW_KEY_SLOTS),
|
|
336
|
+
tools: field('{ allow: ToolId[]; t1Policy?; t2Loader? }', 'Custom tools (allow), optional T1 policy, optional T2 loader function id. Builtins belong on model.config.*.builtInTools.'),
|
|
337
|
+
'tools.allow': field('ToolId[]', 'Custom tools the agent may call. Builtins are declared per model, not here.'),
|
|
338
|
+
'tools.t1Policy': field('(ctx) => ToolId[] | Promise<ToolId[]>', 'Optional T1 policy — which eligible loadTier:T1 tools to wire at turn start.'),
|
|
339
|
+
'tools.t2Loader': field('ToolId', 'Optional function tool id for T2 promotion. Must be in tools.allow; handler returns { loaded: string[] }.'),
|
|
340
|
+
inputs: field('ProfileInputsSpec', 'Text, attachment, voice, slot, and size rules.'),
|
|
341
|
+
'inputs.text': field('boolean', 'Whether the profile accepts text on a turn. False rejects text.'),
|
|
342
|
+
'inputs.attachments': field('{ accept: string[] }', 'File upload allowlist.'),
|
|
343
|
+
'inputs.attachments.accept': field('string[]', 'MIME allowlist for uploaded files. Type-prefix wildcards (image/*, …) are allowed.', ATTACHMENT_ACCEPT_MIMES, 'Kernel-known types (plus wildcards). Hosts may list any MIME; unknown types are rejected at ingress.'),
|
|
344
|
+
'inputs.voice': field('{ accept: string[] }', 'Voice ingress block — not a bare string. Use accept for audio MIME allowlist.'),
|
|
345
|
+
'inputs.voice.accept': field('string[]', 'MIME allowlist for voice blobs. audio/* wildcards are allowed.', VOICE_ACCEPT_MIMES, 'Kernel-known audio types (plus audio/*).'),
|
|
346
|
+
'inputs.maxFiles': field('number', 'Max files per turn. Required when attachments or voice is set.'),
|
|
347
|
+
'inputs.maxBytes': field('number', 'Max bytes per file. Required when attachments or voice is set.'),
|
|
348
|
+
'inputs.maxTurnBytes': field('number', 'Max total bytes per turn. Required when attachments or voice is set.'),
|
|
349
|
+
'inputs.limitsByMime': field('Record<string, number>', 'Optional per-MIME byte caps.'),
|
|
350
|
+
'inputs.limitsByMime.*': field('number', 'Maximum byte limit for files of this MIME type.'),
|
|
351
|
+
'inputs.slots': field('Record<string, string[]>', 'Optional turn-time selectors (e.g. language: ["html", "tsx"]).'),
|
|
352
|
+
'inputs.slots.*': field('string[]', 'Allowed choices for this turn selector.'),
|
|
353
|
+
outputs: field('ProfileOutputsSpec', 'Structured, validation, and streaming output policy.'),
|
|
354
|
+
'outputs.structured': field('StructuredSchemaId | StructuredBySlot | null', 'Registered schema id, slot-mapped ids, or null for free text.'),
|
|
355
|
+
image: field('ProfileImageSpec', 'Pins for an image-role profile. Model id is on model.'),
|
|
356
|
+
'image.aspectRatio': field('string', 'Optional output aspect ratio. Omitted → provider default.'),
|
|
357
|
+
'image.size': field('string', 'Optional output size / resolution. Omitted → provider default.'),
|
|
358
|
+
'image.mimeType': field('string', 'Output MIME for generated images.'),
|
|
359
|
+
'image.maxInputImages': field('number', 'Cap on reference images in one turn.'),
|
|
360
|
+
'image.includeText': field('boolean', 'When true, request interleaved assistant text alongside generated images.'),
|
|
361
|
+
speech: field('ProfileSpeechSpec', 'TTS pins. Model id is on model.'),
|
|
362
|
+
'speech.voice': field('string', 'TTS voice id. Kernel accepts any string; Google preset narrows to named voices.'),
|
|
363
|
+
'speech.format': field(unionType(SPEECH_AUDIO_FORMATS), 'pcm (default) → WAV on both transports. mp3 requires protocol openAi.', SPEECH_AUDIO_FORMATS, {
|
|
364
|
+
pcm: 'Raw 24kHz 16-bit PCM audio (→ WAV container). Supported by Google and OpenAI.',
|
|
365
|
+
mp3: 'MP3 encoded stream. Requires openAi protocol.',
|
|
366
|
+
}),
|
|
367
|
+
live: field('ProfileLiveSpec', 'Bidirectional live audio/video streaming session pins.'),
|
|
368
|
+
'live.voice': field('string', 'TTS voice name for live audio output (e.g. Puck, Aoede, Charon).', GOOGLE_SPEECH_VOICES, 'Google preset vocabulary; kernel accepts any string.'),
|
|
369
|
+
'live.vad': field('LiveVadSpec', 'Voice activity detection, barge-in, and endpointing sensitivity.'),
|
|
370
|
+
'live.vad.activityHandling': field(unionType(LIVE_ACTIVITY_HANDLINGS), 'Barge-in handling when user speaks.', LIVE_ACTIVITY_HANDLINGS),
|
|
371
|
+
'live.vad.startSensitivity': field(unionType(LIVE_SPEECH_SENSITIVITIES), 'Sensitivity for detecting start of speech.', LIVE_SPEECH_SENSITIVITIES),
|
|
372
|
+
'live.vad.endSensitivity': field(unionType(LIVE_SPEECH_SENSITIVITIES), 'Sensitivity for detecting end of speech.', LIVE_SPEECH_SENSITIVITIES),
|
|
373
|
+
'live.vad.prefixPaddingMs': field('number', 'Speech prefix buffer duration in ms.'),
|
|
374
|
+
'live.vad.silenceDurationMs': field('number', 'Required silence before committing end-of-speech in ms.'),
|
|
375
|
+
'live.sessionResumption': field('boolean', 'Enable session resumption handles across WebSocket reconnects.'),
|
|
376
|
+
'live.contextCompression': field(unionType(LIVE_CONTEXT_COMPRESSIONS), 'Context window compression mechanism.', LIVE_CONTEXT_COMPRESSIONS),
|
|
377
|
+
'live.proactiveAudio': field('boolean', 'Allow model to reject responding or stay silent if unprompted.'),
|
|
378
|
+
'live.transcription': field('LiveTranscriptionSpec', 'Enable real-time input/output audio transcriptions.'),
|
|
379
|
+
'live.transcription.input': field('boolean', 'Transcribe user input speech.'),
|
|
380
|
+
'live.transcription.output': field('boolean', 'Transcribe model output speech.'),
|
|
381
|
+
'outputs.validation': field('ProfileValidationSpec', 'Host domain validators and repair policy.'),
|
|
382
|
+
'outputs.validation.fields': field('Record<string, ProfileValidator>', 'Validators keyed by dotted paths into structured output (e.g. diagram.mermaid).'),
|
|
383
|
+
'outputs.validation.fields.*': field('(source: unknown) => { isValid: boolean; error?: string }', 'Host-owned validator function for this structured output field.'),
|
|
384
|
+
'outputs.validation.maxRetries': field('number', 'Repair-turn ceiling after a validator reject.'),
|
|
385
|
+
'outputs.validation.repairGuidance': field('string', 'Instruction appended on a validation repair turn.'),
|
|
386
|
+
'outputs.streaming': field('ProfileStreamingSpec', 'How the turn emits live events.'),
|
|
387
|
+
'outputs.streaming.mode': field(unionType(STREAM_MODES), 'sse or buffered. Omit → THEORUM SSE default (ResolvedGeneration.stream = true).', STREAM_MODES, {
|
|
388
|
+
sse: 'Server-Sent Events emitting live incremental TurnEvents (THEORUM default when mode omitted).',
|
|
389
|
+
buffered: 'Buffers response into a single completed turn event.',
|
|
390
|
+
}),
|
|
391
|
+
'outputs.streaming.streamThoughts': field('boolean', 'Emit model thinking on the turn stream.'),
|
|
392
|
+
turnResumption: field('ProfileTurnResumptionSpec', 'Continue after a non-user stop.'),
|
|
393
|
+
'turnResumption.allowContinue': field('TurnStopKind[]', 'Kinds eligible for a Continue / continueFrom turn.', TURN_STOP_KINDS, {
|
|
394
|
+
length: 'Model hit maximum output token ceiling.',
|
|
395
|
+
stream_incomplete: 'Network connection or stream dropped prematurely.',
|
|
396
|
+
provider_error: 'Upstream provider returned an error code or timeout.',
|
|
397
|
+
tool: 'Turn paused at tool execution boundary.',
|
|
398
|
+
filtered: 'Content safety filter intercepted output.',
|
|
399
|
+
cancelled: 'Turn aborted via AbortSignal.',
|
|
400
|
+
completed: 'Turn finished normally.',
|
|
401
|
+
interrupted: 'Live barge-in interrupted the in-flight response.',
|
|
402
|
+
generation_complete: 'Live model finished generating this utterance; turn may still be open.',
|
|
403
|
+
}),
|
|
404
|
+
'turnResumption.autoContinue': field('TurnStopKind[]', 'Kinds the host may auto-continue once without a CTA.', TURN_STOP_KINDS, {
|
|
405
|
+
length: 'Model hit maximum output token ceiling.',
|
|
406
|
+
stream_incomplete: 'Network connection or stream dropped prematurely.',
|
|
407
|
+
provider_error: 'Upstream provider returned an error code or timeout.',
|
|
408
|
+
tool: 'Turn paused at tool execution boundary.',
|
|
409
|
+
filtered: 'Content safety filter intercepted output.',
|
|
410
|
+
cancelled: 'Turn aborted via AbortSignal.',
|
|
411
|
+
completed: 'Turn finished normally.',
|
|
412
|
+
interrupted: 'Live barge-in interrupted the in-flight response.',
|
|
413
|
+
generation_complete: 'Live model finished generating this utterance; turn may still be open.',
|
|
414
|
+
}),
|
|
415
|
+
guardrails: field('ProfileGuardrailsSpec', 'Quota, canary, sanitize, redact, and egress switches.'),
|
|
416
|
+
'guardrails.quota': field('{ perDay: number }', 'Host HTTP helper — not enforced inside runTurn.'),
|
|
417
|
+
'guardrails.quota.perDay': field('number', 'Daily turn cap used by host quota middleware.'),
|
|
418
|
+
'guardrails.canary': field('boolean', 'Enable per-turn canary token bound to system prompt.'),
|
|
419
|
+
'guardrails.sanitizeInput': field('boolean', 'Strip inbound injection spans.'),
|
|
420
|
+
'guardrails.redactSensitive': field('boolean', 'Redact sensitive spans.'),
|
|
421
|
+
'guardrails.egress': field('ProfileEgressSpec', 'Host check before user-visible text is released.'),
|
|
422
|
+
'guardrails.egress.enforce': field('EgressEnforcer', 'Host function: (context) => { blocked, text, … }.'),
|
|
423
|
+
'guardrails.egress.onBlock': field(unionType(EGRESS_ON_BLOCK), 'reject_to_agent retries; refuse_to_user stops the turn.', EGRESS_ON_BLOCK, {
|
|
424
|
+
reject_to_agent: 'Feeds rejection error back to model for automatic repair turn.',
|
|
425
|
+
refuse_to_user: 'Halts turn immediately and returns refusal to user.',
|
|
426
|
+
}),
|
|
427
|
+
'guardrails.egress.maxRetries': field('number', 'Repair-turn ceiling after an egress block.'),
|
|
428
|
+
'guardrails.egress.repairGuidance': field('string', 'Instruction appended on an egress repair turn.'),
|
|
429
|
+
};
|
|
430
|
+
/** Adjacent tool catalog fields that appear next to profile examples. */
|
|
431
|
+
export const EXTRA_FIELDS = {
|
|
432
|
+
type: field(unionType(TOOL_TYPES), 'Discriminator: builtin (provider-native) or function (host handler).', TOOL_TYPES, {
|
|
433
|
+
builtin: 'Provider-native capability; wire maps to the provider adapter.',
|
|
434
|
+
function: 'Host-owned tool with Zod input/output and a handler. Profile tools.t2Loader may promote T2 tools when output includes { loaded }.',
|
|
435
|
+
}),
|
|
436
|
+
name: field('string', 'Wire tool id — custom: tools.allow; provider builtin: model.config.*.builtInTools. Visibility via loadTier (T0/T1/T2).'),
|
|
437
|
+
description: field('string', 'Model-facing description included in function declarations.'),
|
|
438
|
+
input: field('ZodSchema', 'Zod input schema for function tools; converted to JSON Schema at registration.'),
|
|
439
|
+
output: field('ZodSchema', 'Zod output schema for function tools; validates handler results.'),
|
|
440
|
+
handler: field('ToolHandler', 'Host function or async generator run on model tool calls and invokeTool resumes.'),
|
|
441
|
+
access: field(unionType(TOOL_ACCESS), 'Semantic access level for policy and UI.', TOOL_ACCESS, {
|
|
442
|
+
'read-only': 'Reads host or remote state; no lasting mutation.',
|
|
443
|
+
'read-write': 'May create or update host state.',
|
|
444
|
+
destructive: 'May delete, charge, or otherwise hard-to-undo actions.',
|
|
445
|
+
}),
|
|
446
|
+
loadTier: field(unionType(TOOL_LOAD_TIERS), 'When this tool is wired to the model (profile allow / builtInTools is still required).', TOOL_LOAD_TIERS, {
|
|
447
|
+
T0: 'Wired at turn start when allowed (custom on allow / builtin on the model).',
|
|
448
|
+
T1: 'Wired when profile.tools.t1Policy selects it.',
|
|
449
|
+
T2: 'Deferred until profile.tools.t2Loader returns { loaded } and the kernel promotes those ids.',
|
|
450
|
+
}),
|
|
451
|
+
permission: field(unionType(TOOL_PERMISSION), 'Default permission tier for this tool before the handler runs.', TOOL_PERMISSION, {
|
|
452
|
+
auto: 'Run without an extra host consent step.',
|
|
453
|
+
session_consent: 'Ask once per session, then remember grant.',
|
|
454
|
+
always_confirm: 'Confirm on every invocation.',
|
|
455
|
+
}),
|
|
456
|
+
category: field('string', 'Grouping label for settings and discovery.'),
|
|
457
|
+
paths: field('string[]', "Channel/path availability. Use ['*'] for all paths; omit turn path only matches '*'."),
|
|
458
|
+
};
|
|
459
|
+
/** Look up hover metadata for a dotted path (profile first, then extra). */
|
|
460
|
+
export function fieldMeta(path) {
|
|
461
|
+
return PROFILE_FIELDS[path] ?? EXTRA_FIELDS[path];
|
|
462
|
+
}
|
package/esm/src/kernel/stop.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Normalized turn stop reasons and resume policy.
|
|
3
3
|
*
|
|
4
|
-
* Providers map Interactions `status` /
|
|
4
|
+
* Providers map Interactions `status` / OpenAI `finish_reason` into `TurnStop`.
|
|
5
5
|
* Hosts classify client SSE drops via `turnStopFromClientStreamEnd`.
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
export type TurnStopKind = 'completed' | 'length' | 'tool' | 'filtered' | 'provider_error' | 'cancelled' | 'stream_incomplete';
|
|
9
|
+
import type { TurnStopKind } from './schema.js';
|
|
11
10
|
/** Normalized stop attached to terminal `done` events and host continue requests. */
|
|
12
11
|
export interface TurnStop {
|
|
13
12
|
kind: TurnStopKind;
|
|
@@ -26,18 +25,24 @@ export declare const CONTINUE_INSTRUCTION = "Continue and finish the incomplete
|
|
|
26
25
|
export declare const DEFAULT_AUTO_CONTINUE: readonly TurnStopKind[];
|
|
27
26
|
/** Pause before the one-shot auto-continue so a flaky tunnel can settle. */
|
|
28
27
|
export declare const AUTO_CONTINUE_DELAY_MS = 1500;
|
|
29
|
-
/** Profile
|
|
30
|
-
export interface
|
|
28
|
+
/** Profile turn-continuation policy under top-level `turnResumption`. */
|
|
29
|
+
export interface ProfileTurnResumptionSpec {
|
|
31
30
|
/**
|
|
32
31
|
* Kinds eligible for a Continue / continueFrom turn.
|
|
33
|
-
*
|
|
32
|
+
* When omitted, length / stream_incomplete / provider_error are eligible.
|
|
34
33
|
*/
|
|
35
34
|
allowContinue?: TurnStopKind[];
|
|
36
35
|
/**
|
|
37
|
-
* Kinds the host may auto-continue
|
|
38
|
-
* Kernel does not loop; hosts call continueFrom
|
|
36
|
+
* Kinds the host may auto-continue without a CTA.
|
|
37
|
+
* Kernel does not loop; hosts call continueFrom and pass `continuation`.
|
|
39
38
|
*/
|
|
40
39
|
autoContinue?: TurnStopKind[];
|
|
40
|
+
/**
|
|
41
|
+
* Max continueFrom rounds the kernel will accept for this profile.
|
|
42
|
+
* Compared against `TurnRequest.continuation` (1-based continue attempt).
|
|
43
|
+
* When omitted, only kind allowlists apply (no count cap).
|
|
44
|
+
*/
|
|
45
|
+
maxContinues?: number;
|
|
41
46
|
}
|
|
42
47
|
/** Partial state passed when continuing a resumeable stop. */
|
|
43
48
|
export interface TurnContinueFrom {
|
|
@@ -52,8 +57,8 @@ export declare function isResumeableStop(stop: TurnStop | undefined, allowContin
|
|
|
52
57
|
export declare function isUserCancelledStop(stop: TurnStop | undefined): boolean;
|
|
53
58
|
/** True when profile policy allows one silent auto-continue for this stop. */
|
|
54
59
|
export declare function shouldAutoContinue(stop: TurnStop | undefined, autoContinue?: readonly TurnStopKind[] | undefined): boolean;
|
|
55
|
-
/**
|
|
56
|
-
export declare function
|
|
60
|
+
/** OpenAI-compatible normalized `finish_reason` (+ optional `native_finish_reason`). */
|
|
61
|
+
export declare function turnStopFromOpenAiFinishReason(finishReason: string | null | undefined, nativeFinishReason?: string | null): TurnStop;
|
|
57
62
|
/** Gemini Interactions terminal `status`. */
|
|
58
63
|
export declare function turnStopFromInteractionStatus(status: string | null | undefined): TurnStop;
|
|
59
64
|
/**
|
package/esm/src/kernel/stop.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Normalized turn stop reasons and resume policy.
|
|
3
3
|
*
|
|
4
|
-
* Providers map Interactions `status` /
|
|
4
|
+
* Providers map Interactions `status` / OpenAI `finish_reason` into `TurnStop`.
|
|
5
5
|
* Hosts classify client SSE drops via `turnStopFromClientStreamEnd`.
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
@@ -45,8 +45,8 @@ export function shouldAutoContinue(stop, autoContinue = DEFAULT_AUTO_CONTINUE) {
|
|
|
45
45
|
return false;
|
|
46
46
|
return list.includes(stop.kind) && isResumeableStop(stop);
|
|
47
47
|
}
|
|
48
|
-
/**
|
|
49
|
-
export function
|
|
48
|
+
/** OpenAI-compatible normalized `finish_reason` (+ optional `native_finish_reason`). */
|
|
49
|
+
export function turnStopFromOpenAiFinishReason(finishReason, nativeFinishReason) {
|
|
50
50
|
const native = nativeFinishReason?.trim() || finishReason?.trim() || undefined;
|
|
51
51
|
const effective = (nativeFinishReason || finishReason || '').toLowerCase();
|
|
52
52
|
if (!effective)
|
|
@@ -54,9 +54,9 @@ export function turnStopFromOpenRouter(finishReason, nativeFinishReason) {
|
|
|
54
54
|
if (effective === 'network_error' || effective.includes('network')) {
|
|
55
55
|
return { kind: 'provider_error', native };
|
|
56
56
|
}
|
|
57
|
-
return
|
|
57
|
+
return openAiFinishKind((finishReason || '').toLowerCase(), effective, native);
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function openAiFinishKind(finish, effective, native) {
|
|
60
60
|
if (finish === 'stop') {
|
|
61
61
|
return /error|fail/.test(effective)
|
|
62
62
|
? { kind: 'provider_error', native }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tool execution core for model-initiated and host-initiated calls.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { z } from 'zod';
|
|
7
|
+
import type { Profile, TurnEvent } from '../types.js';
|
|
8
|
+
import type { FunctionToolDef, InvokeToolResume, ModelToolResult, ToolCallEvent, ToolContext, ToolFailure, ToolPause, ToolPermission, ToolStreamEvent, TurnToolSnapshot } from './types.js';
|
|
9
|
+
export declare function isResumeContinuation(resume?: InvokeToolResume): boolean;
|
|
10
|
+
export declare function isToolPause(value: ToolFailure | ToolPause): value is ToolPause;
|
|
11
|
+
export declare function yieldHandlerSideEvent(base: Pick<ToolCallEvent, 'name' | 'callId' | 'arguments'>, event: Exclude<ToolStreamEvent, {
|
|
12
|
+
kind: 'complete';
|
|
13
|
+
}>): Generator<TurnEvent>;
|
|
14
|
+
export declare function permissionGranted(toolName: string, sessionPermissions?: string[]): boolean;
|
|
15
|
+
export declare function checkPermission(toolName: string, permission: ToolPermission, sessionPermissions?: string[], resume?: InvokeToolResume): ToolPause | null;
|
|
16
|
+
export declare function projectForModel(tool: FunctionToolDef, output: unknown): ModelToolResult;
|
|
17
|
+
/** Format model-facing tool output for provider history continuation. */
|
|
18
|
+
export declare function formatToolResult(result: ModelToolResult): string;
|
|
19
|
+
/** Format a tool failure for provider history — structured so the model (or host) sees the code. */
|
|
20
|
+
export declare function formatToolFailureForModel(failure: ToolFailure): ModelToolResult;
|
|
21
|
+
export declare function startToolExecution<T>(tool: {
|
|
22
|
+
input: z.ZodType<T>;
|
|
23
|
+
}, rawInput: unknown, ctx: ToolContext, base: Pick<ToolCallEvent, 'name' | 'callId' | 'arguments'>): Generator<TurnEvent, {
|
|
24
|
+
ok: true;
|
|
25
|
+
data: T;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function executeFunction(tool: FunctionToolDef, rawInput: unknown, ctx: ToolContext, base: Pick<ToolCallEvent, 'name' | 'callId' | 'arguments'>, snapshot?: TurnToolSnapshot): AsyncGenerator<TurnEvent, ModelToolResult | undefined>;
|
|
30
|
+
export declare function notLoadedMessage(tool: FunctionToolDef): string;
|
|
31
|
+
export declare function extractLoadedIds(output: unknown): string[] | undefined;
|
|
32
|
+
/** Strip prototype-pollution keys from provider/host tool args before validation. */
|
|
33
|
+
export declare function plainToolInput(input: unknown): unknown;
|
|
34
|
+
export declare function executeBuiltin(tool: {
|
|
35
|
+
name: string;
|
|
36
|
+
}, ctx: ToolContext, base: Pick<ToolCallEvent, 'name' | 'callId' | 'arguments'>, snapshot: TurnToolSnapshot): AsyncGenerator<TurnEvent, ModelToolResult | undefined>;
|
|
37
|
+
export declare function executeRegisteredTool(args: {
|
|
38
|
+
profile: Profile;
|
|
39
|
+
name: string;
|
|
40
|
+
input: unknown;
|
|
41
|
+
callId: string;
|
|
42
|
+
ctx: Omit<ToolContext, 'callId' | 'profile'>;
|
|
43
|
+
snapshot?: TurnToolSnapshot;
|
|
44
|
+
}): AsyncGenerator<TurnEvent, ModelToolResult | undefined>;
|
|
45
|
+
export declare function newCallId(name: string): string;
|