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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live realtime inbound text — same sanitize + user_data fence as runTurn ingress.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { wrapUserData } from '../../guardrails/canary.js';
|
|
7
|
+
import { guardrailFromHits, projectGuardrailTurnEvent } from '../../guardrails/events.js';
|
|
8
|
+
import { detectionForTrust, resolveGuardrailPolicy } from '../../guardrails/policy.js';
|
|
9
|
+
import { detectText } from '../../guardrails/sanitize.js';
|
|
10
|
+
import { resolveObservabilityPolicy } from '../../observability/resolve-policy.js';
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize profile-controlled inbound text and wrap with user_data fencing.
|
|
13
|
+
*
|
|
14
|
+
* Resolves through the shared policy so Live ingress and the turn engine cannot
|
|
15
|
+
* drift apart on what an unset switch means. Returns an optional guardrail event
|
|
16
|
+
* for the session stream when redaction ran. Match previews follow
|
|
17
|
+
* `observability.include.guardrailMatchPreview`.
|
|
18
|
+
*/
|
|
19
|
+
function prepareLiveInboundText(profile, text) {
|
|
20
|
+
const policy = resolveGuardrailPolicy(profile.guardrails);
|
|
21
|
+
const detected = detectText(text, detectionForTrust(policy, 'untrusted'));
|
|
22
|
+
const raw = guardrailFromHits('live_inbound', 'untrusted', detected.hits, 'redact');
|
|
23
|
+
const includeMatch = resolveObservabilityPolicy(profile.observability).include
|
|
24
|
+
.guardrailMatchPreview;
|
|
25
|
+
const guardrail = raw ? projectGuardrailTurnEvent(raw, includeMatch) : undefined;
|
|
26
|
+
return {
|
|
27
|
+
text: wrapUserData(detected.text),
|
|
28
|
+
...(guardrail ? { guardrail } : {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export { prepareLiveInboundText };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live realtime ingress — profile gates for sendAudio / sendVideo / sendText.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { LiveIngressSpec, Profile } from '../types.js';
|
|
7
|
+
export type LiveIngressChannel = keyof LiveIngressSpec;
|
|
8
|
+
/** Default when `live.ingress.<channel>` is omitted — mic and camera on, typed text off. */
|
|
9
|
+
export declare function liveIngressChannelDefault(channel: LiveIngressChannel): boolean;
|
|
10
|
+
/** Resolve one channel from an ingress spec object (no profile wrapper). */
|
|
11
|
+
export declare function liveIngressEnabledFromSpec(ingress: LiveIngressSpec | undefined, channel: LiveIngressChannel): boolean;
|
|
12
|
+
/** Whether a realtime ingress channel is enabled on the profile. */
|
|
13
|
+
export declare function liveIngressEnabled(profile: Profile, channel: LiveIngressChannel): boolean;
|
|
14
|
+
/** True when at least one realtime ingress channel is enabled. */
|
|
15
|
+
export declare function hasAnyLiveIngress(profile: Profile): boolean;
|
|
16
|
+
/** Reject profiles with every ingress channel disabled. */
|
|
17
|
+
export declare function assertLiveIngressConfigured(profile: Profile): void;
|
|
18
|
+
/** Reject send* calls when the profile disabled that ingress channel. */
|
|
19
|
+
export declare function assertLiveIngress(profile: Profile, channel: LiveIngressChannel): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live realtime ingress — profile gates for sendAudio / sendVideo / sendText.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { TheorumError } from '../../guardrails/error.js';
|
|
7
|
+
const LIVE_INGRESS_CHANNELS = ['audio', 'video', 'text'];
|
|
8
|
+
function assertLiveProfile(profile) {
|
|
9
|
+
if (profile.type !== 'live') {
|
|
10
|
+
throw new TheorumError(`Profile '${profile.id}' is not type 'live'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
11
|
+
}
|
|
12
|
+
return profile;
|
|
13
|
+
}
|
|
14
|
+
/** Default when `live.ingress.<channel>` is omitted — mic and camera on, typed text off. */
|
|
15
|
+
export function liveIngressChannelDefault(channel) {
|
|
16
|
+
return channel !== 'text';
|
|
17
|
+
}
|
|
18
|
+
/** Resolve one channel from an ingress spec object (no profile wrapper). */
|
|
19
|
+
export function liveIngressEnabledFromSpec(ingress, channel) {
|
|
20
|
+
const value = ingress?.[channel];
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return liveIngressChannelDefault(channel);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
/** Whether a realtime ingress channel is enabled on the profile. */
|
|
26
|
+
export function liveIngressEnabled(profile, channel) {
|
|
27
|
+
const live = assertLiveProfile(profile);
|
|
28
|
+
return liveIngressEnabledFromSpec(live.live.ingress, channel);
|
|
29
|
+
}
|
|
30
|
+
/** True when at least one realtime ingress channel is enabled. */
|
|
31
|
+
export function hasAnyLiveIngress(profile) {
|
|
32
|
+
const live = assertLiveProfile(profile);
|
|
33
|
+
return LIVE_INGRESS_CHANNELS.some((channel) => liveIngressEnabledFromSpec(live.live.ingress, channel));
|
|
34
|
+
}
|
|
35
|
+
/** Reject profiles with every ingress channel disabled. */
|
|
36
|
+
export function assertLiveIngressConfigured(profile) {
|
|
37
|
+
const live = assertLiveProfile(profile);
|
|
38
|
+
if (hasAnyLiveIngress(live))
|
|
39
|
+
return;
|
|
40
|
+
throw new TheorumError(`Profile '${live.id}': at least one live.ingress channel (audio, video, text) must be enabled`);
|
|
41
|
+
}
|
|
42
|
+
/** Reject send* calls when the profile disabled that ingress channel. */
|
|
43
|
+
export function assertLiveIngress(profile, channel) {
|
|
44
|
+
if (liveIngressEnabled(profile, channel))
|
|
45
|
+
return;
|
|
46
|
+
throw new TheorumError(`Profile '${profile.id}' live.ingress.${channel} is disabled — cannot send on this channel`);
|
|
47
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { lexiconText } from '../../guardrails/lexicon.js';
|
|
2
|
+
import { profileTurnOutputs } from '../registry/profile-outputs.js';
|
|
1
3
|
const MAX_REPAIR_HISTORY_EXCHANGES = 2;
|
|
2
4
|
const MAX_REPAIR_HISTORY_MESSAGES = MAX_REPAIR_HISTORY_EXCHANGES * 2;
|
|
3
5
|
function scopeHistory(history) {
|
|
@@ -11,28 +13,27 @@ function formatHistoryBlock(messages) {
|
|
|
11
13
|
return '';
|
|
12
14
|
}
|
|
13
15
|
const lines = messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`);
|
|
14
|
-
|
|
16
|
+
const heading = lexiconText('repair.history_heading', { count: messages.length });
|
|
17
|
+
return `${heading}\n${lines.join('\n')}\n\n`;
|
|
15
18
|
}
|
|
16
19
|
function synthesizeRepairPrompt(args) {
|
|
17
20
|
const { profile, repair, history } = args;
|
|
18
21
|
const guidance = repair.guidance ||
|
|
19
|
-
profile
|
|
20
|
-
'
|
|
22
|
+
profileTurnOutputs(profile)?.validation?.repairGuidance ||
|
|
23
|
+
lexiconText('repair.default_guidance');
|
|
21
24
|
const historyBlock = formatHistoryBlock(scopeHistory(history));
|
|
22
|
-
let prompt =
|
|
23
|
-
prompt +=
|
|
24
|
-
prompt +=
|
|
25
|
-
prompt +=
|
|
25
|
+
let prompt = `${lexiconText('repair.prompt_header')}\n\n`;
|
|
26
|
+
prompt += `${lexiconText('repair.prompt_intro')}\n\n`;
|
|
27
|
+
prompt += `${lexiconText('repair.section_previous_output')}\n\`\`\`\n${repair.previousOutput.trim()}\n\`\`\`\n\n`;
|
|
28
|
+
prompt += `${lexiconText('repair.section_validator_rejection')}\n${repair.rejection.trim()}\n\n`;
|
|
26
29
|
if (guidance.trim()) {
|
|
27
|
-
prompt +=
|
|
30
|
+
prompt += `${lexiconText('repair.section_repair_guidance')}\n${guidance.trim()}\n\n`;
|
|
28
31
|
}
|
|
29
32
|
if (historyBlock) {
|
|
30
33
|
prompt += historyBlock;
|
|
31
34
|
}
|
|
32
|
-
prompt +=
|
|
33
|
-
prompt +=
|
|
34
|
-
prompt += `2. Apply the repair guidance without inventing unsupported facts.\n`;
|
|
35
|
-
prompt += `3. Return only the corrected assistant output required by the active profile.`;
|
|
35
|
+
prompt += `${lexiconText('repair.section_instructions')}\n`;
|
|
36
|
+
prompt += lexiconText('repair.prompt_instructions');
|
|
36
37
|
return prompt;
|
|
37
38
|
}
|
|
38
39
|
export { synthesizeRepairPrompt };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ModelProvider, Profile, ResolvedGeneration, TurnEvent, TurnRequest } from '../../types.js';
|
|
2
2
|
import type { StepExecutionState } from './state.js';
|
|
3
|
-
declare function runAttemptsWithValidation(safe: TurnRequest, profile: Profile, generation: ResolvedGeneration, system: string, provider: ModelProvider,
|
|
3
|
+
declare function runAttemptsWithValidation(safe: TurnRequest, profile: Profile, generation: ResolvedGeneration, system: string, provider: ModelProvider, upstream: Record<string, unknown>[], state: StepExecutionState): AsyncGenerator<TurnEvent>;
|
|
4
4
|
export { runAttemptsWithValidation };
|
|
@@ -1,14 +1,40 @@
|
|
|
1
|
+
import { runEnforcer } from '../../../guardrails/egress.js';
|
|
1
2
|
import { TheorumError, throwIfAborted, toErrorEvent } from '../../../guardrails/error.js';
|
|
3
|
+
import { guardrailFromVerdict } from '../../../guardrails/events.js';
|
|
4
|
+
import { lexiconText } from '../../../guardrails/lexicon.js';
|
|
5
|
+
import { resolveGuardrailPolicy } from '../../../guardrails/policy.js';
|
|
2
6
|
import { sanitizeTurnRequest } from '../../../guardrails/sanitize.js';
|
|
7
|
+
import { profileTurnOutputs } from '../../registry/profile-outputs.js';
|
|
3
8
|
import { resolveTurn } from '../../registry/resolve.js';
|
|
4
9
|
import { getStructured } from '../../registry/schemas.js';
|
|
10
|
+
import { injectWouldExceedMaxSteps } from '../../stages.js';
|
|
11
|
+
import { findLast } from '../../util/find-last.js';
|
|
5
12
|
import { collectValidationFailures, formatValidationFailures } from './schema-validation.js';
|
|
13
|
+
import { applyTurnStage } from './stages.js';
|
|
6
14
|
import { executeAttempt } from './steps.js';
|
|
15
|
+
/** Internal reason recorded when a turn is withheld; mapped to public copy on emit. */
|
|
16
|
+
const WITHHELD = 'Turn withheld: egress disclosure violation'; // lexicon-exempt: internal marker mapped by publicError
|
|
7
17
|
function collectAttemptText(events) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
|
|
18
|
+
const parts = [];
|
|
19
|
+
for (const event of events) {
|
|
20
|
+
if (event.type === 'text' && event.text) {
|
|
21
|
+
parts.push(event.text);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return parts.join('');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Project attempt events into the egress payload.
|
|
28
|
+
*
|
|
29
|
+
* Structured output travels alongside text so a profile with `outputs.structured`
|
|
30
|
+
* is covered by its own egress policy rather than passing unexamined.
|
|
31
|
+
*/
|
|
32
|
+
function projectOutbound(events) {
|
|
33
|
+
const structured = findLast(events, (e) => e.type === 'structured')?.structured;
|
|
34
|
+
return {
|
|
35
|
+
text: collectAttemptText(events),
|
|
36
|
+
...(structured !== undefined ? { structured } : {}),
|
|
37
|
+
};
|
|
12
38
|
}
|
|
13
39
|
function buildRepairRequest(safe, previousOutput, rejection, repairGuidance) {
|
|
14
40
|
return {
|
|
@@ -25,31 +51,45 @@ function buildRepairRequest(safe, previousOutput, rejection, repairGuidance) {
|
|
|
25
51
|
}
|
|
26
52
|
async function evaluateEgressOutcome(args) {
|
|
27
53
|
const { egress, attemptEvents, generation, request, profile, canRetry } = args;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
const payload = projectOutbound(attemptEvents);
|
|
55
|
+
const context = {
|
|
56
|
+
stage: 'output_final',
|
|
57
|
+
trust: 'untrusted',
|
|
58
|
+
profileId: profile.id,
|
|
59
|
+
...(generation.canary ? { canary: generation.canary } : {}),
|
|
60
|
+
...(request.input?.slots ? { slots: request.input.slots } : {}),
|
|
61
|
+
...(request.input?.role ? { role: request.input.role } : {}),
|
|
62
|
+
};
|
|
63
|
+
const verdict = await runEnforcer(egress.enforce, payload, context);
|
|
64
|
+
const guardrail = guardrailFromVerdict('output_final', 'untrusted', verdict);
|
|
65
|
+
// `flag` is advisory: the hit is recorded, the turn still releases.
|
|
66
|
+
if (verdict.action === 'allow' || verdict.action === 'flag') {
|
|
67
|
+
return { outcome: { action: 'pass' }, guardrail };
|
|
68
|
+
}
|
|
69
|
+
// The policy supplied safe replacement prose — release that instead.
|
|
70
|
+
if (verdict.action === 'redact') {
|
|
71
|
+
return {
|
|
72
|
+
outcome: { action: 'refusal', event: { type: 'text', text: verdict.text } },
|
|
73
|
+
guardrail,
|
|
74
|
+
};
|
|
38
75
|
}
|
|
39
76
|
if (egress.onBlock === 'refuse_to_user') {
|
|
40
|
-
|
|
77
|
+
// Only emit a text turn when the policy supplied copy. Without it the kernel
|
|
78
|
+
// has nothing to say — an empty text event reads as a successful empty reply —
|
|
79
|
+
// so fall back to the same withheld error the exhausted-retry path uses.
|
|
80
|
+
return {
|
|
81
|
+
outcome: verdict.refusal
|
|
82
|
+
? { action: 'refusal', event: { type: 'text', text: verdict.refusal } }
|
|
83
|
+
: { action: 'withhold', event: toErrorEvent(WITHHELD) },
|
|
84
|
+
guardrail,
|
|
85
|
+
};
|
|
41
86
|
}
|
|
42
87
|
if (canRetry) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const nextRequest = buildRepairRequest(request, attemptText, rejectionMsg, repairGuidance);
|
|
47
|
-
return { action: 'retry', nextRequest };
|
|
88
|
+
const repairGuidance = egress.repairGuidance || lexiconText('egress.default_repair_guidance');
|
|
89
|
+
const nextRequest = buildRepairRequest(request, payload.text, verdict.rejection, repairGuidance);
|
|
90
|
+
return { outcome: { action: 'retry', nextRequest }, guardrail };
|
|
48
91
|
}
|
|
49
|
-
return {
|
|
50
|
-
action: 'withhold',
|
|
51
|
-
event: toErrorEvent('Turn withheld: egress disclosure violation'),
|
|
52
|
-
};
|
|
92
|
+
return { outcome: { action: 'withhold', event: toErrorEvent(WITHHELD) }, guardrail };
|
|
53
93
|
}
|
|
54
94
|
async function evaluateValidationOutcome(args) {
|
|
55
95
|
const { validation, generation, latestStructured, request, canRetry } = args;
|
|
@@ -97,7 +137,7 @@ function updateFlowForRetry(flow, nextReq) {
|
|
|
97
137
|
}
|
|
98
138
|
async function* handleEgressGate(egress, flow, state, profile, maxRetries) {
|
|
99
139
|
const canRetry = flow.currentAttempt < maxRetries;
|
|
100
|
-
const outcome = await evaluateEgressOutcome({
|
|
140
|
+
const { outcome, guardrail } = await evaluateEgressOutcome({
|
|
101
141
|
egress,
|
|
102
142
|
attemptEvents: state.attemptEvents,
|
|
103
143
|
generation: flow.currentGen,
|
|
@@ -105,6 +145,10 @@ async function* handleEgressGate(egress, flow, state, profile, maxRetries) {
|
|
|
105
145
|
profile,
|
|
106
146
|
canRetry,
|
|
107
147
|
});
|
|
148
|
+
if (guardrail) {
|
|
149
|
+
state.allEmittedEvents.push(guardrail);
|
|
150
|
+
yield guardrail;
|
|
151
|
+
}
|
|
108
152
|
if (outcome.action === 'refusal') {
|
|
109
153
|
state.allEmittedEvents.push(outcome.event);
|
|
110
154
|
yield outcome.event;
|
|
@@ -150,19 +194,60 @@ function gateStatusToAction(status, terminalStatus = 'terminal') {
|
|
|
150
194
|
return null;
|
|
151
195
|
}
|
|
152
196
|
async function* executeSingleAttemptCycle(args) {
|
|
153
|
-
const { flow, state, profile, system, provider,
|
|
154
|
-
const validation = profile
|
|
155
|
-
const egress = profile.guardrails.egress;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
197
|
+
const { flow, state, profile, system, provider, upstream, maxRetries } = args;
|
|
198
|
+
const validation = profileTurnOutputs(profile)?.validation;
|
|
199
|
+
const egress = resolveGuardrailPolicy(profile.guardrails).egress;
|
|
200
|
+
// Fresh maxSteps budget per validation/egress attempt. before_end inject
|
|
201
|
+
// re-entry inside this cycle still accumulates stepCount (do not reset there).
|
|
202
|
+
state.stepCount = 0;
|
|
203
|
+
let latestStructured;
|
|
204
|
+
// before_end may inject and re-enter the step loop under maxSteps.
|
|
205
|
+
for (;;) {
|
|
206
|
+
state.attemptEvents = [];
|
|
207
|
+
state.withheldVisible = false;
|
|
208
|
+
const attempt = yield* executeAttempt({
|
|
209
|
+
safe: flow.currentReq,
|
|
210
|
+
profile,
|
|
211
|
+
generation: flow.currentGen,
|
|
212
|
+
system,
|
|
213
|
+
provider,
|
|
214
|
+
upstream,
|
|
215
|
+
state,
|
|
216
|
+
});
|
|
217
|
+
latestStructured = attempt.latestStructured;
|
|
218
|
+
// Tool / gate suspension — do not before_end; finalize with that stop.
|
|
219
|
+
if (state.lastStop?.kind === 'tool' || state.lastStop?.kind === 'gate') {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (state.lastStop?.kind === 'cancelled') {
|
|
223
|
+
return { status: 'terminal' };
|
|
224
|
+
}
|
|
225
|
+
const beforeEnd = yield* applyTurnStage({
|
|
226
|
+
profile,
|
|
227
|
+
generation: flow.currentGen,
|
|
228
|
+
state,
|
|
229
|
+
stage: 'before_end',
|
|
230
|
+
step: Math.max(state.stepCount, 1),
|
|
231
|
+
onStage: flow.currentReq.onStage,
|
|
232
|
+
signal: flow.currentReq.signal,
|
|
233
|
+
host: flow.currentGen.host,
|
|
234
|
+
injectWouldExceedMaxSteps: injectWouldExceedMaxSteps(state.stepCount, flow.currentGen.maxSteps),
|
|
235
|
+
});
|
|
236
|
+
if (beforeEnd.abort) {
|
|
237
|
+
state.lastStop = {
|
|
238
|
+
kind: 'cancelled',
|
|
239
|
+
...(typeof beforeEnd.abort === 'object' && beforeEnd.abort.reason
|
|
240
|
+
? { native: beforeEnd.abort.reason }
|
|
241
|
+
: {}),
|
|
242
|
+
};
|
|
243
|
+
return { status: 'terminal' };
|
|
244
|
+
}
|
|
245
|
+
if (beforeEnd.injectCount > 0) {
|
|
246
|
+
// Host extended the turn — another provider step under maxSteps.
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
166
251
|
if (egress?.enforce) {
|
|
167
252
|
const status = yield* handleEgressGate(egress, flow, state, profile, maxRetries);
|
|
168
253
|
const action = gateStatusToAction(status, 'terminal');
|
|
@@ -178,13 +263,15 @@ async function* executeSingleAttemptCycle(args) {
|
|
|
178
263
|
}
|
|
179
264
|
}
|
|
180
265
|
if (validation || egress?.enforce) {
|
|
181
|
-
|
|
182
|
-
|
|
266
|
+
// Progressive-yield already released text/thought live under egress — unless it
|
|
267
|
+
// withheld them mid-stream. A passing final verdict on the full text supersedes
|
|
268
|
+
// that partial-window decision, so the buffer is released instead of dropped.
|
|
269
|
+
yield* yieldBufferedAttemptEvents(state.attemptEvents, !state.withheldVisible);
|
|
183
270
|
}
|
|
184
271
|
return { status: 'success' };
|
|
185
272
|
}
|
|
186
|
-
async function* runAttemptsWithValidation(safe, profile, generation, system, provider,
|
|
187
|
-
const maxRetries = Math.max(profile
|
|
273
|
+
async function* runAttemptsWithValidation(safe, profile, generation, system, provider, upstream, state) {
|
|
274
|
+
const maxRetries = Math.max(profileTurnOutputs(profile)?.validation?.maxRetries ?? 0, resolveGuardrailPolicy(profile.guardrails).egress?.maxRetries ?? 0);
|
|
188
275
|
const flow = {
|
|
189
276
|
currentAttempt: 0,
|
|
190
277
|
currentGen: generation,
|
|
@@ -198,7 +285,7 @@ async function* runAttemptsWithValidation(safe, profile, generation, system, pro
|
|
|
198
285
|
profile,
|
|
199
286
|
system,
|
|
200
287
|
provider,
|
|
201
|
-
|
|
288
|
+
upstream,
|
|
202
289
|
maxRetries,
|
|
203
290
|
});
|
|
204
291
|
if (step.status === 'terminal' || step.status === 'success') {
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import type { ModelProvider, TurnEvent, TurnRequest } from '../../types.js';
|
|
10
|
+
import type { TraceSink } from '../../../observability/trace-sink.js';
|
|
11
|
+
import type { ModelProvider, TurnEvent, TurnHistoryMessage, TurnRequest } from '../../types.js';
|
|
12
|
+
/** Fold one history message to a compaction transcript line (media → markers). */
|
|
13
|
+
declare function compactionTranscriptLine(m: TurnHistoryMessage): string;
|
|
12
14
|
/** Execute one host turn against a provider adapter. */
|
|
13
|
-
declare function runTurn(req: TurnRequest, provider: ModelProvider,
|
|
14
|
-
export { runTurn };
|
|
15
|
+
declare function runTurn(req: TurnRequest, provider: ModelProvider, sinkOverride?: TraceSink): AsyncGenerator<TurnEvent>;
|
|
16
|
+
export { compactionTranscriptLine, runTurn };
|