@sema-agent/core 7.51.2 → 7.53.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 +698 -2
- package/README.md +3 -1
- package/dist/agents/agent-transcript-tool.d.ts +1 -1
- package/dist/agents/cascade.js +4 -0
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -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/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -5
- package/dist/agents/peer-session-drain.js +271 -180
- package/dist/agents/read-inbox-tool.d.ts +8 -1
- package/dist/agents/read-inbox-tool.js +5 -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 +3 -0
- package/dist/agents/subagent.js +105 -76
- package/dist/agents/tool-filter.d.ts +58 -13
- package/dist/agents/tool-filter.js +34 -14
- package/dist/bench/metrics.d.ts +1 -0
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +17 -13
- package/dist/brain/model-presets.d.ts +4 -5
- package/dist/brain/model-presets.js +6 -5
- 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 +123 -47
- package/dist/brain/route-adjudicator.js +171 -42
- package/dist/brain/route-conformance.js +2 -2
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/status-sink.d.ts +18 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +30 -13
- package/dist/core/allow-entry.d.ts +42 -9
- package/dist/core/allow-entry.js +25 -18
- package/dist/core/auto-compaction.d.ts +80 -20
- package/dist/core/auto-compaction.js +65 -18
- 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 +57 -24
- package/dist/core/checkpoint-store.js +3 -3
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +2 -1
- package/dist/core/compliance.js +3 -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/effective-path-target.d.ts +25 -6
- package/dist/core/effective-path-target.js +24 -2
- package/dist/core/engine-capabilities.d.ts +8 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +49 -21
- package/dist/core/frontmatter-fence.d.ts +10 -0
- package/dist/core/frontmatter-fence.js +6 -0
- package/dist/core/gate-fold.js +8 -25
- package/dist/core/gate-lanes.js +358 -274
- package/dist/core/gate-outcome.d.ts +11 -6
- package/dist/core/governance-codes.d.ts +11 -4
- package/dist/core/governance-codes.js +12 -3
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +283 -739
- package/dist/core/hooks.js +100 -193
- 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 +84 -32
- package/dist/core/locked-config.js +128 -42
- 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 +3 -3
- package/dist/core/mcp-server-spec.d.ts +104 -10
- package/dist/core/mcp-server-spec.js +183 -33
- package/dist/core/mcp.d.ts +31 -20
- package/dist/core/mcp.js +66 -56
- 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 +22 -30
- package/dist/core/memory-engine/engine.js +16 -10
- 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 +5 -16
- package/dist/core/mode-policy.js +7 -19
- 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 +699 -0
- package/dist/core/mods/kernel/events.d.ts +245 -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 +94 -0
- package/dist/core/mods/kernel/receive.js +188 -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 +149 -0
- package/dist/core/mods/kernel/site-rules.js +296 -0
- package/dist/core/mods/stations.d.ts +344 -0
- package/dist/core/mods/stations.js +437 -0
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +13 -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-model.d.ts +58 -9
- package/dist/core/permission-rule-model.js +81 -7
- package/dist/core/permission-rule-org.d.ts +163 -33
- package/dist/core/permission-rule-org.js +205 -71
- 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 +14 -2
- package/dist/core/permission-rules.js +63 -23
- 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/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +12 -2
- package/dist/core/retired-keys.js +15 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- 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 +86 -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 +12 -5
- package/dist/core/runner/image.js +1 -2
- package/dist/core/runner/inline-image.js +1 -1
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- 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/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +3 -1
- package/dist/core/runner/person-path-rows.js +3 -2
- 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 +52 -1
- package/dist/core/runner/prepare-config-doors.js +168 -143
- package/dist/core/runner/prepare-defer-classify.js +7 -11
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +7 -5
- 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-hands-readface.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -1
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
- package/dist/core/runner/prepare-inherited-gate.js +10 -23
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +20 -7
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-park-ask.js +10 -14
- package/dist/core/runner/prepare-policy-chain.d.ts +17 -15
- package/dist/core/runner/prepare-policy-chain.js +104 -109
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +11 -12
- package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
- package/dist/core/runner/prepare-prompt-assembly.js +138 -101
- package/dist/core/runner/prepare-prompt-inputs.d.ts +28 -23
- package/dist/core/runner/prepare-prompt-inputs.js +11 -10
- package/dist/core/runner/prepare-protocol-tools.d.ts +11 -7
- package/dist/core/runner/prepare-protocol-tools.js +20 -13
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- 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 +41 -23
- 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 +13 -9
- package/dist/core/runner/prepare-wiring-manifest.js +14 -11
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +7 -5
- 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-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/resume-preflight.js +38 -16
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +4 -5
- 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 +103 -95
- 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 +14 -11
- 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 +34 -22
- package/dist/core/runner/run-tool-mount-facts.js +5 -3
- package/dist/core/runner/run-turn-boundary.js +32 -37
- package/dist/core/runner/runtask.d.ts +6 -7
- package/dist/core/runner/runtask.js +15 -73
- package/dist/core/runner/session-rule-policy.d.ts +5 -5
- package/dist/core/runner/session-rule-policy.js +13 -8
- 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/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -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 +124 -23
- package/dist/core/runner/sub-agent-deny-lists.js +230 -71
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- 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 +43 -36
- package/dist/core/safety-merge-corpus.js +18 -2
- package/dist/core/secret-env.d.ts +88 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +71 -16
- package/dist/core/session-policy-store.js +138 -28
- 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/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/task-event.d.ts +49 -3
- package/dist/core/task-notification.js +2 -2
- 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 +62 -18
- package/dist/core/tool-policy.js +86 -15
- 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 -2
- package/dist/core/types.js +1 -1
- 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/untrusted-text.d.ts +16 -0
- package/dist/core/untrusted-text.js +7 -2
- package/dist/core/user-attachment.d.ts +25 -4
- package/dist/core/user-attachment.js +63 -42
- package/dist/core/wiring-manifest.d.ts +18 -15
- 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 +5 -4
- package/dist/engine/harness/types.d.ts +20 -16
- package/dist/engine/harness/types.js +7 -1
- package/dist/engine/llm/types.d.ts +12 -7
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +18 -6
- package/dist/engine/loop/types.d.ts +8 -2
- 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 +33 -23
- package/dist/index.js +23 -15
- package/dist/internal/harness.d.ts +2 -1
- package/dist/internal/harness.js +2 -1
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- 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 +72 -56
- package/dist/prompt-assembly/artifact-store.js +60 -52
- package/dist/prompt-assembly/artifact.d.ts +90 -76
- package/dist/prompt-assembly/artifact.js +197 -145
- package/dist/prompt-assembly/assemble.d.ts +36 -29
- package/dist/prompt-assembly/assemble.js +34 -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 +60 -0
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -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 +138 -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 +26 -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 +137 -0
- package/dist/prompt-assembly/overlay.js +339 -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 +71 -0
- package/dist/prompt-assembly/select.js +106 -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 -511
- package/dist/prompts/default.js +20 -288
- package/dist/prompts/supervisor.d.ts +6 -71
- package/dist/prompts/supervisor.js +6 -117
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +4 -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 +306 -103
- package/wire-changes/core@7.52.0.json +5859 -0
- package/wire-changes/core@7.53.0.json +1946 -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
|
@@ -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,83 @@ 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.
|
|
221
|
+
*
|
|
222
|
+
* Exported from the package root for a HOST that sends a request itself (its own `fetch`, outside a first-party brain): it
|
|
223
|
+
* passes this same gate, so the binding does not depend on who sends first. Resolve the credential with
|
|
224
|
+
* {@link resolveRouteCredential} (a credential built by hand carries no identity: it is judged and binds nothing), call this
|
|
225
|
+
* before building, and run the admission it returns immediately before the request goes out; a refusal is the very error a
|
|
226
|
+
* first-party brain throws for the same request. {@link adjudicateModelRoute} stays the read: it judges and binds nothing.
|
|
172
227
|
*/
|
|
173
|
-
export declare function
|
|
174
|
-
model?: Record<string, string>;
|
|
175
|
-
options?: Record<string, string>;
|
|
176
|
-
}): void;
|
|
228
|
+
export declare function admitRouteRequest(model: Pick<Model, "id" | "baseUrl">, credential: RouteCredential, config: RoutePairingConfig): () => void;
|
|
177
229
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
230
|
+
* Enforce "the resolved credential is the request's ONLY credential" on an already-merged header
|
|
231
|
+
* bag — one source's credential rides, the other sources' credential headers do not:
|
|
232
|
+
* - PER-MODEL winner → every credential header ({@link isCredentialHeaderName}) is stripped and the
|
|
233
|
+
* winning bag's own credential headers written back — exactly the ones {@link resolveRouteCredential}
|
|
234
|
+
* read and hashed ({@link ResolvedCredentialFacts}; the caller's bag is not read again): the hook's
|
|
235
|
+
* answer (`options`) or the entry (`model`). The hook's answer is one credential — beside its apiKey,
|
|
236
|
+
* which the brain then writes in its lane's key header (a request that also holds a header of that name
|
|
237
|
+
* never gets here in a first-party brain: its request gate refused it first, `route.credential_answer_conflict`,
|
|
238
|
+
* so the lock supersedes nothing the answer carried), its other credential headers ride (a gateway's `Helicone-Auth`); an
|
|
239
|
+
* entry's credential survives a same- or variant-case deployment spelling that the plain merge order
|
|
240
|
+
* would have let win. A credential header from any OTHER bag (the entry's under a hook answer, the
|
|
241
|
+
* deployment's under either) never rides beside a per-model credential. Whether a header is stripped
|
|
242
|
+
* is decided by its SOURCE, not its name.
|
|
243
|
+
* - DEPLOYMENT-CONFIG winner → untouched. A header-only config keeps its own spelling, and a config
|
|
244
|
+
* carrying BOTH the apiKey knob and an auth header is the documented gateway dual-auth shape — the
|
|
245
|
+
* knob's lock write plus the surviving header, both on the wire.
|
|
246
|
+
* - absent → untouched (no carriers exist).
|
|
181
247
|
*/
|
|
248
|
+
export declare function applyRouteCredentialHeaders(merged: Record<string, string>, credential: RouteCredential): void;
|
|
182
249
|
/** A brain's construction config, received ONCE by the engine's receipt law for caller objects
|
|
183
250
|
* ({@link receiveOwnProperties}: a plain object, every own member read once): the request gate and the pre-flight
|
|
184
251
|
* judge both read this frozen copy, so a config object mutated after construction moves neither and the two cannot
|
|
185
252
|
* 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.
|
|
253
|
+
* same law from that one reading into a frozen bag of the copy's own. The copy's credential — `apiKey` and the
|
|
254
|
+
* credential headers — is a credential the engine now holds: no child spawned on this host inherits a variable whose
|
|
255
|
+
* value equals it (`holdOwnCredentialValues`). */
|
|
187
256
|
export declare function brainConfigSnapshot<T extends {
|
|
257
|
+
apiKey?: string;
|
|
188
258
|
headers?: Record<string, string>;
|
|
189
259
|
}>(given: T, subject: string): Readonly<T>;
|
|
260
|
+
/**
|
|
261
|
+
* The first-party brains' `adjudicateRoute` face, closed over the brain's construction config —
|
|
262
|
+
* EXACTLY the resolution + judgment the brain's own request build runs (single source, two call
|
|
263
|
+
* sites), so a pre-flight verdict and the request-time verdict agree on the same inputs. A refusal is
|
|
264
|
+
* always a verdict here, never a throw. The pre-flight sees the
|
|
265
|
+
* per-model auth it is handed, nothing later: a header a provider-request hook adds afterwards reaches
|
|
266
|
+
* the request build alone, which judges it by the same law.
|
|
267
|
+
*/
|
|
190
268
|
export declare function createBrainRouteJudge(config: {
|
|
191
269
|
apiKey?: string;
|
|
192
270
|
baseUrl?: string;
|
|
193
271
|
headers?: Record<string, string>;
|
|
194
272
|
/** The lane's own default endpoint (the brain's constant), when it has one. */
|
|
195
273
|
laneDefault?: string;
|
|
274
|
+
/** The header the lane writes a per-call apiKey in ({@link resolveRouteCredential}'s `keyHeader`): the brain's own
|
|
275
|
+
* constant, so the judge refuses the requests its request build refuses. */
|
|
276
|
+
keyHeader?: string;
|
|
196
277
|
}): (model: Model, perModelAuth?: {
|
|
197
278
|
apiKey?: string;
|
|
198
279
|
headers?: Record<string, string>;
|
|
@@ -203,7 +284,7 @@ export declare function createBrainRouteJudge(config: {
|
|
|
203
284
|
* `judge` is a brain's optional `adjudicateRoute` face; absent ⇒ `"unknown"` (a custom brain core
|
|
204
285
|
* cannot see into — never guessed at).
|
|
205
286
|
*/
|
|
206
|
-
export type RoutePairingStatus = `ok:${RoutePairingPosture}` | `broken:${"credential_mismatch" | "credential_missing"}` | "unknown";
|
|
287
|
+
export type RoutePairingStatus = `ok:${RoutePairingPosture}` | `broken:${"credential_mismatch" | "credential_missing" | "credential_origin_mismatch" | "credential_answer_conflict"}` | "unknown";
|
|
207
288
|
export declare function routePairingStatus(brain: {
|
|
208
289
|
adjudicateRoute?: (model: Model, perModelAuth?: {
|
|
209
290
|
apiKey?: string;
|
|
@@ -294,8 +375,3 @@ export declare function unroutableSeatNotice(args: {
|
|
|
294
375
|
sessionId?: string;
|
|
295
376
|
runId?: string;
|
|
296
377
|
}): 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;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
2
|
import { BrainError } from "./errors.js";
|
|
3
|
+
import { reportCredentialOriginRefusal } from "./status-sink.js";
|
|
3
4
|
import { receiptRefusal, receiveOwnProperties } from "../core/own-property-receipt.js";
|
|
5
|
+
import { credentialHeaderValues, holdOwnCredentialValues, isCredentialHeaderName, isSchemedCredentialHeader } from "../core/secret-env.js";
|
|
4
6
|
const REDACTED_VALUE = "«redacted»";
|
|
5
7
|
const REDACTED_CREDENTIALS = "[redacted-credentials]";
|
|
6
8
|
function servableParse(raw) {
|
|
@@ -191,23 +193,96 @@ export function resolveRequestRootOf(modelBaseUrl, configBaseUrl, laneDefault =
|
|
|
191
193
|
export function hasAuthCarrier(headers) {
|
|
192
194
|
if (!headers)
|
|
193
195
|
return false;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
return Object.keys(headers).some((name) => isCredentialHeaderName(name));
|
|
197
|
+
}
|
|
198
|
+
function routeOriginOf(rootKey) {
|
|
199
|
+
if (rootKey === "")
|
|
200
|
+
return "";
|
|
201
|
+
const u = servableParse(rootKey);
|
|
202
|
+
return u === undefined ? rootKey : `${u.protocol}//${u.host}`;
|
|
203
|
+
}
|
|
204
|
+
const CREDENTIAL_ORIGIN_BINDINGS_MAX = 16_384;
|
|
205
|
+
const CREDENTIAL_ORIGIN_TABLE = Symbol.for("sema.core.credentialOriginTable");
|
|
206
|
+
function credentialOriginTable() {
|
|
207
|
+
const held = Reflect.get(globalThis, CREDENTIAL_ORIGIN_TABLE);
|
|
208
|
+
if (held instanceof Map)
|
|
209
|
+
return held;
|
|
210
|
+
const table = new Map();
|
|
211
|
+
Reflect.set(globalThis, CREDENTIAL_ORIGIN_TABLE, table);
|
|
212
|
+
return table;
|
|
213
|
+
}
|
|
214
|
+
export function __resetCredentialOriginBindings() {
|
|
215
|
+
credentialOriginTable().clear();
|
|
216
|
+
}
|
|
217
|
+
const resolvedCredentialFacts = new WeakMap();
|
|
218
|
+
const HEADER_VALUE_ENDS = /^[\t\n\r ]+|[\t\n\r ]+$/g;
|
|
219
|
+
const AUTH_SCHEME_CREDENTIALS = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+[\t ]+(\S[\s\S]*)$/;
|
|
220
|
+
function perModelCredential(credential, apiKey, bag, conflict) {
|
|
221
|
+
const headers = {};
|
|
222
|
+
for (const [name, value] of Object.entries(bag ?? {})) {
|
|
223
|
+
if (isCredentialHeaderName(name) && typeof value === "string")
|
|
224
|
+
headers[name] = value;
|
|
197
225
|
}
|
|
198
|
-
|
|
226
|
+
const headerValues = Object.entries(headers).map(([name, value]) => {
|
|
227
|
+
const wire = value.replace(HEADER_VALUE_ENDS, "");
|
|
228
|
+
return isSchemedCredentialHeader(name) ? (AUTH_SCHEME_CREDENTIALS.exec(wire)?.[1] ?? wire) : wire;
|
|
229
|
+
});
|
|
230
|
+
const values = apiKey !== undefined ? [apiKey, ...headerValues] : headerValues;
|
|
231
|
+
const secrets = [...new Set(values.map((v) => v.replace(HEADER_VALUE_ENDS, "")).filter((v) => v !== ""))];
|
|
232
|
+
if (secrets.length === 0 && Object.keys(headers).length === 0 && conflict === undefined)
|
|
233
|
+
return credential;
|
|
234
|
+
const proof = Object.freeze(credential);
|
|
235
|
+
resolvedCredentialFacts.set(proof, Object.freeze({
|
|
236
|
+
identities: Object.freeze(secrets.map((s) => createHash("sha256").update(s).digest("base64url"))),
|
|
237
|
+
headers: Object.freeze(headers),
|
|
238
|
+
...(conflict !== undefined ? { keyHeaderConflict: Object.freeze(conflict) } : {}),
|
|
239
|
+
}));
|
|
240
|
+
return proof;
|
|
241
|
+
}
|
|
242
|
+
function boundElsewhere(identities, requestOrigin) {
|
|
243
|
+
const table = credentialOriginTable();
|
|
244
|
+
return identities.map((id) => table.get(id)).find((bound) => bound !== undefined && bound !== requestOrigin);
|
|
245
|
+
}
|
|
246
|
+
function bindCredentialOrigin(identities, requestOrigin) {
|
|
247
|
+
const table = credentialOriginTable();
|
|
248
|
+
for (const id of identities) {
|
|
249
|
+
table.delete(id);
|
|
250
|
+
if (table.size >= CREDENTIAL_ORIGIN_BINDINGS_MAX) {
|
|
251
|
+
const oldest = table.keys().next();
|
|
252
|
+
if (oldest.done !== true)
|
|
253
|
+
table.delete(oldest.value);
|
|
254
|
+
}
|
|
255
|
+
table.set(id, requestOrigin);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function routeHalves(model, config) {
|
|
259
|
+
const entry = routeKeyOf(model.baseUrl);
|
|
260
|
+
const declared = routeKeyOf(config.baseUrl);
|
|
261
|
+
const deploymentRoot = declared || routeKeyOf(config.laneDefault);
|
|
262
|
+
return { entry, declared, deploymentRoot, request: entry || deploymentRoot };
|
|
263
|
+
}
|
|
264
|
+
function keyHeaderConflict(apiKey, headers, keyHeader) {
|
|
265
|
+
if (apiKey === undefined || apiKey === "" || keyHeader === undefined || headers === undefined)
|
|
266
|
+
return undefined;
|
|
267
|
+
const fold = keyHeader.toLowerCase();
|
|
268
|
+
const written = Object.keys(headers).find((name) => name.toLowerCase() === fold);
|
|
269
|
+
return written === undefined ? undefined : { header: written, keyHeader };
|
|
199
270
|
}
|
|
200
271
|
export function resolveRouteCredential(input) {
|
|
201
|
-
const { optionsApiKey, optionsHeaders, modelHeaders, configApiKey, configHeaders } = input;
|
|
202
|
-
|
|
203
|
-
|
|
272
|
+
const { optionsApiKey, optionsHeaders, modelHeaders, configApiKey, configHeaders, keyHeader } = input;
|
|
273
|
+
holdOwnCredentialValues(credentialHeaderValues(modelHeaders));
|
|
274
|
+
const answerHeadersBorne = hasAuthCarrier(optionsHeaders);
|
|
275
|
+
if (optionsApiKey !== undefined || answerHeadersBorne) {
|
|
276
|
+
if (optionsApiKey !== undefined && typeof optionsApiKey !== "string")
|
|
204
277
|
throw new TypeError(`route pairing: apiKey must be a string (got ${typeof optionsApiKey})`);
|
|
205
|
-
return {
|
|
278
|
+
return perModelCredential({
|
|
279
|
+
source: "per-model",
|
|
280
|
+
...(optionsApiKey !== undefined ? { apiKey: optionsApiKey } : {}),
|
|
281
|
+
...(answerHeadersBorne ? { headersBorne: true, carrierBag: "options" } : {}),
|
|
282
|
+
}, optionsApiKey, answerHeadersBorne ? optionsHeaders : undefined, keyHeaderConflict(optionsApiKey, optionsHeaders, keyHeader));
|
|
206
283
|
}
|
|
207
|
-
if (hasAuthCarrier(optionsHeaders))
|
|
208
|
-
return { source: "per-model", headersBorne: true, carrierBag: "options" };
|
|
209
284
|
if (hasAuthCarrier(modelHeaders))
|
|
210
|
-
return { source: "per-model", headersBorne: true, carrierBag: "model" };
|
|
285
|
+
return perModelCredential({ source: "per-model", headersBorne: true, carrierBag: "model" }, undefined, modelHeaders);
|
|
211
286
|
const configHeadersBorne = hasAuthCarrier(configHeaders);
|
|
212
287
|
if (configApiKey !== undefined || configHeadersBorne) {
|
|
213
288
|
if (configApiKey !== undefined && typeof configApiKey !== "string") {
|
|
@@ -222,15 +297,53 @@ export function resolveRouteCredential(input) {
|
|
|
222
297
|
return { source: "absent" };
|
|
223
298
|
}
|
|
224
299
|
export function adjudicateModelRoute(model, credential, config) {
|
|
225
|
-
const entry =
|
|
226
|
-
const declared = routeKeyOf(config.baseUrl);
|
|
227
|
-
const deploymentRoot = declared || routeKeyOf(config.laneDefault);
|
|
228
|
-
const request = entry || deploymentRoot;
|
|
300
|
+
const { entry, declared, deploymentRoot, request } = routeHalves(model, config);
|
|
229
301
|
const entryShown = displayUrlOf(entry);
|
|
230
302
|
const declaredShown = displayUrlOf(declared);
|
|
231
303
|
const requestShown = displayUrlOf(request);
|
|
232
|
-
if (credential.source === "per-model")
|
|
233
|
-
|
|
304
|
+
if (credential.source === "per-model") {
|
|
305
|
+
const facts = resolvedCredentialFacts.get(credential);
|
|
306
|
+
const conflict = facts?.keyHeaderConflict;
|
|
307
|
+
if (conflict !== undefined) {
|
|
308
|
+
return {
|
|
309
|
+
ok: false,
|
|
310
|
+
code: "route.credential_answer_conflict",
|
|
311
|
+
detail: {
|
|
312
|
+
modelId: model.id,
|
|
313
|
+
entryBaseUrl: entryShown,
|
|
314
|
+
requestBaseUrl: requestShown,
|
|
315
|
+
configBaseUrl: declaredShown,
|
|
316
|
+
fixHint: `model "${model.id}": the request carries both an apiKey and a "${conflict.header}" header, and this lane writes the apiKey in ` +
|
|
317
|
+
`"${conflict.keyHeader}" — one header cannot hold two credentials; hand the key as apiKey or as the header, not both`,
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const identities = facts?.identities;
|
|
322
|
+
const requestOrigin = routeOriginOf(request);
|
|
323
|
+
if (identities === undefined || requestOrigin === "")
|
|
324
|
+
return { ok: true, posture: "per-model" };
|
|
325
|
+
const boundOrigin = boundElsewhere(identities, requestOrigin);
|
|
326
|
+
if (boundOrigin === undefined)
|
|
327
|
+
return { ok: true, posture: "per-model" };
|
|
328
|
+
const boundShown = displayUrlOf(boundOrigin);
|
|
329
|
+
const requestOriginShown = displayUrlOf(requestOrigin);
|
|
330
|
+
return {
|
|
331
|
+
ok: false,
|
|
332
|
+
code: "route.credential_origin_mismatch",
|
|
333
|
+
detail: {
|
|
334
|
+
modelId: model.id,
|
|
335
|
+
entryBaseUrl: entryShown,
|
|
336
|
+
requestBaseUrl: requestShown,
|
|
337
|
+
configBaseUrl: declaredShown,
|
|
338
|
+
boundOrigin: boundShown,
|
|
339
|
+
requestOrigin: requestOriginShown,
|
|
340
|
+
fixHint: `model "${model.id}" targets ${requestShown} (origin ${requestOriginShown}) but its own credential is bound to ${boundShown}, ` +
|
|
341
|
+
`the origin it was first sent to — a credential does not follow its entry to another host: change the entry's credential ` +
|
|
342
|
+
`reference in the same step as its baseUrl (a new credential is a new binding), or point the entry back at ${boundShown}; ` +
|
|
343
|
+
`a binding lasts as long as the engine process, so a provider that only moved domains and accepts the same credential there needs a restart`,
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
234
347
|
if (credential.source === "deployment-config") {
|
|
235
348
|
if (request !== "" && request === deploymentRoot)
|
|
236
349
|
return { ok: true, posture: "paired" };
|
|
@@ -273,22 +386,40 @@ export function adjudicateModelRoute(model, credential, config) {
|
|
|
273
386
|
export function routeRefusalText(verdict) {
|
|
274
387
|
return `${verdict.code}: ${verdict.detail.fixHint}`;
|
|
275
388
|
}
|
|
276
|
-
export function
|
|
389
|
+
export function routeRefusalError(verdict) {
|
|
390
|
+
const { modelId, boundOrigin, requestOrigin } = verdict.detail;
|
|
391
|
+
if (verdict.code === "route.credential_origin_mismatch" && boundOrigin !== undefined && requestOrigin !== undefined) {
|
|
392
|
+
reportCredentialOriginRefusal({ modelId, boundOrigin, requestOrigin });
|
|
393
|
+
}
|
|
394
|
+
return new BrainError("invalid_request", routeRefusalText(verdict));
|
|
395
|
+
}
|
|
396
|
+
export function admitRouteRequest(model, credential, config) {
|
|
397
|
+
const judged = { id: model.id, baseUrl: model.baseUrl };
|
|
398
|
+
const { baseUrl, laneDefault } = config;
|
|
399
|
+
const pairing = { ...(baseUrl !== undefined ? { baseUrl } : {}), ...(laneDefault !== undefined ? { laneDefault } : {}) };
|
|
400
|
+
const judgeOrRefuse = () => {
|
|
401
|
+
const verdict = adjudicateModelRoute(judged, credential, pairing);
|
|
402
|
+
if (!verdict.ok)
|
|
403
|
+
throw routeRefusalError(verdict);
|
|
404
|
+
};
|
|
405
|
+
judgeOrRefuse();
|
|
406
|
+
const identities = credential.source === "per-model" ? resolvedCredentialFacts.get(credential)?.identities : undefined;
|
|
407
|
+
const requestOrigin = routeOriginOf(routeHalves(judged, pairing).request);
|
|
408
|
+
if (identities === undefined || identities.length === 0 || requestOrigin === "")
|
|
409
|
+
return () => { };
|
|
410
|
+
return () => {
|
|
411
|
+
judgeOrRefuse();
|
|
412
|
+
bindCredentialOrigin(identities, requestOrigin);
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
export function applyRouteCredentialHeaders(merged, credential) {
|
|
277
416
|
if (credential.source !== "per-model")
|
|
278
417
|
return;
|
|
279
418
|
for (const k of Object.keys(merged)) {
|
|
280
|
-
if (
|
|
419
|
+
if (isCredentialHeaderName(k))
|
|
281
420
|
delete merged[k];
|
|
282
421
|
}
|
|
283
|
-
|
|
284
|
-
return;
|
|
285
|
-
const winner = credential.carrierBag === "options" ? bags.options : bags.model;
|
|
286
|
-
if (!winner)
|
|
287
|
-
return;
|
|
288
|
-
for (const [k, v] of Object.entries(winner)) {
|
|
289
|
-
if (AUTH_CARRIER_NAMES.has(k.toLowerCase()))
|
|
290
|
-
merged[k] = v;
|
|
291
|
-
}
|
|
422
|
+
Object.assign(merged, resolvedCredentialFacts.get(credential)?.headers);
|
|
292
423
|
}
|
|
293
424
|
export function brainConfigSnapshot(given, subject) {
|
|
294
425
|
const words = { subject, unit: "member", moment: "at construction" };
|
|
@@ -299,7 +430,9 @@ export function brainConfigSnapshot(given, subject) {
|
|
|
299
430
|
throw receiptRefusal(`${subject}.headers must be an object of header names to values`);
|
|
300
431
|
Object.defineProperty(received, "headers", { value: Object.freeze(receiveOwnProperties(headers, { ...words, subject: `${subject}.headers`, unit: "header" })), enumerable: true });
|
|
301
432
|
}
|
|
302
|
-
|
|
433
|
+
const snapshot = Object.freeze(received);
|
|
434
|
+
holdOwnCredentialValues([snapshot.apiKey, ...credentialHeaderValues(snapshot.headers)]);
|
|
435
|
+
return snapshot;
|
|
303
436
|
}
|
|
304
437
|
export function createBrainRouteJudge(config) {
|
|
305
438
|
return (model, perModelAuth) => {
|
|
@@ -309,17 +442,24 @@ export function createBrainRouteJudge(config) {
|
|
|
309
442
|
modelHeaders: model.headers,
|
|
310
443
|
configApiKey: config.apiKey,
|
|
311
444
|
configHeaders: config.headers,
|
|
445
|
+
keyHeader: config.keyHeader,
|
|
312
446
|
});
|
|
313
447
|
return adjudicateModelRoute(model, credential, { baseUrl: config.baseUrl, laneDefault: config.laneDefault });
|
|
314
448
|
};
|
|
315
449
|
}
|
|
450
|
+
const BROKEN_PAIRING_STATUS = {
|
|
451
|
+
"route.credential_mismatch": "broken:credential_mismatch",
|
|
452
|
+
"route.credential_missing": "broken:credential_missing",
|
|
453
|
+
"route.credential_origin_mismatch": "broken:credential_origin_mismatch",
|
|
454
|
+
"route.credential_answer_conflict": "broken:credential_answer_conflict",
|
|
455
|
+
};
|
|
316
456
|
export function routePairingStatus(brain, model, perModelAuth) {
|
|
317
457
|
const verdict = brain.adjudicateRoute?.(model, perModelAuth);
|
|
318
458
|
if (verdict === undefined)
|
|
319
459
|
return "unknown";
|
|
320
460
|
if (verdict.ok)
|
|
321
461
|
return `ok:${verdict.posture}`;
|
|
322
|
-
return verdict.code
|
|
462
|
+
return BROKEN_PAIRING_STATUS[verdict.code];
|
|
323
463
|
}
|
|
324
464
|
export const DERIVED_ROUTE_PREFLIGHT_HOOK_BOUND_MS = 5_000;
|
|
325
465
|
export async function adjudicateDerivedRoute(args) {
|
|
@@ -391,14 +531,3 @@ export function unroutableSeatNotice(args) {
|
|
|
391
531
|
},
|
|
392
532
|
};
|
|
393
533
|
}
|
|
394
|
-
export function authCarrierFingerprint(headers) {
|
|
395
|
-
if (!headers)
|
|
396
|
-
return "";
|
|
397
|
-
const rows = [];
|
|
398
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
399
|
-
const fold = k.toLowerCase();
|
|
400
|
-
if (AUTH_CARRIER_NAMES.has(fold))
|
|
401
|
-
rows.push(`${fold}=${v}`);
|
|
402
|
-
}
|
|
403
|
-
return rows.sort().join("\n");
|
|
404
|
-
}
|