@sema-agent/core 7.51.2 → 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 +504 -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.js +6 -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 +40 -8
- package/dist/core/allow-entry.js +23 -16
- 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/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 +16 -0
- package/dist/core/mcp.d.ts +11 -7
- package/dist/core/mcp.js +11 -5
- 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 +17 -15
- package/dist/core/runner/prepare-policy-chain.js +95 -100
- 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 +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/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 +88 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +52 -16
- package/dist/core/session-policy-store.js +118 -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/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/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 +20 -16
- package/dist/index.js +13 -11
- 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/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 +242 -99
- 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
|
@@ -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
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const ROUTE_ADJUDICATION_CONFORMANCE_CORPUS = [
|
|
2
2
|
{
|
|
3
3
|
name: "per-model-on-root",
|
|
4
|
-
intent: "a per-model credential
|
|
4
|
+
intent: "a per-model credential not bound yet is servable, on the deployment root",
|
|
5
5
|
model: { id: "m-main", baseUrl: "https://gw.example.com" },
|
|
6
6
|
credential: { source: "per-model", apiKey: "k-main" },
|
|
7
7
|
config: { baseUrl: "https://gw.example.com" },
|
|
@@ -9,7 +9,7 @@ export const ROUTE_ADJUDICATION_CONFORMANCE_CORPUS = [
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
name: "per-model-off-root",
|
|
12
|
-
intent: "a per-model credential
|
|
12
|
+
intent: "a per-model credential not bound yet is servable off the deployment root too (the resolver was asked about THIS entry)",
|
|
13
13
|
model: { id: "m-third", baseUrl: "https://third-party.example.net/v1" },
|
|
14
14
|
credential: { source: "per-model", apiKey: "k-third" },
|
|
15
15
|
config: { baseUrl: "https://gw.example.com" },
|
|
@@ -150,3 +150,21 @@ export declare function runWithExtraBodyDropped<T>(observe: (r: ExtraBodyDropped
|
|
|
150
150
|
/** Report the strip of the request the engine has SELECTED to send, to the active per-run sink, if any (called at the
|
|
151
151
|
* engine's commit points, never from `buildRequest`, which is also built speculatively). */
|
|
152
152
|
export declare function reportExtraBodyDropped(report: ExtraBodyDroppedReport): void;
|
|
153
|
+
/** A request refused because the per-model credential it would carry is bound to another origin: the catalog id it was
|
|
154
|
+
* for and the two origins, each in its DISPLAY spelling. Never a value, never a digest of one. */
|
|
155
|
+
export interface CredentialOriginRefusalReport {
|
|
156
|
+
modelId: string;
|
|
157
|
+
boundOrigin: string;
|
|
158
|
+
requestOrigin: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Run `fn` with a per-run credential-origin refusal sink in scope — the FIFTH brain→runner ALS channel, with the fourth's
|
|
162
|
+
* shape and for its reasons: the WHOLE leg (a refused compaction summary, side query or classifier call is refused for the
|
|
163
|
+
* same reason as a turn), not shielded around the leg's internal calls, not revoked on settlement, no-op outside the scope.
|
|
164
|
+
* The refusal is already decided when it is reported.
|
|
165
|
+
*/
|
|
166
|
+
export declare function runWithCredentialOriginRefusals<T>(observe: (r: CredentialOriginRefusalReport) => void, fn: () => Promise<T>): Promise<T>;
|
|
167
|
+
/** Report a credential-origin refusal to the active per-run sink, if any (called by the request gate's refusal,
|
|
168
|
+
* `routeRefusalError`; a judgment that only advises reports nothing). The
|
|
169
|
+
* observer is the Runner's own seat, whose one delivery is the guarded `deliverEngineNotice` — nothing is swallowed here. */
|
|
170
|
+
export declare function reportCredentialOriginRefusal(report: CredentialOriginRefusalReport): void;
|
|
@@ -68,3 +68,10 @@ export function reportExtraBodyDropped(report) {
|
|
|
68
68
|
catch {
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
const credentialOriginRefusalSinkStore = new AsyncLocalStorage();
|
|
72
|
+
export function runWithCredentialOriginRefusals(observe, fn) {
|
|
73
|
+
return credentialOriginRefusalSinkStore.run({ observe }, fn);
|
|
74
|
+
}
|
|
75
|
+
export function reportCredentialOriginRefusal(report) {
|
|
76
|
+
credentialOriginRefusalSinkStore.getStore()?.observe(report);
|
|
77
|
+
}
|
|
@@ -8,8 +8,9 @@ import { type EngineNotice } from "./engine-notice.js";
|
|
|
8
8
|
/** Why an entry grants (or refuses) nothing — the closed set the start-of-run notice names, for every list. `tool_name`: the
|
|
9
9
|
* entry's tool name is one the name judgment reads as stale against the run's roster (`verdict` says which); `near_miss`: the
|
|
10
10
|
* run mounts no tool of that exact name but mounts one that differs only by case (`currentName` is its spelling);
|
|
11
|
-
* `earlier_grammar`: a session's stored rules are written in an earlier rule grammar, in which the entry named no tool
|
|
12
|
-
* session store's list reading, `compileSessionRuleBucket` — this reader never
|
|
11
|
+
* `earlier_grammar`: a session's stored rules are written in an earlier rule grammar, in which the entry named no tool, or which
|
|
12
|
+
* read an entry this grammar reads as no rule (the session store's list reading, `compileSessionRuleBucket` — this reader never
|
|
13
|
+
* answers it). */
|
|
13
14
|
export declare const ALLOW_ENTRY_INERT_REASONS: readonly ["wildcard_all", "namespace_wildcard", "content_unsupported", "grammar", "tool_name", "near_miss", "earlier_grammar"];
|
|
14
15
|
export type AllowEntryInertReason = (typeof ALLOW_ENTRY_INERT_REASONS)[number];
|
|
15
16
|
/** The facts beside a reason: for `tool_name` / `near_miss` the judgment's word, the judgment's own one-line sentence when it has
|
|
@@ -21,7 +22,13 @@ export interface AllowEntryInertDetail {
|
|
|
21
22
|
readonly guidance?: string;
|
|
22
23
|
readonly currentName?: string;
|
|
23
24
|
readonly valueType?: string;
|
|
25
|
+
/** Where the spelling of a `near_miss` comes from, when the station that read it reads more than the run's roster
|
|
26
|
+
* ({@link NearMissCarrier}); absent ⇒ the roster the run mounts. A sentence only — never a field of a wire detail. */
|
|
27
|
+
readonly carriedBy?: NearMissCarrier;
|
|
24
28
|
}
|
|
29
|
+
/** Who carries the spelling a near miss reads as another spelling of: the roster the run mounts, a delegation tool's pool, or
|
|
30
|
+
* only the engine's tool catalog (a tool the engine may mount, which this run's roster does not carry). */
|
|
31
|
+
export type NearMissCarrier = "run" | "pool" | "catalog";
|
|
25
32
|
/**
|
|
26
33
|
* How a list member that is not a string is named wherever a list's entries are named (the inert record's `entry`, the Skill
|
|
27
34
|
* card's binding): its TYPE, in a spelling no readable entry can have — a readable entry's name holds no whitespace and a
|
|
@@ -64,6 +71,25 @@ export type CompiledAllowEntry = {
|
|
|
64
71
|
readonly kind: "domain";
|
|
65
72
|
readonly rule: PersistedRule;
|
|
66
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* THE near-miss reading of a tool name the judgment calls current: the spelling the run mounts when the run mounts no tool of this
|
|
76
|
+
* exact name but one that differs from it only by case (`BASH` beside `Bash`), else `undefined`. Two kinds of name are never a near
|
|
77
|
+
* miss, because the roster they would be read against is not the final one: an engine tool's (the engine mounts some of its tools
|
|
78
|
+
* after a list is read) and a protocol tool's (a refresh mounts and unmounts them while the run goes on) — in both the exact
|
|
79
|
+
* spelling can be the right one by the time it matters. Every station that asks "is this name a case twin of a mounted tool" reads
|
|
80
|
+
* this answer: an entry list ({@link readRuleEntry}), a tool policy's name lists (the prepare-time policy-name audit).
|
|
81
|
+
*/
|
|
82
|
+
export declare function nearMissSpellingOf(name: string, roster: ReadonlySet<string>): string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* THE universe a ROOT-facing tool-name list is read against: every name the engine's catalog carries, and the names the reader knows
|
|
85
|
+
* are carried (`carried`). A list is root-facing when it bounds the tools of the run that holds it — the run's own `allowTools` /
|
|
86
|
+
* `excludeTools` (also the ceiling of its children), a tool policy's name lists, a session's stored rules — and its reader is that
|
|
87
|
+
* run (`carried`: its roster, the caller's own tools, and the exact names of the pools its reachable delegation tools declare and can
|
|
88
|
+
* list) or the store that writes the rules (`carried`: the tools its deployment declared). Another spelling of a carried name is
|
|
89
|
+
* always refused there, whatever a delegation tool injects per spawn: the run's own tools are known, and an entry that is a case
|
|
90
|
+
* twin of one of them names the other tool by mistake. A delegation tool's pool that cannot be listed carries nothing here.
|
|
91
|
+
*/
|
|
92
|
+
export declare function rootFacingToolNames(carried: Iterable<string>): Set<string>;
|
|
67
93
|
/** What one rule entry means under one direction ({@link readRuleEntry}): the row the grammar compiles it to, or the reason it
|
|
68
94
|
* reads as nothing with the facts a sentence about it names. */
|
|
69
95
|
export type RuleEntryReading = {
|
|
@@ -87,9 +113,10 @@ export declare function compiledEntryName(compiled: CompiledAllowEntry): string;
|
|
|
87
113
|
/**
|
|
88
114
|
* Compile one list WHOLE through {@link readRuleEntry}: the entries it reads and the ones it reads as nothing (each with its
|
|
89
115
|
* reason and, for a name the roster refuses, the judgment's word and the spelling to write). An allow list holding `*` voids
|
|
90
|
-
* the list (every other entry on it reads as nothing too: the list grants nothing — the safe direction)
|
|
91
|
-
*
|
|
92
|
-
*
|
|
116
|
+
* the list (every other entry on it reads as nothing too: the list grants nothing — the safe direction) — `*` as the lenient
|
|
117
|
+
* rule parser names it ({@link namesWholeRoster}), so an entry the stricter entry grammar refuses (`* (*)`) still voids. A
|
|
118
|
+
* `*` on a deny list reads as nothing by itself and the rest of that list still refuses: a deny list never loses an entry to
|
|
119
|
+
* a wildcard beside it. `roster` as in {@link readRuleEntry}.
|
|
93
120
|
*/
|
|
94
121
|
export declare function compileAllowList(entries: readonly string[], behavior: "allow" | "deny", roster?: ReadonlySet<string>): {
|
|
95
122
|
rows: Array<{
|
|
@@ -294,11 +321,16 @@ export type AllowEntryInertNote = ({
|
|
|
294
321
|
} & AllowEntryInert) | ({
|
|
295
322
|
readonly source: AllowEntrySource;
|
|
296
323
|
} & AllowEntryInert);
|
|
324
|
+
/**
|
|
325
|
+
* A list entry spelled so a person reads what the engine read: the entry as a JSON string — quotes, backslashes and the usual
|
|
326
|
+
* escapes — with every character that prints as nothing, or as a space, written as `\uXXXX` (`\u{…}` beyond the BMP) and a
|
|
327
|
+
* run of spaces kept space by space. Uncapped and one-to-one (two entries never share a spelling), so a value that BINDS an
|
|
328
|
+
* entry may hold it ({@link renderListEntry} is this, capped for a sentence).
|
|
329
|
+
*/
|
|
330
|
+
export declare function spelledListEntry(entry: string): string;
|
|
297
331
|
/**
|
|
298
332
|
* THE ONE WRITER of a list entry into a sentence (the start-of-run notice, a skill's refusal and its decisions, a sub-agent
|
|
299
|
-
* list's refusals and advisories):
|
|
300
|
-
* character that prints as nothing, or as a space, written as `\uXXXX` (`\u{…}` beyond the BMP) and a run of spaces kept
|
|
301
|
-
* space by space, so what a person reads is what the engine read. Capped on what is printed.
|
|
333
|
+
* list's refusals and advisories): {@link spelledListEntry}, capped on what is printed.
|
|
302
334
|
*/
|
|
303
335
|
export declare function renderListEntry(entry: string): string;
|
|
304
336
|
/**
|
package/dist/core/allow-entry.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { heldBy } from "./tool-name-readings.js";
|
|
2
|
-
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers
|
|
2
|
+
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "./permission-rules.js";
|
|
3
|
+
import { parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
3
4
|
import { judgeRuleToolName, splitNamespacedName } from "./tool-name-aliases.js";
|
|
4
5
|
import { catalogToolNames, isShellFaceToolName } from "./tool-registry.js";
|
|
5
6
|
import { ALLOW_RULE_MEMBERS } from "./allow-rule-shadow.js";
|
|
@@ -12,6 +13,7 @@ import { inlineUntrusted } from "./untrusted-text.js";
|
|
|
12
13
|
import { valueTypeLabel } from "./tool-name-list.js";
|
|
13
14
|
import { deliverEngineNotice } from "./engine-notice.js";
|
|
14
15
|
export const ALLOW_ENTRY_INERT_REASONS = Object.freeze(["wildcard_all", "namespace_wildcard", "content_unsupported", "grammar", "tool_name", "near_miss", "earlier_grammar"]);
|
|
16
|
+
const NEAR_MISS_CARRIER = { run: "the mounted spelling is", pool: "a delegation pool carries", catalog: "the engine's tool catalog names" };
|
|
15
17
|
export function nonStringMemberLabel(value) {
|
|
16
18
|
return `<not a string: ${valueTypeLabel(value)}>`;
|
|
17
19
|
}
|
|
@@ -21,7 +23,6 @@ export function nonListValueLabel(read) {
|
|
|
21
23
|
function ruleRowOf(parsed) {
|
|
22
24
|
return { ...parsed, scope: { kind: "global" }, adds: [{ dot: { actor: "list", counter: 1 }, origin: "user", createdAt: "" }] };
|
|
23
25
|
}
|
|
24
|
-
const TOOL_NAME_SPELLING = /^[^\s()\p{Cc}\p{Cf}]+$/u;
|
|
25
26
|
function mountedSpellingOf(name, roster) {
|
|
26
27
|
const folded = name.toLowerCase();
|
|
27
28
|
for (const mounted of roster)
|
|
@@ -29,6 +30,14 @@ function mountedSpellingOf(name, roster) {
|
|
|
29
30
|
return mounted;
|
|
30
31
|
return undefined;
|
|
31
32
|
}
|
|
33
|
+
export function nearMissSpellingOf(name, roster) {
|
|
34
|
+
if (roster.has(name) || catalogToolNames().has(name) || splitNamespacedName(name) !== undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
return mountedSpellingOf(name, roster);
|
|
37
|
+
}
|
|
38
|
+
export function rootFacingToolNames(carried) {
|
|
39
|
+
return new Set([...catalogToolNames(), ...carried]);
|
|
40
|
+
}
|
|
32
41
|
function toolNameReading(name, roster) {
|
|
33
42
|
const verdict = judgeRuleToolName(name, roster);
|
|
34
43
|
if (verdict.kind === "unprefixed_namespace")
|
|
@@ -36,9 +45,7 @@ function toolNameReading(name, roster) {
|
|
|
36
45
|
if (verdict.kind !== "current") {
|
|
37
46
|
return { kind: "inert", reason: "tool_name", verdict: verdict.kind, guidance: verdict.guidance, ...(verdict.currentName !== undefined ? { currentName: verdict.currentName } : {}) };
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
return undefined;
|
|
41
|
-
const mounted = mountedSpellingOf(name, roster);
|
|
48
|
+
const mounted = nearMissSpellingOf(name, roster);
|
|
42
49
|
return mounted === undefined ? undefined : { kind: "inert", reason: "near_miss", currentName: mounted };
|
|
43
50
|
}
|
|
44
51
|
export function readRuleEntry(raw, behavior, roster) {
|
|
@@ -46,12 +53,10 @@ export function readRuleEntry(raw, behavior, roster) {
|
|
|
46
53
|
const row = (compiled) => ({ kind: "row", compiled });
|
|
47
54
|
if (typeof raw !== "string")
|
|
48
55
|
return { kind: "inert", reason: "grammar", valueType: valueTypeLabel(raw) };
|
|
49
|
-
|
|
56
|
+
const parsed = ruleSpellingOf(raw);
|
|
57
|
+
if (parsed === undefined)
|
|
50
58
|
return inert("grammar");
|
|
51
|
-
const parsed = parsePermissionRule(raw);
|
|
52
59
|
const name = parsed.toolName;
|
|
53
|
-
if (!TOOL_NAME_SPELLING.test(name))
|
|
54
|
-
return inert("grammar");
|
|
55
60
|
if (name === "*")
|
|
56
61
|
return inert("wildcard_all");
|
|
57
62
|
if (roster !== undefined) {
|
|
@@ -98,7 +103,7 @@ function inertReasonOf(code) {
|
|
|
98
103
|
return code === "unsupported.tool" ? "content_unsupported" : "grammar";
|
|
99
104
|
}
|
|
100
105
|
export function compileAllowList(entries, behavior, roster) {
|
|
101
|
-
const voided = behavior === "allow" && entries.some(
|
|
106
|
+
const voided = behavior === "allow" && entries.some(namesWholeRoster);
|
|
102
107
|
const rows = [];
|
|
103
108
|
const inert = [];
|
|
104
109
|
for (const entry of entries) {
|
|
@@ -113,8 +118,8 @@ export function compileAllowList(entries, behavior, roster) {
|
|
|
113
118
|
}
|
|
114
119
|
return { rows, inert };
|
|
115
120
|
}
|
|
116
|
-
function
|
|
117
|
-
return
|
|
121
|
+
function namesWholeRoster(entry) {
|
|
122
|
+
return typeof entry === "string" && entry === entry.trim() && parsePermissionRule(entry).toolName === "*";
|
|
118
123
|
}
|
|
119
124
|
function answersTo(req, has) {
|
|
120
125
|
return heldBy("allowEntry.names", { has }, req);
|
|
@@ -415,11 +420,13 @@ function escapedCodePoint(ch) {
|
|
|
415
420
|
const hex = cp.toString(16).padStart(4, "0");
|
|
416
421
|
return cp > 0xffff ? `\\u{${hex}}` : `\\u${hex}`;
|
|
417
422
|
}
|
|
418
|
-
export function
|
|
419
|
-
|
|
423
|
+
export function spelledListEntry(entry) {
|
|
424
|
+
return JSON.stringify(entry)
|
|
420
425
|
.replace(/ {2,}/g, (run) => "\\u0020".repeat(run.length))
|
|
421
426
|
.replace(UNSEEN_IN_ENTRY, (ch) => (ch === " " ? ch : escapedCodePoint(ch)));
|
|
422
|
-
|
|
427
|
+
}
|
|
428
|
+
export function renderListEntry(entry) {
|
|
429
|
+
return inlineUntrusted(spelledListEntry(entry), ENTRY_RENDER_MAX);
|
|
423
430
|
}
|
|
424
431
|
export function inertEntryPhrase(e) {
|
|
425
432
|
if (e.valueType !== undefined)
|
|
@@ -427,7 +434,7 @@ export function inertEntryPhrase(e) {
|
|
|
427
434
|
const spelling = e.currentName !== undefined ? inlineUntrusted(e.currentName, 120) : undefined;
|
|
428
435
|
const why = (() => {
|
|
429
436
|
if (e.reason === "near_miss")
|
|
430
|
-
return `near_miss:
|
|
437
|
+
return `near_miss: ${NEAR_MISS_CARRIER[e.carriedBy ?? "run"]} "${spelling}"`;
|
|
431
438
|
if (e.reason !== "tool_name" || e.verdict === undefined)
|
|
432
439
|
return e.reason;
|
|
433
440
|
switch (e.verdict) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AgentMessage, type CompactionSettings, type Session, type ThinkingLevel } from "../internal/harness.js";
|
|
2
2
|
import type { Message, Model, Tool } from "../internal/llm.js";
|
|
3
3
|
import type { ContextWindowOverlay } from "./context-settings.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { PostCompactContext, PreCompactContext, PreCompactResult } from "./hooks.js";
|
|
5
5
|
import type { TraceEvent } from "./trace.js";
|
|
6
6
|
import type { Brain } from "./types.js";
|
|
7
7
|
import type { TaskEvent, TaskEventIdentity } from "./task-event.js";
|
|
@@ -108,25 +108,27 @@ export interface MaybeCompactOptions {
|
|
|
108
108
|
/** The session to compact. Typed on the INTERFACE face (typecheck 车 2026-07-24): every member this
|
|
109
109
|
* module touches lives on `Session` (the optional `getPromptEpoch` degrades honestly via `?.()`). */
|
|
110
110
|
session: Session;
|
|
111
|
-
/** This run's
|
|
112
|
-
* the
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
/** This run's prompt identity inputs (and the pinned center layer's provenance): the compaction restates the
|
|
112
|
+
* session's epoch when the identity is unchanged and advances it otherwise — the same identity the prepare-time
|
|
113
|
+
* record names, so a compaction never flaps the record. Absent ⇒ no restatement (a host compacting outside a run). */
|
|
114
|
+
promptIdentity?: {
|
|
115
|
+
readonly layers: import("../prompt-assembly/epoch.js").PromptIdentityLayers;
|
|
116
|
+
readonly sourceRevision?: string;
|
|
117
|
+
};
|
|
118
|
+
/** A compaction-boundary CENTER LAYER CHANGE, pre-assembled by the caller (prepare's seam): an adopted candidate,
|
|
119
|
+
* or an emptied center layer after an explicit revocation. The epoch advances to the identity after the change in
|
|
120
|
+
* the SAME CAS append as the new baseline, and `apply()` (the live prefix / fingerprint swap) runs ONLY after that
|
|
121
|
+
* append succeeded — a failed or blocked compaction swaps nothing. */
|
|
119
122
|
centerAdoption?: {
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
declaredSections: import("../prompt-assembly/epoch.js").EpochDeclaredSections;
|
|
123
|
+
/** The run's identity inputs after the change (`layers.center` absent ⇒ the center layer is emptied). */
|
|
124
|
+
promptIdentity: {
|
|
125
|
+
readonly layers: import("../prompt-assembly/epoch.js").PromptIdentityLayers;
|
|
126
|
+
readonly sourceRevision?: string;
|
|
127
|
+
};
|
|
126
128
|
/** the post-swap prompt overhead in the trigger estimate's coordinate: used for THIS
|
|
127
129
|
* compaction's post-measurement so PTL recovery/anti-thrash see the swapped prefix size. */
|
|
128
130
|
overheadTokensAfter: number;
|
|
129
|
-
apply: (
|
|
131
|
+
apply: (committedIdentity: string) => void;
|
|
130
132
|
};
|
|
131
133
|
/** Model whose context window bounds the budget (the model that runs the next turn). */
|
|
132
134
|
model: Model;
|
|
@@ -196,8 +198,9 @@ export interface MaybeCompactOptions {
|
|
|
196
198
|
contextInstructionFiles?: string;
|
|
197
199
|
signal?: AbortSignal;
|
|
198
200
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
+
* The pass's WITHDRAWAL signal — the run's caller-stop gate (the stop verbs and the task's own
|
|
202
|
+
* `signal`), composed with the cancel signals of the manual callers THIS pass claimed (a SUBSET
|
|
203
|
+
* of `signal`, which may additionally carry the leg's own abort).
|
|
201
204
|
* Used only for failure ATTRIBUTION: when the summary call's failure is its abort outcome and
|
|
202
205
|
* this signal has fired, the thrown error is stamped so {@link isCompactionManualCancel} holds —
|
|
203
206
|
* the caller then treats the pass as withdrawn ("mooted") instead of summarizer evidence. It
|
|
@@ -488,13 +491,13 @@ export interface MaybeCompactOptions {
|
|
|
488
491
|
* no-block/no-instructions (the caller's wrapper owns tracing; an observer bug must never feed the
|
|
489
492
|
* caller's breaker or kill PTL recovery).
|
|
490
493
|
*/
|
|
491
|
-
preCompact?:
|
|
494
|
+
preCompact?: (ctx: PreCompactContext) => PreCompactResult | undefined | Promise<PreCompactResult | undefined>;
|
|
492
495
|
/**
|
|
493
496
|
* design/134 (CC PostCompact parity) — observe-only, fired after the compaction LANDED
|
|
494
497
|
* (appendCompaction done, post measurement taken). A throw is swallowed (same isolation contract
|
|
495
498
|
* as {@link onCompaction}: the compaction already succeeded).
|
|
496
499
|
*/
|
|
497
|
-
postCompact?:
|
|
500
|
+
postCompact?: (ctx: PostCompactContext) => void | Promise<void>;
|
|
498
501
|
/**
|
|
499
502
|
* MF-18 修② — observe-only: fired when a summary call's serialized conversation INPUT was
|
|
500
503
|
* truncated to fit the compaction model's context window (the ~300K-session shape that previously
|