@theoremjs/agents 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +1264 -2
- package/esm/_dnt.polyfills.d.ts +66 -0
- package/esm/_dnt.polyfills.js +51 -0
- package/esm/mod.d.ts +65 -0
- package/esm/mod.js +42 -0
- package/esm/package.json +3 -0
- package/esm/src/cli/bin.d.ts +7 -0
- package/esm/src/cli/bin.js +9 -0
- package/esm/src/cli/commands/bench.d.ts +6 -0
- package/esm/src/cli/commands/bench.js +458 -0
- package/esm/src/cli/commands/egress-compile.d.ts +17 -0
- package/esm/src/cli/commands/egress-compile.js +25 -0
- package/esm/src/cli/commands/eval.d.ts +37 -0
- package/esm/src/cli/commands/eval.js +190 -0
- package/esm/src/cli/commands/fuzz-canary.d.ts +15 -0
- package/esm/src/cli/commands/fuzz-canary.js +202 -0
- package/esm/src/cli/commands/fuzz-guardrails.d.ts +2 -0
- package/esm/src/cli/commands/fuzz-guardrails.js +5 -0
- package/esm/src/cli/commands/profile.d.ts +2 -0
- package/esm/src/cli/commands/profile.js +80 -0
- package/esm/src/cli/commands/run.d.ts +13 -0
- package/esm/src/cli/commands/run.js +45 -0
- package/esm/src/cli/commands/test.d.ts +25 -0
- package/esm/src/cli/commands/test.js +143 -0
- package/esm/src/cli/event-log.d.ts +15 -0
- package/esm/src/cli/event-log.js +136 -0
- package/esm/src/cli/index.d.ts +10 -0
- package/esm/src/cli/index.js +267 -0
- package/esm/src/cli/matrix/fixtures.d.ts +10 -0
- package/esm/src/cli/matrix/fixtures.js +72 -0
- package/esm/src/cli/matrix/synthesizer.d.ts +18 -0
- package/esm/src/cli/matrix/synthesizer.js +103 -0
- package/esm/src/evals/attachments.d.ts +4 -0
- package/esm/src/evals/attachments.js +53 -0
- package/esm/src/evals/breakdown.d.ts +28 -0
- package/esm/src/evals/breakdown.js +68 -0
- package/esm/src/evals/graders/answer.d.ts +13 -0
- package/esm/src/evals/graders/answer.js +133 -0
- package/esm/src/evals/graders/code.d.ts +64 -0
- package/esm/src/evals/graders/code.js +205 -0
- package/esm/src/evals/graders/judge.d.ts +20 -0
- package/esm/src/evals/graders/judge.js +365 -0
- package/esm/src/evals/graders/latency.d.ts +5 -0
- package/esm/src/evals/graders/latency.js +81 -0
- package/esm/src/evals/graders/live.d.ts +11 -0
- package/esm/src/evals/graders/live.js +40 -0
- package/esm/src/evals/graders/media.d.ts +18 -0
- package/esm/src/evals/graders/media.js +92 -0
- package/esm/src/evals/graders/shared.d.ts +11 -0
- package/esm/src/evals/graders/shared.js +62 -0
- package/esm/src/evals/graders/transcript.d.ts +17 -0
- package/esm/src/evals/graders/transcript.js +200 -0
- package/esm/src/evals/mod.d.ts +42 -0
- package/esm/src/evals/mod.js +30 -0
- package/esm/src/evals/record.d.ts +35 -0
- package/esm/src/evals/record.js +105 -0
- package/esm/src/evals/rubrics/catalog.d.ts +148 -0
- package/esm/src/evals/rubrics/catalog.js +846 -0
- package/esm/src/evals/rubrics/mod.d.ts +24 -0
- package/esm/src/evals/rubrics/mod.js +55 -0
- package/esm/src/evals/rubrics/mustache.d.ts +23 -0
- package/esm/src/evals/rubrics/mustache.js +154 -0
- package/esm/src/evals/rubrics/types.d.ts +54 -0
- package/esm/src/evals/rubrics/types.js +74 -0
- package/esm/src/evals/run.d.ts +118 -0
- package/esm/src/evals/run.js +452 -0
- package/esm/src/evals/suite.d.ts +19 -0
- package/esm/src/evals/suite.js +92 -0
- package/esm/src/evals/summary.d.ts +32 -0
- package/esm/src/evals/summary.js +30 -0
- package/esm/src/evals/trial.d.ts +27 -0
- package/esm/src/evals/trial.js +158 -0
- package/esm/src/evals/types.d.ts +260 -0
- package/esm/src/evals/types.js +126 -0
- package/esm/src/evals/verdict.d.ts +18 -0
- package/esm/src/evals/verdict.js +45 -0
- package/esm/src/guardrails/canary-gate.d.ts +21 -0
- package/esm/src/guardrails/canary-gate.js +40 -0
- package/esm/src/guardrails/canary.d.ts +112 -0
- package/esm/src/guardrails/canary.js +901 -0
- package/esm/src/guardrails/compile-egress.d.ts +11 -0
- package/esm/src/guardrails/compile-egress.js +11 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +22 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +303 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +5 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +202 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +3 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +118 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +13 -0
- package/esm/src/guardrails/corpus/live-attacks.js +224 -0
- package/esm/src/guardrails/corpus/mod.d.ts +7 -0
- package/esm/src/guardrails/corpus/mod.js +4 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +12 -0
- package/esm/src/guardrails/corpus/secrets.js +16 -0
- package/esm/src/guardrails/corpus/strings.d.ts +23 -0
- package/esm/src/guardrails/corpus/strings.js +29 -0
- package/esm/src/guardrails/corpus/types.d.ts +32 -0
- package/esm/src/guardrails/corpus/types.js +1 -0
- package/esm/src/guardrails/egress-automata.d.ts +14 -0
- package/esm/src/guardrails/egress-automata.js +6059 -0
- package/esm/src/guardrails/egress-compiler.d.ts +33 -0
- package/esm/src/guardrails/egress-compiler.js +237 -0
- package/esm/src/guardrails/egress-patterns.d.ts +24 -0
- package/esm/src/guardrails/egress-patterns.js +32 -0
- package/esm/src/guardrails/egress-policy.d.ts +27 -0
- package/esm/src/guardrails/egress-policy.js +76 -0
- package/esm/src/guardrails/egress-rules.d.ts +54 -0
- package/esm/src/guardrails/egress-rules.js +49 -0
- package/esm/src/guardrails/egress-stream.d.ts +105 -0
- package/esm/src/guardrails/egress-stream.js +645 -0
- package/esm/src/guardrails/egress-urls.d.ts +83 -0
- package/esm/src/guardrails/egress-urls.js +917 -0
- package/esm/src/guardrails/egress.d.ts +106 -0
- package/esm/src/guardrails/egress.js +374 -0
- package/esm/src/guardrails/error.d.ts +26 -0
- package/esm/src/guardrails/error.js +91 -0
- package/esm/src/guardrails/event-schemas.d.ts +57 -0
- package/esm/src/guardrails/event-schemas.js +41 -0
- package/esm/src/guardrails/events.d.ts +9 -0
- package/esm/src/guardrails/events.js +42 -0
- package/esm/src/guardrails/hits.d.ts +8 -0
- package/esm/src/guardrails/hits.js +19 -0
- package/esm/src/guardrails/injection-patterns.d.ts +15 -0
- package/esm/src/guardrails/injection-patterns.js +94 -0
- package/esm/src/guardrails/injection.d.ts +14 -0
- package/esm/src/guardrails/injection.js +237 -0
- package/esm/src/guardrails/lexicon.d.ts +30 -0
- package/esm/src/guardrails/lexicon.js +478 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +54 -0
- package/esm/src/guardrails/live-outbound-gate.js +305 -0
- package/esm/src/guardrails/mod.d.ts +46 -0
- package/esm/src/guardrails/mod.js +29 -0
- package/esm/src/guardrails/network.d.ts +42 -0
- package/esm/src/guardrails/network.js +328 -0
- package/esm/src/guardrails/normalize.d.ts +6 -0
- package/esm/src/guardrails/normalize.js +132 -0
- package/esm/src/guardrails/policy.d.ts +19 -0
- package/esm/src/guardrails/policy.js +50 -0
- package/esm/src/guardrails/progressive-yield.d.ts +69 -0
- package/esm/src/guardrails/progressive-yield.js +237 -0
- package/esm/src/guardrails/prompt-echo.d.ts +38 -0
- package/esm/src/guardrails/prompt-echo.js +225 -0
- package/esm/src/guardrails/quota.d.ts +20 -0
- package/esm/src/guardrails/quota.js +80 -0
- package/esm/src/guardrails/rules.d.ts +56 -0
- package/esm/src/guardrails/rules.js +54 -0
- package/esm/src/guardrails/sanitize.d.ts +23 -0
- package/esm/src/guardrails/sanitize.js +184 -0
- package/esm/src/guardrails/sensitive.d.ts +29 -0
- package/esm/src/guardrails/sensitive.js +113 -0
- package/esm/src/guardrails/serialize.d.ts +14 -0
- package/esm/src/guardrails/serialize.js +37 -0
- package/esm/src/guardrails/testing.d.ts +9 -0
- package/esm/src/guardrails/testing.js +8 -0
- package/esm/src/guardrails/theorem-error.d.ts +18 -0
- package/esm/src/guardrails/theorem-error.js +53 -0
- package/esm/src/guardrails/thought-guard.d.ts +55 -0
- package/esm/src/guardrails/thought-guard.js +464 -0
- package/esm/src/guardrails/tool-directives.d.ts +19 -0
- package/esm/src/guardrails/tool-directives.js +109 -0
- package/esm/src/guardrails/tool-result.d.ts +61 -0
- package/esm/src/guardrails/tool-result.js +248 -0
- package/esm/src/guardrails/types.d.ts +242 -0
- package/esm/src/guardrails/types.js +54 -0
- package/esm/src/host/client-turn.d.ts +9 -0
- package/esm/src/host/client-turn.js +38 -0
- package/esm/src/host/mint-trace.d.ts +28 -0
- package/esm/src/host/mint-trace.js +59 -0
- package/esm/src/host/mod.d.ts +13 -0
- package/esm/src/host/mod.js +11 -0
- package/esm/src/host/readStreamingJsonStringField.d.ts +2 -0
- package/esm/src/host/readStreamingJsonStringField.js +68 -0
- package/esm/src/host/reply.d.ts +7 -0
- package/esm/src/host/reply.js +35 -0
- package/esm/src/interface/blocks.d.ts +8 -0
- package/esm/src/interface/blocks.js +213 -0
- package/esm/src/interface/composer-actions.d.ts +20 -0
- package/esm/src/interface/composer-actions.js +31 -0
- package/esm/src/interface/draft.d.ts +14 -0
- package/esm/src/interface/draft.js +28 -0
- package/esm/src/interface/from-profile.d.ts +12 -0
- package/esm/src/interface/from-profile.js +144 -0
- package/esm/src/interface/history.d.ts +40 -0
- package/esm/src/interface/history.js +269 -0
- package/esm/src/interface/inputs.d.ts +10 -0
- package/esm/src/interface/inputs.js +64 -0
- package/esm/src/interface/mod.d.ts +31 -0
- package/esm/src/interface/mod.js +21 -0
- package/esm/src/interface/models.d.ts +19 -0
- package/esm/src/interface/models.js +45 -0
- package/esm/src/interface/pending.d.ts +48 -0
- package/esm/src/interface/pending.js +146 -0
- package/esm/src/interface/profile-interface.d.ts +4 -0
- package/esm/src/interface/profile-interface.js +223 -0
- package/esm/src/interface/session.d.ts +64 -0
- package/esm/src/interface/session.js +121 -0
- package/esm/src/interface/tool-calls.d.ts +20 -0
- package/esm/src/interface/tool-calls.js +73 -0
- package/esm/src/interface/tool-invoke.d.ts +4 -0
- package/esm/src/interface/tool-invoke.js +20 -0
- package/esm/src/interface/tool-media.d.ts +9 -0
- package/esm/src/interface/tool-media.js +112 -0
- package/esm/src/interface/types.d.ts +213 -0
- package/esm/src/interface/types.js +4 -0
- package/esm/src/kernel/auth/credential-source.d.ts +23 -0
- package/esm/src/kernel/auth/credential-source.js +11 -0
- package/esm/src/kernel/auth/crypto.d.ts +51 -0
- package/esm/src/kernel/auth/crypto.js +161 -0
- package/esm/src/kernel/auth/mod.d.ts +5 -0
- package/esm/src/kernel/auth/mod.js +5 -0
- package/esm/src/kernel/auth/oauth.d.ts +17 -0
- package/esm/src/kernel/auth/oauth.js +438 -0
- package/esm/src/kernel/auth/scope-refusal.d.ts +9 -0
- package/esm/src/kernel/auth/scope-refusal.js +8 -0
- package/esm/src/kernel/auth/typed-secret.d.ts +13 -0
- package/esm/src/kernel/auth/typed-secret.js +23 -0
- package/esm/src/kernel/auth/types.d.ts +139 -0
- package/esm/src/kernel/auth/types.js +1 -0
- package/esm/src/kernel/default-scope.d.ts +34 -0
- package/esm/src/kernel/default-scope.js +72 -0
- package/esm/src/kernel/engine/compaction.d.ts +70 -0
- package/esm/src/kernel/engine/compaction.js +202 -0
- package/esm/src/kernel/engine/decision.d.ts +41 -0
- package/esm/src/kernel/engine/decision.js +412 -0
- package/esm/src/kernel/engine/hash.d.ts +6 -0
- package/esm/src/kernel/engine/hash.js +22 -0
- package/esm/src/kernel/engine/live-inbound.d.ts +9 -0
- package/esm/src/kernel/engine/live-inbound.js +19 -0
- package/esm/src/kernel/engine/live-ingress.d.ts +9 -0
- package/esm/src/kernel/engine/live-ingress.js +37 -0
- package/esm/src/kernel/engine/media-probe/audio.d.ts +7 -0
- package/esm/src/kernel/engine/media-probe/audio.js +342 -0
- package/esm/src/kernel/engine/media-probe/bytes.d.ts +6 -0
- package/esm/src/kernel/engine/media-probe/bytes.js +26 -0
- package/esm/src/kernel/engine/media-probe/image.d.ts +5 -0
- package/esm/src/kernel/engine/media-probe/image.js +171 -0
- package/esm/src/kernel/engine/media-probe/iso-bmff.d.ts +19 -0
- package/esm/src/kernel/engine/media-probe/iso-bmff.js +48 -0
- package/esm/src/kernel/engine/media-probe/matroska.d.ts +35 -0
- package/esm/src/kernel/engine/media-probe/matroska.js +226 -0
- package/esm/src/kernel/engine/media-probe/mp4.d.ts +22 -0
- package/esm/src/kernel/engine/media-probe/mp4.js +279 -0
- package/esm/src/kernel/engine/media-probe/opus.d.ts +4 -0
- package/esm/src/kernel/engine/media-probe/opus.js +23 -0
- package/esm/src/kernel/engine/media-probe/pdf.d.ts +5 -0
- package/esm/src/kernel/engine/media-probe/pdf.js +117 -0
- package/esm/src/kernel/engine/media-probe/video.d.ts +15 -0
- package/esm/src/kernel/engine/media-probe/video.js +49 -0
- package/esm/src/kernel/engine/record.d.ts +3 -0
- package/esm/src/kernel/engine/record.js +11 -0
- package/esm/src/kernel/engine/repair.d.ts +7 -0
- package/esm/src/kernel/engine/repair.js +36 -0
- package/esm/src/kernel/engine/runner/gates.d.ts +5 -0
- package/esm/src/kernel/engine/runner/gates.js +322 -0
- package/esm/src/kernel/engine/runner/mod.d.ts +15 -0
- package/esm/src/kernel/engine/runner/mod.js +574 -0
- package/esm/src/kernel/engine/runner/schema-validation.d.ts +14 -0
- package/esm/src/kernel/engine/runner/schema-validation.js +105 -0
- package/esm/src/kernel/engine/runner/stages.d.ts +28 -0
- package/esm/src/kernel/engine/runner/stages.js +48 -0
- package/esm/src/kernel/engine/runner/state.d.ts +76 -0
- package/esm/src/kernel/engine/runner/state.js +40 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +14 -0
- package/esm/src/kernel/engine/runner/steps.js +360 -0
- package/esm/src/kernel/engine/runner/stream.d.ts +40 -0
- package/esm/src/kernel/engine/runner/stream.js +225 -0
- package/esm/src/kernel/engine/runner/usage.d.ts +45 -0
- package/esm/src/kernel/engine/runner/usage.js +150 -0
- package/esm/src/kernel/engine/session/mod.d.ts +31 -0
- package/esm/src/kernel/engine/session/mod.js +951 -0
- package/esm/src/kernel/engine/session/session-trace.d.ts +115 -0
- package/esm/src/kernel/engine/session/session-trace.js +489 -0
- package/esm/src/kernel/engine/token-estimate.d.ts +77 -0
- package/esm/src/kernel/engine/token-estimate.js +218 -0
- package/esm/src/kernel/engine/tool-trace.d.ts +58 -0
- package/esm/src/kernel/engine/tool-trace.js +131 -0
- package/esm/src/kernel/engine/tree.d.ts +2 -0
- package/esm/src/kernel/engine/tree.js +17 -0
- package/esm/src/kernel/engine/turn-trace.d.ts +118 -0
- package/esm/src/kernel/engine/turn-trace.js +863 -0
- package/esm/src/kernel/engine/usage.d.ts +30 -0
- package/esm/src/kernel/engine/usage.js +134 -0
- package/esm/src/kernel/interaction-parts.d.ts +10 -0
- package/esm/src/kernel/interaction-parts.js +22 -0
- package/esm/src/kernel/mod.d.ts +47 -0
- package/esm/src/kernel/mod.js +30 -0
- package/esm/src/kernel/profile-graph.d.ts +140 -0
- package/esm/src/kernel/profile-graph.js +151 -0
- package/esm/src/kernel/profile-presence.d.ts +9 -0
- package/esm/src/kernel/profile-presence.js +143 -0
- package/esm/src/kernel/profile-scope.d.ts +36 -0
- package/esm/src/kernel/profile-scope.js +262 -0
- package/esm/src/kernel/registry/attachments.d.ts +35 -0
- package/esm/src/kernel/registry/attachments.js +211 -0
- package/esm/src/kernel/registry/catalog.d.ts +19 -0
- package/esm/src/kernel/registry/catalog.js +78 -0
- package/esm/src/kernel/registry/ingress.d.ts +8 -0
- package/esm/src/kernel/registry/ingress.js +134 -0
- package/esm/src/kernel/registry/kernel-registry.d.ts +12 -0
- package/esm/src/kernel/registry/kernel-registry.js +8 -0
- package/esm/src/kernel/registry/profile-outputs.d.ts +3 -0
- package/esm/src/kernel/registry/profile-outputs.js +7 -0
- package/esm/src/kernel/registry/profiles.d.ts +92 -0
- package/esm/src/kernel/registry/profiles.js +698 -0
- package/esm/src/kernel/registry/provider-request.d.ts +6 -0
- package/esm/src/kernel/registry/provider-request.js +42 -0
- package/esm/src/kernel/registry/resolve.d.ts +14 -0
- package/esm/src/kernel/registry/resolve.js +237 -0
- package/esm/src/kernel/registry/schemas.d.ts +9 -0
- package/esm/src/kernel/registry/schemas.js +25 -0
- package/esm/src/kernel/registry/sole-model.d.ts +2 -0
- package/esm/src/kernel/registry/sole-model.js +4 -0
- package/esm/src/kernel/registry/system-prompt.d.ts +8 -0
- package/esm/src/kernel/registry/system-prompt.js +33 -0
- package/esm/src/kernel/registry/system-role.d.ts +3 -0
- package/esm/src/kernel/registry/system-role.js +9 -0
- package/esm/src/kernel/registry/vault.d.ts +13 -0
- package/esm/src/kernel/registry/vault.js +14 -0
- package/esm/src/kernel/schema.d.ts +172 -0
- package/esm/src/kernel/schema.js +653 -0
- package/esm/src/kernel/scope.d.ts +22 -0
- package/esm/src/kernel/scope.js +25 -0
- package/esm/src/kernel/stages.d.ts +141 -0
- package/esm/src/kernel/stages.js +388 -0
- package/esm/src/kernel/stop.d.ts +93 -0
- package/esm/src/kernel/stop.js +125 -0
- package/esm/src/kernel/system-parts.d.ts +26 -0
- package/esm/src/kernel/system-parts.js +98 -0
- package/esm/src/kernel/tools/activity-label.d.ts +16 -0
- package/esm/src/kernel/tools/activity-label.js +209 -0
- package/esm/src/kernel/tools/events.d.ts +51 -0
- package/esm/src/kernel/tools/events.js +149 -0
- package/esm/src/kernel/tools/execute.d.ts +64 -0
- package/esm/src/kernel/tools/execute.js +801 -0
- package/esm/src/kernel/tools/gate-answer.d.ts +50 -0
- package/esm/src/kernel/tools/gate-answer.js +54 -0
- package/esm/src/kernel/tools/harness.d.ts +16 -0
- package/esm/src/kernel/tools/harness.js +42 -0
- package/esm/src/kernel/tools/invoke.d.ts +10 -0
- package/esm/src/kernel/tools/invoke.js +129 -0
- package/esm/src/kernel/tools/mcp-sessions.d.ts +20 -0
- package/esm/src/kernel/tools/mcp-sessions.js +64 -0
- package/esm/src/kernel/tools/mod.d.ts +9 -0
- package/esm/src/kernel/tools/mod.js +6 -0
- package/esm/src/kernel/tools/model-text.d.ts +11 -0
- package/esm/src/kernel/tools/model-text.js +25 -0
- package/esm/src/kernel/tools/permission.d.ts +6 -0
- package/esm/src/kernel/tools/permission.js +38 -0
- package/esm/src/kernel/tools/project.d.ts +8 -0
- package/esm/src/kernel/tools/project.js +25 -0
- package/esm/src/kernel/tools/registry.d.ts +20 -0
- package/esm/src/kernel/tools/registry.js +84 -0
- package/esm/src/kernel/tools/remote.d.ts +110 -0
- package/esm/src/kernel/tools/remote.js +998 -0
- package/esm/src/kernel/tools/resolve.d.ts +28 -0
- package/esm/src/kernel/tools/resolve.js +259 -0
- package/esm/src/kernel/tools/schema.d.ts +10 -0
- package/esm/src/kernel/tools/schema.js +174 -0
- package/esm/src/kernel/tools/signed-in-fetch.d.ts +18 -0
- package/esm/src/kernel/tools/signed-in-fetch.js +45 -0
- package/esm/src/kernel/tools/stage-run.d.ts +93 -0
- package/esm/src/kernel/tools/stage-run.js +140 -0
- package/esm/src/kernel/tools/types.d.ts +287 -0
- package/esm/src/kernel/tools/types.js +1 -0
- package/esm/src/kernel/turn-events.d.ts +599 -0
- package/esm/src/kernel/turn-events.js +425 -0
- package/esm/src/kernel/types.d.ts +884 -0
- package/esm/src/kernel/types.js +1 -0
- package/esm/src/kernel/util/base64.d.ts +2 -0
- package/esm/src/kernel/util/base64.js +13 -0
- package/esm/src/kernel/util/exact-type.d.ts +31 -0
- package/esm/src/kernel/util/exact-type.js +1 -0
- package/esm/src/kernel/util/find-last.d.ts +3 -0
- package/esm/src/kernel/util/find-last.js +9 -0
- package/esm/src/kernel/util/mime.d.ts +2 -0
- package/esm/src/kernel/util/mime.js +4 -0
- package/esm/src/kernel/util/record.d.ts +1 -0
- package/esm/src/kernel/util/record.js +3 -0
- package/esm/src/observability/destinations.d.ts +9 -0
- package/esm/src/observability/destinations.js +38 -0
- package/esm/src/observability/jsonl.d.ts +18 -0
- package/esm/src/observability/jsonl.js +117 -0
- package/esm/src/observability/mod.d.ts +22 -0
- package/esm/src/observability/mod.js +16 -0
- package/esm/src/observability/openinference.d.ts +11 -0
- package/esm/src/observability/openinference.js +205 -0
- package/esm/src/observability/otlp.d.ts +68 -0
- package/esm/src/observability/otlp.js +98 -0
- package/esm/src/observability/phoenix.d.ts +29 -0
- package/esm/src/observability/phoenix.js +71 -0
- package/esm/src/observability/policy.d.ts +14 -0
- package/esm/src/observability/policy.js +69 -0
- package/esm/src/observability/resolve-policy.d.ts +8 -0
- package/esm/src/observability/resolve-policy.js +58 -0
- package/esm/src/observability/spans.d.ts +16 -0
- package/esm/src/observability/spans.js +56 -0
- package/esm/src/observability/trace-catalog.d.ts +111 -0
- package/esm/src/observability/trace-catalog.js +865 -0
- package/esm/src/observability/trace-record.d.ts +23 -0
- package/esm/src/observability/trace-record.js +223 -0
- package/esm/src/observability/trace-schema.d.ts +54 -0
- package/esm/src/observability/trace-schema.js +59 -0
- package/esm/src/observability/trace-sink.d.ts +13 -0
- package/esm/src/observability/trace-sink.js +3 -0
- package/esm/src/observability/trace-span.d.ts +92 -0
- package/esm/src/observability/trace-span.js +199 -0
- package/esm/src/observability/trace.d.ts +11 -0
- package/esm/src/observability/trace.js +35 -0
- package/esm/src/observability/types.d.ts +85 -0
- package/esm/src/observability/types.js +3 -0
- package/esm/src/presets/google/speech-voices.d.ts +8 -0
- package/esm/src/presets/google/speech-voices.js +38 -0
- package/esm/src/presets/google-limits.d.ts +12 -0
- package/esm/src/presets/google-limits.js +18 -0
- package/esm/src/presets/google.d.ts +104 -0
- package/esm/src/presets/google.js +188 -0
- package/esm/src/presets/mod.d.ts +10 -0
- package/esm/src/presets/mod.js +9 -0
- package/esm/src/presets/openrouter.d.ts +13 -0
- package/esm/src/presets/openrouter.js +13 -0
- package/esm/src/presets/typesafe.d.ts +2 -0
- package/esm/src/presets/typesafe.js +2 -0
- package/esm/src/providers/create-provider.d.ts +21 -0
- package/esm/src/providers/create-provider.js +90 -0
- package/esm/src/providers/decision/usage.d.ts +3 -0
- package/esm/src/providers/decision/usage.js +25 -0
- package/esm/src/providers/google/api-error.d.ts +8 -0
- package/esm/src/providers/google/api-error.js +123 -0
- package/esm/src/providers/google/grounding.d.ts +5 -0
- package/esm/src/providers/google/grounding.js +226 -0
- package/esm/src/providers/google/interactions/framing.d.ts +15 -0
- package/esm/src/providers/google/interactions/framing.js +263 -0
- package/esm/src/providers/google/interactions/mod.d.ts +2 -0
- package/esm/src/providers/google/interactions/mod.js +2 -0
- package/esm/src/providers/google/interactions/steps.d.ts +12 -0
- package/esm/src/providers/google/interactions/steps.js +200 -0
- package/esm/src/providers/google/interactions/stream.d.ts +24 -0
- package/esm/src/providers/google/interactions/stream.js +297 -0
- package/esm/src/providers/google/keys.d.ts +20 -0
- package/esm/src/providers/google/keys.js +87 -0
- package/esm/src/providers/google/live/framing.d.ts +51 -0
- package/esm/src/providers/google/live/framing.js +597 -0
- package/esm/src/providers/google/live/mod.d.ts +7 -0
- package/esm/src/providers/google/live/mod.js +7 -0
- package/esm/src/providers/google/live/openapi-schema.d.ts +1 -0
- package/esm/src/providers/google/live/openapi-schema.js +42 -0
- package/esm/src/providers/google/live/session.d.ts +14 -0
- package/esm/src/providers/google/live/session.js +150 -0
- package/esm/src/providers/google/live/stream.d.ts +51 -0
- package/esm/src/providers/google/live/stream.js +263 -0
- package/esm/src/providers/google/thinking.d.ts +3 -0
- package/esm/src/providers/google/thinking.js +8 -0
- package/esm/src/providers/google/urls.d.ts +6 -0
- package/esm/src/providers/google/urls.js +6 -0
- package/esm/src/providers/google/usage.d.ts +7 -0
- package/esm/src/providers/google/usage.js +49 -0
- package/esm/src/providers/local/local.d.ts +12 -0
- package/esm/src/providers/local/local.js +186 -0
- package/esm/src/providers/local/mod.d.ts +8 -0
- package/esm/src/providers/local/mod.js +8 -0
- package/esm/src/providers/mod.d.ts +11 -0
- package/esm/src/providers/mod.js +7 -0
- package/esm/src/providers/openrouter/cache-control.d.ts +5 -0
- package/esm/src/providers/openrouter/cache-control.js +4 -0
- package/esm/src/providers/openrouter/chat.d.ts +64 -0
- package/esm/src/providers/openrouter/chat.js +490 -0
- package/esm/src/providers/openrouter/image.d.ts +24 -0
- package/esm/src/providers/openrouter/image.js +187 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +19 -0
- package/esm/src/providers/openrouter/openai/compat.js +162 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +9 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +115 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +10 -0
- package/esm/src/providers/openrouter/openai/sdk-messages.js +99 -0
- package/esm/src/providers/openrouter/openai/usage.d.ts +8 -0
- package/esm/src/providers/openrouter/openai/usage.js +56 -0
- package/esm/src/providers/openrouter/resolve-api-key.d.ts +3 -0
- package/esm/src/providers/openrouter/resolve-api-key.js +8 -0
- package/esm/src/providers/openrouter/speech.d.ts +18 -0
- package/esm/src/providers/openrouter/speech.js +109 -0
- package/esm/src/providers/probe.d.ts +5 -0
- package/esm/src/providers/probe.js +15 -0
- package/esm/src/providers/shared/builtin-wire.d.ts +4 -0
- package/esm/src/providers/shared/builtin-wire.js +9 -0
- package/esm/src/providers/shared/pcm.d.ts +14 -0
- package/esm/src/providers/shared/pcm.js +78 -0
- package/esm/src/providers/shared/response-identity.d.ts +11 -0
- package/esm/src/providers/shared/response-identity.js +12 -0
- package/esm/src/providers/shared/sse.d.ts +10 -0
- package/esm/src/providers/shared/sse.js +79 -0
- package/esm/src/providers/shared/structured-output.d.ts +4 -0
- package/esm/src/providers/shared/structured-output.js +11 -0
- package/esm/src/providers/shared/tool-args.d.ts +25 -0
- package/esm/src/providers/shared/tool-args.js +84 -0
- package/esm/src/providers/shared/upstream-tap.d.ts +8 -0
- package/esm/src/providers/shared/upstream-tap.js +85 -0
- package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
- package/esm/src/providers/shared/upstream-tape.js +88 -0
- package/esm/src/providers/shared/vault.d.ts +13 -0
- package/esm/src/providers/shared/vault.js +41 -0
- package/esm/src/providers/types.d.ts +19 -0
- package/esm/src/providers/types.js +1 -0
- package/esm/src/surface/formats.d.ts +28 -0
- package/esm/src/surface/formats.js +139 -0
- package/esm/src/surface/mod.d.ts +14 -0
- package/esm/src/surface/mod.js +10 -0
- package/esm/src/surface/runtime.d.ts +40 -0
- package/esm/src/surface/runtime.js +439 -0
- package/esm/src/surface/tools.d.ts +15 -0
- package/esm/src/surface/tools.js +80 -0
- package/esm/src/surface/types.d.ts +104 -0
- package/esm/src/surface/types.js +4 -0
- package/package.json +108 -4
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
import { egressChecksProblem } from '../../guardrails/egress.js';
|
|
2
|
+
import { streamPlanOf } from '../../guardrails/egress-stream.js';
|
|
3
|
+
import { TheoremError } from '../../guardrails/error.js';
|
|
4
|
+
import { validateLexiconOverrides } from '../../guardrails/lexicon.js';
|
|
5
|
+
import { SENSITIVE_GROUPS } from '../../guardrails/sensitive.js';
|
|
6
|
+
import { resolveObservabilityPolicy } from '../../observability/resolve-policy.js';
|
|
7
|
+
import { assertLiveIngressConfigured } from '../engine/live-ingress.js';
|
|
8
|
+
import { outOfScopeFields } from '../profile-scope.js';
|
|
9
|
+
import { CACHE_MODES, CACHE_TTLS, IMAGE_ATTACHMENT_ACCEPT_MIMES, isKeySlotName, isValidPair, isValidProfileProtocol, PROFILE_FIELDS, PROFILE_TYPES, protocolsForProfileType, THINKING_LEVELS, } from '../schema.js';
|
|
10
|
+
import { isContinueStopKind } from '../stop.js';
|
|
11
|
+
import { assertSystemPrompt } from '../system-parts.js';
|
|
12
|
+
import { profileToolAllow, profileToolsSpec } from '../tools/resolve.js';
|
|
13
|
+
import { isTurnMediaRef } from './attachments.js';
|
|
14
|
+
import { mediaKindForMime, mimeAllowed, profileInputs } from './catalog.js';
|
|
15
|
+
import { soleModelId } from './sole-model.js';
|
|
16
|
+
function assertModelRoute(profileId, modelId, binding, type) {
|
|
17
|
+
if (type === 'decision' && !binding.apiId.trim()) {
|
|
18
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}' must set apiId`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
19
|
+
}
|
|
20
|
+
if (type === 'decision' && !isValidProfileProtocol(type, binding.protocol)) {
|
|
21
|
+
throw new TheoremError('config',
|
|
22
|
+
// lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
23
|
+
`Profile ${profileId} model '${modelId}': type 'decision' cannot use protocol '${binding.protocol}'. Supported: decision`);
|
|
24
|
+
}
|
|
25
|
+
if (!isValidPair(binding.protocol, binding.provider)) {
|
|
26
|
+
throw new TheoremError('config',
|
|
27
|
+
// lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
28
|
+
`Profile ${profileId} model '${modelId}': protocol '${binding.protocol}' is not valid for provider '${binding.provider}'`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function validateDecisionBinding(profileId, modelId, binding) {
|
|
32
|
+
assertModelRoute(profileId, modelId, binding, 'decision');
|
|
33
|
+
if ('retry' in binding) {
|
|
34
|
+
throw new TheoremError('config',
|
|
35
|
+
// lexicon-exempt: developer contract / internal diagnostic
|
|
36
|
+
`Profile ${profileId} model '${modelId}': decision retry configuration is unsupported; POSTs are never retried`);
|
|
37
|
+
}
|
|
38
|
+
if (binding.timeoutMs !== undefined &&
|
|
39
|
+
(!Number.isFinite(binding.timeoutMs) || binding.timeoutMs <= 0)) {
|
|
40
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}' timeoutMs must be > 0`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function validateDecisionModel(input) {
|
|
44
|
+
const modelId = input.models ? soleModelId(input.models) : undefined;
|
|
45
|
+
if (!modelId) {
|
|
46
|
+
throw new TheoremError('config', `Profile ${input.id}: type 'decision' must declare exactly one model`);
|
|
47
|
+
}
|
|
48
|
+
validateDecisionBinding(input.id, modelId, input.models[modelId]);
|
|
49
|
+
}
|
|
50
|
+
function validateDecisionConfig(input) {
|
|
51
|
+
if (input.inputs.state !== 'json') {
|
|
52
|
+
throw new TheoremError('config', `Profile ${input.id}: decision inputs.state must be 'json'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
53
|
+
}
|
|
54
|
+
const cap = input.inputs.maxStateBytes;
|
|
55
|
+
if (cap !== undefined && !(Number.isInteger(cap) && cap > 0)) {
|
|
56
|
+
throw new TheoremError('config', `Profile ${input.id}: decision inputs.maxStateBytes must be a positive integer`);
|
|
57
|
+
}
|
|
58
|
+
if (!input.decision.contract.trim()) {
|
|
59
|
+
throw new TheoremError('config', `Profile ${input.id}: decision.contract must be non-empty`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function defineDecisionProfile(input) {
|
|
63
|
+
validateDecisionModel(input);
|
|
64
|
+
validateDecisionConfig(input);
|
|
65
|
+
assertSlotName(input.id, 'key', input.key);
|
|
66
|
+
for (const [modelId, binding] of Object.entries(input.models)) {
|
|
67
|
+
assertSlotName(input.id, `models.${modelId}.key`, binding.key);
|
|
68
|
+
if (!binding.key && !input.key) {
|
|
69
|
+
throw new TheoremError('config', `Profile ${input.id} model '${modelId}': a decision model needs models.*.key or the profile key`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
assertObservability(input.id, input.observability);
|
|
73
|
+
return { ...input, identity: { handle: input.identity.handle } };
|
|
74
|
+
}
|
|
75
|
+
function defineHostProfile(input) {
|
|
76
|
+
assertHostTools(input.id, input.tools);
|
|
77
|
+
assertObservability(input.id, input.observability);
|
|
78
|
+
return {
|
|
79
|
+
type: 'host',
|
|
80
|
+
id: input.id,
|
|
81
|
+
tools: { allow: input.tools.allow },
|
|
82
|
+
guardrails: input.guardrails,
|
|
83
|
+
observability: input.observability,
|
|
84
|
+
lexicon: input.lexicon,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function assertHostTools(profileId, tools) {
|
|
88
|
+
if (!Array.isArray(tools.allow)) {
|
|
89
|
+
throw new TheoremError('config', `Profile ${profileId}: type 'host' must set tools.allow`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function assertModelsNonEmpty(profileId, models) {
|
|
93
|
+
if (Object.keys(models).length === 0) {
|
|
94
|
+
throw new TheoremError('config', `Profile ${profileId} must declare at least one model`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function resolveDefaultModel(profileId, input) {
|
|
98
|
+
const ids = Object.keys(input.models);
|
|
99
|
+
const inferred = input.defaultModel ?? soleModelId(input.models);
|
|
100
|
+
if (!inferred) {
|
|
101
|
+
throw new TheoremError('config', `Profile ${profileId} must set defaultModel when more than one model is declared`);
|
|
102
|
+
}
|
|
103
|
+
if (!input.models[inferred]) {
|
|
104
|
+
throw new TheoremError('config', `Profile ${profileId} defaultModel '${inferred}' is not declared`);
|
|
105
|
+
}
|
|
106
|
+
if (input.allowModelSelect && ids.length < 2) {
|
|
107
|
+
throw new TheoremError('config', `Profile ${profileId} allowModelSelect requires at least two models`);
|
|
108
|
+
}
|
|
109
|
+
return inferred;
|
|
110
|
+
}
|
|
111
|
+
function assertModelBinding(profileId, modelId, binding) {
|
|
112
|
+
assertModelRoute(profileId, modelId, binding);
|
|
113
|
+
assertModelEfforts(profileId, modelId, binding);
|
|
114
|
+
if (binding.cache) {
|
|
115
|
+
assertCacheSpec(profileId, modelId, binding, binding.cache);
|
|
116
|
+
}
|
|
117
|
+
assertInteractionsPersistence(profileId, modelId, binding);
|
|
118
|
+
assertLocalServer(profileId, modelId, binding);
|
|
119
|
+
}
|
|
120
|
+
function assertSlotName(profileId, path, slot) {
|
|
121
|
+
if (slot !== undefined && !isKeySlotName(slot)) {
|
|
122
|
+
throw new TheoremError('config', `Profile ${profileId}: ${path} '${slot}' is not a key slot name; use letters, digits, '-' and '_', up to 32 characters`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** A model's key and fallback, each its own or the profile's. A fallback is never implied. */
|
|
126
|
+
function assertKeySlot(profileId, modelId, binding, profile) {
|
|
127
|
+
assertSlotName(profileId, `models.${modelId}.key`, binding.key);
|
|
128
|
+
assertSlotName(profileId, `models.${modelId}.fallbackKey`, binding.fallbackKey);
|
|
129
|
+
const key = binding.key ?? profile.key;
|
|
130
|
+
const fallback = binding.fallbackKey ?? profile.fallbackKey;
|
|
131
|
+
if (binding.provider !== 'local' && !key) {
|
|
132
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}': a ${binding.provider} model needs models.*.key or the profile key`);
|
|
133
|
+
}
|
|
134
|
+
if (fallback === undefined)
|
|
135
|
+
return;
|
|
136
|
+
if (fallback === key) {
|
|
137
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}': fallbackKey '${fallback}' is the same slot as its key`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function assertLocalServer(profileId, modelId, binding) {
|
|
141
|
+
if (binding.server === undefined) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const tag = `Profile ${profileId} model '${modelId}'`;
|
|
145
|
+
if (binding.provider !== 'local') {
|
|
146
|
+
throw new TheoremError('config', `${tag}: server is only valid when provider is 'local'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
147
|
+
}
|
|
148
|
+
if (typeof binding.server !== 'string' || binding.server.trim() === '') {
|
|
149
|
+
throw new TheoremError('config', `${tag}: server must be a non-empty string`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function assertModelEfforts(profileId, modelId, binding) {
|
|
153
|
+
const efforts = binding.efforts;
|
|
154
|
+
if (!efforts || Object.keys(efforts).length === 0) {
|
|
155
|
+
if (binding.defaultEffort || binding.allowEffortSelect) {
|
|
156
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}': defaultEffort and allowEffortSelect require efforts`);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
for (const [alias, level] of Object.entries(efforts)) {
|
|
161
|
+
if (!THINKING_LEVELS.includes(level)) {
|
|
162
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}' effort '${alias}': '${level}' is not a thinking level (${THINKING_LEVELS.join(', ')})`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const keys = Object.keys(efforts);
|
|
166
|
+
const defaultAlias = binding.defaultEffort ?? (keys.length === 1 ? keys[0] : undefined);
|
|
167
|
+
if (!defaultAlias) {
|
|
168
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}' must set defaultEffort when more than one effort is declared`);
|
|
169
|
+
}
|
|
170
|
+
if (!efforts[defaultAlias]) {
|
|
171
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}' defaultEffort '${defaultAlias}' is not declared`);
|
|
172
|
+
}
|
|
173
|
+
if (binding.allowEffortSelect && keys.length < 2) {
|
|
174
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}' allowEffortSelect requires at least two efforts`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function assertTypeProtocols(profile) {
|
|
178
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
179
|
+
if (!isValidProfileProtocol(profile.type, binding.protocol)) {
|
|
180
|
+
const valid = protocolsForProfileType(profile.type).join(', ');
|
|
181
|
+
throw new TheoremError('config', `Profile ${profile.id} model '${modelId}': type '${profile.type}' cannot use protocol '${binding.protocol}'. Supported: ${valid}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function profileModelFields(input) {
|
|
186
|
+
return {
|
|
187
|
+
models: input.models,
|
|
188
|
+
defaultModel: resolveDefaultModel(input.id, input),
|
|
189
|
+
allowModelSelect: input.allowModelSelect,
|
|
190
|
+
maxSteps: input.maxSteps,
|
|
191
|
+
key: input.key,
|
|
192
|
+
fallbackKey: input.fallbackKey,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function assertContinueKindList(profileId, path, kinds) {
|
|
196
|
+
if (!kinds?.length)
|
|
197
|
+
return;
|
|
198
|
+
for (const kind of kinds) {
|
|
199
|
+
if (!isContinueStopKind(kind)) {
|
|
200
|
+
throw new TheoremError('config', `Profile ${profileId}: turnBehaviour.resumption.${path} may only include ContinueStopKind ` + // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
201
|
+
`(length | stream_incomplete | provider_error); got '${kind}'`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function assertResumption(profileId, resumption) {
|
|
206
|
+
if (!resumption)
|
|
207
|
+
return;
|
|
208
|
+
assertContinueKindList(profileId, 'allowContinue', resumption.allowContinue);
|
|
209
|
+
assertContinueKindList(profileId, 'autoContinue', resumption.autoContinue);
|
|
210
|
+
}
|
|
211
|
+
function isRecord(value) {
|
|
212
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
213
|
+
}
|
|
214
|
+
/** `*` spans a map's entries; a path through an absent parent yields nothing. */
|
|
215
|
+
function valuesAt(root, path) {
|
|
216
|
+
let level = [root];
|
|
217
|
+
for (const key of path) {
|
|
218
|
+
level = level.flatMap((value) => {
|
|
219
|
+
if (!isRecord(value))
|
|
220
|
+
return [];
|
|
221
|
+
return key === '*' ? Object.values(value) : [value[key]];
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return level;
|
|
225
|
+
}
|
|
226
|
+
const REQUIRED_PATHS = Object.entries(PROFILE_FIELDS)
|
|
227
|
+
.filter(([, meta]) => meta.required === true)
|
|
228
|
+
.map(([path, meta]) => [path, meta.profileTypes])
|
|
229
|
+
.sort(([a], [b]) => a.split('.').length - b.split('.').length);
|
|
230
|
+
/** A definition may come over the network (a playground draft), so its shape is checked before anything reads it. */
|
|
231
|
+
function assertProfileShape(input) {
|
|
232
|
+
if (!isRecord(input)) {
|
|
233
|
+
throw new TheoremError('config', 'Profile definition must be an object'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
234
|
+
}
|
|
235
|
+
const id = typeof input.id === 'string' && input.id.trim() ? input.id : undefined;
|
|
236
|
+
if (!id) {
|
|
237
|
+
throw new TheoremError('config', 'Profile definition must set id'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
238
|
+
}
|
|
239
|
+
const type = PROFILE_TYPES.find((known) => known === input.type);
|
|
240
|
+
if (!type) {
|
|
241
|
+
throw new TheoremError('config', `Profile ${id}: type must be one of ${PROFILE_TYPES.join(', ')}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/** A field under an optional parent the definition leaves out is not required. */
|
|
245
|
+
function assertRequiredFields(input) {
|
|
246
|
+
const { id, type } = input;
|
|
247
|
+
const root = input;
|
|
248
|
+
for (const [path, profileTypes] of REQUIRED_PATHS) {
|
|
249
|
+
if (profileTypes && !profileTypes.includes(type))
|
|
250
|
+
continue;
|
|
251
|
+
const keys = path.split('.');
|
|
252
|
+
const parents = valuesAt(root, keys.slice(0, -1));
|
|
253
|
+
const last = keys[keys.length - 1];
|
|
254
|
+
const missing = parents.some((parent) => {
|
|
255
|
+
if (!isRecord(parent))
|
|
256
|
+
return false;
|
|
257
|
+
const value = parent[last];
|
|
258
|
+
return value === undefined || value === null || value === '';
|
|
259
|
+
});
|
|
260
|
+
if (missing) {
|
|
261
|
+
throw new TheoremError('config', `Profile ${id}: type '${type}' must set ${path}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** Covers untyped hosts that the definition types can't stop. */
|
|
266
|
+
function assertFieldScope(input) {
|
|
267
|
+
const [field] = outOfScopeFields(input);
|
|
268
|
+
if (!field)
|
|
269
|
+
return;
|
|
270
|
+
const { offValue, reason } = field.scope;
|
|
271
|
+
const allowed = offValue === undefined ? '' : ` (other than ${offValue})`;
|
|
272
|
+
throw new TheoremError('config', `Profile ${input.id}: type '${input.type}' must not set ${field.path}${allowed} — ${reason}`);
|
|
273
|
+
}
|
|
274
|
+
function assertTurnBehaviour(profileId, input) {
|
|
275
|
+
const tb = input.turnBehaviour;
|
|
276
|
+
assertResumption(profileId, tb?.resumption);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The canary lives in the system prompt, and speech has none: Gemini TTS rejects developer
|
|
280
|
+
* instructions and OpenAI-compatible `/audio/speech` has no field for one.
|
|
281
|
+
*/
|
|
282
|
+
function speechGuardrails(input) {
|
|
283
|
+
return { ...input.guardrails, canary: false };
|
|
284
|
+
}
|
|
285
|
+
function assertEgress(profileId, guardrails) {
|
|
286
|
+
const egress = guardrails?.egress;
|
|
287
|
+
const fail = (message) => new TheoremError('config', `Profile ${profileId}: ${message}`);
|
|
288
|
+
if (egress && (egress.enforce === undefined) === (egress.checks === undefined)) {
|
|
289
|
+
throw fail(
|
|
290
|
+
// lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
291
|
+
'guardrails.egress takes enforce (your own check) or checks (the bundled ones), one of the two');
|
|
292
|
+
}
|
|
293
|
+
if (egress?.enforce !== undefined && typeof egress.enforce !== 'function') {
|
|
294
|
+
throw fail('guardrails.egress.enforce must be a function'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
295
|
+
}
|
|
296
|
+
const problem = egress?.checks === undefined
|
|
297
|
+
? undefined
|
|
298
|
+
: egressChecksProblem('guardrails.egress.checks', egress.checks);
|
|
299
|
+
if (problem !== undefined)
|
|
300
|
+
throw fail(problem);
|
|
301
|
+
for (const key of ['maxRetries', 'holdback']) {
|
|
302
|
+
const value = egress?.[key];
|
|
303
|
+
if (value !== undefined && (!Number.isInteger(value) || value < 0)) {
|
|
304
|
+
throw new TheoremError('config', `Profile ${profileId}: guardrails.egress.${key} must be a non-negative integer`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (egress?.holdback !== undefined &&
|
|
308
|
+
(egress.enforce === undefined || streamPlanOf(egress.enforce))) {
|
|
309
|
+
throw new TheoremError('config', `Profile ${profileId}: guardrails.egress.holdback applies only to a host egress.enforce; the bundled policy holds exactly what could still become a match`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** A misspelt group would leave the group it meant on, silently. */
|
|
313
|
+
function assertRedactSensitive(profileId, guardrails) {
|
|
314
|
+
const selection = guardrails?.redactSensitive;
|
|
315
|
+
if (selection === undefined || typeof selection === 'boolean')
|
|
316
|
+
return;
|
|
317
|
+
const groups = new Set(SENSITIVE_GROUPS);
|
|
318
|
+
const bad = Object.entries(selection).find(([group, on]) => !groups.has(group) || typeof on !== 'boolean');
|
|
319
|
+
if (bad !== undefined) {
|
|
320
|
+
throw new TheoremError('config', `Profile ${profileId}: guardrails.redactSensitive.${bad[0]} is not a group (${SENSITIVE_GROUPS.join(', ')}) set to a boolean`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function assertObservability(profileId, spec) {
|
|
324
|
+
try {
|
|
325
|
+
const policy = resolveObservabilityPolicy(spec);
|
|
326
|
+
if (!Number.isFinite(policy.retainForDays)) {
|
|
327
|
+
throw new TheoremError('config', `Profile ${profileId}: observability.retainForDays must be a finite number`);
|
|
328
|
+
}
|
|
329
|
+
if (policy.rotateAfterMiB <= 0 || !Number.isFinite(policy.rotateAfterMiB)) {
|
|
330
|
+
throw new TheoremError('config', `Profile ${profileId}: observability.rotateAfterMiB must be a positive number`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
if (err instanceof TheoremError && err.message.startsWith('Profile ')) {
|
|
335
|
+
throw err;
|
|
336
|
+
}
|
|
337
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
338
|
+
throw new TheoremError('config', `Profile ${profileId}: ${message}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function assertIdentitySystem(input) {
|
|
342
|
+
if (input.type === 'host' || input.type === 'decision' || input.type === 'speech')
|
|
343
|
+
return;
|
|
344
|
+
const { system, systemByRole } = input.identity;
|
|
345
|
+
if (system !== undefined)
|
|
346
|
+
assertSystemPrompt(system, `Profile ${input.id} identity.system`);
|
|
347
|
+
for (const [role, prompt] of Object.entries(systemByRole ?? {})) {
|
|
348
|
+
assertSystemPrompt(prompt, `Profile ${input.id} identity.systemByRole.${role}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function defineProfile(input) {
|
|
352
|
+
assertProfileShape(input);
|
|
353
|
+
assertFieldScope(input);
|
|
354
|
+
assertRequiredFields(input);
|
|
355
|
+
if (input.lexicon)
|
|
356
|
+
validateLexiconOverrides(input.lexicon, `Profile ${input.id}`);
|
|
357
|
+
assertIdentitySystem(input);
|
|
358
|
+
assertRedactSensitive(input.id, input.guardrails);
|
|
359
|
+
if (input.type === 'host') {
|
|
360
|
+
return defineHostProfile(input);
|
|
361
|
+
}
|
|
362
|
+
if (input.type === 'decision') {
|
|
363
|
+
return defineDecisionProfile(input);
|
|
364
|
+
}
|
|
365
|
+
assertModelsNonEmpty(input.id, input.models);
|
|
366
|
+
assertTurnBehaviour(input.id, input);
|
|
367
|
+
assertEgress(input.id, input.guardrails);
|
|
368
|
+
assertObservability(input.id, input.observability);
|
|
369
|
+
assertSlotName(input.id, 'key', input.key);
|
|
370
|
+
assertSlotName(input.id, 'fallbackKey', input.fallbackKey);
|
|
371
|
+
for (const [modelId, binding] of Object.entries(input.models)) {
|
|
372
|
+
assertModelBinding(input.id, modelId, binding);
|
|
373
|
+
assertKeySlot(input.id, modelId, binding, input);
|
|
374
|
+
}
|
|
375
|
+
const identity = input.type === 'speech'
|
|
376
|
+
? { handle: input.identity.handle }
|
|
377
|
+
: {
|
|
378
|
+
handle: input.identity.handle,
|
|
379
|
+
system: input.identity.system,
|
|
380
|
+
systemByRole: input.identity.systemByRole,
|
|
381
|
+
};
|
|
382
|
+
const guardrails = input.guardrails;
|
|
383
|
+
const observability = input.observability;
|
|
384
|
+
const lexicon = input.lexicon;
|
|
385
|
+
const modelFields = profileModelFields(input);
|
|
386
|
+
let profile;
|
|
387
|
+
switch (input.type) {
|
|
388
|
+
case 'text':
|
|
389
|
+
profile = {
|
|
390
|
+
type: 'text',
|
|
391
|
+
id: input.id,
|
|
392
|
+
identity,
|
|
393
|
+
...modelFields,
|
|
394
|
+
tools: input.tools,
|
|
395
|
+
inputs: input.inputs,
|
|
396
|
+
outputs: input.outputs,
|
|
397
|
+
turnBehaviour: input.turnBehaviour,
|
|
398
|
+
guardrails,
|
|
399
|
+
observability,
|
|
400
|
+
lexicon,
|
|
401
|
+
};
|
|
402
|
+
break;
|
|
403
|
+
case 'image':
|
|
404
|
+
profile = {
|
|
405
|
+
type: 'image',
|
|
406
|
+
id: input.id,
|
|
407
|
+
identity,
|
|
408
|
+
...modelFields,
|
|
409
|
+
image: input.image,
|
|
410
|
+
tools: input.tools,
|
|
411
|
+
inputs: input.inputs,
|
|
412
|
+
outputs: input.outputs,
|
|
413
|
+
turnBehaviour: input.turnBehaviour,
|
|
414
|
+
guardrails,
|
|
415
|
+
observability,
|
|
416
|
+
lexicon,
|
|
417
|
+
};
|
|
418
|
+
assertImageAccept(profile.id, profile.inputs.attachments?.accept);
|
|
419
|
+
assertImagePinValues(profile.id, profile.image);
|
|
420
|
+
break;
|
|
421
|
+
case 'speech':
|
|
422
|
+
profile = {
|
|
423
|
+
type: 'speech',
|
|
424
|
+
id: input.id,
|
|
425
|
+
identity,
|
|
426
|
+
...modelFields,
|
|
427
|
+
speech: input.speech,
|
|
428
|
+
outputs: input.outputs,
|
|
429
|
+
turnBehaviour: input.turnBehaviour,
|
|
430
|
+
guardrails: speechGuardrails(input),
|
|
431
|
+
observability,
|
|
432
|
+
lexicon,
|
|
433
|
+
};
|
|
434
|
+
break;
|
|
435
|
+
case 'live': {
|
|
436
|
+
profile = {
|
|
437
|
+
type: 'live',
|
|
438
|
+
id: input.id,
|
|
439
|
+
identity,
|
|
440
|
+
...modelFields,
|
|
441
|
+
live: input.live,
|
|
442
|
+
tools: { allow: input.tools.allow },
|
|
443
|
+
turnBehaviour: input.turnBehaviour,
|
|
444
|
+
guardrails,
|
|
445
|
+
observability,
|
|
446
|
+
lexicon,
|
|
447
|
+
};
|
|
448
|
+
assertLiveIngressConfigured(profile);
|
|
449
|
+
assertLiveCompression(profile.id, profile.live.contextCompression);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
default: {
|
|
453
|
+
const _exhaustive = input;
|
|
454
|
+
throw new TheoremError('config', `Unknown profile type '${String(_exhaustive)}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
assertStructuredSlot(profile);
|
|
458
|
+
assertTypeProtocols(profile);
|
|
459
|
+
return profile;
|
|
460
|
+
}
|
|
461
|
+
/** A turn can pass no value outside the slot's choices, so any other mapped key is dead. */
|
|
462
|
+
function assertStructuredSlot(profile) {
|
|
463
|
+
if (profile.type === 'live')
|
|
464
|
+
return;
|
|
465
|
+
const structured = profile.outputs?.structured;
|
|
466
|
+
if (!structured || typeof structured === 'string')
|
|
467
|
+
return;
|
|
468
|
+
const choices = profileInputs(profile)?.slots?.[structured.by];
|
|
469
|
+
if (!choices) {
|
|
470
|
+
throw new TheoremError('config', `Profile ${profile.id}: outputs.structured.by '${structured.by}' is not a slot in inputs.slots`);
|
|
471
|
+
}
|
|
472
|
+
const unknown = Object.keys(structured.map).filter((value) => !choices.includes(value));
|
|
473
|
+
if (unknown.length) {
|
|
474
|
+
throw new TheoremError('config', `Profile ${profile.id}: outputs.structured.map maps ${unknown.join(', ')}, not a choice of slot '${structured.by}'`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const IMAGE_PIN_RULES = {
|
|
478
|
+
n: { ok: (v) => Number.isInteger(v) && v >= 1, rule: 'a whole number of 1 or more' }, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
479
|
+
seed: { ok: Number.isInteger, rule: 'a whole number' }, // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
480
|
+
outputCompression: {
|
|
481
|
+
ok: (v) => Number.isInteger(v) && v >= 0 && v <= 100,
|
|
482
|
+
rule: 'a whole number from 0 to 100', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
function assertImagePinValues(profileId, image) {
|
|
486
|
+
for (const [name, { ok, rule }] of Object.entries(IMAGE_PIN_RULES)) {
|
|
487
|
+
const value = image[name];
|
|
488
|
+
if (value !== undefined && !ok(value)) {
|
|
489
|
+
throw new TheoremError('config', `Profile ${profileId}: image.${name} must be ${rule}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
image.references?.forEach((reference, index) => {
|
|
493
|
+
const where = `Profile ${profileId}: image.references[${index}]`;
|
|
494
|
+
if (mediaKindForMime(reference.mimeType) !== 'image') {
|
|
495
|
+
throw new TheoremError('config', `${where} must be an image, not '${reference.mimeType}'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
496
|
+
}
|
|
497
|
+
const source = isTurnMediaRef(reference) ? reference.uri : reference.data;
|
|
498
|
+
if (!source) {
|
|
499
|
+
throw new TheoremError('config', `${where} needs its bytes or its uri`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
function assertImageAccept(profileId, accept) {
|
|
504
|
+
const outside = accept?.filter((rule) => !mimeAllowed(IMAGE_ATTACHMENT_ACCEPT_MIMES, rule));
|
|
505
|
+
if (!outside?.length)
|
|
506
|
+
return;
|
|
507
|
+
throw new TheoremError('config',
|
|
508
|
+
// lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
509
|
+
`Profile ${profileId}: an image profile's attachments take images, video and PDF only, ` +
|
|
510
|
+
`not ${outside.join(', ')}`);
|
|
511
|
+
}
|
|
512
|
+
function assertLiveCompression(profileId, spec) {
|
|
513
|
+
if (!spec)
|
|
514
|
+
return;
|
|
515
|
+
const tag = `Profile ${profileId} live.contextCompression`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
516
|
+
const trigger = spec.triggerTokens;
|
|
517
|
+
const target = spec.slidingWindow.targetTokens;
|
|
518
|
+
assertWholeTokens(`${tag}.triggerTokens`, trigger);
|
|
519
|
+
assertWholeTokens(`${tag}.slidingWindow.targetTokens`, target);
|
|
520
|
+
if (trigger !== undefined && target !== undefined && target >= trigger) {
|
|
521
|
+
throw new TheoremError('config',
|
|
522
|
+
// lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
523
|
+
`${tag}: slidingWindow.targetTokens must be below triggerTokens`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function assertWholeTokens(tag, value) {
|
|
527
|
+
if (value === undefined || (Number.isInteger(value) && value > 0))
|
|
528
|
+
return;
|
|
529
|
+
throw new TheoremError('config', `${tag} must be a whole number above 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
530
|
+
}
|
|
531
|
+
function assertCompactionSpec(registered, owner, modelId, spec) {
|
|
532
|
+
const tag = `Profile ${owner.id} model ${modelId} compaction`; // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
533
|
+
assertCompactionBudget(tag, spec);
|
|
534
|
+
assertCompactionRetain(tag, spec);
|
|
535
|
+
if (spec.meter != null && spec.meter !== 'history' && spec.meter !== 'input') {
|
|
536
|
+
throw new TheoremError('config', `${tag}: meter must be 'history' or 'input'`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
537
|
+
}
|
|
538
|
+
if (spec.profile === undefined) {
|
|
539
|
+
if (owner.type !== 'text' || owner.inputs.text === false) {
|
|
540
|
+
throw new TheoremError('config', `${tag}: a profile that compacts itself must be a text profile that takes text`);
|
|
541
|
+
}
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const compactor = registered.get(spec.profile);
|
|
545
|
+
if (!compactor) {
|
|
546
|
+
throw new TheoremError('config', `${tag}: compaction profile '${spec.profile}' must be registered before '${owner.id}'`);
|
|
547
|
+
}
|
|
548
|
+
if (compactor.type !== 'text' || compactor.inputs.text === false) {
|
|
549
|
+
throw new TheoremError('config', `${tag}: compaction profile '${spec.profile}' must be a text profile that takes text`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function assertCacheSpec(profileId, modelId, binding, spec) {
|
|
553
|
+
const tag = `Profile ${profileId} model '${modelId}'`;
|
|
554
|
+
if (binding.provider !== 'openrouter') {
|
|
555
|
+
throw new TheoremError('config', `${tag}: cache is only valid when protocol is 'openAi' and provider is 'openrouter'`);
|
|
556
|
+
}
|
|
557
|
+
if (!CACHE_MODES.includes(spec.mode)) {
|
|
558
|
+
throw new TheoremError('config', `${tag}: cache.mode must be one of ${CACHE_MODES.join(' | ')}`);
|
|
559
|
+
}
|
|
560
|
+
if (spec.ttl != null && !CACHE_TTLS.includes(spec.ttl)) {
|
|
561
|
+
throw new TheoremError('config', `${tag}: cache.ttl must be one of ${CACHE_TTLS.join(' | ')}`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function assertInteractionsPersistence(profileId, modelId, binding) {
|
|
565
|
+
if (binding.protocol === 'geminiInteractions') {
|
|
566
|
+
if (binding.persistViaInteractionId === undefined) {
|
|
567
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}': persistViaInteractionId is required on a 'geminiInteractions' binding — true chains on Google's stored interaction, false sends the host's history plus this turn's steps every call`);
|
|
568
|
+
}
|
|
569
|
+
if (binding.persistViaInteractionId && binding.store === false) {
|
|
570
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}': persistViaInteractionId: true needs store left on — Google chains only from a stored interaction`);
|
|
571
|
+
}
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (binding.store === undefined && binding.persistViaInteractionId === undefined) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const which = binding.store !== undefined && binding.persistViaInteractionId !== undefined
|
|
578
|
+
? 'store and persistViaInteractionId' // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
579
|
+
: binding.store !== undefined
|
|
580
|
+
? 'store'
|
|
581
|
+
: 'persistViaInteractionId';
|
|
582
|
+
throw new TheoremError('config', `Profile ${profileId} model '${modelId}': ${which} is only valid when protocol is 'geminiInteractions' and provider is 'google'`);
|
|
583
|
+
}
|
|
584
|
+
function assertCompactionBudget(tag, spec) {
|
|
585
|
+
if (spec.maxTokens <= 0) {
|
|
586
|
+
throw new TheoremError('config', `${tag}: maxTokens must be > 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
587
|
+
}
|
|
588
|
+
if (spec.compactAt <= 0 || spec.compactAt >= 1) {
|
|
589
|
+
throw new TheoremError('config', `${tag}: compactAt must be in (0, 1)`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function assertCompactionRetain(tag, spec) {
|
|
593
|
+
if (spec.previousExchanges < 0) {
|
|
594
|
+
throw new TheoremError('config', `${tag}: previousExchanges must be >= 0`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
595
|
+
}
|
|
596
|
+
if (spec.previousExchanges < 1 && spec.previousExchanges >= spec.compactAt) {
|
|
597
|
+
throw new TheoremError('config', `${tag}: previousExchanges as fraction (${spec.previousExchanges}) must be < compactAt (${spec.compactAt})`);
|
|
598
|
+
}
|
|
599
|
+
if (spec.previousExchanges > 1 && !Number.isInteger(spec.previousExchanges)) {
|
|
600
|
+
throw new TheoremError('config', `${tag}: previousExchanges >= 1 must be an integer`); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function assertCustomToolsOnly(tools, profile) {
|
|
604
|
+
for (const id of profileToolAllow(profile)) {
|
|
605
|
+
const tool = tools.get(id);
|
|
606
|
+
if (tool?.type === 'builtin') {
|
|
607
|
+
throw new TheoremError('config', profile.type === 'host'
|
|
608
|
+
? `Profile ${profile.id} lists builtin '${id}' in tools.allow — type 'host' never runs a model` // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
609
|
+
: `Profile ${profile.id} lists builtin '${id}' in tools.allow — declare it on models.*.builtInTools instead`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function assertProfileToolLoader(tools, profile) {
|
|
614
|
+
const loaderId = profileToolsSpec(profile)?.t2Loader;
|
|
615
|
+
if (!loaderId) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (!profileToolAllow(profile).includes(loaderId)) {
|
|
619
|
+
throw new TheoremError('config', `Profile ${profile.id} tools.t2Loader '${loaderId}' must also be listed in tools.allow`);
|
|
620
|
+
}
|
|
621
|
+
const tool = tools.get(loaderId);
|
|
622
|
+
if (tool?.type !== 'function') {
|
|
623
|
+
throw new TheoremError('config', `Profile ${profile.id} tools.t2Loader '${loaderId}' must be a registered type: 'function' tool`);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function* modelBuiltinIds(profile) {
|
|
627
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
628
|
+
for (const id of binding.builtInTools ?? []) {
|
|
629
|
+
yield { modelId, id };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function assertModelBuiltInTools(tools, profile) {
|
|
634
|
+
for (const { modelId, id } of modelBuiltinIds(profile)) {
|
|
635
|
+
const tool = tools.get(id);
|
|
636
|
+
if (tool?.type !== 'builtin') {
|
|
637
|
+
throw new TheoremError('config', `Profile ${profile.id} model '${modelId}' lists '${id}' in builtInTools — not a registered builtin`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function assertMediaLimits(profile) {
|
|
642
|
+
const inputs = profileInputs(profile);
|
|
643
|
+
if (!inputs) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const { attachments, voice, maxFiles, maxBytes, maxTurnBytes, limitsByMime } = inputs;
|
|
647
|
+
const limits = { maxFiles, maxBytes, maxTurnBytes };
|
|
648
|
+
if ((attachments || voice) && Object.values(limits).some((value) => value === undefined)) {
|
|
649
|
+
throw new TheoremError('config', `Profile ${profile.id} must set maxFiles, maxBytes, and maxTurnBytes`);
|
|
650
|
+
}
|
|
651
|
+
for (const [mime, value] of Object.entries(limitsByMime ?? {})) {
|
|
652
|
+
limits[`limitsByMime['${mime}']`] = value;
|
|
653
|
+
}
|
|
654
|
+
for (const [name, value] of Object.entries(limits)) {
|
|
655
|
+
if (value !== undefined && !(Number.isInteger(value) && value > 0)) {
|
|
656
|
+
throw new TheoremError('config', `Profile ${profile.id}: inputs.${name} must be a positive integer`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
/** Profiles are checked against `tools`, so register a scope's tools before its profiles. */
|
|
661
|
+
function createProfileRegistry(tools) {
|
|
662
|
+
const profiles = new Map();
|
|
663
|
+
const register = (profileInput) => {
|
|
664
|
+
const profile = defineProfile(profileInput);
|
|
665
|
+
assertCustomToolsOnly(tools, profile);
|
|
666
|
+
assertProfileToolLoader(tools, profile);
|
|
667
|
+
if (profile.type !== 'host' && profile.type !== 'decision') {
|
|
668
|
+
assertModelBuiltInTools(tools, profile);
|
|
669
|
+
assertMediaLimits(profile);
|
|
670
|
+
for (const [modelId, binding] of Object.entries(profile.models)) {
|
|
671
|
+
if (binding.compaction) {
|
|
672
|
+
assertCompactionSpec(profiles, profile, modelId, binding.compaction);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
profiles.set(profile.id, profile);
|
|
677
|
+
};
|
|
678
|
+
return {
|
|
679
|
+
register,
|
|
680
|
+
registerMany(profilesList) {
|
|
681
|
+
for (const p of profilesList) {
|
|
682
|
+
register(p);
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
get(id) {
|
|
686
|
+
const profile = profiles.get(id);
|
|
687
|
+
if (!profile) {
|
|
688
|
+
throw new TheoremError('config', `Unknown profile '${id}'`);
|
|
689
|
+
}
|
|
690
|
+
return profile;
|
|
691
|
+
},
|
|
692
|
+
find: (id) => profiles.get(id),
|
|
693
|
+
has: (id) => profiles.has(id),
|
|
694
|
+
list: () => [...profiles.values()],
|
|
695
|
+
clear: () => profiles.clear(),
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
export { createProfileRegistry, defineProfile };
|