@sema-agent/core 7.51.1 → 7.52.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/CHANGELOG.md +550 -2
- package/dist/agents/agent-transcript-tool.d.ts +1 -1
- package/dist/agents/cascade.js +4 -0
- package/dist/agents/cumulative-stats.d.ts +3 -0
- package/dist/agents/cumulative-stats.js +3 -0
- package/dist/agents/list-agents-tool.d.ts +1 -1
- package/dist/agents/read-inbox-tool.d.ts +1 -1
- package/dist/agents/repair-loop.js +4 -0
- package/dist/agents/send-message-tool.d.ts +11 -16
- package/dist/agents/send-message-tool.js +11 -13
- package/dist/agents/subagent.d.ts +5 -0
- package/dist/agents/subagent.js +7 -4
- package/dist/agents/tool-filter.d.ts +40 -13
- package/dist/agents/tool-filter.js +28 -14
- package/dist/bench/metrics.d.ts +1 -0
- package/dist/brain/anthropic.js +16 -12
- package/dist/brain/model-presets.d.ts +4 -5
- package/dist/brain/model-presets.js +4 -4
- package/dist/brain/open-responses.js +16 -12
- package/dist/brain/openai.js +16 -12
- package/dist/brain/request-params.d.ts +12 -33
- package/dist/brain/request-params.js +5 -2
- package/dist/brain/request-route.d.ts +9 -8
- package/dist/brain/request-route.js +1 -0
- package/dist/brain/route-adjudicator.d.ts +117 -47
- package/dist/brain/route-adjudicator.js +171 -42
- package/dist/brain/route-conformance.js +2 -2
- package/dist/brain/status-sink.d.ts +18 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/core/allow-entry.d.ts +42 -8
- package/dist/core/allow-entry.js +24 -17
- package/dist/core/auto-compaction.d.ts +23 -20
- package/dist/core/auto-compaction.js +37 -16
- package/dist/core/auto-promote.d.ts +2 -1
- package/dist/core/auto-promote.js +2 -2
- package/dist/core/caller-tool-name.d.ts +9 -0
- package/dist/core/caller-tool-name.js +13 -0
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +36 -12
- package/dist/core/checkpoint-store.js +1 -1
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +2 -1
- package/dist/core/credential-vocabulary.d.ts +33 -8
- package/dist/core/credential-vocabulary.js +32 -7
- package/dist/core/decision-receipt.d.ts +2 -2
- package/dist/core/delegation-frames.d.ts +9 -2
- package/dist/core/delegation-name.d.ts +18 -0
- package/dist/core/delegation-name.js +47 -0
- package/dist/core/engine-capabilities.d.ts +8 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +36 -19
- package/dist/core/frontmatter-fence.d.ts +10 -0
- package/dist/core/frontmatter-fence.js +6 -0
- package/dist/core/gate-fold.js +5 -23
- package/dist/core/gate-lanes.js +356 -270
- package/dist/core/governance-codes.d.ts +9 -4
- package/dist/core/governance-codes.js +9 -3
- package/dist/core/hooks.d.ts +276 -734
- package/dist/core/hooks.js +97 -192
- package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
- package/dist/{tools/fs → core}/image-admission.js +24 -6
- package/dist/core/locked-config.d.ts +26 -5
- package/dist/core/locked-config.js +31 -6
- package/dist/core/mcp-connection-pool.d.ts +4 -1
- package/dist/core/mcp-connection-pool.js +7 -2
- package/dist/core/mcp-probe.d.ts +12 -9
- package/dist/core/mcp-probe.js +2 -2
- package/dist/core/mcp-server-spec.d.ts +53 -7
- package/dist/core/mcp-server-spec.js +84 -1
- package/dist/core/mcp.d.ts +11 -7
- package/dist/core/mcp.js +27 -9
- package/dist/core/memory-engine/data-plane.js +5 -1
- package/dist/core/memory-engine/distiller.js +16 -2
- package/dist/core/memory-engine/engine.d.ts +8 -11
- package/dist/core/memory-engine/engine.js +9 -5
- package/dist/core/memory-engine/frontmatter.d.ts +6 -0
- package/dist/core/memory-engine/frontmatter.js +19 -16
- package/dist/core/memory.d.ts +2 -2
- package/dist/core/mode-policy.d.ts +4 -0
- package/dist/core/mode-policy.js +7 -5
- package/dist/core/model-seat.d.ts +4 -1
- package/dist/core/mods/dollar.d.ts +109 -0
- package/dist/core/mods/dollar.js +154 -0
- package/dist/core/mods/host.d.ts +23 -0
- package/dist/core/mods/host.js +21 -0
- package/dist/core/mods/kernel/budget.d.ts +43 -0
- package/dist/core/mods/kernel/budget.js +83 -0
- package/dist/core/mods/kernel/dispatch.d.ts +100 -0
- package/dist/core/mods/kernel/dispatch.js +701 -0
- package/dist/core/mods/kernel/events.d.ts +242 -0
- package/dist/core/mods/kernel/events.js +210 -0
- package/dist/core/mods/kernel/index.d.ts +6 -0
- package/dist/core/mods/kernel/index.js +5 -0
- package/dist/core/mods/kernel/receive.d.ts +65 -0
- package/dist/core/mods/kernel/receive.js +123 -0
- package/dist/core/mods/kernel/registration.d.ts +131 -0
- package/dist/core/mods/kernel/registration.js +102 -0
- package/dist/core/mods/kernel/site-rules.d.ts +84 -0
- package/dist/core/mods/kernel/site-rules.js +104 -0
- package/dist/core/mods/stations.d.ts +350 -0
- package/dist/core/mods/stations.js +444 -0
- package/dist/core/own-property-receipt.js +11 -23
- package/dist/core/parent-spec-seat.d.ts +18 -7
- package/dist/core/parent-spec-seat.js +6 -2
- package/dist/core/path-identity.js +2 -1
- package/dist/core/permission-rule-syntax.d.ts +15 -0
- package/dist/core/permission-rule-syntax.js +24 -16
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +26 -15
- package/dist/core/prompt-section-seat.d.ts +15 -27
- package/dist/core/prompt-section-seat.js +73 -40
- package/dist/core/protocol-http-auth.d.ts +3 -1
- package/dist/core/protocol-http-auth.js +2 -0
- package/dist/core/reminder-mint.d.ts +2 -2
- package/dist/core/remote-env.d.ts +24 -12
- package/dist/core/remote-env.js +1 -0
- package/dist/core/resume-startup-cause.d.ts +16 -1
- package/dist/core/resume-startup-cause.js +4 -1
- package/dist/core/retired-keys.d.ts +4 -0
- package/dist/core/retired-keys.js +3 -0
- package/dist/core/runner/announce-once-ledger.d.ts +31 -1
- package/dist/core/runner/announce-once-ledger.js +22 -0
- package/dist/core/runner/assemble-result.d.ts +9 -0
- package/dist/core/runner/assemble-result.js +3 -1
- package/dist/core/runner/call-hook-stations.d.ts +23 -27
- package/dist/core/runner/call-hook-stations.js +58 -42
- package/dist/core/runner/caller-stop-gate.d.ts +25 -0
- package/dist/core/runner/caller-stop-gate.js +7 -0
- package/dist/core/runner/compaction-seams.d.ts +8 -22
- package/dist/core/runner/compaction-seams.js +28 -53
- package/dist/core/runner/construction-doors.js +11 -1
- package/dist/core/runner/contracts.d.ts +85 -83
- package/dist/core/runner/credential-origin-seat.d.ts +2 -0
- package/dist/core/runner/credential-origin-seat.js +19 -0
- package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
- package/dist/core/runner/gate-exit.d.ts +47 -36
- package/dist/core/runner/gate-exit.js +8 -2
- package/dist/core/runner/image.js +1 -2
- package/dist/core/runner/inline-image.js +1 -1
- package/dist/core/runner/notice-seat-input.d.ts +11 -0
- package/dist/core/runner/notice-seat-input.js +1 -0
- package/dist/core/runner/person-path-rows.js +1 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
- package/dist/core/runner/prepare-ask-lane.js +6 -15
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
- package/dist/core/runner/prepare-config-doors.d.ts +5 -0
- package/dist/core/runner/prepare-config-doors.js +9 -0
- package/dist/core/runner/prepare-defer-classify.js +3 -4
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
- package/dist/core/runner/prepare-gate-stations.js +11 -10
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
- package/dist/core/runner/prepare-inherited-gate.js +8 -21
- package/dist/core/runner/prepare-memory-engine-session.js +1 -1
- package/dist/core/runner/prepare-park-ask.js +10 -14
- package/dist/core/runner/prepare-policy-chain.d.ts +18 -16
- package/dist/core/runner/prepare-policy-chain.js +98 -101
- package/dist/core/runner/prepare-project-context.js +7 -10
- package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
- package/dist/core/runner/prepare-prompt-assembly.js +134 -101
- package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
- package/dist/core/runner/prepare-prompt-inputs.js +5 -7
- package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
- package/dist/core/runner/prepare-protocol-tools.js +2 -2
- package/dist/core/runner/prepare-safety-scan.js +6 -10
- package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
- package/dist/core/runner/prepare-suspend-saga.js +7 -5
- package/dist/core/runner/prepare-task.js +33 -15
- package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
- package/dist/core/runner/prepare-turn-wiring.js +29 -27
- package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
- package/dist/core/runner/prepare-wiring-manifest.js +7 -7
- package/dist/core/runner/resume-apply.d.ts +5 -2
- package/dist/core/runner/resume-apply.js +24 -42
- package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
- package/dist/core/runner/resume-checkpoint-screen.js +9 -1
- package/dist/core/runner/resume-preflight.js +38 -16
- package/dist/core/runner/retired-runner-deps.js +3 -1
- package/dist/core/runner/run-brain-sinks.d.ts +3 -2
- package/dist/core/runner/run-brain-sinks.js +5 -1
- package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
- package/dist/core/runner/run-compaction-machinery.js +7 -33
- package/dist/core/runner/run-harness-handlers.js +4 -2
- package/dist/core/runner/run-identity-wiring.d.ts +3 -1
- package/dist/core/runner/run-identity-wiring.js +3 -2
- package/dist/core/runner/run-leg.js +62 -84
- package/dist/core/runner/run-notification-lane.js +1 -0
- package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
- package/dist/core/runner/run-recovery-lanes.js +11 -10
- package/dist/core/runner/run-settle-and-teardown.js +9 -28
- package/dist/core/runner/run-stop-and-final-verify.js +18 -25
- package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
- package/dist/core/runner/run-terminal-adoption.js +33 -22
- package/dist/core/runner/run-tool-mount-facts.js +5 -3
- package/dist/core/runner/run-turn-boundary.js +23 -34
- package/dist/core/runner/runtask.d.ts +6 -7
- package/dist/core/runner/runtask.js +14 -72
- package/dist/core/runner/session-rule-policy.d.ts +6 -4
- package/dist/core/runner/session-rule-policy.js +17 -10
- package/dist/core/runner/side-call-account.d.ts +68 -0
- package/dist/core/runner/side-call-account.js +61 -0
- package/dist/core/runner/stream-halt-verbs.js +12 -17
- package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
- package/dist/core/runner/stream-steer-verb.d.ts +1 -1
- package/dist/core/runner/stream-steer-verb.js +20 -38
- package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
- package/dist/core/runner/sub-agent-deny-lists.js +228 -71
- package/dist/core/runner/tool-end-body.d.ts +5 -6
- package/dist/core/runner/tool-end-body.js +6 -4
- package/dist/core/runner/tool-not-executed.d.ts +15 -0
- package/dist/core/runner/tool-not-executed.js +30 -0
- package/dist/core/runner/tool-occurrence.d.ts +8 -7
- package/dist/core/runner/tool-occurrence.js +5 -5
- package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
- package/dist/core/runner-deps.d.ts +20 -28
- package/dist/core/safety-merge-corpus.js +18 -2
- package/dist/core/secret-env.d.ts +93 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +93 -17
- package/dist/core/session-policy-store.js +178 -25
- package/dist/core/session-reconcile.d.ts +4 -0
- package/dist/core/session-reconcile.js +2 -1
- package/dist/core/shell-read-silence.js +3 -32
- package/dist/core/side-query.d.ts +5 -3
- package/dist/core/skill-grant.d.ts +3 -2
- package/dist/core/skill-grant.js +17 -9
- package/dist/core/skills-directory.d.ts +31 -11
- package/dist/core/skills-directory.js +655 -104
- package/dist/core/task-event.d.ts +13 -3
- package/dist/core/task-result.d.ts +8 -0
- package/dist/core/task-spec.d.ts +20 -19
- package/dist/core/task-stream.d.ts +14 -4
- package/dist/core/tighten-task-spec.d.ts +4 -3
- package/dist/core/tighten-task-spec.js +2 -4
- package/dist/core/tool-check-reading.d.ts +74 -0
- package/dist/core/tool-check-reading.js +80 -0
- package/dist/core/tool-policy.d.ts +59 -18
- package/dist/core/tool-policy.js +77 -12
- package/dist/core/tool-registry.d.ts +20 -0
- package/dist/core/tool-registry.js +4 -0
- package/dist/core/tool-spec.d.ts +10 -6
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +51 -32
- package/dist/core/types.d.ts +2 -1
- package/dist/core/types.js +1 -0
- package/dist/core/unc-spelling.d.ts +10 -0
- package/dist/core/unc-spelling.js +3 -0
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-egress.js +4 -1
- package/dist/core/user-attachment.d.ts +12 -2
- package/dist/core/user-attachment.js +17 -2
- package/dist/core/wiring-manifest.d.ts +14 -13
- package/dist/core/wiring-manifest.js +2 -2
- package/dist/engine/compaction/utils.js +1 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
- package/dist/engine/execution-env/symlink-chain.js +1 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/engine/harness/agent-harness.js +3 -3
- package/dist/engine/harness/types.d.ts +17 -16
- package/dist/engine/harness/types.js +7 -1
- package/dist/engine/llm/types.d.ts +12 -7
- package/dist/engine/loop/agent-loop.js +1 -1
- package/dist/engine/loop/types.d.ts +1 -1
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/import-validate.js +3 -10
- package/dist/engine/session/session.d.ts +4 -4
- package/dist/engine/session/session.js +4 -4
- package/dist/index.d.ts +24 -17
- package/dist/index.js +15 -12
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/run-spec.d.ts +4 -6
- package/dist/orchestration/run-spec.js +39 -63
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow.d.ts +11 -3
- package/dist/orchestration/workflow.js +11 -2
- package/dist/prompt-assembly/artifact-store.d.ts +37 -56
- package/dist/prompt-assembly/artifact-store.js +31 -49
- package/dist/prompt-assembly/artifact.d.ts +89 -76
- package/dist/prompt-assembly/artifact.js +167 -146
- package/dist/prompt-assembly/assemble.d.ts +34 -29
- package/dist/prompt-assembly/assemble.js +33 -220
- package/dist/prompt-assembly/bands.d.ts +20 -0
- package/dist/prompt-assembly/bands.js +18 -0
- package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
- package/dist/prompt-assembly/catalog/constitution.js +69 -0
- package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
- package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
- package/dist/prompt-assembly/catalog/index.d.ts +11 -0
- package/dist/prompt-assembly/catalog/index.js +59 -0
- package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
- package/dist/prompt-assembly/catalog/modes.js +137 -0
- package/dist/prompt-assembly/catalog/role.d.ts +10 -0
- package/dist/prompt-assembly/catalog/role.js +157 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
- package/dist/prompt-assembly/catalog/schema.js +25 -0
- package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
- package/dist/prompt-assembly/catalog/sections.js +98 -0
- package/dist/prompt-assembly/catalog/team.d.ts +4 -0
- package/dist/prompt-assembly/catalog/team.js +258 -0
- package/dist/prompt-assembly/composer.d.ts +24 -28
- package/dist/prompt-assembly/composer.js +124 -46
- package/dist/prompt-assembly/epoch.d.ts +89 -79
- package/dist/prompt-assembly/epoch.js +115 -142
- package/dist/prompt-assembly/explain.d.ts +58 -34
- package/dist/prompt-assembly/explain.js +21 -24
- package/dist/prompt-assembly/overlay.d.ts +127 -0
- package/dist/prompt-assembly/overlay.js +301 -0
- package/dist/prompt-assembly/renderers.d.ts +21 -0
- package/dist/prompt-assembly/renderers.js +39 -0
- package/dist/prompt-assembly/section-usage.d.ts +3 -3
- package/dist/prompt-assembly/section-usage.js +1 -1
- package/dist/prompt-assembly/select.d.ts +56 -0
- package/dist/prompt-assembly/select.js +69 -0
- package/dist/prompt-assembly/sugar.d.ts +31 -0
- package/dist/prompt-assembly/sugar.js +20 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
- package/dist/prompt-assembly/types.d.ts +45 -148
- package/dist/prompts/coordinator.d.ts +2 -21
- package/dist/prompts/coordinator.js +3 -255
- package/dist/prompts/cowork.d.ts +27 -16
- package/dist/prompts/cowork.js +2 -23
- package/dist/prompts/default.d.ts +38 -500
- package/dist/prompts/default.js +20 -285
- package/dist/prompts/supervisor.d.ts +6 -71
- package/dist/prompts/supervisor.js +6 -117
- package/dist/stores/file/session-policy-store.d.ts +3 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
- package/dist/tools/fs/dir-entries.d.ts +4 -2
- package/dist/tools/fs/dir-entries.js +16 -4
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +22 -11
- package/dist/tools/fs/fs-pdf.js +17 -13
- package/dist/tools/fs/fs-read.js +4 -3
- package/dist/tools/fs/fs-shared.d.ts +1 -10
- package/dist/tools/fs/fs-shared.js +3 -4
- package/dist/tools/fs/fs-write.js +2 -1
- package/dist/tools/fs/notebook.js +1 -1
- package/dist/tools/fs/pdf.d.ts +20 -10
- package/dist/tools/fs/pdf.js +19 -10
- package/dist/tools/fs/program-read-model.d.ts +68 -8
- package/dist/tools/fs/program-read-model.js +112 -33
- package/dist/tools/fs/program-read-model.rows.js +30 -10
- package/dist/tools/fs/read-deny-home.d.ts +25 -9
- package/dist/tools/fs/read-deny-home.js +28 -27
- package/dist/tools/fs/read-deny.d.ts +41 -11
- package/dist/tools/fs/read-deny.js +51 -5
- package/dist/tools/fs/safety.d.ts +4 -26
- package/dist/tools/fs/safety.js +10 -32
- package/dist/tools/fs/search.js +15 -10
- package/dist/tools/fs/shell-read-argv.d.ts +3 -2
- package/dist/tools/fs/shell-read-argv.js +118 -38
- package/dist/tools/fs/shell-read-establish.d.ts +5 -5
- package/dist/tools/fs/shell-read-establish.js +11 -61
- package/dist/tools/fs/shell-read-identity.js +6 -3
- package/dist/tools/fs/shell-read-judge.d.ts +13 -15
- package/dist/tools/fs/shell-read-judge.js +18 -4
- package/dist/tools/fs/shell-read-plan.d.ts +2 -2
- package/dist/tools/web.js +1 -1
- package/package.json +13 -1
- package/test/export-surface.snapshot.json +256 -99
- package/wire-changes/core@7.51.1.json +5 -5
- package/wire-changes/core@7.51.2.json +293 -0
- package/wire-changes/core@7.52.0.json +5859 -0
- package/dist/core/hook-seats.d.ts +0 -66
- package/dist/core/hook-seats.js +0 -37
- package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
- package/dist/prompt-assembly/packs/sema-default.js +0 -147
- package/dist/prompts/simple-sections.d.ts +0 -58
- package/dist/prompts/simple-sections.js +0 -43
|
@@ -5,13 +5,14 @@ import { STREAM_TORN_WARNING, createRepetitionPoll, modelOutputInvalidOf, noteRe
|
|
|
5
5
|
import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } from "./tool-call-id.js";
|
|
6
6
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
7
|
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
|
|
8
|
-
import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders } from "./request-params.js";
|
|
9
|
-
import {
|
|
8
|
+
import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
|
|
9
|
+
import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
|
|
10
10
|
import { requestRouteOf } from "./request-route.js";
|
|
11
11
|
import { declaredOffSpelling, mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
|
|
12
12
|
import { runStreamingBrain } from "./stream-engine.js";
|
|
13
13
|
import { refuseRetiredTimeoutKnobs } from "./timeout.js";
|
|
14
14
|
import { resolveFetch } from "./transport.js";
|
|
15
|
+
const OPEN_RESPONSES_KEY_HEADER = "authorization";
|
|
15
16
|
const DEGENERATE_POLL_CHARS = 64;
|
|
16
17
|
const MALFORMED_SAMPLE_CHARS = 160;
|
|
17
18
|
const MALFORMED_ARGS_CHARS = 200;
|
|
@@ -239,20 +240,22 @@ export function createOpenResponsesBrain(given = {}) {
|
|
|
239
240
|
maxRetries: options?.maxRetries,
|
|
240
241
|
stallTimeouts: options?.stallTimeouts,
|
|
241
242
|
buildRequest: (overrides) => {
|
|
242
|
-
const {
|
|
243
|
+
const routed = { id: model.id, baseUrl: model.baseUrl };
|
|
244
|
+
const { root, origin: rootOrigin } = resolveRequestRootOf(routed.baseUrl, config.baseUrl);
|
|
243
245
|
if (!root) {
|
|
244
246
|
throw new Error("createOpenResponsesBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
|
|
245
247
|
}
|
|
248
|
+
const modelHeaders = headerBagOnce(model.headers);
|
|
249
|
+
const optionsHeaders = headerBagOnce(options?.headers);
|
|
246
250
|
const routeCredential = resolveRouteCredential({
|
|
247
251
|
optionsApiKey: options?.apiKey,
|
|
248
|
-
optionsHeaders
|
|
249
|
-
modelHeaders
|
|
252
|
+
optionsHeaders,
|
|
253
|
+
modelHeaders,
|
|
250
254
|
configApiKey: config.apiKey,
|
|
251
255
|
configHeaders: config.headers,
|
|
256
|
+
keyHeader: OPEN_RESPONSES_KEY_HEADER,
|
|
252
257
|
});
|
|
253
|
-
const
|
|
254
|
-
if (!routeVerdict.ok)
|
|
255
|
-
throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
|
|
258
|
+
const routeAdmission = admitRouteRequest(routed, routeCredential, { baseUrl: config.baseUrl });
|
|
256
259
|
const apiKey = routeCredential.apiKey;
|
|
257
260
|
if (options?.stop !== undefined && options.stop.length > 0) {
|
|
258
261
|
throw new Error("createOpenResponsesBrain: options.stop is not expressible in the Open Responses request (the wire form has no stop-sequence field); " +
|
|
@@ -285,11 +288,11 @@ export function createOpenResponsesBrain(given = {}) {
|
|
|
285
288
|
}
|
|
286
289
|
if (effort !== undefined)
|
|
287
290
|
body.reasoning = { effort };
|
|
288
|
-
const headers = mergeHeaders(
|
|
289
|
-
applyRouteCredentialHeaders(headers, routeCredential
|
|
291
|
+
const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
|
|
292
|
+
applyRouteCredentialHeaders(headers, routeCredential);
|
|
290
293
|
lockHeader(headers, "content-type", "application/json");
|
|
291
294
|
if (apiKey)
|
|
292
|
-
headers
|
|
295
|
+
lockHeader(headers, OPEN_RESPONSES_KEY_HEADER, `Bearer ${apiKey}`);
|
|
293
296
|
const { body: wire, dropped: extraBodyDropped } = applyExtraBody(body, model.extraBody, RESPONSES_RESERVED);
|
|
294
297
|
return {
|
|
295
298
|
url: joinRequestUrl(root, "/responses"),
|
|
@@ -300,6 +303,7 @@ export function createOpenResponsesBrain(given = {}) {
|
|
|
300
303
|
outputCapTokens: effectiveOutputCap(wire, OUTPUT_CAP_KEYS.responses),
|
|
301
304
|
capDerivedPaths: OUTPUT_CAP_KEYS.responses,
|
|
302
305
|
onCommitted: () => {
|
|
306
|
+
routeAdmission();
|
|
303
307
|
sentMaxTokens = builtMaxTokens;
|
|
304
308
|
sentMaxTokensLane = builtMaxTokensLane;
|
|
305
309
|
sentEffort = effort;
|
|
@@ -773,5 +777,5 @@ export function createOpenResponsesBrain(given = {}) {
|
|
|
773
777
|
},
|
|
774
778
|
});
|
|
775
779
|
};
|
|
776
|
-
return { stream, adjudicateRoute: createBrainRouteJudge(config) };
|
|
780
|
+
return { stream, adjudicateRoute: createBrainRouteJudge({ ...config, keyHeader: OPEN_RESPONSES_KEY_HEADER }) };
|
|
777
781
|
}
|
package/dist/brain/openai.js
CHANGED
|
@@ -5,15 +5,16 @@ import { STREAM_TORN_WARNING, createRepetitionPoll, createStreamEndLedger, echoF
|
|
|
5
5
|
import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } from "./tool-call-id.js";
|
|
6
6
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
7
|
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
|
|
8
|
-
import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders } from "./request-params.js";
|
|
8
|
+
import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
|
|
9
9
|
import { BrainError, providerErrorWord } from "./errors.js";
|
|
10
10
|
import { inBandProviderError } from "./provider-dialect.js";
|
|
11
|
-
import {
|
|
11
|
+
import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
|
|
12
12
|
import { requestRouteOf } from "./request-route.js";
|
|
13
13
|
import { declaredOffSpelling, mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
|
|
14
14
|
import { runStreamingBrain } from "./stream-engine.js";
|
|
15
15
|
import { refuseRetiredTimeoutKnobs } from "./timeout.js";
|
|
16
16
|
import { resolveFetch } from "./transport.js";
|
|
17
|
+
const OPENAI_KEY_HEADER = "authorization";
|
|
17
18
|
function closeToolCallAccum(acc, responseIds, closed) {
|
|
18
19
|
if (acc.closedTc)
|
|
19
20
|
return acc.closedTc;
|
|
@@ -342,20 +343,22 @@ export function createOpenAIBrain(given = {}) {
|
|
|
342
343
|
maxRetries: options?.maxRetries,
|
|
343
344
|
stallTimeouts: options?.stallTimeouts,
|
|
344
345
|
buildRequest: (overrides) => {
|
|
345
|
-
const {
|
|
346
|
+
const routed = { id: model.id, baseUrl: model.baseUrl };
|
|
347
|
+
const { root, origin: rootOrigin } = resolveRequestRootOf(routed.baseUrl, config.baseUrl);
|
|
346
348
|
if (!root) {
|
|
347
349
|
throw new Error("createOpenAIBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
|
|
348
350
|
}
|
|
351
|
+
const modelHeaders = headerBagOnce(model.headers);
|
|
352
|
+
const optionsHeaders = headerBagOnce(options?.headers);
|
|
349
353
|
const routeCredential = resolveRouteCredential({
|
|
350
354
|
optionsApiKey: options?.apiKey,
|
|
351
|
-
optionsHeaders
|
|
352
|
-
modelHeaders
|
|
355
|
+
optionsHeaders,
|
|
356
|
+
modelHeaders,
|
|
353
357
|
configApiKey: config.apiKey,
|
|
354
358
|
configHeaders: config.headers,
|
|
359
|
+
keyHeader: OPENAI_KEY_HEADER,
|
|
355
360
|
});
|
|
356
|
-
const
|
|
357
|
-
if (!routeVerdict.ok)
|
|
358
|
-
throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
|
|
361
|
+
const routeAdmission = admitRouteRequest(routed, routeCredential, { baseUrl: config.baseUrl });
|
|
359
362
|
const apiKey = routeCredential.apiKey;
|
|
360
363
|
const body = {
|
|
361
364
|
model: model.id,
|
|
@@ -385,11 +388,11 @@ export function createOpenAIBrain(given = {}) {
|
|
|
385
388
|
if (options?.stop)
|
|
386
389
|
body.stop = options.stop;
|
|
387
390
|
applyThinking(body, model, options?.reasoning);
|
|
388
|
-
const headers = mergeHeaders(
|
|
389
|
-
applyRouteCredentialHeaders(headers, routeCredential
|
|
391
|
+
const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
|
|
392
|
+
applyRouteCredentialHeaders(headers, routeCredential);
|
|
390
393
|
lockHeader(headers, "content-type", "application/json");
|
|
391
394
|
if (apiKey)
|
|
392
|
-
headers
|
|
395
|
+
lockHeader(headers, OPENAI_KEY_HEADER, `Bearer ${apiKey}`);
|
|
393
396
|
const { body: wire, dropped: extraBodyDropped } = applyExtraBody(body, model.extraBody, OPENAI_RESERVED);
|
|
394
397
|
return {
|
|
395
398
|
url: joinRequestUrl(root, "/chat/completions"),
|
|
@@ -400,6 +403,7 @@ export function createOpenAIBrain(given = {}) {
|
|
|
400
403
|
outputCapTokens: effectiveOutputCap(wire, OUTPUT_CAP_KEYS.openai),
|
|
401
404
|
capDerivedPaths: [mtField],
|
|
402
405
|
onCommitted: () => {
|
|
406
|
+
routeAdmission();
|
|
403
407
|
sentMaxTokens = builtMaxTokens;
|
|
404
408
|
sentMaxTokensLane = builtMaxTokensLane;
|
|
405
409
|
},
|
|
@@ -749,5 +753,5 @@ export function createOpenAIBrain(given = {}) {
|
|
|
749
753
|
},
|
|
750
754
|
});
|
|
751
755
|
};
|
|
752
|
-
return { stream, adjudicateRoute: createBrainRouteJudge(config) };
|
|
756
|
+
return { stream, adjudicateRoute: createBrainRouteJudge({ ...config, keyHeader: OPENAI_KEY_HEADER }) };
|
|
753
757
|
}
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request-parameter passthrough (design/40). A deployment can set official provider params it wants but
|
|
3
|
-
* core doesn't model as first-class fields (OpenAI `frequency_penalty`/`presence_penalty`/`top_p`/`seed`/
|
|
4
|
-
* `logit_bias`; Anthropic `top_k`/`top_p`/`stop_sequences`; …) via `Model.extraBody`, and core merges them
|
|
5
|
-
* into the brain's request body. Anchored at the **Model** level (not per-turn) so the serialized body
|
|
6
|
-
* stays byte-stable across turns → the prefix cache holds (design/9/31).
|
|
7
|
-
*
|
|
8
|
-
* Symmetric: each brain owns its structural + tuning keys (`reserved`) and ALWAYS wins — `extraBody` can
|
|
9
|
-
* only supply the long tail, never clobber `messages`/`tools`/`temperature`/etc. Stripping is the safety
|
|
10
|
-
* enforcement (the brains are standalone-usable), and a strip is never silent: {@link applyExtraBody}
|
|
11
|
-
* RETURNS the keys it stripped, the brain reports them on the request it built
|
|
12
|
-
* (`SSERequest.extraBodyDropped`), the stream engine reports them to the run's sink when it commits that
|
|
13
|
-
* request (`reportExtraBodyDropped`), and the Runner announces them (`route.extra_body_reserved_dropped`).
|
|
14
|
-
* One reading, at the one place the strip happens — so an `extraBody` a host brain swapped in for a single
|
|
15
|
-
* request, or one on the compaction model, is said the same way the task's own model spec is.
|
|
16
|
-
*/
|
|
17
1
|
/** OpenAI request-body keys core owns; `extraBody` cannot override these. */
|
|
18
2
|
export declare const OPENAI_RESERVED: ReadonlySet<string>;
|
|
19
3
|
/** Anthropic request-body keys core owns; `extraBody` cannot override these. (Note the asymmetry vs
|
|
@@ -43,16 +27,6 @@ export declare function applyExtraBody(body: Record<string, unknown>, extraBody:
|
|
|
43
27
|
body: Record<string, unknown>;
|
|
44
28
|
dropped: readonly string[];
|
|
45
29
|
};
|
|
46
|
-
/** The auth-bearing header names (case-fold), single-sourced for the credential enforcement
|
|
47
|
-
* (`applyRouteCredentialHeaders`, route-adjudicator.ts — the ONE authority over auth spelling: a
|
|
48
|
-
* per-model winner strips every carrier, any case, and re-asserts the winning bag's own),
|
|
49
|
-
* {@link mergeHeaders} (which EXEMPTS them — see there), and the route credential resolver (which
|
|
50
|
-
* reads carrier PRESENCE per bag to classify a headers-borne credential). The former per-call
|
|
51
|
-
* strip helper that lived here (`stripAuthHeaders` — "per-call auth replaces construction-time
|
|
52
|
-
* auth", triggered on `options.apiKey` presence) is retired: the three brains now run the
|
|
53
|
-
* route-credential resolution instead, whose per-model arm strips a strict superset of what the
|
|
54
|
-
* helper did. */
|
|
55
|
-
export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
|
|
56
30
|
/**
|
|
57
31
|
* #343 — the shared USER-HEADER merge layer (`model.headers` → construction `config.headers` →
|
|
58
32
|
* per-call `options.headers`, later bag wins), CASE-FOLD deduplicated: HTTP header field names are
|
|
@@ -62,10 +36,11 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
|
|
|
62
36
|
* override silently defeated. A later bag's entry replaces an earlier case-variant instead; the
|
|
63
37
|
* WINNER'S spelling and value survive (a single-spelling config is byte-identical on the wire).
|
|
64
38
|
*
|
|
65
|
-
* EXEMPT: the
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
39
|
+
* EXEMPT: the credential headers ({@link isCredentialHeaderName}, the engine's one reading of which
|
|
40
|
+
* header carries a credential: `authorization`, `x-api-key`, `api-key`, `cookie`, …, any case) pass
|
|
41
|
+
* through with plain spread semantics (same-spelling override only, no case-fold dedup), so that the
|
|
42
|
+
* credential enforcement (`applyRouteCredentialHeaders`, route-adjudicator.ts) stays the ONE authority
|
|
43
|
+
* over credential spelling and this layer never becomes a second, subtly different one.
|
|
69
44
|
*
|
|
70
45
|
* The exemption is NOT what keeps a header-only `ANTHROPIC_AUTH_TOKEN` alive: dedup keeps the
|
|
71
46
|
* winner's spelling, a lone `Authorization` has nothing to dedup against, and the credential
|
|
@@ -90,9 +65,9 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
|
|
|
90
65
|
* user-bag merge precisely so they "can NEVER be overridden" (council design/40) — and without the
|
|
91
66
|
* delete a user bag carrying `Content-Type: text/plain` survives BESIDE the lowercase lock, the
|
|
92
67
|
* platform `Headers` fold turning the pair into `text/plain, application/json` on the wire: a lock
|
|
93
|
-
* that decides nothing.
|
|
94
|
-
*
|
|
95
|
-
*
|
|
68
|
+
* that decides nothing. WHICH credential headers ride is the jurisdiction of `applyRouteCredentialHeaders`
|
|
69
|
+
* (route-adjudicator.ts); the key header a brain writes after it (`authorization` / `x-api-key`, from the
|
|
70
|
+
* apiKey knob) is a lock like any other — one value per name, whatever spelling a bag carried.
|
|
96
71
|
*/
|
|
97
72
|
export declare function lockHeader(headers: Record<string, string>, lowerName: string, value: string): void;
|
|
98
73
|
/**
|
|
@@ -103,6 +78,10 @@ export declare function lockHeader(headers: Record<string, string>, lowerName: s
|
|
|
103
78
|
* losing the user's betas from the merge AND double-sending the header).
|
|
104
79
|
*/
|
|
105
80
|
export declare function takeHeaderCasefold(headers: Record<string, string>, lowerName: string): string | undefined;
|
|
81
|
+
/** A caller's header bag read ONCE, into a plain copy of its own enumerable entries — what a request build hands both
|
|
82
|
+
* the route law (which credential rides, and its identity) and the merge (what goes on the wire), so a bag whose getters
|
|
83
|
+
* or keys answer differently on a second read cannot be judged as one bag and sent as another. */
|
|
84
|
+
export declare function headerBagOnce(bag: Readonly<Record<string, string>> | undefined): Record<string, string> | undefined;
|
|
106
85
|
export declare function mergeHeaders(...bags: Array<Record<string, string> | undefined>): Record<string, string>;
|
|
107
86
|
/**
|
|
108
87
|
* The output-cap key(s) each lane's wire form uses. A lane's set is exactly the keys THAT lane's
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isCredentialHeaderName } from "../core/secret-env.js";
|
|
1
2
|
export const OPENAI_RESERVED = new Set([
|
|
2
3
|
"model",
|
|
3
4
|
"messages",
|
|
@@ -52,7 +53,6 @@ export function applyExtraBody(body, extraBody, reserved) {
|
|
|
52
53
|
}
|
|
53
54
|
return { body: { ...passthrough, ...body }, dropped: dropped.length === 0 ? NO_KEYS : dropped };
|
|
54
55
|
}
|
|
55
|
-
export const AUTH_CARRIER_NAMES = new Set(["authorization", "x-api-key"]);
|
|
56
56
|
export function lockHeader(headers, lowerName, value) {
|
|
57
57
|
for (const k of Object.keys(headers)) {
|
|
58
58
|
if (k !== lowerName && k.toLowerCase() === lowerName)
|
|
@@ -70,6 +70,9 @@ export function takeHeaderCasefold(headers, lowerName) {
|
|
|
70
70
|
}
|
|
71
71
|
return undefined;
|
|
72
72
|
}
|
|
73
|
+
export function headerBagOnce(bag) {
|
|
74
|
+
return bag === undefined ? undefined : { ...bag };
|
|
75
|
+
}
|
|
73
76
|
export function mergeHeaders(...bags) {
|
|
74
77
|
const out = {};
|
|
75
78
|
const spellingByFold = new Map();
|
|
@@ -78,7 +81,7 @@ export function mergeHeaders(...bags) {
|
|
|
78
81
|
continue;
|
|
79
82
|
for (const [name, value] of Object.entries(bag)) {
|
|
80
83
|
const fold = name.toLowerCase();
|
|
81
|
-
if (
|
|
84
|
+
if (isCredentialHeaderName(name)) {
|
|
82
85
|
out[name] = value;
|
|
83
86
|
continue;
|
|
84
87
|
}
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
import type { RouteCredential } from "../internal/llm.js";
|
|
14
14
|
/** Where the credential a request would carry came from.
|
|
15
15
|
* - `"per-model"` — resolved FOR this model: a per-call key/headers from the per-model auth hook
|
|
16
|
-
* (`getApiKeyAndHeaders`), or
|
|
17
|
-
*
|
|
16
|
+
* (`getApiKeyAndHeaders`), or a credential header declared on `Model.headers`. Bound to the
|
|
17
|
+
* origin of the first request sent carrying it; refused only on its way to another origin.
|
|
18
18
|
* - `"deployment-config"` — the brain construction-time fallback (`config.apiKey` / an auth header
|
|
19
19
|
* in `config.headers`). Paired only with the deployment's own root (`config.baseUrl`, else the lane's default).
|
|
20
20
|
* - `"absent"` — no credential anywhere. Legal for keyless gateways; refused only where an entry
|
|
@@ -40,8 +40,9 @@ export type RequestRootOrigin = (typeof REQUEST_ROOT_ORIGINS)[number];
|
|
|
40
40
|
* - `credentialSource` — {@link RouteCredentialSource}.
|
|
41
41
|
* - `carrier` — how the credential rode ({@link RouteCarrier}); absent exactly when `credentialSource` is `"absent"`.
|
|
42
42
|
* - `carrierBag` — the header bag whose auth header rode ({@link RouteCarrierBag}); present exactly when one did:
|
|
43
|
-
* with carrier `"header"`, and beside carrier `"apiKey"`
|
|
44
|
-
* and an auth header in `
|
|
43
|
+
* with carrier `"header"`, and beside carrier `"apiKey"` when one source sets both — the per-model hook's answer
|
|
44
|
+
* (`apiKey` and an auth header in its `headers`) or the deployment (`config.apiKey` and an auth header in
|
|
45
|
+
* `config.headers`); both ride.
|
|
45
46
|
* - `root` — which knob named the request's root ({@link RequestRootOrigin}).
|
|
46
47
|
* The legal shapes are exactly the rows of {@link ROUTE_CREDENTIAL_PAIRING}.
|
|
47
48
|
*/
|
|
@@ -62,16 +63,16 @@ export interface RouteCredentialPairingRow {
|
|
|
62
63
|
/** The knob(s) the credential is read from, in this engine's own spelling (`getApiKeyAndHeaders` is the per-model
|
|
63
64
|
* auth hook, whose answer reaches the brain as the per-call options). Empty for `"absent"`. */
|
|
64
65
|
readonly credentialKnobs: readonly string[];
|
|
65
|
-
/** The root knob the credential is paired with: `"Model.baseUrl"` for a per-model credential (paired
|
|
66
|
-
*
|
|
66
|
+
/** The root knob the credential is paired with: `"Model.baseUrl"` for a per-model credential (paired with the
|
|
67
|
+
* ORIGIN that knob named the first time the credential was judged), `"config.baseUrl"` for the deployment credential (paired only
|
|
67
68
|
* when the request targets that root — or, undeclared, the lane's own default endpoint). Absent for
|
|
68
69
|
* `"absent"`. */
|
|
69
70
|
readonly pairsWith?: "Model.baseUrl" | "config.baseUrl";
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
72
73
|
* The pairing law as a table — one row per route shape a request can carry, in resolution precedence (an earlier
|
|
73
|
-
* row's source wins when several are configured; the three
|
|
74
|
-
* judged as one). A consumer naming "which key was that" reads the row a {@link RequestRoute} lands on (same
|
|
74
|
+
* row's source wins when several are configured; one rank per source — the first three rows are the per-model hook's
|
|
75
|
+
* answer, the three deployment rows the deployment config, each source's credential judged as one). A consumer naming "which key was that" reads the row a {@link RequestRoute} lands on (same
|
|
75
76
|
* `credentialSource` / `carrier` / `carrierBag`), never a sentence. The verdicts the law gives these shapes are the
|
|
76
77
|
* conformance corpus's (`ROUTE_ADJUDICATION_CONFORMANCE_CORPUS`).
|
|
77
78
|
*/
|
|
@@ -14,6 +14,7 @@ export function requestRouteOf(credential, root) {
|
|
|
14
14
|
}
|
|
15
15
|
export const ROUTE_CREDENTIAL_PAIRING = Object.freeze([
|
|
16
16
|
{ credentialSource: "per-model", carrier: "apiKey", credentialKnobs: ["getApiKeyAndHeaders().apiKey"], pairsWith: "Model.baseUrl" },
|
|
17
|
+
{ credentialSource: "per-model", carrier: "apiKey", carrierBag: "options", credentialKnobs: ["getApiKeyAndHeaders().apiKey", "getApiKeyAndHeaders().headers"], pairsWith: "Model.baseUrl" },
|
|
17
18
|
{ credentialSource: "per-model", carrier: "header", carrierBag: "options", credentialKnobs: ["getApiKeyAndHeaders().headers"], pairsWith: "Model.baseUrl" },
|
|
18
19
|
{ credentialSource: "per-model", carrier: "header", carrierBag: "model", credentialKnobs: ["Model.headers"], pairsWith: "Model.baseUrl" },
|
|
19
20
|
{ credentialSource: "deployment-config", carrier: "apiKey", credentialKnobs: ["config.apiKey"], pairsWith: "config.baseUrl" },
|
|
@@ -11,10 +11,19 @@
|
|
|
11
11
|
* external consumers (a server annotating a model roster, a conformance suite) import so there is
|
|
12
12
|
* exactly one implementation of the law.
|
|
13
13
|
*
|
|
14
|
-
* The judgment, by credential source (see {@link RouteCredentialSource})
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* The judgment, by credential source (see {@link RouteCredentialSource}). ONE law for both credential
|
|
15
|
+
* sources — **a credential has exactly one root** — differing only in where that root comes from:
|
|
16
|
+
* - `per-model` → a per-model credential (auth hook or a credential header on `Model.headers`) is bound to the
|
|
17
|
+
* ORIGIN (scheme + host + port) of the first request the engine SENT carrying it, in the engine's one
|
|
18
|
+
* credential-origin table ({@link credentialOriginTable}) — written only by a brain's request gate when the request
|
|
19
|
+
* goes out ({@link admitRouteRequest}), read by every judgment; a request carrying it to another origin is
|
|
20
|
+
* refused (`route.credential_origin_mismatch`), a request to another path of the same origin is not. An
|
|
21
|
+
* entry whose `baseUrl` moves to another host while its credential stays the same therefore sends
|
|
22
|
+
* nothing: the credential belongs to the host it was first sent to, and a NEW credential (the entry's
|
|
23
|
+
* credential reference changed with its address) is a new binding. The binding lasts for the process. Before any
|
|
24
|
+
* origin is read, a per-model credential is ONE value per header: a request whose per-call `apiKey` is set beside a
|
|
25
|
+
* header of the very name its lane writes that key in carries two values for one header, and is refused
|
|
26
|
+
* (`route.credential_answer_conflict`) wherever it is going.
|
|
18
27
|
* - `deployment-config` → a deployment credential has exactly ONE root: the one its brain sends to when
|
|
19
28
|
* an entry names none — `config.baseUrl`, else the lane's own default endpoint
|
|
20
29
|
* (`RoutePairingConfig.laneDefault`). A request to that root is `"paired"`; a request to any other
|
|
@@ -32,13 +41,18 @@
|
|
|
32
41
|
* Bad-value loudness (garbage / contradiction / absence / misplacement):
|
|
33
42
|
* - garbage — non-string URL/credential inputs throw a `TypeError` here (a programmer error must
|
|
34
43
|
* not be laundered into a polite verdict); whitespace-only URLs normalize to "" (undeclared).
|
|
35
|
-
* - contradiction — the torn pair refuses with `route.credential_mismatch` + both halves named
|
|
44
|
+
* - contradiction — the torn pair refuses with `route.credential_mismatch` + both halves named; a bound
|
|
45
|
+
* per-model credential sent elsewhere refuses with `route.credential_origin_mismatch` + both origins named; a
|
|
46
|
+
* per-call key beside its own header refuses with `route.credential_answer_conflict` + both header names named.
|
|
47
|
+
* Each is a VERDICT, one shape for every caller of the law ({@link adjudicateModelRoute}, which throws on garbage
|
|
48
|
+
* alone): the request gate, a pre-flight, the read face and a failover chain read the same refusal.
|
|
36
49
|
* - absence — `route.credential_missing` with the fix hint, exactly where absence is a hole.
|
|
37
50
|
* - misplacement — a credential in the URL, or a non-http(s) root: {@link assertServableRequestRoot}.
|
|
38
51
|
*/
|
|
39
52
|
import type { Model } from "../internal/llm.js";
|
|
40
53
|
import type { RouteAdjudication, RouteCredential, RoutePairingConfig, RoutePairingPosture } from "../internal/llm.js";
|
|
41
54
|
import type { Brain, EngineNotice } from "../core/types.js";
|
|
55
|
+
import { BrainError } from "./errors.js";
|
|
42
56
|
import type { RequestRootOrigin } from "./request-route.js";
|
|
43
57
|
/** A base URL's ROUTE KEY — the value the pairing law COMPARES, folded to WIRE equivalence: the
|
|
44
58
|
* halves the wire itself treats as case-insensitive (scheme and host, RFC 9110) fold, the scheme's
|
|
@@ -119,25 +133,39 @@ export declare function resolveRequestRootOf(modelBaseUrl: string | undefined, c
|
|
|
119
133
|
root: string;
|
|
120
134
|
origin: RequestRootOrigin;
|
|
121
135
|
};
|
|
122
|
-
/** Whether a header bag carries
|
|
136
|
+
/** Whether a header bag carries a credential header (any case) — the engine's one reading of which header carries a
|
|
137
|
+
* credential ({@link isCredentialHeaderName}: `Authorization`, `X-Api-Key`, Azure's `api-key`, `x-goog-api-key`,
|
|
138
|
+
* `Cookie`, …), the same reading the withheld-value intake keeps. */
|
|
123
139
|
export declare function hasAuthCarrier(headers: Record<string, string> | undefined): boolean;
|
|
140
|
+
/** Test seam: empties the process's credential-origin table — every per-model credential is unbound again, as at a
|
|
141
|
+
* process start. Never called by production code (a binding lasts as long as the process; restarting rebinds). A test
|
|
142
|
+
* suite that sends one fixture credential to two local mock upstreams (two `listen(0)` ports are two origins) calls it
|
|
143
|
+
* between cases, or gives each case its own fixture credential. */
|
|
144
|
+
export declare function __resetCredentialOriginBindings(): void;
|
|
124
145
|
/**
|
|
125
146
|
* Resolve WHICH credential a request would carry, tagged with its source — the input half of the
|
|
126
|
-
* adjudication.
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
147
|
+
* adjudication. One rank per SOURCE, most-specific wins; within a rank the apiKey knob and the rank's
|
|
148
|
+
* credential headers are ONE credential (they ride together and are bound together):
|
|
149
|
+
* 1. the per-model auth hook's answer — `options.apiKey` (presence, not truthiness: an empty string
|
|
150
|
+
* is a present credential that fails closed at emit — no key header, no fallback to the
|
|
151
|
+
* deployment's) and/or a credential header ({@link hasAuthCarrier}) in `options.headers` (a
|
|
152
|
+
* gateway's own `Helicone-Auth` beside the key, or the key spelled as a header — not both: an `apiKey`
|
|
153
|
+
* beside a header of the very name the lane writes the key in (`keyHeader`) is resolved all the same, with
|
|
154
|
+
* that conflict recorded beside it for the law to refuse). The bag MUST be
|
|
155
|
+
* per-model sourced (the hook's headers) — that provenance is what makes it a credential of THIS
|
|
156
|
+
* entry, bound to its own origin below. A caller that folds run-wide headers into the per-call
|
|
157
|
+
* options must strip credential headers first (the harness merge does), or a deployment
|
|
133
158
|
* credential would ride here and be judged per-model,
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* dual-auth escape shape, and both ride exactly as they always have; the pairing gate judges
|
|
159
|
+
* 2. a credential header in `model.headers` (the ENTRY's own declared credential),
|
|
160
|
+
* 3. the deployment config (`config.apiKey` and/or a credential header in `config.headers` — the
|
|
161
|
+
* documented gateway dual-auth shape, both riding as they always have; the pairing gate judges
|
|
138
162
|
* the pair as one deployment credential),
|
|
139
|
-
*
|
|
140
|
-
* 1–
|
|
163
|
+
* 4. absent.
|
|
164
|
+
* 1–2 are `per-model`; 3 is `deployment-config`. A per-model credential that carries a secret leaves
|
|
165
|
+
* here with its identity beside it (a digest, held by this module and never a member of the returned
|
|
166
|
+
* object), which {@link adjudicateModelRoute} reads against the origin table and {@link admitRouteRequest}
|
|
167
|
+
* binds when its request is sent. Resolution does not judge the route: what it found — an identity, a key beside
|
|
168
|
+
* its own header — is the law's to rule on, in one verdict shape for every caller.
|
|
141
169
|
*/
|
|
142
170
|
export declare function resolveRouteCredential(input: {
|
|
143
171
|
optionsApiKey?: string;
|
|
@@ -145,10 +173,23 @@ export declare function resolveRouteCredential(input: {
|
|
|
145
173
|
modelHeaders?: Record<string, string>;
|
|
146
174
|
configApiKey?: string;
|
|
147
175
|
configHeaders?: Record<string, string>;
|
|
176
|
+
/** The header the caller's lane writes a per-call `apiKey` in (`authorization`, `x-api-key`), when it has one. Per-call
|
|
177
|
+
* options that carry an `apiKey` and a header of that name (any case) are two values for one header: the credential
|
|
178
|
+
* is resolved with that conflict beside it, and the law refuses it (`route.credential_answer_conflict`) rather than
|
|
179
|
+
* let the key win. Optional like every input here: a caller that does not know its lane leaves it out, and then no
|
|
180
|
+
* conflict is recorded and none is refused. */
|
|
181
|
+
keyHeader?: string;
|
|
148
182
|
}): RouteCredential;
|
|
149
183
|
/**
|
|
150
|
-
* The pairing law (module note above).
|
|
151
|
-
*
|
|
184
|
+
* The pairing law (module note above). A READ: it judges against the engine's credential-origin table and writes
|
|
185
|
+
* nothing, so a pre-flight, the read face ({@link routePairingStatus}) and any consumer may call it freely — same
|
|
186
|
+
* inputs and same table, same verdict. A per-model credential {@link resolveRouteCredential} resolved with a key beside
|
|
187
|
+
* its own header is refused first (`route.credential_answer_conflict`, both header names in the fix — wherever the
|
|
188
|
+
* request goes, since the request itself is contradictory); otherwise it is judged against the origin it is bound to
|
|
189
|
+
* (refused when that is another one); one not bound yet, or built by hand (no resolution facts — the published corpus
|
|
190
|
+
* is such input), is servable. Every refusal is a verdict: the law throws on garbage alone. The table is written by one
|
|
191
|
+
* seat only: a first-party brain's request gate ({@link admitRouteRequest}), when the engine commits the request to be
|
|
192
|
+
* sent. The verdict reports nothing either: the request gate's refusal is {@link routeRefusalError}.
|
|
152
193
|
*/
|
|
153
194
|
export declare function adjudicateModelRoute(model: Pick<Model, "id" | "baseUrl">, credential: RouteCredential, config: RoutePairingConfig): RouteAdjudication;
|
|
154
195
|
/** Render a refusal as the one-line detail text the brains put in their thrown error (the machine
|
|
@@ -156,43 +197,77 @@ export declare function adjudicateModelRoute(model: Pick<Model, "id" | "baseUrl"
|
|
|
156
197
|
export declare function routeRefusalText(verdict: Extract<RouteAdjudication, {
|
|
157
198
|
ok: false;
|
|
158
199
|
}>): string;
|
|
200
|
+
/** The request gate's refusal — the ONE error a first-party brain throws when the pairing law refuses the request it is
|
|
201
|
+
* building ({@link admitRouteRequest}). A credential-origin refusal is also reported to the run it happens in (the run
|
|
202
|
+
* announces `route.credential_origin_mismatch`): here, at the request that is refused, and not in the law, whose other
|
|
203
|
+
* callers only advise (a pre-flight, the read face) and leave the outcome to the seat that asked. */
|
|
204
|
+
export declare function routeRefusalError(verdict: Extract<RouteAdjudication, {
|
|
205
|
+
ok: false;
|
|
206
|
+
}>): BrainError;
|
|
159
207
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
208
|
+
* THE request gate of a first-party brain — the one seat that writes the credential-origin table. Judged while the
|
|
209
|
+
* request is built ({@link adjudicateModelRoute}, the same law the pre-flight and the read face read): a refusal is
|
|
210
|
+
* thrown here ({@link routeRefusalError}) before the body is built, and binds nothing — every refusal the law rules,
|
|
211
|
+
* a key beside its own header included, leaves as that one error (`<code>: <fix>`); the gate rules nothing of its
|
|
212
|
+
* own. An admitted request hands back its
|
|
213
|
+
* ADMISSION, which the brain runs from `SSERequest.onCommitted` — the engine's one point at which THIS request is the one
|
|
214
|
+
* that is sent: the law judged again and, admitted, the per-model credential bound to the request's origin, in ONE
|
|
215
|
+
* synchronous step; refused (the credential went out to another origin since the build — caller code runs while a body
|
|
216
|
+
* is built), the same refusal is thrown there, before anything is sent. A request built speculatively and declined (the
|
|
217
|
+
* context-overflow recovery's candidate), or one whose build fails after the gate, never reaches its admission and binds
|
|
218
|
+
* nothing. A credential is bound to the origin it was first SENT to, never to one it was only judged for. The request's
|
|
219
|
+
* halves (the entry's id and root, the brain's root and lane default) are read ONCE, here: both judgments and the binding
|
|
220
|
+
* are of the request this build is sending, whatever the caller's objects say by the time it is committed.
|
|
172
221
|
*/
|
|
173
|
-
export declare function
|
|
174
|
-
model?: Record<string, string>;
|
|
175
|
-
options?: Record<string, string>;
|
|
176
|
-
}): void;
|
|
222
|
+
export declare function admitRouteRequest(model: Pick<Model, "id" | "baseUrl">, credential: RouteCredential, config: RoutePairingConfig): () => void;
|
|
177
223
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
224
|
+
* Enforce "the resolved credential is the request's ONLY credential" on an already-merged header
|
|
225
|
+
* bag — one source's credential rides, the other sources' credential headers do not:
|
|
226
|
+
* - PER-MODEL winner → every credential header ({@link isCredentialHeaderName}) is stripped and the
|
|
227
|
+
* winning bag's own credential headers written back — exactly the ones {@link resolveRouteCredential}
|
|
228
|
+
* read and hashed ({@link ResolvedCredentialFacts}; the caller's bag is not read again): the hook's
|
|
229
|
+
* answer (`options`) or the entry (`model`). The hook's answer is one credential — beside its apiKey,
|
|
230
|
+
* which the brain then writes in its lane's key header (a request that also holds a header of that name
|
|
231
|
+
* never gets here in a first-party brain: its request gate refused it first, `route.credential_answer_conflict`,
|
|
232
|
+
* so the lock supersedes nothing the answer carried), its other credential headers ride (a gateway's `Helicone-Auth`); an
|
|
233
|
+
* entry's credential survives a same- or variant-case deployment spelling that the plain merge order
|
|
234
|
+
* would have let win. A credential header from any OTHER bag (the entry's under a hook answer, the
|
|
235
|
+
* deployment's under either) never rides beside a per-model credential. Whether a header is stripped
|
|
236
|
+
* is decided by its SOURCE, not its name.
|
|
237
|
+
* - DEPLOYMENT-CONFIG winner → untouched. A header-only config keeps its own spelling, and a config
|
|
238
|
+
* carrying BOTH the apiKey knob and an auth header is the documented gateway dual-auth shape — the
|
|
239
|
+
* knob's lock write plus the surviving header, both on the wire.
|
|
240
|
+
* - absent → untouched (no carriers exist).
|
|
181
241
|
*/
|
|
242
|
+
export declare function applyRouteCredentialHeaders(merged: Record<string, string>, credential: RouteCredential): void;
|
|
182
243
|
/** A brain's construction config, received ONCE by the engine's receipt law for caller objects
|
|
183
244
|
* ({@link receiveOwnProperties}: a plain object, every own member read once): the request gate and the pre-flight
|
|
184
245
|
* judge both read this frozen copy, so a config object mutated after construction moves neither and the two cannot
|
|
185
246
|
* disagree about the root a credential is paired with. The header bag is a caller object too: it is received by the
|
|
186
|
-
* same law from that one reading into a frozen bag of the copy's own.
|
|
247
|
+
* same law from that one reading into a frozen bag of the copy's own. The copy's credential — `apiKey` and the
|
|
248
|
+
* credential headers — is a credential the engine now holds: no child spawned on this host inherits a variable whose
|
|
249
|
+
* value equals it (`holdOwnCredentialValues`). */
|
|
187
250
|
export declare function brainConfigSnapshot<T extends {
|
|
251
|
+
apiKey?: string;
|
|
188
252
|
headers?: Record<string, string>;
|
|
189
253
|
}>(given: T, subject: string): Readonly<T>;
|
|
254
|
+
/**
|
|
255
|
+
* The first-party brains' `adjudicateRoute` face, closed over the brain's construction config —
|
|
256
|
+
* EXACTLY the resolution + judgment the brain's own request build runs (single source, two call
|
|
257
|
+
* sites), so a pre-flight verdict and the request-time verdict agree on the same inputs. A refusal is
|
|
258
|
+
* always a verdict here, never a throw. The pre-flight sees the
|
|
259
|
+
* per-model auth it is handed, nothing later: a header a provider-request hook adds afterwards reaches
|
|
260
|
+
* the request build alone, which judges it by the same law.
|
|
261
|
+
*/
|
|
190
262
|
export declare function createBrainRouteJudge(config: {
|
|
191
263
|
apiKey?: string;
|
|
192
264
|
baseUrl?: string;
|
|
193
265
|
headers?: Record<string, string>;
|
|
194
266
|
/** The lane's own default endpoint (the brain's constant), when it has one. */
|
|
195
267
|
laneDefault?: string;
|
|
268
|
+
/** The header the lane writes a per-call apiKey in ({@link resolveRouteCredential}'s `keyHeader`): the brain's own
|
|
269
|
+
* constant, so the judge refuses the requests its request build refuses. */
|
|
270
|
+
keyHeader?: string;
|
|
196
271
|
}): (model: Model, perModelAuth?: {
|
|
197
272
|
apiKey?: string;
|
|
198
273
|
headers?: Record<string, string>;
|
|
@@ -203,7 +278,7 @@ export declare function createBrainRouteJudge(config: {
|
|
|
203
278
|
* `judge` is a brain's optional `adjudicateRoute` face; absent ⇒ `"unknown"` (a custom brain core
|
|
204
279
|
* cannot see into — never guessed at).
|
|
205
280
|
*/
|
|
206
|
-
export type RoutePairingStatus = `ok:${RoutePairingPosture}` | `broken:${"credential_mismatch" | "credential_missing"}` | "unknown";
|
|
281
|
+
export type RoutePairingStatus = `ok:${RoutePairingPosture}` | `broken:${"credential_mismatch" | "credential_missing" | "credential_origin_mismatch" | "credential_answer_conflict"}` | "unknown";
|
|
207
282
|
export declare function routePairingStatus(brain: {
|
|
208
283
|
adjudicateRoute?: (model: Model, perModelAuth?: {
|
|
209
284
|
apiKey?: string;
|
|
@@ -294,8 +369,3 @@ export declare function unroutableSeatNotice(args: {
|
|
|
294
369
|
sessionId?: string;
|
|
295
370
|
runId?: string;
|
|
296
371
|
}): EngineNotice;
|
|
297
|
-
/** The auth carriers of a header bag, canonicalized (case-fold name → value, sorted) — the
|
|
298
|
-
* Model-visible credential fingerprint used by catalog-change disclosure (a same-name entry whose
|
|
299
|
-
* URL moved while this fingerprint did not is the "address changed, credential reference did not"
|
|
300
|
-
* shape worth announcing). */
|
|
301
|
-
export declare function authCarrierFingerprint(headers: Record<string, string> | undefined): string;
|