@sema-agent/core 7.52.0 → 7.54.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 +470 -0
- package/LICENSE +3 -1
- package/README.md +3 -1
- 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/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 -6
- package/dist/agents/peer-session-drain.js +271 -182
- package/dist/agents/read-inbox-tool.d.ts +7 -0
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/send-message-tool.js +3 -4
- package/dist/agents/subagent.d.ts +6 -5
- package/dist/agents/subagent.js +117 -83
- package/dist/agents/tool-filter.d.ts +25 -4
- package/dist/agents/tool-filter.js +13 -3
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +4 -4
- package/dist/brain/model-presets.js +2 -1
- package/dist/brain/route-adjudicator.d.ts +6 -0
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +33 -14
- package/dist/core/allow-entry.d.ts +3 -1
- package/dist/core/allow-entry.js +3 -2
- package/dist/core/approval-object.d.ts +87 -0
- package/dist/core/approval-object.js +85 -0
- package/dist/core/arg-summary.d.ts +3 -2
- package/dist/core/arg-summary.js +17 -11
- package/dist/core/ask-origin.d.ts +43 -27
- package/dist/core/ask-origin.js +20 -11
- package/dist/core/ask-question.d.ts +0 -1
- package/dist/core/ask-question.js +4 -67
- package/dist/core/ask-unresolvable-notice.d.ts +3 -2
- package/dist/core/auto-compaction.d.ts +106 -50
- package/dist/core/auto-compaction.js +69 -53
- package/dist/core/auto-mode-prompt.d.ts +4 -2
- package/dist/core/auto-mode-prompt.js +4 -3
- package/dist/core/canonical-json.d.ts +69 -0
- package/dist/core/canonical-json.js +130 -8
- package/dist/core/checkpoint-requirements.d.ts +13 -7
- package/dist/core/checkpoint-requirements.js +4 -1
- package/dist/core/checkpoint-store.d.ts +60 -36
- package/dist/core/checkpoint-store.js +7 -6
- package/dist/core/child-clamp-seats.d.ts +45 -6
- package/dist/core/child-clamp-seats.js +56 -4
- package/dist/core/compliance.js +3 -1
- package/dist/core/consolidate-scope.d.ts +1 -1
- package/dist/core/cross-boundary-taint.d.ts +213 -0
- package/dist/core/cross-boundary-taint.js +196 -0
- package/dist/core/decision-receipt.d.ts +44 -1
- package/dist/core/decision-receipt.js +16 -0
- package/dist/core/effective-path-target.d.ts +28 -6
- package/dist/core/effective-path-target.js +28 -2
- package/dist/core/engine-notice.d.ts +47 -4
- package/dist/core/engine-notice.js +17 -1
- package/dist/core/engine-storage-backend.d.ts +73 -0
- package/dist/core/engine-storage-backend.js +1 -0
- package/dist/core/file-history-store.d.ts +6 -2
- package/dist/core/file-history-store.js +1 -0
- package/dist/core/gate-fold.js +13 -4
- package/dist/core/gate-lanes.js +41 -34
- package/dist/core/gate-outcome.d.ts +18 -9
- package/dist/core/governance-codes.d.ts +9 -2
- package/dist/core/governance-codes.js +9 -0
- 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 +21 -19
- package/dist/core/hooks.js +6 -5
- package/dist/core/locked-config.d.ts +110 -31
- package/dist/core/locked-config.js +155 -42
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/mcp-probe.js +1 -1
- package/dist/core/mcp-server-spec.d.ts +152 -17
- package/dist/core/mcp-server-spec.js +223 -40
- package/dist/core/mcp.d.ts +21 -14
- package/dist/core/mcp.js +59 -54
- package/dist/core/memory-engine/engine.d.ts +14 -19
- package/dist/core/memory-engine/engine.js +9 -6
- package/dist/core/memory-engine/export-bundle.d.ts +1 -1
- package/dist/core/memory-engine/export-bundle.js +1 -1
- package/dist/core/memory-engine/file-backend.d.ts +2 -6
- package/dist/core/memory-engine/file-backend.js +1 -12
- package/dist/core/memory.d.ts +3 -3
- package/dist/core/mode-policy.d.ts +16 -26
- package/dist/core/mode-policy.js +5 -18
- package/dist/core/mods/kernel/dispatch.js +13 -15
- package/dist/core/mods/kernel/events.d.ts +9 -6
- package/dist/core/mods/kernel/events.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +41 -11
- package/dist/core/mods/kernel/receive.js +124 -54
- package/dist/core/mods/kernel/site-rules.d.ts +80 -15
- package/dist/core/mods/kernel/site-rules.js +232 -40
- package/dist/core/mods/stations.d.ts +0 -6
- package/dist/core/mods/stations.js +0 -7
- 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 +3 -1
- package/dist/core/parent-spec-seat.d.ts +12 -15
- package/dist/core/parent-spec-seat.js +14 -6
- package/dist/core/pause-registry.d.ts +35 -1
- package/dist/core/pause-registry.js +10 -0
- package/dist/core/permission-rule-consent.d.ts +18 -5
- package/dist/core/permission-rule-consent.js +13 -8
- package/dist/core/permission-rule-model.d.ts +87 -15
- package/dist/core/permission-rule-model.js +87 -8
- package/dist/core/permission-rule-org.d.ts +200 -33
- package/dist/core/permission-rule-org.js +217 -91
- package/dist/core/permission-rule-session.js +2 -2
- package/dist/core/permission-rule-store.d.ts +8 -0
- package/dist/core/permission-rule-store.js +1 -0
- package/dist/core/permission-rule-sync.js +3 -2
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +37 -8
- package/dist/core/read-only-shell.d.ts +1 -1
- package/dist/core/read-only-shell.js +30 -9
- package/dist/core/remote-env.d.ts +41 -1
- package/dist/core/remote-env.js +18 -0
- package/dist/core/resume-startup-cause.d.ts +18 -0
- package/dist/core/resume-startup-cause.js +12 -0
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +8 -2
- package/dist/core/retired-keys.js +12 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/approval-feedback.d.ts +46 -0
- package/dist/core/runner/approval-feedback.js +41 -0
- package/dist/core/runner/compaction-call-options.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.js +2 -2
- package/dist/core/runner/contracts.d.ts +34 -41
- package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
- package/dist/core/runner/cross-boundary-lane.js +190 -0
- package/dist/core/runner/decide-continuation.d.ts +4 -4
- package/dist/core/runner/decide-continuation.js +3 -1
- package/dist/core/runner/gate-exit.d.ts +32 -12
- package/dist/core/runner/gate-exit.js +77 -34
- package/dist/core/runner/inherited-ask-grants.js +2 -2
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- 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 +5 -3
- package/dist/core/runner/person-path-rows.js +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
- package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
- package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
- package/dist/core/runner/prepare-config-doors.d.ts +58 -15
- package/dist/core/runner/prepare-config-doors.js +167 -146
- package/dist/core/runner/prepare-context-lane.js +6 -3
- package/dist/core/runner/prepare-defer-classify.js +4 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
- package/dist/core/runner/prepare-delegation-surface.js +8 -7
- package/dist/core/runner/prepare-execution-env.d.ts +1 -2
- package/dist/core/runner/prepare-execution-env.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
- package/dist/core/runner/prepare-gate-stations.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -3
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
- package/dist/core/runner/prepare-inherited-gate.js +3 -2
- 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 +19 -6
- 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.d.ts +1 -1
- package/dist/core/runner/prepare-park-ask.js +9 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
- package/dist/core/runner/prepare-policy-chain.js +14 -19
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +6 -4
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-assembly.js +20 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
- package/dist/core/runner/prepare-prompt-inputs.js +6 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
- package/dist/core/runner/prepare-protocol-tools.js +19 -12
- 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.d.ts +7 -2
- package/dist/core/runner/prepare-safety-scan.js +34 -27
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
- package/dist/core/runner/prepare-suspend-saga.js +6 -2
- package/dist/core/runner/prepare-task.js +22 -19
- package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
- package/dist/core/runner/prepare-turn-wiring.js +14 -49
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -5
- package/dist/core/runner/prepare-wiring-manifest.js +9 -6
- package/dist/core/runner/prepare-workspace-restore.js +2 -2
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +16 -14
- package/dist/core/runner/resume-apply.js +4 -4
- 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-policy-outcome.d.ts +2 -2
- package/dist/core/runner/resume-policy-outcome.js +18 -15
- package/dist/core/runner/resume-preflight.js +2 -2
- package/dist/core/runner/resume-review-outcome.js +3 -3
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +1 -4
- package/dist/core/runner/run-leg.js +97 -43
- package/dist/core/runner/run-recovery-lanes.js +5 -3
- package/dist/core/runner/run-settle-and-teardown.js +2 -0
- package/dist/core/runner/run-terminal-adoption.js +21 -9
- package/dist/core/runner/run-turn-boundary.js +9 -3
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/session-file-state-replay.d.ts +5 -2
- package/dist/core/runner/session-file-state-replay.js +3 -2
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
- package/dist/core/runner/sub-agent-deny-lists.js +7 -5
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner/tool-face-overlay.d.ts +24 -6
- package/dist/core/runner/tool-face-overlay.js +24 -15
- package/dist/core/runner-deps.d.ts +37 -22
- package/dist/core/session-policy-store.d.ts +19 -0
- package/dist/core/session-policy-store.js +22 -2
- package/dist/core/session-store.d.ts +35 -16
- package/dist/core/session-store.js +20 -12
- package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
- package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
- package/dist/core/shell-wrapper-table.d.ts +5 -3
- package/dist/core/shell-wrapper-table.js +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
- package/dist/core/store-contracts/session-store-contract.js +639 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
- package/dist/core/task-event.d.ts +44 -6
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-registry-agent.js +1 -0
- package/dist/core/task-registry-shared.d.ts +5 -0
- package/dist/core/task-registry.d.ts +7 -0
- package/dist/core/task-registry.js +17 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +7 -7
- package/dist/core/tighten-task-spec.d.ts +6 -4
- package/dist/core/tighten-task-spec.js +2 -2
- package/dist/core/tool-catalog-entries.d.ts +1 -1
- package/dist/core/tool-catalog-entries.js +52 -52
- package/dist/core/tool-face-reader.d.ts +49 -0
- package/dist/core/tool-face-reader.js +94 -0
- package/dist/core/tool-face.d.ts +17 -8
- package/dist/core/tool-face.js +1 -0
- package/dist/core/tool-policy.d.ts +67 -23
- package/dist/core/tool-policy.js +72 -60
- package/dist/core/tool-roster.d.ts +46 -28
- package/dist/core/tool-roster.js +91 -14
- package/dist/core/tool-spec-members.js +2 -2
- package/dist/core/tool-spec.d.ts +20 -4
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +24 -2
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +23 -0
- package/dist/core/untrusted-text.js +10 -10
- package/dist/core/user-attachment.d.ts +56 -2
- package/dist/core/user-attachment.js +139 -56
- package/dist/core/wiring-manifest.d.ts +4 -2
- package/dist/engine/compaction/compaction.d.ts +2 -2
- package/dist/engine/compaction/utils.d.ts +2 -2
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +10 -4
- package/dist/engine/llm/types.d.ts +7 -2
- package/dist/engine/llm/types.js +2 -1
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +17 -5
- package/dist/engine/loop/types.d.ts +7 -1
- package/dist/engine/session/repo-utils.d.ts +5 -0
- package/dist/engine/session/repo-utils.js +9 -0
- package/dist/engine/session/storage-base.js +2 -2
- package/dist/index.d.ts +37 -17
- package/dist/index.js +31 -14
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/internal/llm.d.ts +1 -1
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/orchestration/run-workflow-tool.d.ts +3 -0
- package/dist/orchestration/run-workflow-tool.js +1 -0
- package/dist/orchestration/workflow-governance.d.ts +1 -1
- package/dist/orchestration/workflow.d.ts +3 -0
- package/dist/orchestration/workflow.js +2 -1
- package/dist/prompt-assembly/artifact-store.d.ts +44 -9
- package/dist/prompt-assembly/artifact-store.js +38 -12
- package/dist/prompt-assembly/artifact.d.ts +12 -11
- package/dist/prompt-assembly/artifact.js +52 -21
- package/dist/prompt-assembly/assemble.d.ts +2 -0
- package/dist/prompt-assembly/assemble.js +2 -1
- package/dist/prompt-assembly/catalog/index.js +2 -1
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +19 -2
- package/dist/prompt-assembly/catalog/schema.js +3 -0
- package/dist/prompt-assembly/composer.d.ts +2 -0
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/overlay.d.ts +21 -11
- package/dist/prompt-assembly/overlay.js +57 -19
- package/dist/prompt-assembly/select.d.ts +21 -6
- package/dist/prompt-assembly/select.js +41 -4
- package/dist/prompts/default.d.ts +1 -12
- package/dist/prompts/default.js +0 -3
- package/dist/shell/lex.d.ts +87 -8
- package/dist/shell/lex.js +283 -49
- package/dist/stores/file/index.d.ts +8 -6
- 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 +14 -3
- package/dist/tools/fs/bash-program-position.d.ts +3 -15
- package/dist/tools/fs/bash-program-position.js +3 -10
- package/dist/tools/fs/bash-readonly-classifier.js +2 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-bash.js +72 -101
- package/dist/tools/fs/fs-read.d.ts +12 -0
- package/dist/tools/fs/fs-read.js +10 -9
- package/dist/tools/fs/fs-shared.d.ts +27 -31
- package/dist/tools/fs/fs-shared.js +12 -15
- package/dist/tools/fs/fs-write.js +1 -1
- package/dist/tools/fs/index.js +2 -2
- package/dist/tools/fs/read-deny-home.d.ts +1 -1
- package/dist/tools/fs/read-deny-home.js +5 -4
- package/dist/tools/fs/read-deny.d.ts +16 -3
- package/dist/tools/fs/read-deny.js +12 -9
- package/dist/tools/fs/safety.d.ts +7 -6
- package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
- package/dist/tools/fs/shell-read-bindings.js +15 -16
- package/dist/tools/fs/shell-read-device.js +1 -1
- package/dist/tools/fs/shell-read-plan.js +36 -47
- package/dist/tools/fs/shell-read-word.d.ts +1 -6
- package/dist/tools/fs/shell-read-word.js +2 -20
- package/dist/tools/fs/shell-reentry-text.js +1 -1
- package/dist/tools/web.d.ts +2 -2
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +5651 -5457
- package/wire-changes/core@7.53.0.json +1968 -0
- package/wire-changes/core@7.54.0.json +2870 -0
- package/dist/core/shell-scan.d.ts +0 -60
- package/dist/core/shell-scan.js +0 -183
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReceiptFault } from "./mods/kernel/receive.js";
|
|
1
2
|
import type { ToolContentOrigin, ToolEffect } from "./tool-spec.js";
|
|
2
3
|
import type { ToolFamily, ToolPathTarget, ToolRenderHints, ToolRuleFace } from "./tool-face.js";
|
|
3
4
|
/** Definition of an MCP server to connect to for the duration of one task, then discard. */
|
|
@@ -54,6 +55,8 @@ export interface McpServerSpec {
|
|
|
54
55
|
* override is AUTHORITATIVE and may LOWER an effect (vouch a tool is `read`/`idempotent`) as well as raise it
|
|
55
56
|
* (`egress` / irreversible). This is the ONLY trusted way to drop an MCP tool below the fail-closed `write`
|
|
56
57
|
* default (design F). Folds over the server hints in prepare-task (caller > server hint > fail-closed write).
|
|
58
|
+
* `boundaryFlow: "engine"` vouches that a call's effect stays inside the engine: the tool's roster row states no boundary legs
|
|
59
|
+
* (`boundaryLegs: []`) instead of the legs its effect implies — it needs `egress: false` beside it (refused otherwise).
|
|
57
60
|
*
|
|
58
61
|
* A DIFFERENT AXIS from {@link contentOrigin}: this one is about what a call DOES (repeat-safety,
|
|
59
62
|
* blast radius, reversibility — it feeds the approval gate); the content class is about what a call
|
|
@@ -61,7 +64,7 @@ export interface McpServerSpec {
|
|
|
61
64
|
* a read-only tool can return third-party text, and a deployment's own writer brings back nothing
|
|
62
65
|
* external — so vouching on one axis never quietly vouches on the other.
|
|
63
66
|
*
|
|
64
|
-
* THE SHAPE IS JUDGED, NOT TRUSTED: {@link
|
|
67
|
+
* THE SHAPE IS JUDGED, NOT TRUSTED: {@link readToolAxes} is the one reader of this declaration (an A2A peer's `toolAxes` is read by it too). The
|
|
65
68
|
* preparation door runs it on every entry, and a declaration it cannot read refuses the whole prepare
|
|
66
69
|
* (`config.invalid_argument`) — one bad tool refuses the entry, a bad key is never dropped and a bad word
|
|
67
70
|
* never defaults, because a caller's safety axis that silently reads as absent is a trust root that is not
|
|
@@ -74,16 +77,19 @@ export interface McpServerSpec {
|
|
|
74
77
|
effect?: ToolEffect;
|
|
75
78
|
egress?: boolean;
|
|
76
79
|
irreversibility?: "always" | "never";
|
|
80
|
+
boundaryFlow?: "engine";
|
|
77
81
|
}>;
|
|
78
82
|
/**
|
|
79
83
|
* design/388 §1.1 / §2.7 — the OPTIONAL per-tool FACE overlay for this server's tools (keyed by the server's
|
|
80
84
|
* RAW tool name, like {@link allowTools} / {@link toolAxes}): a deployment lets an MCP tool enter a specialised
|
|
81
85
|
* approval card, be confirmed by path, carry a display name / summary, or narrow its rule vocabulary. TRUSTED
|
|
82
|
-
* (the deployment writes it)
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* `
|
|
86
|
+
* (the deployment writes it). It is read once by `readToolFaces` at the preparation door and at `materializeMcpTools`: a
|
|
87
|
+
* declaration that does not fit on its own (a member the types do not have, a member of the wrong kind, a word outside a
|
|
88
|
+
* closed table, a reserved approval card, an empty tool name) refuses the entry under `config.tool_face_invalid` — the whole
|
|
89
|
+
* prepare at the door. What only the schema can say is judged at every listing (first mount and every refresh): a
|
|
90
|
+
* `pathTarget.param` that is not a top-level property, an `approvalCard` whose keys the schema lacks, a `ruleFace` naming a
|
|
91
|
+
* non-scalar key ⇒ that tool is NOT mounted and the leg announces `config.tool_face_invalid` once; a key naming a tool the
|
|
92
|
+
* listing does not carry announces `config.tool_face_undeclared` once. Applied whole or not at all.
|
|
87
93
|
*/
|
|
88
94
|
toolFaces?: Record<string, {
|
|
89
95
|
family?: ToolFamily;
|
|
@@ -167,31 +173,151 @@ export interface McpServerSpec {
|
|
|
167
173
|
}
|
|
168
174
|
type ToolAxes = NonNullable<McpServerSpec["toolAxes"]>;
|
|
169
175
|
/**
|
|
170
|
-
* {@link
|
|
176
|
+
* The surfaces that declare `toolAxes`, which {@link readToolAxes} names its reading by: an MCP server's keys are the server's
|
|
177
|
+
* remote TOOL names, an A2A peer's keys are the peer's remote SKILL ids.
|
|
178
|
+
*/
|
|
179
|
+
export type ToolAxesSurface = "mcp" | "a2a";
|
|
180
|
+
/**
|
|
181
|
+
* {@link readToolAxes}'s answer. `ok: true` carries the declaration as a PLAIN COPY the reader built from
|
|
171
182
|
* what it judged (never the caller's own object); `toolAxes` is absent when the entry declares nothing — no
|
|
172
183
|
* key at all, or only empty per-tool objects (which the engine reads the same as no declaration). `ok: false`
|
|
173
|
-
* carries one sentence naming the tool and the member that did not fit, never a value.
|
|
184
|
+
* carries one sentence naming the tool (or skill) and the member that did not fit, never a value.
|
|
174
185
|
*/
|
|
175
|
-
export type
|
|
186
|
+
export type ToolAxesRead = {
|
|
176
187
|
ok: true;
|
|
177
188
|
toolAxes?: ToolAxes;
|
|
178
189
|
} | {
|
|
179
190
|
ok: false;
|
|
180
191
|
problem: string;
|
|
181
192
|
};
|
|
193
|
+
/** A name the declarer wrote, quoted and bounded for a sentence (the sentence names; it never carries a value). The bound holds on the
|
|
194
|
+
* QUOTED form — escaping a quote, a backslash or a control character lengthens a name — so a sentence's budget is spent the same way
|
|
195
|
+
* whatever the name holds. */
|
|
196
|
+
export declare function quotedName(name: string): string;
|
|
197
|
+
/** The kind of a value that is plain data, for a sentence that names what was declared without carrying it. */
|
|
198
|
+
export declare function dataKind(value: unknown): string;
|
|
199
|
+
/**
|
|
200
|
+
* Where a declaration stops being one: the path from the declaration's root — a list index as a number, a member name as a string — and
|
|
201
|
+
* the clause that says what is there ("it contains itself; …"): a kind and a remedy, never a value the declarer wrote.
|
|
202
|
+
*/
|
|
203
|
+
export interface DeclarationFault {
|
|
204
|
+
readonly path: readonly (string | number)[];
|
|
205
|
+
readonly problem: string;
|
|
206
|
+
}
|
|
207
|
+
/** {@link receiveDeclaration}'s answer: the engine's own frozen copy, or where and why the declaration could not be received. */
|
|
208
|
+
export type DeclarationRead = {
|
|
209
|
+
readonly ok: true;
|
|
210
|
+
readonly value: unknown;
|
|
211
|
+
} | {
|
|
212
|
+
readonly ok: false;
|
|
213
|
+
readonly fault: DeclarationFault;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* A receipt's refusal of a declaration (or of the list that carries declarations), said as a declaration refusal says it. The receipt's
|
|
217
|
+
* path does not say which kind of container each name came from, so a segment of digits alone is read as a list index.
|
|
218
|
+
*/
|
|
219
|
+
export declare function declarationFaultOf(fault: ReceiptFault): DeclarationFault;
|
|
220
|
+
/** What a declaration's own type fixes at one path, beyond being plain data: a string there, or a record of names to strings. */
|
|
221
|
+
export interface DeclarationPathRule {
|
|
222
|
+
readonly path: readonly string[];
|
|
223
|
+
readonly holds: "a string" | "names to strings";
|
|
224
|
+
/** The member must be there (a declaration with no value at the path is refused, as one with a value of another kind is). */
|
|
225
|
+
readonly required?: true;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Per surface, every path its declaration's type fixes beyond plain data — the declarer's `name` (a string, always there) and every record
|
|
229
|
+
* the type writes as `Record<string, string>`: an MCP server's `transport.env` (a stdio server's environment) and `transport.headers` (an
|
|
230
|
+
* HTTP server's headers); an A2A peer's `headers`. A path that is not its surface's (an A2A peer's `transport`) is not judged by another
|
|
231
|
+
* surface's row: it is a member the surface does not read.
|
|
232
|
+
*/
|
|
233
|
+
export declare const DECLARATION_PATH_RULES: Readonly<Record<ToolAxesSurface, readonly DeclarationPathRule[]>>;
|
|
234
|
+
/** The rules of a declaration that fixes no path beyond plain data and lists of names (a `toolAxes` record, a lock declaration): the one
|
|
235
|
+
* empty table, passed on purpose — {@link receiveDeclaration} takes its rules as an argument and refuses an absent one. */
|
|
236
|
+
export declare const NO_DECLARATION_PATH_RULES: readonly DeclarationPathRule[];
|
|
237
|
+
/**
|
|
238
|
+
* The one reading of a declaration a caller hands the engine — an MCP server's or an A2A peer's entry (the snapshot), a `toolAxes`
|
|
239
|
+
* record ({@link readToolAxes}), a lock declaration: the engine's one receipt of a caller's object in its declaration mode, so the
|
|
240
|
+
* value is plain data held to the engine's one depth ({@link MODS_VALUE_DEPTH} levels below it) and every own member is read once, then, when the copy is a record, the declaration's own
|
|
241
|
+
* vocabulary over the copy (a list holds names; no member is named `__proto__`; each path `rules` names holds what its rule says — an
|
|
242
|
+
* entry's rules are its surface's {@link DECLARATION_PATH_RULES}). The copy is the answer — the caller's object is never handed on and
|
|
243
|
+
* never read again. A copy that is not a record is answered as it is, for the reader that expected a record to word.
|
|
244
|
+
*
|
|
245
|
+
* `rules` is required: a surface's {@link DECLARATION_PATH_RULES} row, or an empty list ({@link NO_DECLARATION_PATH_RULES} is the engine's
|
|
246
|
+
* own) for a declaration that fixes no path. A value that is not a list (an absent one — what an `at` outside the surfaces indexes to — `null`, a string, an inherited
|
|
247
|
+
* member of the table) is a caller's mistake about which declaration it holds, refused loudly (`config.invalid_argument`) rather than
|
|
248
|
+
* read as "no path rule", which would judge the declaration by less than its surface fixes.
|
|
249
|
+
*/
|
|
250
|
+
export declare function receiveDeclaration(declared: unknown, rules: readonly DeclarationPathRule[]): DeclarationRead;
|
|
251
|
+
/**
|
|
252
|
+
* The per-tool declarations a spec carries as ONE record keyed by the remote tool (or skill) name — what each is called in a
|
|
253
|
+
* sentence about it. Every reader of such a record ({@link readToolAxes}, `readToolFaces`) opens it through
|
|
254
|
+
* {@link receivePerToolDeclaration} and words a refusal through the two sentence functions below, so the one receipt, the one
|
|
255
|
+
* fault vocabulary and the one sentence shape are not written twice.
|
|
256
|
+
*/
|
|
257
|
+
declare const PER_TOOL_DECLARATIONS: {
|
|
258
|
+
readonly toolAxes: {
|
|
259
|
+
readonly plural: "axes";
|
|
260
|
+
readonly singular: "axis";
|
|
261
|
+
};
|
|
262
|
+
readonly toolFaces: {
|
|
263
|
+
readonly plural: "faces";
|
|
264
|
+
readonly singular: "face";
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
type PerToolDeclaration = keyof typeof PER_TOOL_DECLARATIONS;
|
|
268
|
+
/** {@link receivePerToolDeclaration}'s answer: the receipt's copy as a record keyed by name, or the one sentence that says why not. */
|
|
269
|
+
type PerToolDeclarationRead = {
|
|
270
|
+
readonly ok: true;
|
|
271
|
+
readonly perTool: Readonly<Record<string, unknown>>;
|
|
272
|
+
} | {
|
|
273
|
+
readonly ok: false;
|
|
274
|
+
readonly problem: string;
|
|
275
|
+
};
|
|
182
276
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
|
|
277
|
+
* A per-tool declaration's receipt: {@link receiveDeclaration} (plain data held to the engine's one depth, every own member read once,
|
|
278
|
+
* lists of names, no `__proto__`), then the copy must be a record of names. A refusal names the tool (or skill) and the member where
|
|
279
|
+
* reading stopped, never a value. `unit` is what the keys are called (`tool` / `skill`).
|
|
280
|
+
*/
|
|
281
|
+
export declare function receivePerToolDeclaration(declared: unknown, field: PerToolDeclaration, unit: string): PerToolDeclarationRead;
|
|
282
|
+
/** The sentence for one tool's (or skill's) declaration that does not fit: `detail` says which member and why. */
|
|
283
|
+
export declare function perToolEntryRefusal(field: PerToolDeclaration, unit: string, key: string, detail: string): string;
|
|
284
|
+
/** The sentence for a per-tool declaration that names a tool (or skill) with an empty name. */
|
|
285
|
+
export declare function perToolEmptyName(field: PerToolDeclaration, unit: string): string;
|
|
286
|
+
/**
|
|
287
|
+
* The one reader of a raw `toolAxes` declaration — {@link McpServerSpec.toolAxes} and {@link A2aServerSpec.toolAxes} are the same
|
|
288
|
+
* declaration, and the engine's preparation door, its materialization of an MCP server and of an A2A peer, and every host that
|
|
289
|
+
* assembles the field from JSON ask this same function. `at` names the surface the declaration sits on, because that decides what its
|
|
290
|
+
* keys are called in the sentence: `"mcp"` — the server's remote tools; `"a2a"` — the peer's remote skills. Absent ⇒ `{ ok: true }`
|
|
291
|
+
* (nothing declared). Anything else must be an object of per-tool objects whose members are exactly `effect` /
|
|
292
|
+
* `egress` / `irreversibility` / `boundaryFlow` with legal values; a declaration of any other shape, an empty tool name, an
|
|
187
293
|
* unknown member, a word outside the vocabulary or a non-boolean `egress` on ANY one tool refuses the whole
|
|
188
294
|
* declaration (`ok: false`) — the good tools are not kept and nothing is defaulted, since a caller axis is
|
|
189
295
|
* two-way (it can vouch a tool down as well as mark it up) and a silently lost half is a trust root that is
|
|
190
|
-
* not in force.
|
|
191
|
-
*
|
|
192
|
-
*
|
|
296
|
+
* not in force. `boundaryFlow: "engine"` (the call's effect stays inside the engine — the tool's roster row states no
|
|
297
|
+
* boundary legs) needs `egress: false` in the same entry — `egress: true` contradicts it and an absent one leaves a server hint
|
|
298
|
+
* or the peer default marking it an external write — refused here; whether the other axes contradict each other (`egress`
|
|
299
|
+
* over a non-write effect) is the ingestion's own arithmetic. The declaration is read once, as plain data, by {@link receiveDeclaration} — a
|
|
300
|
+
* Proxy, a map, a class instance, a record built on a prototype or in another realm, a getter that throws, a structure that contains
|
|
301
|
+
* itself, a tool named `__proto__` are refusals that say where they sit (never the thrown text), not a throw — and judged on that copy.
|
|
302
|
+
* `at` is the caller's own word, not the declarer's: one outside {@link ToolAxesSurface} throws `config.invalid_argument` naming the
|
|
303
|
+
* surfaces, whatever is declared.
|
|
304
|
+
*/
|
|
305
|
+
export declare function readToolAxes(declared: unknown, at: ToolAxesSurface): ToolAxesRead;
|
|
306
|
+
/** {@link readToolAxes} for an MCP server's declaration — the entry a host that mounts MCP servers asks. */
|
|
307
|
+
export declare function readMcpToolAxes(declared: unknown): ToolAxesRead;
|
|
308
|
+
/**
|
|
309
|
+
* The word a sentence about one declaration of the surface `at` names the declarer by — `MCP server`, `A2A peer`. The same row the
|
|
310
|
+
* `toolAxes` refusal below reads, so every sentence the engine writes about a declaration it cannot read calls the declarer one thing.
|
|
193
311
|
*/
|
|
194
|
-
export declare function
|
|
312
|
+
export declare function declarerWord(at: ToolAxesSurface): string;
|
|
313
|
+
/**
|
|
314
|
+
* The refusal an entry's unreadable `toolAxes` becomes (`config.invalid_argument`), ONE sentence for every surface that declares them:
|
|
315
|
+
* the subject is the surface's declarer word and `name` (already quoted and bounded by the caller: `MCP server "db"`, `A2A peer "agent"`),
|
|
316
|
+
* `problem` is {@link readToolAxes}'s own sentence.
|
|
317
|
+
*/
|
|
318
|
+
export declare function toolAxesRefusal(at: ToolAxesSurface, name: string, problem: string): Error & {
|
|
319
|
+
code: "config.invalid_argument";
|
|
320
|
+
};
|
|
195
321
|
/** The identity of one mount of a declared server, as the host minted it ({@link McpServerSpec.fingerprint}). */
|
|
196
322
|
export interface McpMountIdentity {
|
|
197
323
|
readonly fingerprint: string;
|
|
@@ -242,11 +368,20 @@ export interface A2aServerSpec {
|
|
|
242
368
|
* raise it — e.g. `{ egress: false, effect: "read" }` vouches that a skill only reads. Note that
|
|
243
369
|
* lowering `effect` WITHOUT clearing `egress` is a contradiction prepare-task refuses by design (an
|
|
244
370
|
* external write must be repeat-unsafe).
|
|
371
|
+
*
|
|
372
|
+
* THE SHAPE IS JUDGED, NOT TRUSTED, by the same reader as {@link McpServerSpec.toolAxes} ({@link readToolAxes} with `at: "a2a"`) and
|
|
373
|
+
* with the same disposition: a task's preparation door runs it on every peer of `TaskSpec.a2a`, and a declaration it cannot read
|
|
374
|
+
* refuses the whole prepare (`config.invalid_argument`, subject `A2A peer "<name>"`) before any peer is dialed — one bad skill refuses
|
|
375
|
+
* the task, a bad key is never dropped and a bad word never defaults. The materialization entrance (`materializeA2aTools`, the library
|
|
376
|
+
* entrance) asks it once per peer, when it reads that peer's declaration: there a declaration it cannot read fails THAT peer's dial
|
|
377
|
+
* (never dialed, a warning whose cause is the same refusal), its siblings unaffected; a refresh does not ask again, it dials the copy
|
|
378
|
+
* that was judged (a peer refused at the entrance is refused again by every refresh, from the stored refusal).
|
|
245
379
|
*/
|
|
246
380
|
toolAxes?: Record<string, {
|
|
247
381
|
effect?: ToolEffect;
|
|
248
382
|
egress?: boolean;
|
|
249
383
|
irreversibility?: "always" | "never";
|
|
384
|
+
boundaryFlow?: "engine";
|
|
250
385
|
}>;
|
|
251
386
|
}
|
|
252
387
|
/**
|
|
@@ -1,30 +1,171 @@
|
|
|
1
1
|
import { SAFETY_AXIS_VOCABULARY } from "./safety-axis-vocab.js";
|
|
2
|
-
|
|
2
|
+
import { INLINE_TRUNCATION_MARK, inlineUntrusted } from "./untrusted-text.js";
|
|
3
|
+
import { DECLARATION_RECEIPT_MODE } from "./own-property-receipt.js";
|
|
4
|
+
import { isPlainRecord, receive } from "./mods/kernel/receive.js";
|
|
5
|
+
import { MODS_VALUE_DEPTH } from "./mods/kernel/site-rules.js";
|
|
6
|
+
const TOOL_AXES_SURFACES = {
|
|
7
|
+
mcp: { key: "tool", declarer: "MCP server" },
|
|
8
|
+
a2a: { key: "skill", declarer: "A2A peer" },
|
|
9
|
+
};
|
|
10
|
+
function invalidArgument(message) {
|
|
11
|
+
const refusal = new Error(message);
|
|
12
|
+
refusal.code = "config.invalid_argument";
|
|
13
|
+
return refusal;
|
|
14
|
+
}
|
|
15
|
+
function toolAxesSurfaceOf(at) {
|
|
16
|
+
if (typeof at === "string" && Object.hasOwn(TOOL_AXES_SURFACES, at))
|
|
17
|
+
return TOOL_AXES_SURFACES[at];
|
|
18
|
+
throw invalidArgument(`toolAxes is read for a surface, and the surfaces are ${Object.keys(TOOL_AXES_SURFACES).map((s) => JSON.stringify(s)).join(" | ")} ` +
|
|
19
|
+
`(an MCP server's tools, an A2A peer's skills); the surface named is not one of them.`);
|
|
20
|
+
}
|
|
21
|
+
const TOOL_AXIS_MEMBERS = { effect: true, egress: true, irreversibility: true, boundaryFlow: true };
|
|
3
22
|
const TOOL_AXIS_MEMBER_NAMES = Object.keys(TOOL_AXIS_MEMBERS);
|
|
4
23
|
const TOOL_EFFECT_WORDS = SAFETY_AXIS_VOCABULARY.toolEffect;
|
|
5
24
|
const ENGINE_DERIVED_IRREVERSIBILITY = "maybe";
|
|
6
25
|
const IRREVERSIBILITY_POLE_WORDS = SAFETY_AXIS_VOCABULARY.irreversibility.filter((word) => word !== ENGINE_DERIVED_IRREVERSIBILITY);
|
|
7
26
|
const irreversibilityPolesLockstep = true;
|
|
8
27
|
void irreversibilityPolesLockstep;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const toolAxesDeclarationsAreOne = true;
|
|
29
|
+
void toolAxesDeclarationsAreOne;
|
|
30
|
+
export function quotedName(name) {
|
|
31
|
+
const whole = JSON.stringify(name);
|
|
32
|
+
if (name.length <= 80 && whole.length <= QUOTED_NAME_MAX)
|
|
33
|
+
return whole;
|
|
34
|
+
let cut = Math.min(name.length, 80);
|
|
35
|
+
while (cut > 0 && JSON.stringify(`${name.slice(0, cut)}…`).length > QUOTED_NAME_MAX)
|
|
36
|
+
cut--;
|
|
37
|
+
return JSON.stringify(`${name.slice(0, cut)}…`);
|
|
38
|
+
}
|
|
39
|
+
const QUOTED_NAME_MAX = 83;
|
|
40
|
+
function memberNames(path) {
|
|
41
|
+
let rendered = "";
|
|
42
|
+
for (const [index, name] of path.entries()) {
|
|
43
|
+
if (rendered.length > 120)
|
|
44
|
+
return `${rendered}${INLINE_TRUNCATION_MARK}`;
|
|
45
|
+
rendered += `${index === 0 ? "" : "."}${quotedName(String(name))}`;
|
|
46
|
+
}
|
|
47
|
+
return rendered;
|
|
48
|
+
}
|
|
49
|
+
export function dataKind(value) {
|
|
50
|
+
return value === null ? "null" : value === undefined ? "undefined" : Array.isArray(value) ? "an array" : typeof value === "object" ? "a record" : `a ${typeof value}`;
|
|
51
|
+
}
|
|
52
|
+
const RECEIPT_FAULT_PROBLEMS = {
|
|
53
|
+
proxy: "it is a Proxy, whose traps would run as it is read; copy it into plain data (a spread or a JSON round trip)",
|
|
54
|
+
"not-data": "it is not plain data (a function, a bigint, a symbol, or an object whose prototype is neither Object.prototype nor null: a class instance, " +
|
|
55
|
+
"a map, `process.env`, a record built on a prototype or in another realm — a `vm` context, a worker); copy it into plain data (a spread " +
|
|
56
|
+
"or a JSON round trip), writing out any member it inherits",
|
|
57
|
+
"getter-threw": "its getter threw while it was read",
|
|
58
|
+
"prototype-moved": "its prototype changed while it was read",
|
|
59
|
+
cycle: "it contains itself; declare it as plain data",
|
|
60
|
+
"too-deep": `it is nested past ${MODS_VALUE_DEPTH} levels`,
|
|
61
|
+
uncopyable: "it could not be copied whole",
|
|
62
|
+
accessor: "it is an accessor",
|
|
63
|
+
hidden: "it is a member that is not an enumerable data property",
|
|
64
|
+
};
|
|
65
|
+
export function declarationFaultOf(fault) {
|
|
66
|
+
return { path: fault.path.map((key) => (/^(?:0|[1-9]\d*)$/.test(key) ? Number(key) : key)), problem: RECEIPT_FAULT_PROBLEMS[fault.kind] };
|
|
67
|
+
}
|
|
68
|
+
export const DECLARATION_PATH_RULES = Object.freeze({
|
|
69
|
+
mcp: Object.freeze([
|
|
70
|
+
{ path: ["name"], holds: "a string", required: true },
|
|
71
|
+
{ path: ["transport", "env"], holds: "names to strings" },
|
|
72
|
+
{ path: ["transport", "headers"], holds: "names to strings" },
|
|
73
|
+
].map((rule) => Object.freeze({ ...rule, path: Object.freeze([...rule.path]) }))),
|
|
74
|
+
a2a: Object.freeze([
|
|
75
|
+
{ path: ["name"], holds: "a string", required: true },
|
|
76
|
+
{ path: ["headers"], holds: "names to strings" },
|
|
77
|
+
].map((rule) => Object.freeze({ ...rule, path: Object.freeze([...rule.path]) }))),
|
|
78
|
+
});
|
|
79
|
+
export const NO_DECLARATION_PATH_RULES = Object.freeze([]);
|
|
80
|
+
const PROTO_MEMBER = `it is a member named "__proto__", which a copy by assignment would turn into a prototype — not a name a declaration can carry`;
|
|
81
|
+
function declarationVocabularyFault(root, rules) {
|
|
82
|
+
const asked = new Set();
|
|
83
|
+
const path = [];
|
|
84
|
+
const stack = [];
|
|
85
|
+
const ruleAt = () => rules.find((rule) => rule.path.length === path.length && rule.path.every((key, i) => key === path[i]));
|
|
86
|
+
const onRuleRoute = () => rules.some((rule) => rule.path.length > path.length && path.every((key, i) => key === rule.path[i]));
|
|
87
|
+
const ruleProblem = (rule, value) => {
|
|
88
|
+
if (rule.holds === "a string")
|
|
89
|
+
return typeof value === "string" ? undefined : `it is ${dataKind(value)}, not a string`;
|
|
90
|
+
if (!isPlainRecord(value))
|
|
91
|
+
return `it is ${dataKind(value)}, not a record of names to strings`;
|
|
92
|
+
for (const [key, member] of Object.entries(value)) {
|
|
93
|
+
path.push(key);
|
|
94
|
+
if (key === "__proto__")
|
|
95
|
+
return PROTO_MEMBER;
|
|
96
|
+
if (member !== undefined && typeof member !== "string")
|
|
97
|
+
return `it is ${dataKind(member)}, not a string: this record maps names to strings`;
|
|
98
|
+
path.pop();
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
};
|
|
102
|
+
const meet = (value) => {
|
|
103
|
+
if (typeof value !== "object" || value === null || (asked.has(value) && !onRuleRoute()))
|
|
104
|
+
return undefined;
|
|
105
|
+
asked.add(value);
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
for (let index = 0; index < value.length; index++) {
|
|
108
|
+
const own = Object.hasOwn(value, index);
|
|
109
|
+
if (own && typeof value[index] === "string")
|
|
110
|
+
continue;
|
|
111
|
+
path.push(index);
|
|
112
|
+
return `it is ${own ? dataKind(value[index]) : "a hole"}, not a string: every list a declaration carries is a list of names`;
|
|
113
|
+
}
|
|
114
|
+
const named = Object.keys(value).find((key) => !(/^(?:0|[1-9]\d*)$/.test(key) && Number(key) < value.length));
|
|
115
|
+
if (named === undefined)
|
|
116
|
+
return undefined;
|
|
117
|
+
path.push(named);
|
|
118
|
+
return "it is a list member that is not an index: a list declares its names at its indices only";
|
|
119
|
+
}
|
|
120
|
+
stack.push({ members: Object.entries(value), next: 0 });
|
|
121
|
+
return undefined;
|
|
122
|
+
};
|
|
123
|
+
for (const rule of rules) {
|
|
124
|
+
if (rule.required !== true)
|
|
125
|
+
continue;
|
|
126
|
+
let at = root;
|
|
127
|
+
for (const key of rule.path)
|
|
128
|
+
at = isPlainRecord(at) && Object.hasOwn(at, key) ? at[key] : undefined;
|
|
129
|
+
if (at === undefined)
|
|
130
|
+
return { path: [...rule.path], problem: `it is missing: a declaration carries ${rule.holds} here` };
|
|
131
|
+
}
|
|
132
|
+
let problem = meet(root);
|
|
133
|
+
while (problem === undefined && stack.length > 0) {
|
|
134
|
+
const frame = stack[stack.length - 1];
|
|
135
|
+
if (frame.next === frame.members.length) {
|
|
136
|
+
stack.pop();
|
|
137
|
+
if (stack.length > 0)
|
|
138
|
+
path.pop();
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const [key, member] = frame.members[frame.next++];
|
|
142
|
+
path.push(key);
|
|
143
|
+
const rule = key === "__proto__" || member === undefined ? undefined : ruleAt();
|
|
144
|
+
if (key === "__proto__")
|
|
145
|
+
problem = PROTO_MEMBER;
|
|
146
|
+
else if (rule !== undefined)
|
|
147
|
+
problem = ruleProblem(rule, member);
|
|
148
|
+
else {
|
|
149
|
+
const depth = stack.length;
|
|
150
|
+
problem = meet(member);
|
|
151
|
+
if (problem === undefined && stack.length > depth)
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (problem === undefined)
|
|
155
|
+
path.pop();
|
|
156
|
+
}
|
|
157
|
+
return problem === undefined ? undefined : { path, problem };
|
|
158
|
+
}
|
|
159
|
+
export function receiveDeclaration(declared, rules) {
|
|
160
|
+
if (!Array.isArray(rules)) {
|
|
161
|
+
throw invalidArgument(`a declaration is received with the path rules of its surface (${Object.keys(DECLARATION_PATH_RULES).map((s) => `DECLARATION_PATH_RULES[${JSON.stringify(s)}]`).join(" | ")}), ` +
|
|
162
|
+
`or an empty list when it fixes no path; what was passed is ${dataKind(rules)}, not a list of rules.`);
|
|
163
|
+
}
|
|
164
|
+
const received = receive(declared, DECLARATION_RECEIPT_MODE);
|
|
165
|
+
if (!received.ok)
|
|
166
|
+
return { ok: false, fault: declarationFaultOf(received.fault) };
|
|
167
|
+
const fault = isPlainRecord(received.value) ? declarationVocabularyFault(received.value, rules) : undefined;
|
|
168
|
+
return fault === undefined ? { ok: true, value: received.value } : { ok: false, fault };
|
|
28
169
|
}
|
|
29
170
|
function isToolEffectWord(value) {
|
|
30
171
|
return typeof value === "string" && TOOL_EFFECT_WORDS.includes(value);
|
|
@@ -34,40 +175,71 @@ function isIrreversibilityPoleWord(value) {
|
|
|
34
175
|
}
|
|
35
176
|
function readToolAxisEntry(raw) {
|
|
36
177
|
if (!isPlainRecord(raw))
|
|
37
|
-
return `the declaration is ${
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
178
|
+
return `the declaration is ${dataKind(raw)}, not an object of axes`;
|
|
179
|
+
const listed = Object.keys(raw);
|
|
180
|
+
const unknown = listed.find((member) => !TOOL_AXIS_MEMBER_NAMES.includes(member));
|
|
181
|
+
if (unknown !== undefined)
|
|
182
|
+
return `unknown member ${quotedName(unknown)} (the members are ${TOOL_AXIS_MEMBER_NAMES.join(", ")})`;
|
|
183
|
+
const declared = Object.create(null);
|
|
184
|
+
for (const member of TOOL_AXIS_MEMBER_NAMES) {
|
|
185
|
+
const value = raw[member];
|
|
186
|
+
if (value !== undefined && !listed.includes(member))
|
|
187
|
+
return `member ${quotedName(member)} is a hidden member: its name answers a value the declaration does not list as its own`;
|
|
188
|
+
declared[member] = value;
|
|
41
189
|
}
|
|
42
|
-
const { effect, egress, irreversibility } =
|
|
190
|
+
const { effect, egress, irreversibility, boundaryFlow } = declared;
|
|
43
191
|
if (effect !== undefined && !isToolEffectWord(effect))
|
|
44
192
|
return `member "effect" is not one of ${TOOL_EFFECT_WORDS.join(" | ")}`;
|
|
45
193
|
if (egress !== undefined && typeof egress !== "boolean")
|
|
46
194
|
return `member "egress" is not a boolean`;
|
|
47
195
|
if (irreversibility !== undefined && !isIrreversibilityPoleWord(irreversibility))
|
|
48
196
|
return `member "irreversibility" is not one of ${IRREVERSIBILITY_POLE_WORDS.join(" | ")}`;
|
|
197
|
+
if (boundaryFlow !== undefined && boundaryFlow !== "engine")
|
|
198
|
+
return `member "boundaryFlow" is not "engine"`;
|
|
199
|
+
if (boundaryFlow === "engine" && egress !== false)
|
|
200
|
+
return `member "boundaryFlow": "engine" needs "egress": false in the same entry — add it, or drop "boundaryFlow" (${egress === true ? '"egress": true contradicts it' : 'with no "egress", the server hint or the peer default stays in force'})`;
|
|
49
201
|
return {
|
|
50
202
|
...(effect !== undefined ? { effect } : {}),
|
|
51
203
|
...(egress !== undefined ? { egress } : {}),
|
|
52
204
|
...(irreversibility !== undefined ? { irreversibility } : {}),
|
|
205
|
+
...(boundaryFlow !== undefined ? { boundaryFlow } : {}),
|
|
53
206
|
};
|
|
54
207
|
}
|
|
55
|
-
|
|
208
|
+
const PER_TOOL_DECLARATIONS = {
|
|
209
|
+
toolAxes: { plural: "axes", singular: "axis" },
|
|
210
|
+
toolFaces: { plural: "faces", singular: "face" },
|
|
211
|
+
};
|
|
212
|
+
export function receivePerToolDeclaration(declared, field, unit) {
|
|
213
|
+
const received = receiveDeclaration(declared, NO_DECLARATION_PATH_RULES);
|
|
214
|
+
if (!received.ok) {
|
|
215
|
+
const [key, ...member] = received.fault.path;
|
|
216
|
+
if (key === undefined)
|
|
217
|
+
return { ok: false, problem: `${field} cannot be read: ${received.fault.problem}` };
|
|
218
|
+
const where = member.length > 0 ? `member ${memberNames(member)}: ` : "";
|
|
219
|
+
return { ok: false, problem: perToolEntryRefusal(field, unit, String(key), `${where}${received.fault.problem}`) };
|
|
220
|
+
}
|
|
221
|
+
const perTool = received.value;
|
|
222
|
+
if (!isPlainRecord(perTool))
|
|
223
|
+
return { ok: false, problem: `${field} is ${dataKind(perTool)}, not an object of per-${unit} ${PER_TOOL_DECLARATIONS[field].singular} declarations` };
|
|
224
|
+
return { ok: true, perTool };
|
|
225
|
+
}
|
|
226
|
+
export function perToolEntryRefusal(field, unit, key, detail) {
|
|
227
|
+
return `the ${PER_TOOL_DECLARATIONS[field].plural} declared for ${unit} ${quotedName(key)} do not fit: ${detail}`;
|
|
228
|
+
}
|
|
229
|
+
export function perToolEmptyName(field, unit) {
|
|
230
|
+
return `${field} has an empty ${unit} name`;
|
|
231
|
+
}
|
|
232
|
+
export function readToolAxes(declared, at) {
|
|
233
|
+
const unit = toolAxesSurfaceOf(at).key;
|
|
56
234
|
if (declared === undefined)
|
|
57
235
|
return { ok: true };
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return { ok: false, problem: `toolAxes is ${describeKind(declared)}, not an object of per-tool axis declarations` };
|
|
62
|
-
perTool = Object.entries(declared);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
return { ok: false, problem: "toolAxes could not be read: a property threw while it was read" };
|
|
66
|
-
}
|
|
236
|
+
const received = receivePerToolDeclaration(declared, "toolAxes", unit);
|
|
237
|
+
if (!received.ok)
|
|
238
|
+
return { ok: false, problem: received.problem };
|
|
67
239
|
const entries = [];
|
|
68
|
-
for (const [
|
|
69
|
-
if (
|
|
70
|
-
return { ok: false, problem: "toolAxes
|
|
240
|
+
for (const [key, raw] of Object.entries(received.perTool)) {
|
|
241
|
+
if (key.length === 0)
|
|
242
|
+
return { ok: false, problem: perToolEmptyName("toolAxes", unit) };
|
|
71
243
|
let entry;
|
|
72
244
|
try {
|
|
73
245
|
entry = readToolAxisEntry(raw);
|
|
@@ -76,9 +248,20 @@ export function readMcpToolAxes(declared) {
|
|
|
76
248
|
entry = "a member threw while it was read";
|
|
77
249
|
}
|
|
78
250
|
if (typeof entry === "string")
|
|
79
|
-
return { ok: false, problem:
|
|
251
|
+
return { ok: false, problem: perToolEntryRefusal("toolAxes", unit, key, entry) };
|
|
80
252
|
if (Object.keys(entry).length > 0)
|
|
81
|
-
entries.push([
|
|
253
|
+
entries.push([key, entry]);
|
|
82
254
|
}
|
|
83
255
|
return entries.length > 0 ? { ok: true, toolAxes: Object.fromEntries(entries) } : { ok: true };
|
|
84
256
|
}
|
|
257
|
+
export function readMcpToolAxes(declared) {
|
|
258
|
+
return readToolAxes(declared, "mcp");
|
|
259
|
+
}
|
|
260
|
+
export function declarerWord(at) {
|
|
261
|
+
return toolAxesSurfaceOf(at).declarer;
|
|
262
|
+
}
|
|
263
|
+
export function toolAxesRefusal(at, name, problem) {
|
|
264
|
+
return invalidArgument(`${toolAxesSurfaceOf(at).declarer} ${name} declares toolAxes the engine cannot read: ${inlineUntrusted(problem, 240)}. A per-tool safety-axis declaration is the ` +
|
|
265
|
+
`caller's trust root for the approval gate, so it is refused rather than partly applied — dropping one key or defaulting one word ` +
|
|
266
|
+
`would leave a declaration that reads as in force while it is not.`);
|
|
267
|
+
}
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -110,14 +110,15 @@ export interface MaterializedMcp {
|
|
|
110
110
|
* G1 通告层续批 (CC `mcp_instructions_delta` parity) — the run-scoped instruction-delta ref the run
|
|
111
111
|
* loop drains at turn boundaries (`spec.attachments.mcpInstructions` opt-in; `newTools` consumption
|
|
112
112
|
* discipline: drained only when the announcement survived the byte cap intact).
|
|
113
|
-
* - `pendingRemovals`:
|
|
114
|
-
* "servers have disconnected — their instructions above no
|
|
115
|
-
*
|
|
116
|
-
* disconnect.
|
|
117
|
-
* - `pendingAdds`: the CC add lane ("# MCP Server Instructions" reminder).
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
113
|
+
* - `pendingRemovals`: a server whose instructions are in the model's context and whose transport closed
|
|
114
|
+
* mid-task, or whose re-dial failed (the CC "servers have disconnected — their instructions above no
|
|
115
|
+
* longer apply" copy names only what the model holds — the lane's ledger, `instructionsTold`).
|
|
116
|
+
* `dispose()` disarms first: task teardown is not a disconnect.
|
|
117
|
+
* - `pendingAdds`: the CC add lane ("# MCP Server Instructions" reminder). A server's INITIAL
|
|
118
|
+
* instructions ride the stable system prompt instead (design/64 §17.3, deliberate CC deviation:
|
|
119
|
+
* prefix-cache-correct); this lane carries what comes later — a re-dial queues its new
|
|
120
|
+
* connection's text here, and a run adds the text of a server it is to teach and has not told.
|
|
121
|
+
* A materialization alone takes every entry of `serverInstructions` as taught; a run states its block.
|
|
121
122
|
*/
|
|
122
123
|
instructionsDelta: {
|
|
123
124
|
pendingAdds: McpInstructionsEntry[];
|
|
@@ -163,7 +164,7 @@ export interface MaterializedMcp {
|
|
|
163
164
|
droppedTools: McpDroppedTool[];
|
|
164
165
|
/**
|
|
165
166
|
* design/99 §E9 — per-server status, a PURE PROJECTION of THIS materialization (no new connection logic).
|
|
166
|
-
* One entry per declared server. **Semantics = the moment of this task leg's materialize** — core never
|
|
167
|
+
* One entry per declared server DIALED (none for one `dialable` kept from the dial). **Semantics = the moment of this task leg's materialize** — core never
|
|
167
168
|
* persists MCP connections, so it is "did this leg connect", NOT a live session-health signal between tasks.
|
|
168
169
|
* A consumer (service) surfacing it as session status must understand that. `toolNames` are the namespaced
|
|
169
170
|
* (`<server>__<tool>`) names exposed.
|
|
@@ -178,7 +179,7 @@ export interface MaterializedMcp {
|
|
|
178
179
|
/**
|
|
179
180
|
* RefreshMcpTools (CC 2.1.218 parity, anchored to the CLI form) — re-read the tool list of one server (or
|
|
180
181
|
* every CONNECTED server when `server` is omitted) over the EXISTING connection. NEVER dials or
|
|
181
|
-
* re-dials: a dead/never-connected server reports `not_connected
|
|
182
|
+
* re-dials: a dead/never-connected server reports `not_connected` (so does one whose connection closed while the walk was reading); a live listTools failure
|
|
182
183
|
* reports `failed` (the connection is left as-is). A refreshed entry carries the NEW intake
|
|
183
184
|
* result (tools/axes/dropped — the same schema-gate/namespacing/axis pipeline as connect) plus
|
|
184
185
|
* the added/removed namespaced tool names relative to the previous listing; the caller (the
|
|
@@ -229,6 +230,7 @@ export interface McpRefreshResult {
|
|
|
229
230
|
* contact it (no tools/list round trip; the engine must not hand a severed server a request
|
|
230
231
|
* channel or splice its post-revocation text into the model catalog). */
|
|
231
232
|
status: "refreshed" | "not_connected" | "failed" | "revoked";
|
|
233
|
+
/** The same reading as {@link McpReconnectResult.toolCount}: the server's tools the producer holds after the call. */
|
|
232
234
|
toolCount: number;
|
|
233
235
|
added: string[];
|
|
234
236
|
/**
|
|
@@ -243,6 +245,9 @@ export interface McpRefreshResult {
|
|
|
243
245
|
tools?: AgentTool[];
|
|
244
246
|
axes?: McpToolAxis[];
|
|
245
247
|
dropped?: McpDroppedTool[];
|
|
248
|
+
/** On a `"refreshed"` entry whose connection carries instructions: that connection's entry as its dial disposed of it (`author: "host"` when replaced) —
|
|
249
|
+
* a refresh re-reads tools, never instructions; a run whose roster change first leaves something of the server teaches it. */
|
|
250
|
+
instructions?: McpInstructionsEntry;
|
|
246
251
|
/**
|
|
247
252
|
* Set on `not_connected` / `failed` / `revoked`; ALSO set on a `"refreshed"` entry whose listing walk
|
|
248
253
|
* did not finish (see {@link listingIncomplete}) — a refresh that produced usable tools is not a
|
|
@@ -287,12 +292,14 @@ export interface McpReconnectResult {
|
|
|
287
292
|
/** Why, on every non-`reconnected` outcome (neutralized + bounded when it embeds remote text).
|
|
288
293
|
* Never the only machine-readable fact: see {@link status}. */
|
|
289
294
|
reason?: string;
|
|
295
|
+
/** How many of the server's tools the producer holds after the call — its roster: the run's (the valve and the deployment's
|
|
296
|
+
* overlay applied) for the run handle's `reconnectMcpServer`, this materialization's own handles for `MaterializedMcp.reconnect`
|
|
297
|
+
* (no run). A name no server is declared under holds nothing. Equal to `tools.length` whenever `tools` is present. */
|
|
290
298
|
toolCount: number;
|
|
291
|
-
/**
|
|
292
|
-
* ago — a first successful dial adds its whole roster, a failed dial removes it. */
|
|
299
|
+
/** The namespaced names that came onto / left that roster (a first dial adds its whole roster, a failed dial removes it). */
|
|
293
300
|
added: string[];
|
|
294
301
|
removed: string[];
|
|
295
|
-
/** See the type docstring: present iff this result asserts a roster for `prefix
|
|
302
|
+
/** See the type docstring: present iff this result asserts a roster for `prefix` — and then it is that roster's tools of the server. */
|
|
296
303
|
tools?: AgentTool[];
|
|
297
304
|
axes?: McpToolAxis[];
|
|
298
305
|
dropped?: McpDroppedTool[];
|
|
@@ -609,7 +616,7 @@ reminderDisclosure?: {
|
|
|
609
616
|
}, poolOpts?: {
|
|
610
617
|
pool: McpConnectionPool;
|
|
611
618
|
ownsPool?: boolean;
|
|
612
|
-
}, promptSections?: PromptSectionDisposer): Promise<MaterializedMcp>;
|
|
619
|
+
}, promptSections?: PromptSectionDisposer, dialable?: (spec: McpServerSpec) => boolean): Promise<MaterializedMcp>;
|
|
613
620
|
/**
|
|
614
621
|
* Fold the caller's AUTHORITATIVE per-tool override (design F: caller = trust root) over the server-hint axis.
|
|
615
622
|
* Unlike server hints, the caller may RAISE or LOWER any axis: `effect` sets the repeat-safety class (lower to
|