@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,5 +1,11 @@
|
|
|
1
|
-
import { escapeForDisclosure, parseRuleText, programRunReachOf, ruleReachesProgramRun } from "./permission-rule-model.js";
|
|
1
|
+
import { COMMAND_RULE_TOOL, PATH_RULE_FACES, escapeForDisclosure, parseRuleText, programRunReachOf, ruleReachesProgramRun, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
2
|
+
import { isNameableToolName, ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
3
|
+
import { isShellFaceToolName, pathTargetOf } from "./tool-registry.js";
|
|
2
4
|
import { writerOf } from "./permission-rule-store.js";
|
|
5
|
+
import { canonicalize } from "./canonical-json.js";
|
|
6
|
+
import { types } from "node:util";
|
|
7
|
+
import { describeFault, receive } from "./mods/kernel/receive.js";
|
|
8
|
+
import { DEFAULT_CAPS } from "./permission-rules.js";
|
|
3
9
|
export const ORG_UNAVAILABLE_DECISION_REASON = "org_unavailable";
|
|
4
10
|
export const ORG_ADJUDICATION_TIMEOUT_MS = 15_000;
|
|
5
11
|
export function settleOrgVerdictWithin(p, fallback, opts) {
|
|
@@ -48,7 +54,7 @@ export function createOrgRulePartition(cfg) {
|
|
|
48
54
|
let memoryState;
|
|
49
55
|
const readState = async () => {
|
|
50
56
|
const state = cfg.persistence !== undefined ? await cfg.persistence.readOrgState() : memoryState;
|
|
51
|
-
return state === undefined ? undefined :
|
|
57
|
+
return state === undefined ? undefined : orgStateCopyOf(state);
|
|
52
58
|
};
|
|
53
59
|
const writeState = async (state) => {
|
|
54
60
|
if (cfg.persistence !== undefined)
|
|
@@ -75,7 +81,7 @@ export function createOrgRulePartition(cfg) {
|
|
|
75
81
|
throw new Error(`the org snapshot source did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms`);
|
|
76
82
|
if ("e" in settled)
|
|
77
83
|
throw settled.e;
|
|
78
|
-
fetched =
|
|
84
|
+
fetched = settled.v === undefined ? undefined : orgSnapshotCopyOf(settled.v);
|
|
79
85
|
}
|
|
80
86
|
catch (err) {
|
|
81
87
|
fetchFailed = true;
|
|
@@ -96,29 +102,30 @@ export function createOrgRulePartition(cfg) {
|
|
|
96
102
|
const persisted = await guardedRead("persistence");
|
|
97
103
|
if (fetched === undefined)
|
|
98
104
|
return persisted;
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
disclosures.push(`org snapshot refused: ${
|
|
105
|
+
const judged = validateOrgSnapshot(fetched, nowMs);
|
|
106
|
+
if ("refused" in judged) {
|
|
107
|
+
disclosures.push(`org snapshot refused: ${judged.refused}; the previously installed snapshot (if any) remains in force`);
|
|
102
108
|
return persisted;
|
|
103
109
|
}
|
|
110
|
+
const incoming = judged.admitted;
|
|
104
111
|
const persistedFloor = persisted === undefined ? undefined : antiRollbackFloor(persisted);
|
|
105
112
|
if (persisted !== undefined && !persistedMarkIsCoherent(persisted)) {
|
|
106
113
|
disclosures.push(`the durable org state carries a high-water mark (${escapeForDisclosure(persisted.revisionHighWater)}) that is not a finite number at or above its own snapshot revision (${escapeForDisclosure(persisted.snapshot?.revision)}) — the anti-rollback fence is being held at ${escapeForDisclosure(persistedFloor)} instead, and the durable state should be repaired`);
|
|
107
114
|
}
|
|
108
|
-
if (persistedFloor !== undefined &&
|
|
109
|
-
disclosures.push(`org snapshot revision ${
|
|
115
|
+
if (persistedFloor !== undefined && incoming.revision < persistedFloor) {
|
|
116
|
+
disclosures.push(`org snapshot revision ${incoming.revision} is below the installed high-water mark ${escapeForDisclosure(persistedFloor)} — refusing the rollback; the current deny set is unchanged`);
|
|
110
117
|
return persisted;
|
|
111
118
|
}
|
|
112
|
-
if (persisted !== undefined &&
|
|
119
|
+
if (persisted !== undefined && incoming.revision === persisted.snapshot.revision && sameOrgPolicyAsHeld(incoming, persisted.snapshot) && incoming.fetchedAtMs <= persisted.snapshot.fetchedAtMs) {
|
|
113
120
|
return persisted;
|
|
114
121
|
}
|
|
115
|
-
if (persisted !== undefined &&
|
|
116
|
-
disclosures.push(`org snapshot revision ${
|
|
122
|
+
if (persisted !== undefined && incoming.revision === persisted.snapshot.revision && !sameOrgPolicyAsHeld(incoming, persisted.snapshot)) {
|
|
123
|
+
disclosures.push(`org snapshot revision ${incoming.revision} equals the installed revision but carries DIFFERENT policy content — refusing the swap; the current deny set is unchanged`);
|
|
117
124
|
return persisted;
|
|
118
125
|
}
|
|
119
126
|
const state = {
|
|
120
|
-
revisionHighWater: Math.max(persistedFloor !== undefined && Number.isFinite(persistedFloor) ? persistedFloor : 0,
|
|
121
|
-
snapshot:
|
|
127
|
+
revisionHighWater: Math.max(persistedFloor !== undefined && Number.isFinite(persistedFloor) ? persistedFloor : 0, incoming.revision),
|
|
128
|
+
snapshot: incoming,
|
|
122
129
|
installedAtMs: nowMs,
|
|
123
130
|
};
|
|
124
131
|
try {
|
|
@@ -142,21 +149,25 @@ export function createOrgRulePartition(cfg) {
|
|
|
142
149
|
disclosures.push(`the installed org snapshot claims a FUTURE observation time (${escapeForDisclosure(installed.snapshot.fetchedAtMs)} vs now ${decisionMs}) — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask`);
|
|
143
150
|
return { source: "org", status: "unavailable", rules: [], disclosures };
|
|
144
151
|
}
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
disclosures.push(`the installed org snapshot (revision ${escapeForDisclosure(installed.snapshot.revision)}) does not meet this version's validator: ${
|
|
152
|
+
const serving = validateOrgSnapshot(installed.snapshot, decisionMs);
|
|
153
|
+
if ("refused" in serving) {
|
|
154
|
+
disclosures.push(`the installed org snapshot (revision ${escapeForDisclosure(installed.snapshot.revision)}) does not meet this version's validator: ${serving.refused} — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask`);
|
|
148
155
|
return { source: "org", status: "unavailable", rules: [], disclosures };
|
|
149
156
|
}
|
|
150
|
-
|
|
157
|
+
const served = serving.admitted;
|
|
158
|
+
if (withinBound(served)) {
|
|
151
159
|
const freshlyInstalled = fetched !== undefined && installed.snapshot === fetched;
|
|
152
160
|
if (!freshlyInstalled && !fetchFailed && fetched === undefined) {
|
|
153
161
|
disclosures.push("org snapshot source returned no snapshot — continuing on the last-known-good");
|
|
154
162
|
}
|
|
163
|
+
const degraded = describeUnreadableOrgRows(readListedOrgRules(served.rules).unreadable, served.revision);
|
|
164
|
+
if (degraded !== undefined)
|
|
165
|
+
disclosures.push(degraded);
|
|
155
166
|
return {
|
|
156
167
|
source: "org",
|
|
157
168
|
status: freshlyInstalled ? "fresh" : "last-known-good",
|
|
158
|
-
rules: structuredClone(
|
|
159
|
-
revision:
|
|
169
|
+
rules: structuredClone(served.rules),
|
|
170
|
+
revision: served.revision,
|
|
160
171
|
disclosures,
|
|
161
172
|
};
|
|
162
173
|
}
|
|
@@ -188,98 +199,213 @@ function persistedMarkIsCoherent(state) {
|
|
|
188
199
|
Number.isFinite(state.snapshot.revision) &&
|
|
189
200
|
state.revisionHighWater >= state.snapshot.revision);
|
|
190
201
|
}
|
|
191
|
-
function sameOrgPolicyContent(a, b) {
|
|
202
|
+
export function sameOrgPolicyContent(a, b) {
|
|
192
203
|
if (a.rules.length !== b.rules.length)
|
|
193
204
|
return false;
|
|
194
|
-
|
|
205
|
+
const policy = (r) => canonicalize(orgRowPolicyOf(r));
|
|
206
|
+
return a.rules.every((r, i) => policy(b.rules[i]) === policy(r));
|
|
195
207
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
function sameOrgPolicyAsHeld(a, b) {
|
|
209
|
+
return !("refused" in b) && sameOrgPolicyContent(a, b);
|
|
210
|
+
}
|
|
211
|
+
const DISCLOSED_UNREADABLE_ROWS = 20;
|
|
212
|
+
function reachOfRowHead(text, behavior) {
|
|
213
|
+
const head = typeof text === "string" ? ruleSpellingOf(text)?.toolName : undefined;
|
|
214
|
+
if (head === undefined || !isNameableToolName(head))
|
|
215
|
+
return "every";
|
|
216
|
+
if (head === COMMAND_RULE_TOOL || isShellFaceToolName(head))
|
|
217
|
+
return { words: [COMMAND_RULE_TOOL], faces: [] };
|
|
218
|
+
if (ruleToolGrammarOf(head) === undefined)
|
|
219
|
+
return "every";
|
|
220
|
+
const column = Object.hasOwn(PATH_RULE_FACES, head) ? PATH_RULE_FACES[head]?.[behavior] : undefined;
|
|
221
|
+
return { words: [head], faces: column ?? [] };
|
|
222
|
+
}
|
|
223
|
+
function rowReaches(reach, words, face) {
|
|
224
|
+
if (reach === "every")
|
|
225
|
+
return true;
|
|
226
|
+
return reach.words.some((w) => words.includes(w)) || (face !== undefined && reach.faces.includes(face));
|
|
227
|
+
}
|
|
228
|
+
function listRefusalOf(rules) {
|
|
229
|
+
if (typeof rules === "object" && rules !== null && types.isProxy(rules))
|
|
230
|
+
return "rules is a Proxy, not an array";
|
|
231
|
+
if (!Array.isArray(rules))
|
|
232
|
+
return "rules is not an array";
|
|
233
|
+
const cap = DEFAULT_CAPS.maxRules;
|
|
234
|
+
return rules.length > cap ? `rules list has ${rules.length} entries; the cap is ${cap} — refused whole (not truncated)` : undefined;
|
|
235
|
+
}
|
|
236
|
+
export const ORG_ROW_POLICY_MEMBERS = Object.freeze(["rule", "behavior"]);
|
|
237
|
+
const ORG_SNAPSHOT_ENVELOPE_MEMBERS = Object.freeze(["revision", "fetchedAtMs", "signature"]);
|
|
238
|
+
const ORG_STATE_MEMBERS = Object.freeze(["revisionHighWater", "installedAtMs"]);
|
|
239
|
+
function scalarMembersOf(value, members) {
|
|
240
|
+
const held = {};
|
|
241
|
+
if (typeof value !== "object" || value === null)
|
|
242
|
+
return held;
|
|
243
|
+
for (const k of members) {
|
|
244
|
+
const v = Object.hasOwn(value, k) ? value[k] : undefined;
|
|
245
|
+
if (typeof v === "string" || typeof v === "boolean" || v === null || (typeof v === "number" && Number.isFinite(v)))
|
|
246
|
+
held[k] = v;
|
|
209
247
|
}
|
|
210
|
-
return
|
|
248
|
+
return held;
|
|
249
|
+
}
|
|
250
|
+
function orgRowPolicyOf(row) {
|
|
251
|
+
return scalarMembersOf(row, ORG_ROW_POLICY_MEMBERS);
|
|
211
252
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
253
|
+
const ORG_RULES_DEPTH = 2;
|
|
254
|
+
const ORG_RULES_RECEIPT = Object.freeze({ accessors: "call-once", depth: ORG_RULES_DEPTH, others: "copy", contain: true, isProxy: (v) => types.isProxy(v) });
|
|
255
|
+
export function orgRuleListOf(rules) {
|
|
256
|
+
const refused = listRefusalOf(rules);
|
|
257
|
+
if (refused !== undefined)
|
|
258
|
+
return { refused };
|
|
259
|
+
const received = receive(rules, ORG_RULES_RECEIPT);
|
|
260
|
+
if (!received.ok)
|
|
261
|
+
return { refused: `rules could not be received (${describeFault(received.fault)})` };
|
|
262
|
+
const copy = received.value;
|
|
263
|
+
return { rows: Array.from({ length: copy.length }, (_, i) => orgRowPolicyOf(Object.hasOwn(copy, i) ? copy[i] : undefined)) };
|
|
264
|
+
}
|
|
265
|
+
export function orgSnapshotCopyOf(v) {
|
|
266
|
+
if (typeof v === "object" && v !== null && types.isProxy(v))
|
|
267
|
+
throw new Error("the snapshot is a Proxy, not data");
|
|
268
|
+
const envelope = scalarMembersOf(v, ORG_SNAPSHOT_ENVELOPE_MEMBERS);
|
|
269
|
+
const rules = typeof v === "object" && v !== null ? v.rules : undefined;
|
|
270
|
+
const refused = listRefusalOf(rules);
|
|
271
|
+
if (refused !== undefined)
|
|
272
|
+
return { ...envelope, refused };
|
|
273
|
+
const list = orgRuleListOf(rules);
|
|
274
|
+
if ("refused" in list)
|
|
275
|
+
throw new Error(`the snapshot's ${list.refused}`);
|
|
276
|
+
return { ...envelope, rules: list.rows };
|
|
277
|
+
}
|
|
278
|
+
function orgStateCopyOf(state) {
|
|
279
|
+
if (typeof state === "object" && state !== null && types.isProxy(state))
|
|
280
|
+
throw new Error("the persisted org state is a Proxy, not data");
|
|
281
|
+
const snapshot = typeof state === "object" && state !== null ? state.snapshot : undefined;
|
|
282
|
+
return { ...scalarMembersOf(state, ORG_STATE_MEMBERS), snapshot: orgSnapshotCopyOf(snapshot ?? null) };
|
|
283
|
+
}
|
|
284
|
+
function readOrgRow(row, index) {
|
|
285
|
+
const { rule: text, behavior: said } = orgRowPolicyOf(row);
|
|
286
|
+
const behavior = said === "ask" ? "ask" : "deny";
|
|
287
|
+
const unreadable = (code, detail) => ({
|
|
288
|
+
kind: "unreadable",
|
|
289
|
+
index,
|
|
290
|
+
rule: typeof text === "string" ? text : text === undefined ? "<unreadable>" : escapeForDisclosure(text),
|
|
291
|
+
code,
|
|
292
|
+
...(detail !== undefined ? { detail } : {}),
|
|
293
|
+
behavior,
|
|
294
|
+
reach: reachOfRowHead(text, behavior),
|
|
295
|
+
});
|
|
296
|
+
if (typeof text !== "string" || text === "")
|
|
297
|
+
return unreadable("invalid.grammar", "no rule text");
|
|
298
|
+
if (said !== "deny" && said !== "ask")
|
|
299
|
+
return unreadable("invalid.grammar", said === undefined ? "no behavior" : `behavior "${escapeForDisclosure(said)}" is not deny or ask`);
|
|
300
|
+
const parsed = parseRuleText(text, said);
|
|
301
|
+
if ("reject" in parsed)
|
|
302
|
+
return unreadable(parsed.reject.code);
|
|
303
|
+
if (parsed.rule.tool !== COMMAND_RULE_TOOL)
|
|
304
|
+
return unreadable("unsupported.tool");
|
|
305
|
+
return { kind: "read", index, rule: text, behavior: said, parsed: parsed.rule };
|
|
306
|
+
}
|
|
307
|
+
export function readOrgRules(rules) {
|
|
308
|
+
const list = orgRuleListOf(rules);
|
|
309
|
+
if ("refused" in list)
|
|
310
|
+
return list;
|
|
311
|
+
const rows = list.rows.map((row, i) => readOrgRow(row, i + 1));
|
|
312
|
+
const unreadable = rows.filter((r) => r.kind === "unreadable");
|
|
313
|
+
return {
|
|
314
|
+
rows,
|
|
315
|
+
unreadable,
|
|
316
|
+
verdictFor(call) {
|
|
317
|
+
const words = typeof call.tool === "string" ? [call.tool] : call.tool;
|
|
318
|
+
const command = words.includes(COMMAND_RULE_TOOL) && typeof call.command === "string" ? call.command : undefined;
|
|
319
|
+
let ask;
|
|
320
|
+
let unjudged;
|
|
321
|
+
for (const row of rows) {
|
|
322
|
+
if (row.kind === "unreadable") {
|
|
323
|
+
if (!rowReaches(row.reach, words, call.face))
|
|
324
|
+
continue;
|
|
325
|
+
const verdict = { behavior: row.behavior, rule: row.rule, unreadable: unreadableOrgRowReason(row) };
|
|
326
|
+
if (row.behavior === "deny")
|
|
327
|
+
return verdict;
|
|
328
|
+
ask ??= verdict;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (command === undefined)
|
|
332
|
+
continue;
|
|
333
|
+
const outcome = programRunReachOf(row.parsed, command);
|
|
334
|
+
if (outcome.reach === "not_reached")
|
|
335
|
+
continue;
|
|
336
|
+
if (outcome.reach === "unreadable") {
|
|
337
|
+
unjudged ??= { behavior: "ask", rule: row.rule, unreadable: outcome.reason };
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (row.behavior === "deny")
|
|
341
|
+
return { behavior: "deny", rule: row.rule };
|
|
342
|
+
ask ??= { behavior: "ask", rule: row.rule };
|
|
343
|
+
}
|
|
344
|
+
return unjudged ?? ask;
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
export function unenforceableOrgRules(rules) {
|
|
349
|
+
return readListedOrgRules(rules).unreadable.map((u) => ({ rule: u.rule, code: u.code }));
|
|
350
|
+
}
|
|
351
|
+
function readListedOrgRules(rules) {
|
|
352
|
+
const reading = readOrgRules(rules);
|
|
353
|
+
if ("refused" in reading)
|
|
354
|
+
throw new TypeError(`the org rules cannot be read: ${reading.refused}`);
|
|
355
|
+
return reading;
|
|
356
|
+
}
|
|
357
|
+
export function unreadableOrgRowReason(row) {
|
|
358
|
+
return `row ${row.index} of the organization snapshot is not a rule this layer reads (${row.code}${row.detail !== undefined ? `: ${row.detail}` : ""}); republish it in the shell tool's command grammar`;
|
|
359
|
+
}
|
|
360
|
+
export function orgUnreadableClause(reason) {
|
|
361
|
+
return `which could not be read against it (${reason})`;
|
|
362
|
+
}
|
|
363
|
+
export function describeUnreadableOrgRows(unreadable, revision) {
|
|
364
|
+
if (unreadable.length === 0)
|
|
365
|
+
return undefined;
|
|
366
|
+
const named = unreadable
|
|
367
|
+
.slice(0, DISCLOSED_UNREADABLE_ROWS)
|
|
368
|
+
.map((u) => `row ${u.index} "${escapeForDisclosure(u.rule)}" (${u.code}${u.detail !== undefined ? `: ${u.detail}` : ""})`)
|
|
216
369
|
.join(", ");
|
|
217
|
-
const more =
|
|
218
|
-
return
|
|
370
|
+
const more = unreadable.length - DISCLOSED_UNREADABLE_ROWS;
|
|
371
|
+
return (`the org snapshot${revision !== undefined ? ` (revision ${escapeForDisclosure(revision)})` : ""} is served DEGRADED: ${unreadable.length} row(s) this layer cannot read — ${named}${more > 0 ? ` and ${more} more` : ""}; ` +
|
|
372
|
+
`each stands at its strictest reading over the calls it may reach (a deny row, or one whose behavior cannot be read, refuses them; an ask row asks a person), and every readable row stands as published — republish the named rows in the shell tool's command grammar`);
|
|
219
373
|
}
|
|
220
|
-
function validateOrgSnapshot(s, nowMs) {
|
|
374
|
+
export function validateOrgSnapshot(s, nowMs) {
|
|
221
375
|
if (typeof s.revision !== "number" || !Number.isFinite(s.revision))
|
|
222
|
-
return `revision is not a finite number (${escapeForDisclosure(s.revision)})
|
|
376
|
+
return { refused: `revision is not a finite number (${escapeForDisclosure(s.revision)})` };
|
|
223
377
|
if (typeof s.fetchedAtMs !== "number" || !Number.isFinite(s.fetchedAtMs))
|
|
224
|
-
return `fetchedAtMs is not a finite number (${escapeForDisclosure(s.fetchedAtMs)})
|
|
378
|
+
return { refused: `fetchedAtMs is not a finite number (${escapeForDisclosure(s.fetchedAtMs)})` };
|
|
225
379
|
if (s.fetchedAtMs > nowMs + ORG_FETCHED_AT_SKEW_ALLOWANCE_MS) {
|
|
226
|
-
return `fetchedAtMs is ${s.fetchedAtMs - nowMs}ms in the future (allowance ${ORG_FETCHED_AT_SKEW_ALLOWANCE_MS}ms) — a future-dated observation would satisfy the staleness bound indefinitely
|
|
380
|
+
return { refused: `fetchedAtMs is ${s.fetchedAtMs - nowMs}ms in the future (allowance ${ORG_FETCHED_AT_SKEW_ALLOWANCE_MS}ms) — a future-dated observation would satisfy the staleness bound indefinitely` };
|
|
227
381
|
}
|
|
228
|
-
|
|
229
|
-
return "rules is not an array";
|
|
230
|
-
for (const r of s.rules) {
|
|
231
|
-
if (typeof r?.rule !== "string" || r.rule === "")
|
|
232
|
-
return "a rule entry carries no rule text";
|
|
233
|
-
if (r.behavior !== "deny" && r.behavior !== "ask") {
|
|
234
|
-
return `rule "${escapeForDisclosure(r.rule)}" carries behavior "${escapeForDisclosure(r.behavior)}" — the org layer has no allow bucket`;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
const unenforceable = unenforceableOrgRules(s.rules);
|
|
238
|
-
const first = unenforceable[0];
|
|
239
|
-
if (first !== undefined) {
|
|
240
|
-
const rest = unenforceable.length > 1 ? ` (${unenforceable.length} in all — ${describeUnenforceable(unenforceable)})` : "";
|
|
241
|
-
return `rule "${escapeForDisclosure(first.rule)}" does not parse (${first.code}) — an unenforceable deny/ask must not stand as policy${rest}`;
|
|
242
|
-
}
|
|
243
|
-
return undefined;
|
|
382
|
+
return "refused" in s ? { refused: s.refused } : { admitted: s };
|
|
244
383
|
}
|
|
245
384
|
export function orgRuleVerdictFor(rules, call) {
|
|
246
|
-
|
|
247
|
-
let unreadable;
|
|
248
|
-
for (const r of rules) {
|
|
249
|
-
const parsed = parseRuleText(r.rule, r.behavior);
|
|
250
|
-
if ("reject" in parsed)
|
|
251
|
-
continue;
|
|
252
|
-
if (parsed.rule.tool !== call.tool)
|
|
253
|
-
continue;
|
|
254
|
-
const outcome = programRunReachOf(parsed.rule, call.command);
|
|
255
|
-
if (outcome.reach === "not_reached")
|
|
256
|
-
continue;
|
|
257
|
-
if (outcome.reach === "unreadable") {
|
|
258
|
-
unreadable ??= { behavior: "ask", rule: r.rule, unreadable: outcome.reason };
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (r.behavior === "deny")
|
|
262
|
-
return { behavior: "deny", rule: r.rule };
|
|
263
|
-
ask ??= { behavior: "ask", rule: r.rule };
|
|
264
|
-
}
|
|
265
|
-
return unreadable ?? ask;
|
|
385
|
+
return readListedOrgRules(rules).verdictFor(call);
|
|
266
386
|
}
|
|
267
387
|
export function orgRuleShadows(orgRules, rule) {
|
|
268
388
|
return compileOrgShadowPredicate(orgRules)(rule);
|
|
269
389
|
}
|
|
270
390
|
export function compileOrgShadowPredicate(orgRules) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
continue;
|
|
275
|
-
const parsed = parseRuleText(d.rule, "deny");
|
|
276
|
-
if (!("reject" in parsed))
|
|
277
|
-
denies.push(parsed.rule);
|
|
278
|
-
}
|
|
391
|
+
const { rows } = readListedOrgRules(orgRules);
|
|
392
|
+
const denies = rows.flatMap((r) => (r.kind === "read" && r.behavior === "deny" ? [r.parsed] : []));
|
|
393
|
+
const standing = rows.flatMap((r) => (r.kind === "unreadable" && r.behavior === "deny" ? [r.reach] : []));
|
|
279
394
|
return (rule) => {
|
|
280
|
-
if (
|
|
395
|
+
if (standing.length > 0) {
|
|
396
|
+
const word = isShellFaceToolName(rule.tool) ? COMMAND_RULE_TOOL : rule.tool;
|
|
397
|
+
const target = pathTargetOf(rule.tool);
|
|
398
|
+
const faces = Object.hasOwn(PATH_RULE_FACES, rule.tool)
|
|
399
|
+
? (PATH_RULE_FACES[rule.tool]?.allow ?? [])
|
|
400
|
+
: target !== undefined
|
|
401
|
+
? [target.access === "read" ? "read" : "write"]
|
|
402
|
+
: [undefined];
|
|
403
|
+
if (standing.some((reach) => faces.some((face) => rowReaches(reach, [word], face))))
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
if (rule.tool !== COMMAND_RULE_TOOL || denies.length === 0)
|
|
281
407
|
return false;
|
|
282
|
-
return denies.some((d) =>
|
|
408
|
+
return denies.some((d) => ruleReachesProgramRun(d, rule.command));
|
|
283
409
|
};
|
|
284
410
|
}
|
|
285
411
|
export function isWritablePermissionRuleStore(store) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { readRowBehavior } from "./permission-rule-model.js";
|
|
1
|
+
import { readRowBehavior, RULE_ADD_ORIGINS } from "./permission-rule-model.js";
|
|
2
2
|
import { isValidConsentScope, normalizePersistedRule } from "./permission-rule-store.js";
|
|
3
3
|
function isValidOverlayAdd(add) {
|
|
4
4
|
const a = add;
|
|
5
5
|
if (typeof a?.dot?.actor !== "string" || a.dot.actor === "" || typeof a.dot.counter !== "number" || !Number.isFinite(a.dot.counter))
|
|
6
6
|
return false;
|
|
7
|
-
if (
|
|
7
|
+
if (!RULE_ADD_ORIGINS.some((word) => a.origin === word))
|
|
8
8
|
return false;
|
|
9
9
|
return typeof a.createdAt === "string";
|
|
10
10
|
}
|
|
@@ -45,6 +45,7 @@ import type { PersistedRule, RuleAdd, RuleDot, RuleScope, RuleTombstone } from "
|
|
|
45
45
|
import { type RuleBehavior } from "./permission-rule-model.js";
|
|
46
46
|
import type { RuleSyncDropReason, RuleQuarantineReason } from "./governance-codes.js";
|
|
47
47
|
import type { StoreDurability, StoreFidelity } from "./checkpoint-store.js";
|
|
48
|
+
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
48
49
|
/** One store read: the rules as persisted, their tombstones, and the revision they were read at. */
|
|
49
50
|
export interface StoredRules {
|
|
50
51
|
/** Rules with their FULL add sets — tombstoned dots already removed by the backend on read. */
|
|
@@ -92,6 +93,13 @@ export type RuleOwner = {
|
|
|
92
93
|
} | {
|
|
93
94
|
kind: "local-owner";
|
|
94
95
|
};
|
|
96
|
+
/** The owner discriminants as a value: the `kind` words of {@link RuleOwner}, both directions fenced at compile time
|
|
97
|
+
* ({@link RuleOwnerKindsCoverEveryArm} / {@link RuleOwnerArmsCoverEveryKind}). */
|
|
98
|
+
export declare const RULE_OWNER_KINDS: readonly ["principal", "local-owner"];
|
|
99
|
+
/** Compile fence: an arm whose `kind` is not in {@link RULE_OWNER_KINDS} turns the argument into that word. */
|
|
100
|
+
export type RuleOwnerKindsCoverEveryArm = AssertAllKeysHandled<Exclude<RuleOwner["kind"], (typeof RULE_OWNER_KINDS)[number]>>;
|
|
101
|
+
/** Compile fence, the other direction: a word in {@link RULE_OWNER_KINDS} that no arm carries. */
|
|
102
|
+
export type RuleOwnerArmsCoverEveryKind = AssertAllKeysHandled<Exclude<(typeof RULE_OWNER_KINDS)[number], RuleOwner["kind"]>>;
|
|
95
103
|
/** Do two owners name the same bucket? */
|
|
96
104
|
export declare function sameRuleOwner(a: RuleOwner, b: RuleOwner): boolean;
|
|
97
105
|
/** Resolves one owner's durable partition. `undefined` ⇒ a partition that reports zero rules. This is
|
|
@@ -3,6 +3,7 @@ import { canonicalize } from "./canonical-json.js";
|
|
|
3
3
|
import { escapeForDisclosure, isRuleBehavior, isRuleLive, parseRuleText } from "./permission-rule-model.js";
|
|
4
4
|
import { thrownText } from "./thrown-value.js";
|
|
5
5
|
import { validatePath } from "./effective-path-target.js";
|
|
6
|
+
export const RULE_OWNER_KINDS = Object.freeze(["principal", "local-owner"]);
|
|
6
7
|
export function sameRuleOwner(a, b) {
|
|
7
8
|
return a.kind === "principal" ? b.kind === "principal" && a.principal === b.principal : b.kind === "local-owner";
|
|
8
9
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeForDisclosure, isRuleBehavior, parseRuleText } from "./permission-rule-model.js";
|
|
1
|
+
import { escapeForDisclosure, isRuleBehavior, parseRuleText, RULE_ADD_ORIGINS } from "./permission-rule-model.js";
|
|
2
2
|
import { applyTombstones, errText, isProjectScopeRoot, joinFrontiers, joinRuleStates, normalizePersistedRule, readRuleWriteOutcome, ruleSyncVector, sameRuleIdentity, screenRuleSyncState, writerOf, } from "./permission-rule-store.js";
|
|
3
3
|
import { emitTrace } from "./trace.js";
|
|
4
4
|
export const PERMISSION_RULE_SYNC_PATH = "/v1/permission-rules/sync";
|
|
@@ -273,6 +273,7 @@ function pickScopeCaptured(v) {
|
|
|
273
273
|
function pickScope(v) {
|
|
274
274
|
return pickScopeCaptured(v).scope;
|
|
275
275
|
}
|
|
276
|
+
const isRuleAddOrigin = (v) => RULE_ADD_ORIGINS.some((word) => v === word);
|
|
276
277
|
function pickAdd(v) {
|
|
277
278
|
const a = v;
|
|
278
279
|
const dot = pickDot(a?.dot);
|
|
@@ -280,7 +281,7 @@ function pickAdd(v) {
|
|
|
280
281
|
const createdAt = a?.createdAt;
|
|
281
282
|
if (dot === undefined)
|
|
282
283
|
return undefined;
|
|
283
|
-
if (origin
|
|
284
|
+
if (!isRuleAddOrigin(origin))
|
|
284
285
|
return undefined;
|
|
285
286
|
if (typeof createdAt !== "string")
|
|
286
287
|
return undefined;
|
|
@@ -47,7 +47,10 @@
|
|
|
47
47
|
* The NAME lane is the same shape for the tools of `NAME_RULE_FACES` (the `Skill` tool): a `Skill(<name>)` / `Skill(<prefix>:*)`
|
|
48
48
|
* deny or ask — the reference's own spelling — compiles through the same validator (the model's name grammar) and is judged on the
|
|
49
49
|
* one string the call names (`nameRuleSubjectOf`) with `nameRuleAdmits`, the reader the persisted rule store uses. The parameter
|
|
50
|
-
* spelling `Skill(skill:<name>)` those tools used to take is retired: refused loudly, its sentence naming the CC form.
|
|
50
|
+
* spelling `Skill(skill:<name>)` those tools used to take is retired: refused loudly, its sentence naming the CC form. A KEYED
|
|
51
|
+
* value tool (`Artifact(action:<word>)`) takes the same lane for content keyed by its own parameter (`nameGrammarReads`); its
|
|
52
|
+
* other arguments stay on the generic `param:pattern` lane. A call of it that names no value for the key as the tool reads it
|
|
53
|
+
* is refused by the key's deny or ask rule (`keyedValueAbsentReason`, the persisted lane's reading).
|
|
51
54
|
*
|
|
52
55
|
* 🔴 与 CC 的有意差(design/127 r2):
|
|
53
56
|
* - CC 参数级 generic 道无 allow lane(gnn 只有 deny/ask)—— 我们同样,且整个 policy 无 allow 臂。
|
|
@@ -126,6 +129,9 @@ export interface PermissionRuleCaps {
|
|
|
126
129
|
* 的热路径成本上界。 */
|
|
127
130
|
maxScalarValueChars: number;
|
|
128
131
|
}
|
|
132
|
+
/** The caps a rule set is held to unless the caller names its own — `maxRules` is the ONE bound on how many rules a set
|
|
133
|
+
* carries, read by the compiler here and by the org layer's list judgment (which refuses a longer list before it is copied). */
|
|
134
|
+
export declare const DEFAULT_CAPS: Readonly<PermissionRuleCaps>;
|
|
129
135
|
export interface PermissionRulePolicyOptions {
|
|
130
136
|
/** Invalid/unsupported rules: `"throw"`(default — 部署作者写错要立刻知道)aggregates ALL issues
|
|
131
137
|
* into one error; `"skip"` drops them (pair with {@link validatePermissionRules} for a report, and/or
|
|
@@ -2,9 +2,10 @@ import { attest } from "./tool-policy.js";
|
|
|
2
2
|
import { MCP_NAMESPACE, protocolOf } from "./protocol-table.js";
|
|
3
3
|
import { catalogRuleFaceOf, isShellFaceToolName, pathTargetOf } from "./tool-registry.js";
|
|
4
4
|
import { indexOfUnescaped, lastIndexOfUnescaped, parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
5
|
-
import { COMMAND_RULE_TOOL, NAME_RULE_FACES, PATH_RULE_BASE_LABEL, PATH_RULE_FACES, callPathFaceOf, isUsablePathBase, laneToolNamesOf, nameRuleAdmits, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleBasesNeeded
|
|
5
|
+
import { COMMAND_RULE_TOOL, NAME_RULE_FACES, PATH_RULE_BASE_LABEL, PATH_RULE_FACES, callPathFaceOf, isUsablePathBase, keyedValueAbsentReason, laneToolNamesOf, nameGrammarReads, nameRuleAdmits, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleBasesNeeded } from "./permission-rule-model.js";
|
|
6
6
|
import { pathFamilyOf } from "../tools/fs/safety.js";
|
|
7
|
-
import { callTargetReadingOf, callTargetViewsOf } from "./effective-path-target.js";
|
|
7
|
+
import { argsAsTheToolReads, callTargetReadingOf, callTargetViewsOf } from "./effective-path-target.js";
|
|
8
|
+
import { canonicalize, UNCANONICALIZABLE } from "./canonical-json.js";
|
|
8
9
|
import { bindPathRules, judgeRuleRows, policyAnchors, rowsWhereTheyLead } from "./path-rule-binding.js";
|
|
9
10
|
import { ruleDenialSentence } from "./rule-denial-sentence.js";
|
|
10
11
|
import { heldValues } from "./tool-name-readings.js";
|
|
@@ -24,7 +25,7 @@ function describeBehavior(v) {
|
|
|
24
25
|
default: return v === null ? "null" : "an object";
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
|
-
const DEFAULT_CAPS = {
|
|
28
|
+
export const DEFAULT_CAPS = {
|
|
28
29
|
maxRules: 256,
|
|
29
30
|
maxRuleLength: 512,
|
|
30
31
|
maxPatternLength: 256,
|
|
@@ -164,7 +165,7 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
|
|
|
164
165
|
continue;
|
|
165
166
|
}
|
|
166
167
|
const face = resolved.face;
|
|
167
|
-
if (
|
|
168
|
+
if (nameGrammarReads(toolName, content)) {
|
|
168
169
|
const parsedName = parseRuleText(text, verb);
|
|
169
170
|
if ("reject" in parsedName) {
|
|
170
171
|
bad(text, "invalid.command_rule", `${parsedName.reject.code}: ${parsedName.reject.message}`);
|
|
@@ -242,12 +243,16 @@ export function validatePermissionRules(rules, opts) {
|
|
|
242
243
|
function isPlainObjectArgs(args) {
|
|
243
244
|
return typeof args === "object" && args !== null && !Array.isArray(args);
|
|
244
245
|
}
|
|
245
|
-
function matchParamRules(rules,
|
|
246
|
+
function matchParamRules(rules, req, maxScalarValueChars) {
|
|
247
|
+
const args = req.args;
|
|
246
248
|
if (!isPlainObjectArgs(args))
|
|
247
249
|
return undefined;
|
|
248
250
|
for (const r of rules) {
|
|
249
|
-
if (!Object.hasOwn(args, r.param))
|
|
251
|
+
if (!Object.hasOwn(args, r.param)) {
|
|
252
|
+
if (readingAbsorbs(req, r.param, r.pattern))
|
|
253
|
+
return r;
|
|
250
254
|
continue;
|
|
255
|
+
}
|
|
251
256
|
const raw = args[r.param];
|
|
252
257
|
if (raw === undefined || raw === null)
|
|
253
258
|
continue;
|
|
@@ -262,6 +267,17 @@ function matchParamRules(rules, args, maxScalarValueChars) {
|
|
|
262
267
|
}
|
|
263
268
|
return undefined;
|
|
264
269
|
}
|
|
270
|
+
function readingAbsorbs(req, param, pattern) {
|
|
271
|
+
if (pattern.includes("*"))
|
|
272
|
+
return false;
|
|
273
|
+
try {
|
|
274
|
+
const read = canonicalize(argsAsTheToolReads(req, { ...req.args, [param]: pattern }));
|
|
275
|
+
return read !== UNCANONICALIZABLE && read === canonicalize(req.args);
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
265
281
|
function ruleDenial(req, ruleText, source) {
|
|
266
282
|
return ruleDenialSentence({ source: "permission", tool: req.toolName, rule: ruleText, ...(source ? { origin: source } : {}) });
|
|
267
283
|
}
|
|
@@ -387,6 +403,16 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
387
403
|
}
|
|
388
404
|
return unreadable;
|
|
389
405
|
};
|
|
406
|
+
const keylessRefusal = (entry, req) => {
|
|
407
|
+
for (const r of [...entry.content.deny, ...entry.content.ask]) {
|
|
408
|
+
if (r.parsed.match !== "name" || nameRuleSubjectOf(r.parsed.tool, req.args) !== undefined)
|
|
409
|
+
continue;
|
|
410
|
+
const absent = keyedValueAbsentReason(r.parsed.tool);
|
|
411
|
+
if (absent !== undefined)
|
|
412
|
+
return ruleDenialSentence({ source: "permission", tool: req.toolName, rule: r.ruleText, ...(r.source ? { origin: r.source } : {}), detail: absent });
|
|
413
|
+
}
|
|
414
|
+
return undefined;
|
|
415
|
+
};
|
|
390
416
|
const unreadableAsk = (hit, lane, req) => ({
|
|
391
417
|
action: "ask",
|
|
392
418
|
message: `tool "${req.toolName}" needs approval: the call could not be read against the permission rule ${hit.rule.ruleText} (${hit.unreadable}) — a person must decide`,
|
|
@@ -433,11 +459,14 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
433
459
|
return face !== undefined ? attest(denied, req, { pathDenial: { rule: contentDeny.rule.ruleText, view: contentDeny.view, face } }) : denied;
|
|
434
460
|
}
|
|
435
461
|
if (entry) {
|
|
436
|
-
const paramDeny = matchParamRules(entry.param.deny, req
|
|
462
|
+
const paramDeny = matchParamRules(entry.param.deny, req, caps.maxScalarValueChars);
|
|
437
463
|
if (paramDeny) {
|
|
438
464
|
return { action: "deny", message: ruleDenial(req, paramDeny.ruleText, paramDeny.source) };
|
|
439
465
|
}
|
|
440
466
|
}
|
|
467
|
+
const keyless = entry !== undefined ? keylessRefusal(entry, req) : undefined;
|
|
468
|
+
if (keyless !== undefined)
|
|
469
|
+
return { action: "deny", message: keyless };
|
|
441
470
|
if (contentDeny?.unreadable !== undefined)
|
|
442
471
|
return unreadableAsk({ rule: contentDeny.rule, unreadable: contentDeny.unreadable }, "content_deny", req);
|
|
443
472
|
if (entry) {
|
|
@@ -458,7 +487,7 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
458
487
|
return { action: "ask", message: ruleAskMessage(contentAsk.rule.ruleText, contentAsk.rule.source), matchedAskRule: contentAsk.rule.ruleText };
|
|
459
488
|
}
|
|
460
489
|
if (entry) {
|
|
461
|
-
const paramAsk = matchParamRules(entry.param.ask, req
|
|
490
|
+
const paramAsk = matchParamRules(entry.param.ask, req, caps.maxScalarValueChars);
|
|
462
491
|
if (paramAsk) {
|
|
463
492
|
return { action: "ask", message: ruleAskMessage(paramAsk.ruleText, paramAsk.source), matchedAskRule: paramAsk.ruleText };
|
|
464
493
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ShellCommandShape } from "./shell-
|
|
1
|
+
import { type ShellCommandShape } from "./shell-run-candidates.js";
|
|
2
2
|
import { type LineDescriptors } from "../tools/fs/shell-read-device.js";
|
|
3
3
|
/** The reader's answer: read-only, or not — with the first reason the reading stopped on (a sentence a
|
|
4
4
|
* trace or a card can show; never adjudication input). */
|