@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
|
@@ -2,12 +2,15 @@ import { screenGateOutcome, SETTLEMENT_IS_REFUSAL } from "../gate-outcome.js";
|
|
|
2
2
|
import { askFaceOf, classifyAsk, headlessApprovalRemedy } from "../ask-origin.js";
|
|
3
3
|
import { wireOf } from "../tool-name-readings.js";
|
|
4
4
|
import { isProtectedWrite } from "../tool-registry.js";
|
|
5
|
-
import { attestationOf, decisionText } from "../tool-policy.js";
|
|
5
|
+
import { attestationOf, decisionText, licenseCovers } from "../tool-policy.js";
|
|
6
6
|
import { pathDenialCardOf } from "../../tools/fs/safety.js";
|
|
7
|
-
import {
|
|
7
|
+
import { argsAsTheToolReads } from "../effective-path-target.js";
|
|
8
8
|
import { askUnresolvableRemedySeat } from "../ask-unresolvable-notice.js";
|
|
9
9
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
10
10
|
import { formatHookFeedback } from "../reminder-mint.js";
|
|
11
|
+
import { types } from "node:util";
|
|
12
|
+
import { receive } from "../mods/kernel/receive.js";
|
|
13
|
+
import { describeBindingFault, MAX_DEPTH } from "../canonical-json.js";
|
|
11
14
|
export function mintGateOutcome(facts) {
|
|
12
15
|
const settlement = facts.settled !== undefined ? Object.freeze({ ...facts.settled.settlement, who: Object.freeze({ ...facts.settled.settlement.who }) }) : undefined;
|
|
13
16
|
const attribution = facts.classifier !== undefined ? Object.freeze({ ...facts.classifier, ...(facts.classifier.fallback !== undefined ? { fallback: Object.freeze({ ...facts.classifier.fallback }) } : {}) }) : undefined;
|
|
@@ -81,12 +84,7 @@ export function createSettlementLedger(call) {
|
|
|
81
84
|
`(\`attestation\`), never a resolver's member; denied fail-closed rather than recording a settlement nobody can attribute`, contractRefusal(origin));
|
|
82
85
|
}
|
|
83
86
|
const edited = resolved.action === "allow" && resolved.updatedInput !== undefined;
|
|
84
|
-
const licensed = () =>
|
|
85
|
-
if (edited)
|
|
86
|
-
return record.inputDigest === boundInputHashOf(resolved.updatedInput);
|
|
87
|
-
const askedDigest = boundInputHashOf(req.args);
|
|
88
|
-
return record.inputDigest === askedDigest && (resolved.presentedInput === undefined || boundInputHashOf(resolved.presentedInput) === askedDigest);
|
|
89
|
-
};
|
|
87
|
+
const licensed = () => edited ? licenseCovers(record.inputDigest, resolved.updatedInput) : licenseCovers(record.inputDigest, req.args) && (resolved.presentedInput === undefined || licenseCovers(record.inputDigest, resolved.presentedInput));
|
|
90
88
|
const bound = record?.settlement !== undefined && record.origin === origin && sameClasses(record.classes, asked.classes) ? record.settlement : undefined;
|
|
91
89
|
const refusal = bound !== undefined && SETTLEMENT_IS_REFUSAL[bound.kind] ? bound : undefined;
|
|
92
90
|
if (resolved.action === "allow") {
|
|
@@ -127,7 +125,7 @@ export function createSettlementLedger(call) {
|
|
|
127
125
|
return { decision, deniedBy: refusal ? "ask_resolution" : "policy" };
|
|
128
126
|
},
|
|
129
127
|
keepForBytes(runs) {
|
|
130
|
-
if (settled !== undefined && settled.licensed
|
|
128
|
+
if (settled !== undefined && !licenseCovers(settled.licensed, runs))
|
|
131
129
|
settled = undefined;
|
|
132
130
|
},
|
|
133
131
|
causeOf(decision) {
|
|
@@ -153,32 +151,63 @@ export function cloneObserverInput(input) {
|
|
|
153
151
|
return input;
|
|
154
152
|
}
|
|
155
153
|
}
|
|
154
|
+
const RECORD_RECEIPT = Object.freeze({ accessors: "refuse", depth: MAX_DEPTH, others: "refuse", contain: true, isProxy: (v) => types.isProxy(v) });
|
|
156
155
|
export function frozenInputSnapshot(input) {
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
const received = receive(input, RECORD_RECEIPT);
|
|
157
|
+
if (!received.ok)
|
|
158
|
+
return notRecorded(input, received.fault);
|
|
159
|
+
const record = received.value;
|
|
160
|
+
const symbolMarkers = new Map();
|
|
161
|
+
for (const fault of received.faults ?? []) {
|
|
162
|
+
const above = fault.path.slice(0, -1);
|
|
163
|
+
const holder = memberAt(record, above);
|
|
164
|
+
const last = fault.path[fault.path.length - 1];
|
|
165
|
+
if (typeof holder !== "object" || holder === null || last === undefined)
|
|
166
|
+
continue;
|
|
167
|
+
const rawHolder = memberAt(input, above);
|
|
168
|
+
const symbolKeyed = fault.kind === "hidden" && (Object.hasOwn(holder, last) || typeof rawHolder !== "object" || rawHolder === null || !Object.hasOwn(rawHolder, last));
|
|
169
|
+
if (symbolKeyed) {
|
|
170
|
+
const listed = symbolMarkers.get(holder);
|
|
171
|
+
if (listed === undefined)
|
|
172
|
+
symbolMarkers.set(holder, [notRecorded(input, fault)]);
|
|
173
|
+
else
|
|
174
|
+
listed.push(notRecorded(input, fault));
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
Object.defineProperty(holder, last, { value: notRecorded(input, fault), enumerable: true, writable: true, configurable: true });
|
|
159
178
|
}
|
|
160
|
-
|
|
161
|
-
|
|
179
|
+
for (const [holder, markers] of symbolMarkers) {
|
|
180
|
+
let key = "[not recorded: symbol-keyed members]";
|
|
181
|
+
while (Object.hasOwn(holder, key))
|
|
182
|
+
key += "'";
|
|
183
|
+
Object.defineProperty(holder, key, { value: markers, enumerable: true, writable: true, configurable: true });
|
|
162
184
|
}
|
|
185
|
+
return freezeRecord(record);
|
|
163
186
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
187
|
+
function freezeRecord(record) {
|
|
188
|
+
const pending = [record];
|
|
189
|
+
for (let at = pending.pop(); at !== undefined || pending.length > 0; at = pending.pop()) {
|
|
190
|
+
if (typeof at !== "object" || at === null || Object.isFrozen(at))
|
|
191
|
+
continue;
|
|
192
|
+
Object.freeze(at);
|
|
193
|
+
for (const member of Object.values(at))
|
|
194
|
+
pending.push(member);
|
|
172
195
|
}
|
|
173
|
-
return
|
|
196
|
+
return record;
|
|
197
|
+
}
|
|
198
|
+
const notRecorded = (input, fault) => `[not recorded: ${describeBindingFault(input, fault)}]`;
|
|
199
|
+
function memberAt(value, path) {
|
|
200
|
+
let at = value;
|
|
201
|
+
for (const key of path)
|
|
202
|
+
at = typeof at === "object" && at !== null ? at[key] : undefined;
|
|
203
|
+
return at;
|
|
174
204
|
}
|
|
175
205
|
const PARK_FAILURE_CAUSE_MAX = 600;
|
|
176
|
-
function withParkFailureCause(reason, parkFailed) {
|
|
206
|
+
function withParkFailureCause(reason, parkFailed, nobodyJudged) {
|
|
177
207
|
if (parkFailed === undefined)
|
|
178
208
|
return reason;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
`left to say, not the reason the call stopped.`);
|
|
209
|
+
const note = `${reason} — note: a durable approval park was attempted for this call and could not be minted (${inlineUntrusted(parkFailed, PARK_FAILURE_CAUSE_MAX)})`;
|
|
210
|
+
return nobodyJudged ? `${note}; with nobody to judge the call, that is why it stopped — it can be issued again once its park can be minted.` : `${note}.`;
|
|
182
211
|
}
|
|
183
212
|
export function passAskFace(pass) {
|
|
184
213
|
return askFaceOf(wireOf("gate.askFace", pass.req));
|
|
@@ -191,11 +220,15 @@ export async function engineFailClosedExit(pass, reason) {
|
|
|
191
220
|
return { block: true, reason: formatHookFeedback(reason, input.reminderMark), gate, finalInput, preToolContext: pass.preToolContext };
|
|
192
221
|
}
|
|
193
222
|
export const unownedArgsMessage = (subject, reason) => `${subject} could not be taken into the gate's own copy (${reason}) — denied fail-closed`;
|
|
194
|
-
export async function
|
|
195
|
-
pass.
|
|
196
|
-
pass.
|
|
223
|
+
export async function untakenValueExit(pass, untaken) {
|
|
224
|
+
pass.currentInput = untaken.value;
|
|
225
|
+
pass.decision = { action: "deny", message: untaken.message };
|
|
226
|
+
pass.deniedBy = untaken.deniedBy;
|
|
197
227
|
return await exitGate(pass);
|
|
198
228
|
}
|
|
229
|
+
export async function unownedArgsExit(pass, reason) {
|
|
230
|
+
return await untakenValueExit(pass, { value: pass.currentInput, message: unownedArgsMessage(`the arguments of "${pass.toolName}"`, reason), deniedBy: "policy" });
|
|
231
|
+
}
|
|
199
232
|
export function hookDenyExit(pass, deniedReason) {
|
|
200
233
|
const { input } = pass;
|
|
201
234
|
return {
|
|
@@ -220,7 +253,7 @@ export async function exitGate(pass) {
|
|
|
220
253
|
const { input, toolName, toolCallId, ledger, notifyPermissionDenied } = pass;
|
|
221
254
|
if (pass.decision.action === "deny") {
|
|
222
255
|
if (pass.decision.updatedInput !== undefined) {
|
|
223
|
-
pass.currentInput = pass.decision.updatedInput;
|
|
256
|
+
pass.currentInput = argsAsTheToolReads(pass.req, pass.decision.updatedInput);
|
|
224
257
|
}
|
|
225
258
|
if (pass.deniedBy === undefined)
|
|
226
259
|
throw new Error(`the tool gate refused "${toolName}" without a refusing layer — every deny site attributes itself`);
|
|
@@ -235,11 +268,13 @@ export async function exitGate(pass) {
|
|
|
235
268
|
const unresolvable = foldCause ?? unresolvableCauseOfSettlement(gate);
|
|
236
269
|
const tableSeat = unresolvable !== undefined ? askUnresolvableRemedySeat(unresolvable) : undefined;
|
|
237
270
|
const remedySeat = tableSeat === "absent" && input.askSeatForm === "declared_none" && pass.askClassification?.classes.has("content_question") !== true ? "declared_none" : tableSeat;
|
|
238
|
-
const
|
|
239
|
-
const
|
|
271
|
+
const bindingRefusal = attestationOf(pass.decision, { toolCallId, toolName })?.bindingRefusal;
|
|
272
|
+
const remedy = bindingRefusal !== undefined ? undefined : remedySeat !== undefined ? headlessApprovalRemedy({ mode: input.permissionMode?.(), classification: foldCause !== undefined ? ANCESTOR_UNRESOLVED_CLASSIFICATION : pass.askClassification, approverSeat: remedySeat, coveredWrite: isProtectedWrite(pass.req), ...(pass.shellUnplacedOptions !== undefined ? { unplacedOptions: pass.shellUnplacedOptions } : {}) }) : undefined;
|
|
273
|
+
const denyReason = withParkFailureCause(`${decisionText(pass.decision) ?? `tool "${toolName}" denied by policy`}${remedy !== undefined ? ` — ${remedy}` : ""}`, pass.parkFailed, unresolvable !== undefined);
|
|
240
274
|
if (unresolvable !== undefined) {
|
|
241
275
|
const cause = unresolvable;
|
|
242
|
-
|
|
276
|
+
const noticeRemedy = bindingRefusal ?? remedy;
|
|
277
|
+
pass.notifier.notify(() => input.onAskUnresolvable?.({ toolName, toolCallId, cause, parkLaneExisted: pass.suspendAsk !== undefined, ...(noticeRemedy !== undefined ? { remedy: noticeRemedy } : {}) }), "toolGate.onAskUnresolvable");
|
|
243
278
|
}
|
|
244
279
|
const finalInput = frozenInputSnapshot(pass.currentInput);
|
|
245
280
|
await notifyPermissionDenied({
|
|
@@ -265,9 +300,17 @@ export async function exitGate(pass) {
|
|
|
265
300
|
if (pass.decision.action === "allow") {
|
|
266
301
|
const rw = pass.decision.updatedInput !== undefined ? pass.decision.updatedInput : pass.policyRewrite;
|
|
267
302
|
if (rw !== undefined)
|
|
268
|
-
pass.currentInput = rw;
|
|
303
|
+
pass.currentInput = argsAsTheToolReads(pass.req, rw);
|
|
269
304
|
}
|
|
270
305
|
ledger.keepForBytes(pass.currentInput);
|
|
306
|
+
const crossBoundary = await input.crossBoundaryCheck?.exit({ toolName, args: pass.currentInput, toolCallId }, ledger.settled?.settlement.who.party === "person" ? "person" : "policy");
|
|
307
|
+
if (crossBoundary !== undefined) {
|
|
308
|
+
pass.decision = { action: "deny", message: crossBoundary.message, decisionReason: "safety" };
|
|
309
|
+
pass.deniedBy = crossBoundary.kind === "unconfirmed" ? "ask_resolution" : "policy";
|
|
310
|
+
if (crossBoundary.kind === "unconfirmed")
|
|
311
|
+
ledger.set({ settlement: { kind: "blanket_allow_refused", who: { party: "none" }, when: Date.now() }, origin: ledger.settled?.origin ?? "safety_tighten" });
|
|
312
|
+
return exitGate(pass);
|
|
313
|
+
}
|
|
271
314
|
const release = pass.arbitration?.kind === "released" ? pass.arbitration : (() => { if (ledger.settled !== undefined)
|
|
272
315
|
return undefined; const a = pass.arbitration === undefined ? input.ancestorArbitrationOf?.(toolCallId) : undefined; return a?.kind === "released" ? a : undefined; })();
|
|
273
316
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
2
2
|
import { askApproverIdentity, attest, attestationOf } from "../tool-policy.js";
|
|
3
|
-
import {
|
|
3
|
+
import { exactBoundInputHashOf } from "../canonical-json.js";
|
|
4
4
|
export function askGrantCovers(granted, asked) {
|
|
5
5
|
return asked !== undefined && asked.length > 0 && granted.length > 0 && asked.every((c) => c === "ordinary" || granted.includes(c));
|
|
6
6
|
}
|
|
@@ -34,5 +34,5 @@ export function consumeInheritedAskGrant(grants, onAsk, humanReviewRef, decision
|
|
|
34
34
|
const settlement = attestationOf(grant.settled, req)?.settlement;
|
|
35
35
|
if (settlement === undefined || decision.action !== "ask")
|
|
36
36
|
return reused;
|
|
37
|
-
return attest(reused, req, { settlement, ...(decision.origin !== undefined ? { origin: decision.origin } : {}), ...(decision.classes !== undefined ? { classes: decision.classes } : {}), inputDigest:
|
|
37
|
+
return attest(reused, req, { settlement, ...(decision.origin !== undefined ? { origin: decision.origin } : {}), ...(decision.classes !== undefined ? { classes: decision.classes } : {}), inputDigest: exactBoundInputHashOf(grant.presented) });
|
|
38
38
|
}
|
|
@@ -15,22 +15,28 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentTool } from "../../internal/harness.js";
|
|
17
17
|
import type { McpDroppedTool, McpReconnectResult, MaterializedMcp, McpToolAxis } from "../mcp.js";
|
|
18
|
-
import type { RunnerDeps, ToolEffect } from "../types.js";
|
|
18
|
+
import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
|
|
19
19
|
import type { IrreversibilityTier, ToolFaceSnapshot } from "./contracts.js";
|
|
20
20
|
import type { RosterBuilder, ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
21
|
+
import type { McpInstructionsEntry } from "../prompt-section-seat.js";
|
|
21
22
|
/** One server's assertion about its own prefix domain — the shape both faces reduce to. `tools` is
|
|
22
23
|
* the roster the domain must END UP with (empty withdraws it); `added`/`removed` are the reporting
|
|
23
|
-
* half and never drive the swap.
|
|
24
|
+
* half and never drive the swap. `instructions`: the server's current connection's instructions entry, when the face that produced
|
|
25
|
+
* the delta hands it back (a refresh does; a re-dial queues its new text itself). */
|
|
24
26
|
export interface McpRosterDelta {
|
|
25
27
|
server: string;
|
|
26
28
|
prefix: string;
|
|
27
29
|
tools: AgentTool[];
|
|
28
30
|
axes: McpToolAxis[];
|
|
29
31
|
dropped: McpDroppedTool[];
|
|
32
|
+
instructions?: McpInstructionsEntry;
|
|
30
33
|
}
|
|
31
34
|
/** The seats a roster application writes through. Every one of them is borrowed from the phase that
|
|
32
35
|
* minted it — this module owns nothing. */
|
|
33
36
|
export interface McpRosterApplyInput {
|
|
37
|
+
/** borrowed-readonly — the run's spec; only `excludeAllTools` is read: the valve's whole-face switch decides what a live roster
|
|
38
|
+
* change may mount, beside the selector (`valveLeaves`). */
|
|
39
|
+
spec: Pick<TaskSpec, "excludeAllTools">;
|
|
34
40
|
/** borrowed-readonly — the frozen tool face; its selector keeps a tool the leg may not use from ever
|
|
35
41
|
* being mounted by a face the model or a host can trigger. */
|
|
36
42
|
toolFaceSnapshot: Pick<ToolFaceSnapshot, "selector">;
|
|
@@ -48,20 +54,53 @@ export interface McpRosterApplyInput {
|
|
|
48
54
|
}>;
|
|
49
55
|
/** borrowed-mutable — the run's shared roster. */
|
|
50
56
|
roster: RosterBuilder;
|
|
57
|
+
/** borrowed-readonly — the run's frozen MCP server declarations: a roster reading is of a declared server, and a name none is declared under holds nothing. */
|
|
58
|
+
declared: ReadonlyArray<{
|
|
59
|
+
readonly name: string;
|
|
60
|
+
}>;
|
|
61
|
+
/** borrowed-mutable — the materialization's instructions delta lane; a roster change settles the server's adds on it. */
|
|
62
|
+
instructionsDelta: MaterializedMcp["instructionsDelta"];
|
|
63
|
+
}
|
|
64
|
+
/** What one application did to the server's domain on the roster — read off the roster before and after the swap, the ONE source of
|
|
65
|
+
* "how many of this server's tools are mounted" for the re-dial's notice and receipt and the refresh tool's receipt. A name the
|
|
66
|
+
* valve left out, a name the deployment's `toolFaces` overlay refused and a name the server listed but the roster did not take are
|
|
67
|
+
* all simply absent from `mounted`. */
|
|
68
|
+
export interface McpRosterReading {
|
|
69
|
+
/** The names of the server's tools the roster holds after the application, in wire order. */
|
|
70
|
+
mounted: string[];
|
|
71
|
+
/** The names `mounted` holds that the roster did not hold before. */
|
|
72
|
+
added: string[];
|
|
73
|
+
/** The names the roster held before that `mounted` no longer holds. */
|
|
74
|
+
removed: string[];
|
|
75
|
+
/** The names the server listed that the roster did not take (the valve's, the overlay's). */
|
|
76
|
+
excluded: string[];
|
|
51
77
|
}
|
|
52
78
|
/** What one application did, for the caller's receipt. `applied:false` means the axis fold refused
|
|
53
|
-
* and the domain was left exactly as it was (the tables restored) — no roster write happened. */
|
|
54
|
-
export type McpRosterApplyOutcome = {
|
|
79
|
+
* and the domain was left exactly as it was (the tables restored) — no roster write happened, so `mounted` is what it held. */
|
|
80
|
+
export type McpRosterApplyOutcome = ({
|
|
55
81
|
applied: true;
|
|
56
|
-
|
|
57
|
-
excluded: string[];
|
|
58
|
-
} | {
|
|
82
|
+
} & McpRosterReading) | ({
|
|
59
83
|
applied: false;
|
|
60
84
|
foldError: string;
|
|
61
|
-
};
|
|
85
|
+
} & McpRosterReading);
|
|
86
|
+
/** What the roster holds of one server with no swap made — the reading of an arm that asserted no roster (a failed refresh, a refused re-dial, a fold refusal).
|
|
87
|
+
* A name no server is declared under holds nothing, even when it mints a declared server's prefix. */
|
|
88
|
+
export declare function rosterHolding(input: Pick<McpRosterApplyInput, "roster" | "declared">, serverName: string): McpRosterReading;
|
|
89
|
+
/** A refresh or re-dial result read off the roster instead of the server's own listing: `toolCount` is the tools mounted, `added` /
|
|
90
|
+
* `removed` the names that came onto / left the roster, and — on a result that asserts a roster — `tools` the roster's tools of the
|
|
91
|
+
* server (with `axes` cut to them), so `toolCount === tools.length`. One meaning, the one `MaterializedMcp.refresh` / `reconnect` give
|
|
92
|
+
* for their own handles. */
|
|
93
|
+
export declare function settledOnRoster<T extends {
|
|
94
|
+
toolCount: number;
|
|
95
|
+
added: string[];
|
|
96
|
+
removed: string[];
|
|
97
|
+
tools?: AgentTool[];
|
|
98
|
+
axes?: McpToolAxis[];
|
|
99
|
+
}>(result: T, reading: McpRosterReading, roster: Pick<RosterBuilder, "tools">): T;
|
|
62
100
|
/**
|
|
63
|
-
* Apply one server's delta to the live roster
|
|
64
|
-
*
|
|
101
|
+
* Apply one server's delta to the live roster, and settle the server's instruction adds on the delta lane: the teaching answer over the
|
|
102
|
+
* roster after the swap, against the lane's ledger of what the model holds (both faces — the model's refresh and the host's re-dial —
|
|
103
|
+
* through this one step).
|
|
65
104
|
*/
|
|
66
105
|
export declare function applyMcpRosterDelta(input: McpRosterApplyInput, delta: McpRosterDelta): McpRosterApplyOutcome;
|
|
67
106
|
/** The seats the post-application commit writes through. */
|
|
@@ -105,5 +144,8 @@ export interface McpRedialInput extends McpRosterApplyInput, McpRosterCommitInpu
|
|
|
105
144
|
* leave the model holding tools that can only fail), and an arm that never touched the connection —
|
|
106
145
|
* unknown name, revoked server, re-dial already in flight, disposed materialization — asserts nothing
|
|
107
146
|
* and the mounted roster is left alone.
|
|
147
|
+
*
|
|
148
|
+
* The result it hands back counts what the ROSTER took: `toolCount` is the server's tools mounted after the swap, `added` /
|
|
149
|
+
* `removed` the names that came onto / left the roster, and the notice reads the same three — one source for "how many are mounted".
|
|
108
150
|
*/
|
|
109
151
|
export declare function redialMcpServer(input: McpRedialInput, server: string): Promise<McpReconnectResult>;
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
+
import { valveLeaves } from "../../agents/tool-filter.js";
|
|
2
|
+
import { instructionsTaught, instructionsTold, tellInstructions, toolNamesOfServer, withdrawInstructionAdds } from "../mounted-mcp.js";
|
|
1
3
|
import { deliverEngineNotice } from "../types.js";
|
|
2
4
|
import { thrownText } from "../thrown-value.js";
|
|
5
|
+
export function rosterHolding(input, serverName) {
|
|
6
|
+
const mounted = input.declared.some((d) => d.name === serverName) ? toolNamesOfServer(input.roster.tools.map((t) => t.name), serverName) : [];
|
|
7
|
+
return { mounted, added: [], removed: [], excluded: [] };
|
|
8
|
+
}
|
|
9
|
+
export function settledOnRoster(result, reading, roster) {
|
|
10
|
+
const settled = { ...result, toolCount: reading.mounted.length, added: reading.added, removed: reading.removed };
|
|
11
|
+
if (result.tools === undefined)
|
|
12
|
+
return settled;
|
|
13
|
+
const held = new Set(reading.mounted);
|
|
14
|
+
return { ...settled, tools: roster.tools.filter((t) => held.has(t.name)), ...(result.axes !== undefined ? { axes: result.axes.filter((a) => held.has(a.name)) } : {}) };
|
|
15
|
+
}
|
|
16
|
+
function settleInstructions(lane, delta, taught) {
|
|
17
|
+
if (!taught)
|
|
18
|
+
return withdrawInstructionAdds(lane, delta.server);
|
|
19
|
+
if (delta.instructions !== undefined && !instructionsTold(lane, delta.server) && !lane.pendingAdds.some((e) => e.server === delta.server))
|
|
20
|
+
tellInstructions(lane, delta.instructions);
|
|
21
|
+
}
|
|
3
22
|
export function applyMcpRosterDelta(input, delta) {
|
|
4
|
-
const { toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes, toolEffects, axisExplicitNegatives, roster } = input;
|
|
5
|
-
const
|
|
6
|
-
const
|
|
23
|
+
const { spec, toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes, toolEffects, axisExplicitNegatives, roster } = input;
|
|
24
|
+
const domainNow = () => toolNamesOfServer(roster.tools.map((t) => t.name), delta.server);
|
|
25
|
+
const before = domainNow();
|
|
26
|
+
const leaves = valveLeaves(spec, toolFaceSnapshot.selector);
|
|
27
|
+
const pushable = delta.tools.filter(leaves);
|
|
28
|
+
const valveExcluded = delta.tools.filter((t) => !leaves(t)).map((t) => t.name);
|
|
7
29
|
const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(delta.prefix)));
|
|
8
30
|
const restoreDomain = (m, snap) => {
|
|
9
31
|
for (const name of [...m.keys()])
|
|
@@ -19,15 +41,20 @@ export function applyMcpRosterDelta(input, delta) {
|
|
|
19
41
|
for (const name of priorDomainNegatives.keys())
|
|
20
42
|
axisExplicitNegatives.delete(name);
|
|
21
43
|
try {
|
|
22
|
-
foldProtocolAxes(delta.axes.filter((a) => !
|
|
44
|
+
foldProtocolAxes(delta.axes.filter((a) => !valveExcluded.includes(a.name)), "MCP");
|
|
23
45
|
}
|
|
24
46
|
catch (foldErr) {
|
|
25
47
|
restoreDomain(toolEffects, priorDomainEffects);
|
|
26
48
|
restoreDomain(axisExplicitNegatives, priorDomainNegatives);
|
|
27
|
-
|
|
49
|
+
settleInstructions(input.instructionsDelta, delta, instructionsTaught(leaves, delta.server, roster.tools, delta.tools));
|
|
50
|
+
return { applied: false, foldError: thrownText(foldErr), ...rosterHolding(input, delta.server) };
|
|
28
51
|
}
|
|
29
52
|
roster.replaceDomain(delta.prefix, withFaces(pushable, delta.server).map((t) => remoteToolOffload(t)), { source: "mcp", mountedBy: "optional", origin: { peer: delta.server } });
|
|
30
|
-
|
|
53
|
+
const mounted = domainNow();
|
|
54
|
+
const heldNow = new Set(mounted);
|
|
55
|
+
const heldBefore = new Set(before);
|
|
56
|
+
settleInstructions(input.instructionsDelta, delta, instructionsTaught(leaves, delta.server, roster.tools, delta.tools));
|
|
57
|
+
return { applied: true, mounted, added: mounted.filter((n) => !heldBefore.has(n)), removed: before.filter((n) => !heldNow.has(n)), excluded: delta.tools.map((t) => t.name).filter((n) => !heldNow.has(n)) };
|
|
31
58
|
}
|
|
32
59
|
export async function commitMcpRosterChanges(input) {
|
|
33
60
|
await input.rebuildHarnessToolsRef.current?.();
|
|
@@ -38,9 +65,11 @@ export async function commitMcpRosterChanges(input) {
|
|
|
38
65
|
return undefined;
|
|
39
66
|
}
|
|
40
67
|
export async function redialMcpServer(input, server) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
68
|
+
const listed = await input.mcp.reconnect(server);
|
|
69
|
+
let result = settledOnRoster(listed, rosterHolding(input, listed.server), input.roster);
|
|
70
|
+
if (listed.tools !== undefined) {
|
|
71
|
+
const outcome = applyMcpRosterDelta(input, { server: listed.server, prefix: listed.prefix, tools: listed.tools, axes: listed.axes ?? [], dropped: listed.dropped ?? [] });
|
|
72
|
+
result = settledOnRoster(listed, outcome, input.roster);
|
|
44
73
|
if (!outcome.applied)
|
|
45
74
|
input.deps.onError?.(Object.assign(new Error(`MCP re-dial of "${server}" connected but its safety-axis fold refused, so its tools were not mounted: ${outcome.foldError}`), { code: "mcp.server_unavailable" }), { phase: "mcp", sessionId: input.sessionId });
|
|
46
75
|
const ungated = await commitMcpRosterChanges(input);
|
|
@@ -117,12 +117,15 @@ export interface PermissionRuleLanes {
|
|
|
117
117
|
} | undefined;
|
|
118
118
|
}
|
|
119
119
|
/** The ORG lane: the org's verdict about one call, off the store view `read` supplies — the gate's `orgRules` face over the
|
|
120
|
-
* call's pinned read ({@link createPermissionRuleLanes}), and {@link orgVerdictBeforeClaim} over a view of its own.
|
|
120
|
+
* call's pinned read ({@link createPermissionRuleLanes}), and {@link orgVerdictBeforeClaim} over a view of its own.
|
|
121
|
+
* `onDegraded` receives the snapshot's degraded line ({@link describeUnreadableOrgRows}) on every adjudication over a
|
|
122
|
+
* snapshot carrying a row this layer cannot read. */
|
|
121
123
|
export declare function orgRuleLane(cfg: {
|
|
122
124
|
read: (toolCallId: string) => Promise<Pick<EffectivePermissionRules, "org">>;
|
|
123
125
|
questionToolMounted: boolean;
|
|
124
126
|
questionToolName: string;
|
|
125
127
|
onRevisionDefect: (message: string) => void;
|
|
128
|
+
onDegraded: (message: string) => void;
|
|
126
129
|
}): NonNullable<PermissionRuleLanes["org"]>;
|
|
127
130
|
/** The org's CURRENT verdict about a parked call, read by the resume entry before the claim (the row's session and principal,
|
|
128
131
|
* bounded like the gate's wait; unreadable ⇒ unavailable). The mount and a revision defect are the resumed leg's facts, so
|
|
@@ -172,7 +175,7 @@ export declare function createPermissionRuleLanes(cfg: {
|
|
|
172
175
|
questionToolMounted: boolean;
|
|
173
176
|
questionToolName: string;
|
|
174
177
|
onRevisionDefect: (message: string) => void;
|
|
175
|
-
/** Sink for a judgment against the rows that threw. */
|
|
178
|
+
/** Sink for a judgment against the rows that threw, and for the org lane's degraded-snapshot line. */
|
|
176
179
|
onDisclosure: (message: string) => void;
|
|
177
180
|
/** The run's one reader of the person's rules: the call's ONE read, pinned, that the read station, the org lane and the
|
|
178
181
|
* personal lane all take (the lanes consume a reader; they do not make one). */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { persistedRuleMandateOf, ruleClearanceOf } from "../ask-origin.js";
|
|
2
2
|
import {} from "../hooks.js";
|
|
3
|
-
import { adjudicatePersistedDomainRules, adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, domainRuleSubjectOf, faceRowsSpeakFor, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
3
|
+
import { adjudicatePersistedDomainRules, adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, domainRuleSubjectOf, escapeForDisclosure, faceRowsSpeakFor, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
4
4
|
import { callTargetViewsOf, validatePath } from "../effective-path-target.js";
|
|
5
5
|
import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
|
|
6
6
|
import { heldBy, seatOf, wireOf } from "../tool-name-readings.js";
|
|
7
|
-
import {
|
|
7
|
+
import { describeUnreadableOrgRows, readOrgRules, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
8
8
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
|
|
9
9
|
import { ownerStoreOf } from "../permission-rule-provider.js";
|
|
10
10
|
import { thrownText } from "../thrown-value.js";
|
|
@@ -18,12 +18,19 @@ export function inheritedAskRuleEvidence(provider) {
|
|
|
18
18
|
return Object.freeze({ orgRevisionAbsent: org, orgRuleAbsent: org, personalRuleDotsAbsent: personal });
|
|
19
19
|
}
|
|
20
20
|
export function orgRevisionEvidenceOf(resolution, onDefect) {
|
|
21
|
-
|
|
21
|
+
let reported;
|
|
22
|
+
try {
|
|
23
|
+
reported = resolution.revision;
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
onDefect(`the org rule partition's revision could not be read (${thrownText(err)}) — the adjudication stands, but the ask carries no revision evidence for this call`);
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
22
29
|
if (reported === undefined)
|
|
23
30
|
return {};
|
|
24
31
|
if (typeof reported === "number" && Number.isFinite(reported))
|
|
25
32
|
return { revision: reported };
|
|
26
|
-
onDefect(`the org rule partition reported revision ${
|
|
33
|
+
onDefect(`the org rule partition reported revision ${typeof reported === "string" ? `"${escapeForDisclosure(reported)}"` : escapeForDisclosure(reported)} — a snapshot revision is a finite number; ` +
|
|
27
34
|
`the adjudication stands, but the ask carries no revision evidence for this call`);
|
|
28
35
|
return {};
|
|
29
36
|
}
|
|
@@ -59,11 +66,16 @@ export function orgRuleLane(cfg) {
|
|
|
59
66
|
if (resolution.status === "unavailable" || resolution.status === "ungoverned") {
|
|
60
67
|
return { status: "unavailable", disclosures: resolution.status === "ungoverned" ? ["the store reports no org partition while the provider declares one — unreadable governance, never an empty one"] : resolution.disclosures };
|
|
61
68
|
}
|
|
62
|
-
const
|
|
69
|
+
const reading = readOrgRules(resolution.rules);
|
|
70
|
+
if ("refused" in reading)
|
|
71
|
+
return { status: "unavailable", disclosures: [`the store's org resolution cannot be read (${reading.refused}) — unreadable governance, never an empty one`] };
|
|
63
72
|
const command = req.args?.command;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
73
|
+
const face = callPathFaceOf(req);
|
|
74
|
+
const verdict = reading.verdictFor({ tool: laneToolNamesOf(req), ...(typeof command === "string" ? { command } : {}), ...(face !== undefined ? { face } : {}) });
|
|
75
|
+
const revisionCell = orgRevisionEvidenceOf(resolution, cfg.onRevisionDefect);
|
|
76
|
+
const degraded = describeUnreadableOrgRows(reading.unreadable, revisionCell.revision);
|
|
77
|
+
if (degraded !== undefined)
|
|
78
|
+
cfg.onDegraded(degraded);
|
|
67
79
|
return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell };
|
|
68
80
|
},
|
|
69
81
|
};
|
|
@@ -72,7 +84,7 @@ export function orgVerdictBeforeClaim(cfg, req) {
|
|
|
72
84
|
const unreadable = { status: "unavailable", disclosures: [] };
|
|
73
85
|
const read = () => {
|
|
74
86
|
const store = ownerStoreOf(cfg.provider, cfg.principal, cfg.localOwnerDeclared);
|
|
75
|
-
return orgRuleLane({ read: () => store.effective({ sessionId: cfg.sessionId }), questionToolMounted: true, questionToolName: ASK_USER_QUESTION_TOOL_NAME, onRevisionDefect: () => undefined }).adjudicate(req);
|
|
87
|
+
return orgRuleLane({ read: () => store.effective({ sessionId: cfg.sessionId }), questionToolMounted: true, questionToolName: ASK_USER_QUESTION_TOOL_NAME, onRevisionDefect: () => undefined, onDegraded: () => undefined }).adjudicate(req);
|
|
76
88
|
};
|
|
77
89
|
return settleOrgVerdictWithin(Promise.resolve().then(read).catch(() => unreadable), unreadable, { timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS, ...(cfg.signal !== undefined ? { signal: cfg.signal } : {}) });
|
|
78
90
|
}
|
|
@@ -101,7 +113,7 @@ export function createPermissionRuleLanes(cfg) {
|
|
|
101
113
|
throw new Error(read.unreadable ?? "the store did not answer");
|
|
102
114
|
return { org: read.org };
|
|
103
115
|
};
|
|
104
|
-
const org = provider.partitions.org ? orgRuleLane({ read: orgViewOf, questionToolMounted: cfg.questionToolMounted, questionToolName: cfg.questionToolName, onRevisionDefect: cfg.onRevisionDefect }) : undefined;
|
|
116
|
+
const org = provider.partitions.org ? orgRuleLane({ read: orgViewOf, questionToolMounted: cfg.questionToolMounted, questionToolName: cfg.questionToolName, onRevisionDefect: cfg.onRevisionDefect, onDegraded: cfg.onDisclosure }) : undefined;
|
|
105
117
|
const personalAdjudicate = async (req) => {
|
|
106
118
|
if (anonymous && !cfg.localOwnerDeclared)
|
|
107
119
|
return undefined;
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Every read's outcome lands in the run's unit, per personal partition (the durable one — its user and project rows — and
|
|
16
16
|
* the session one), and only where it is newer, by the order the reads STARTED: a read that answers late never replaces
|
|
17
|
-
* a later one. The prepare read is the unit's first write. A reader that is not a call (the compaction
|
|
18
|
-
*
|
|
17
|
+
* a later one. The prepare read is the unit's first write. A reader that is not a call (the compaction's kept-tail
|
|
18
|
+
* normalizer, the user-attachment seat's reader (the compaction restore's too), the Artifact tool's local read, a read root's grant eligibility, the classifier's table, a language server's session read)
|
|
19
19
|
* reads the unit's newest tables: while the rows the unit last took are compiling it waits for them (within
|
|
20
20
|
* {@link PATH_TABLES_READY_BOUND_MS}), and a read of tables still compiling, or that could not be compiled, refuses —
|
|
21
21
|
* never the tables of rows the unit has since replaced.
|
|
@@ -237,7 +237,9 @@ export declare function ruleStoreUnreadableNotice(onNotice: RunnerDeps["onNotice
|
|
|
237
237
|
/**
|
|
238
238
|
* The run's ONE `permission.rule_row_dropped` notice (audience user): the person's store served project-scoped rows whose
|
|
239
239
|
* scope root is not a complete absolute path, left out of the checks; minted by whichever read met them first, once per
|
|
240
|
-
* run. The sentence names no path; the rows
|
|
240
|
+
* run. The sentence names no path; the rows ride the detail: `rule` is the rule text as the store served it, through the one
|
|
241
|
+
* reading ({@link noticeRuleText}) that `permission.allowed_tool_not_granted`'s `entries[].entry` uses too — the machine-read side carries the
|
|
242
|
+
* original, the sentence spells — and `scopeRoot` neutralized and bounded. The trace keeps every read's line.
|
|
241
243
|
*/
|
|
242
244
|
export declare function ruleRowsDroppedNotice(onNotice: RunnerDeps["onNotice"], ids: {
|
|
243
245
|
readonly sessionId: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ownerStoreOf } from "../permission-rule-provider.js";
|
|
2
2
|
import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
|
|
3
|
-
import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted } from "../permission-rule-model.js";
|
|
3
|
+
import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted, noticeRuleText } from "../permission-rule-model.js";
|
|
4
4
|
import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
|
|
5
5
|
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
|
|
6
6
|
import { thrownText } from "../thrown-value.js";
|
|
@@ -382,7 +382,7 @@ export function ruleRowsDroppedNotice(onNotice, ids) {
|
|
|
382
382
|
deliverEngineNotice(onNotice, {
|
|
383
383
|
code: "permission.rule_row_dropped",
|
|
384
384
|
message: `${rows.length} of your persisted permission rule rows ${one ? "is" : "are"} scoped to a project directory that is not written as a complete absolute path, so ${one ? "it takes" : "they take"} no part in this run's checks — a deny or ask row among them protects nothing. Rewrite ${one ? "it" : "them"} with the project's full path, or remove ${one ? "it" : "them"} (this notice's detail names ${one ? "it" : "them"}).`,
|
|
385
|
-
detail: { rows: rows.slice(0, 8).map((r) => ({ rule:
|
|
385
|
+
detail: { rows: rows.slice(0, 8).map((r) => ({ rule: noticeRuleText(r.rule), scopeRoot: inlineUntrusted(r.scopeRoot, 200) })), count: rows.length, sessionId: ids.sessionId, runId: ids.runId },
|
|
386
386
|
});
|
|
387
387
|
};
|
|
388
388
|
}
|
|
@@ -38,10 +38,9 @@ export interface PrepareAcquireReconcileInput {
|
|
|
38
38
|
* suspension point BEFORE reconcile) and `suspendedBatch` (wake-reconcile SKIPS those calls —
|
|
39
39
|
* they are resumed, not crash-interrupted). Absent for a fresh/non-durable run. */
|
|
40
40
|
resume: Pick<PrepareResume, "leafId" | "suspendedBatch"> | undefined;
|
|
41
|
-
/** borrowed-readonly
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
toolEffects: Map<string, ToolEffect>;
|
|
41
|
+
/** borrowed-readonly — the safety scan's pre-mount reading ({@link import("./prepare-safety-scan.js").PrepareSafetyScanResult.wakeEffects}):
|
|
42
|
+
* this reconcile runs before any tool mounts, so a contested name reads its least repeat-safe member; it classifies an orphan's retry-safety. */
|
|
43
|
+
wakeEffects: Map<string, ToolEffect>;
|
|
45
44
|
/** borrowed-readonly — subagent transcript persistence: the trusted spawner's placement
|
|
46
45
|
* declaration for THIS run's session ({@link RunInternals.sessionPlacement}), forwarded verbatim
|
|
47
46
|
* into the create-form acquire so the store creates the child's transcript into its subagent
|
|
@@ -8,7 +8,7 @@ import { detectConflicts, settleBranchWriteBack } from "./branch-writeback.js";
|
|
|
8
8
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
9
9
|
const RECONCILE_MAX_RETRIES = 3;
|
|
10
10
|
export async function prepareAcquireReconcile(input) {
|
|
11
|
-
const { sessions, spec, resume,
|
|
11
|
+
const { sessions, spec, resume, wakeEffects } = input;
|
|
12
12
|
let acquired;
|
|
13
13
|
let session;
|
|
14
14
|
let conflictRef;
|
|
@@ -64,7 +64,7 @@ export async function prepareAcquireReconcile(input) {
|
|
|
64
64
|
throw e;
|
|
65
65
|
}
|
|
66
66
|
if (userAttachmentOf(msg) !== undefined) {
|
|
67
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" is an attachment frame
|
|
67
|
+
const e = new Error(`resumeAt entry "${spec.resumeAt}" is an attachment frame — a file the engine delivered, not a turn boundary; resume at the user message it belongs to, or at another user message or finished assistant turn`);
|
|
68
68
|
e.code = "resume_at.not_a_message";
|
|
69
69
|
throw e;
|
|
70
70
|
}
|
|
@@ -100,7 +100,7 @@ export async function prepareAcquireReconcile(input) {
|
|
|
100
100
|
own.ids = new Set();
|
|
101
101
|
branch = { storage, leafBefore, leafSet, own };
|
|
102
102
|
}
|
|
103
|
-
wakeRecovered = (await reconcileInterruptedSession(session,
|
|
103
|
+
wakeRecovered = (await reconcileInterruptedSession(session, wakeEffects, resume?.suspendedBatch)).recovered;
|
|
104
104
|
break;
|
|
105
105
|
}
|
|
106
106
|
catch (err) {
|
|
@@ -2,7 +2,7 @@ import { cardDisclosureOf } from "../allow-entry.js";
|
|
|
2
2
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
3
3
|
import { classicEventBase, observeClassic } from "../mods/stations.js";
|
|
4
4
|
import { advertisedWritableRootsSeat, exemptRootsSeat, attest, attestationOf, isLiveApproverSeat, resolveAsk } from "../tool-policy.js";
|
|
5
|
-
import {
|
|
5
|
+
import { exactBoundInputHashOf } from "../canonical-json.js";
|
|
6
6
|
import { composeCallSignal, raceSettlementAgainstSignal } from "./abort-race.js";
|
|
7
7
|
import { failedJudgmentDeny, policyCheckAbortedDeny } from "./policy-throw.js";
|
|
8
8
|
import { askMandateMember } from "../ask-origin.js";
|
|
@@ -85,8 +85,8 @@ export function prepareAskLane(input) {
|
|
|
85
85
|
};
|
|
86
86
|
const settlement = { kind: "approver_unavailable", who: { party: "none" }, when: now() };
|
|
87
87
|
return decision.action === "ask"
|
|
88
|
-
? attest(intercepted, req, { settlement, ...(decision.origin !== undefined ? { origin: decision.origin } : {}), ...(decision.classes !== undefined ? { classes: decision.classes } : {}), inputDigest:
|
|
89
|
-
: attest(intercepted, req, { settlement, inputDigest:
|
|
88
|
+
? attest(intercepted, req, { settlement, ...(decision.origin !== undefined ? { origin: decision.origin } : {}), ...(decision.classes !== undefined ? { classes: decision.classes } : {}), inputDigest: exactBoundInputHashOf(req.args) })
|
|
89
|
+
: attest(intercepted, req, { settlement, inputDigest: exactBoundInputHashOf(req.args) });
|
|
90
90
|
}
|
|
91
91
|
const grantReuse = consumeInheritedAskGrant(inheritedAskGrants, onAsk, humanReviewRef, decision, req);
|
|
92
92
|
if (grantReuse !== undefined)
|