@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
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
* hanging / what brings it back), the three word types and the resume match DERIVED from it, and the
|
|
4
4
|
* registry-bound `gate` × `pendingAction` pair with its checked constructor. A vocabulary module on the
|
|
5
5
|
* floor: it types against `checkpoint-store.ts` (type-only, no runtime edge) and `checkpoint-store.ts`
|
|
6
|
-
* reaches down for the pair type
|
|
6
|
+
* reaches down for the pair type and the card reading; its one runtime edge is the card vocabulary
|
|
7
|
+
* (`tool-face.ts`, itself a leaf).
|
|
7
8
|
*/
|
|
8
9
|
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
9
10
|
import type { Checkpoint, CheckpointGate, CheckpointRow, PendingAction, ResumeOutcome } from "./checkpoint-store.js";
|
|
11
|
+
import { type ToolApprovalCard } from "./tool-face.js";
|
|
10
12
|
/**
|
|
11
13
|
* THE registry of pauses — one row per way a run can stop and wait, and the ONE place the three faces
|
|
12
14
|
* of that pause are declared together: WHY it stopped (`CheckpointGate.kind`, the row key), WHAT it left
|
|
@@ -93,6 +95,38 @@ export declare function isQuestionGate(gate: {
|
|
|
93
95
|
readonly kind: GateKind;
|
|
94
96
|
readonly approvalCard?: unknown;
|
|
95
97
|
}): boolean;
|
|
98
|
+
/** What a parked row's gate says about its approval card — the four answers {@link parkedApprovalCardOf} gives. */
|
|
99
|
+
export type ParkedApprovalCardReading =
|
|
100
|
+
/** An approval gate carrying a word of the closed card set (`TOOL_APPROVAL_CARDS`). */
|
|
101
|
+
{
|
|
102
|
+
readonly kind: "card";
|
|
103
|
+
readonly card: ToolApprovalCard;
|
|
104
|
+
}
|
|
105
|
+
/** An approval gate carrying no card. */
|
|
106
|
+
| {
|
|
107
|
+
readonly kind: "no_card";
|
|
108
|
+
}
|
|
109
|
+
/** An approval gate carrying a value outside the closed set (a word a later engine minted, a hand-edited row,
|
|
110
|
+
* a non-string). `word` is the value as read, unscreened: a caller that renders it bounds and redacts it. */
|
|
111
|
+
| {
|
|
112
|
+
readonly kind: "unknown_word";
|
|
113
|
+
readonly word: unknown;
|
|
114
|
+
}
|
|
115
|
+
/** Not an approval gate: a registered gate whose `pending` column is not `tool_approval`, or a gate kind the
|
|
116
|
+
* registry does not hold. No card is read on it. */
|
|
117
|
+
| {
|
|
118
|
+
readonly kind: "not_approval";
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* The one reading of a parked row's approval card. A gate is an approval gate when its kind is a registry row whose
|
|
122
|
+
* `pending` column is `tool_approval`; only there is the card read, and only a member of the closed set is a card.
|
|
123
|
+
* Each member of `gate` is read once (the value screened is the value answered). Pure: the answer says nothing about
|
|
124
|
+
* how loud an `unknown_word` should be — that is the caller's to decide.
|
|
125
|
+
*/
|
|
126
|
+
export declare function parkedApprovalCardOf(gate: {
|
|
127
|
+
readonly kind: unknown;
|
|
128
|
+
readonly approvalCard?: unknown;
|
|
129
|
+
}): ParkedApprovalCardReading;
|
|
96
130
|
/** The one resume-match predicate: does this outcome gate word answer a checkpoint of this kind? `wake`
|
|
97
131
|
* answers any (it passed its own, stricter, non-gate validation upstream); every other word must be the
|
|
98
132
|
* row's `resume` column. Replaces a six-arm hand-written disjunction. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isToolApprovalCard } from "./tool-face.js";
|
|
1
2
|
export const PAUSE_REGISTRY = {
|
|
2
3
|
human: { pending: "tool_approval", resume: "policy_ask", facet: "A", taskStatus: "suspended", terminal: false },
|
|
3
4
|
irreversible_ask: { pending: "tool_approval", resume: "policy_ask", facet: "A", taskStatus: "suspended", terminal: false },
|
|
@@ -15,6 +16,15 @@ export function facetOf(gate) {
|
|
|
15
16
|
export function isQuestionGate(gate) {
|
|
16
17
|
return PAUSE_REGISTRY[gate.kind].pending === "tool_approval" && gate.approvalCard === "question";
|
|
17
18
|
}
|
|
19
|
+
export function parkedApprovalCardOf(gate) {
|
|
20
|
+
const kind = gate.kind;
|
|
21
|
+
if (!isGateKind(kind) || PAUSE_REGISTRY[kind].pending !== "tool_approval")
|
|
22
|
+
return { kind: "not_approval" };
|
|
23
|
+
const word = gate.approvalCard;
|
|
24
|
+
if (word === undefined)
|
|
25
|
+
return { kind: "no_card" };
|
|
26
|
+
return isToolApprovalCard(word) ? { kind: "card", card: word } : { kind: "unknown_word", word };
|
|
27
|
+
}
|
|
18
28
|
export function resumeGateMatches(gateKind, outcomeGate) {
|
|
19
29
|
return outcomeGate === "wake" || PAUSE_REGISTRY[gateKind].resume === outcomeGate;
|
|
20
30
|
}
|
|
@@ -38,8 +38,15 @@ export interface RuleCandidate {
|
|
|
38
38
|
rule: string;
|
|
39
39
|
scope: RuleScope;
|
|
40
40
|
}
|
|
41
|
+
/** The approval-record kinds, spelled once: {@link RuleApprovalKind} is derived from this tuple. */
|
|
42
|
+
export declare const RULE_APPROVAL_KINDS: readonly ["card", "import", "starter"];
|
|
41
43
|
/** What the approval record is for — it decides the provenance the minted adds carry. */
|
|
42
|
-
export type RuleApprovalKind =
|
|
44
|
+
export type RuleApprovalKind = (typeof RULE_APPROVAL_KINDS)[number];
|
|
45
|
+
/** The approval-record states, spelled once: {@link RuleApprovalState} is derived from this tuple. */
|
|
46
|
+
export declare const RULE_APPROVAL_STATES: readonly ["pending", "approved", "redeemed"];
|
|
47
|
+
/** Where one approval record stands: `pending` as minted, `approved` once a person confirms, `redeemed` once a
|
|
48
|
+
* redemption has recorded its dot (recorded before the rule is written, so a crash leaves a replayable intent). */
|
|
49
|
+
export type RuleApprovalState = (typeof RULE_APPROVAL_STATES)[number];
|
|
43
50
|
/**
|
|
44
51
|
* design/375 §4.1 — one "don't ask again" OPTION as the record stores it: references into the
|
|
45
52
|
* record's `candidates` by index, never rule text of its own. The display triple (rule, match,
|
|
@@ -78,6 +85,10 @@ export type RuleOffer2 = {
|
|
|
78
85
|
readonly uncoveredSegments?: number;
|
|
79
86
|
readonly uncoveredDetail?: readonly UncoveredSegmentDetail[];
|
|
80
87
|
};
|
|
88
|
+
/** The CURRENT record-form version stamp ({@link RuleApprovalRecord.schema}), spelled once: every mint writes it and
|
|
89
|
+
* every readback compares against it by equality (an older or absent stamp is a stale row, never a floor). A
|
|
90
|
+
* durable backend's stale gate reads this value rather than its own number. */
|
|
91
|
+
export declare const RULE_APPROVAL_RECORD_SCHEMA = 4;
|
|
81
92
|
/** A durable record of one pending/settled human decision about minting rules.
|
|
82
93
|
*
|
|
83
94
|
* Identity (design/182 §4.5, additive): a v1 record carries `principal` and nothing else — it stays
|
|
@@ -101,9 +112,9 @@ export interface RuleApprovalRecord {
|
|
|
101
112
|
* bargain, unchanged). `4`: every candidate carries its `behavior` (the settings import now carries
|
|
102
113
|
* the deny/ask lists too) — a `schema: 3` row is stale by the same equality gate.
|
|
103
114
|
*/
|
|
104
|
-
schema:
|
|
115
|
+
schema: typeof RULE_APPROVAL_RECORD_SCHEMA;
|
|
105
116
|
kind: RuleApprovalKind;
|
|
106
|
-
state:
|
|
117
|
+
state: RuleApprovalState;
|
|
107
118
|
candidates: RuleCandidate[];
|
|
108
119
|
/**
|
|
109
120
|
* The card's OPTION structure over `candidates` (design/375 §4.1). The flat candidate list stays
|
|
@@ -504,8 +515,10 @@ export declare function redeemRuleTicket(opts: {
|
|
|
504
515
|
owner?: RuleOwner;
|
|
505
516
|
deps: RuleConsentDeps;
|
|
506
517
|
}): Promise<RedeemResult>;
|
|
518
|
+
/** The settings layer names, spelled once: {@link ImportedSettingsLayer} is derived from this tuple. */
|
|
519
|
+
export declare const IMPORTED_SETTINGS_LAYERS: readonly ["userSettings", "projectSettings", "localSettings"];
|
|
507
520
|
/** The three user-editable settings layers this version reads. */
|
|
508
|
-
export type ImportedSettingsLayer =
|
|
521
|
+
export type ImportedSettingsLayer = (typeof IMPORTED_SETTINGS_LAYERS)[number];
|
|
509
522
|
/** One layer to read. `root` is the canonical directory a project-layer rule is scoped to — taken from
|
|
510
523
|
* where the FILE actually lives, since the local layer is not always at a repository root. */
|
|
511
524
|
export interface CcImportLayer {
|
|
@@ -528,7 +541,7 @@ export interface ImportPreview {
|
|
|
528
541
|
reason: string;
|
|
529
542
|
}>;
|
|
530
543
|
/**
|
|
531
|
-
* ALWAYS EMPTY in this version — a compatibility seat, kept on the wire shape so an existing reader
|
|
544
|
+
* ALWAYS EMPTY in this version — a compatibility seat (RL-1336-import-translated), kept on the wire shape so an existing reader
|
|
532
545
|
* keeps finding the key (removing a wire field would break readers for no gain).
|
|
533
546
|
*
|
|
534
547
|
* The seat once carried the entries whose MATCH FORM was rewritten on the way in (the space-star
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { escapeForDisclosure, hasUnrenderableCharacters, parseRuleText, ruleAdmitsCommand, ruleBreadthWarningsOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand, RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, isRuleBehavior, } from "./permission-rule-model.js";
|
|
2
|
+
import { escapeForDisclosure, hasUnrenderableCharacters, parseRuleText, ruleAdmitsCommand, ruleBreadthWarningsOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand, EDITED_RULE_BREADTH_WARNING_CODES, UNCOVERED_SEGMENT_REASONS, RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, isRuleBehavior, } from "./permission-rule-model.js";
|
|
3
3
|
import { effectiveOrThrow } from "./permission-rule-provider.js";
|
|
4
4
|
import { errText, isValidConsentScope, landRuleAdd, sameRuleOwner, sameScope, writerOf } from "./permission-rule-store.js";
|
|
5
|
+
const isUncoveredSegmentReason = (v) => UNCOVERED_SEGMENT_REASONS.some((word) => v === word);
|
|
6
|
+
export const RULE_APPROVAL_KINDS = Object.freeze(["card", "import", "starter"]);
|
|
7
|
+
export const RULE_APPROVAL_STATES = Object.freeze(["pending", "approved", "redeemed"]);
|
|
8
|
+
export const RULE_APPROVAL_RECORD_SCHEMA = 4;
|
|
5
9
|
export class InMemoryRuleApprovalRecordStore {
|
|
6
10
|
rows = new Map();
|
|
7
11
|
async get(id) {
|
|
8
12
|
const r = this.rows.get(id);
|
|
9
13
|
if (r === undefined)
|
|
10
14
|
return undefined;
|
|
11
|
-
if (r.schema !==
|
|
15
|
+
if (r.schema !== RULE_APPROVAL_RECORD_SCHEMA) {
|
|
12
16
|
return {
|
|
13
17
|
staleSchema: true,
|
|
14
18
|
id: r.id,
|
|
@@ -156,7 +160,7 @@ function approvalRecordDamageOf(rec) {
|
|
|
156
160
|
if (typeof row !== "object" ||
|
|
157
161
|
row === null ||
|
|
158
162
|
typeof row.segment !== "string" ||
|
|
159
|
-
(
|
|
163
|
+
!UNCOVERED_SEGMENT_REASONS.some((word) => row.reason === word)) {
|
|
160
164
|
return `card batch offer ${at} reason row ${ri} is not a segment/reason row from the closed reason set`;
|
|
161
165
|
}
|
|
162
166
|
}
|
|
@@ -195,7 +199,7 @@ function approvalRecordDamageOf(rec) {
|
|
|
195
199
|
return "the edited seat is not an object";
|
|
196
200
|
const w = rec.edited.warnings;
|
|
197
201
|
if (w !== undefined) {
|
|
198
|
-
if (!Array.isArray(w) || w.length !== 1 || (
|
|
202
|
+
if (!Array.isArray(w) || w.length !== 1 || !EDITED_RULE_BREADTH_WARNING_CODES.some((code) => w[0] === code)) {
|
|
199
203
|
return "the edited seat's warnings are not a single-code array from the closed breadth-code set";
|
|
200
204
|
}
|
|
201
205
|
}
|
|
@@ -238,7 +242,7 @@ export function ruleOffersOfRecord(rec) {
|
|
|
238
242
|
return true;
|
|
239
243
|
const segment = row.segment;
|
|
240
244
|
const reason = row.reason;
|
|
241
|
-
if (typeof segment !== "string" || (reason
|
|
245
|
+
if (typeof segment !== "string" || !isUncoveredSegmentReason(reason))
|
|
242
246
|
return true;
|
|
243
247
|
detail.push({ segment, reason });
|
|
244
248
|
}
|
|
@@ -348,7 +352,7 @@ export async function prepareCardApproval(opts) {
|
|
|
348
352
|
const record = {
|
|
349
353
|
id: mintId(opts.deps, "rar"),
|
|
350
354
|
...recordIdentityOf(owner),
|
|
351
|
-
schema:
|
|
355
|
+
schema: RULE_APPROVAL_RECORD_SCHEMA,
|
|
352
356
|
kind: "card",
|
|
353
357
|
state: "pending",
|
|
354
358
|
rev: 0,
|
|
@@ -667,6 +671,7 @@ async function applyRedemption(args) {
|
|
|
667
671
|
function originOfRecordKind(kind) {
|
|
668
672
|
return kind === "import" ? "imported-cc" : kind === "starter" ? "starter" : "user";
|
|
669
673
|
}
|
|
674
|
+
export const IMPORTED_SETTINGS_LAYERS = Object.freeze(["userSettings", "projectSettings", "localSettings"]);
|
|
670
675
|
const IMPORT_UNCOVERED_LAYERS = { flagSettings: "not-imported-v1", policySettings: "not-imported-v1" };
|
|
671
676
|
export async function prepareCcImport(opts) {
|
|
672
677
|
const owner = resolveCallerOwner(opts.principal, opts.owner, "prepareCcImport");
|
|
@@ -760,7 +765,7 @@ export async function prepareCcImport(opts) {
|
|
|
760
765
|
const record = {
|
|
761
766
|
id: mintId(opts.deps, "rar"),
|
|
762
767
|
...recordIdentityOf(owner),
|
|
763
|
-
schema:
|
|
768
|
+
schema: RULE_APPROVAL_RECORD_SCHEMA,
|
|
764
769
|
kind: "import",
|
|
765
770
|
state: "pending",
|
|
766
771
|
rev: 0,
|
|
@@ -794,7 +799,7 @@ export async function prepareStarterBatch(opts) {
|
|
|
794
799
|
const record = {
|
|
795
800
|
id: mintId(opts.deps, "rar"),
|
|
796
801
|
...recordIdentityOf(owner),
|
|
797
|
-
schema:
|
|
802
|
+
schema: RULE_APPROVAL_RECORD_SCHEMA,
|
|
798
803
|
kind: "starter",
|
|
799
804
|
state: "pending",
|
|
800
805
|
rev: 0,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* The two DIRECTIONS read a command differently, on purpose: an allow must cover the WHOLE call
|
|
16
16
|
* ({@link ruleAdmitsCommand} — one recorded yes clears exactly what it spells), while a deny/ask
|
|
17
17
|
* speaks about any PROGRAM RUN the call may perform ({@link programRunReachOf} — whole command or
|
|
18
|
-
* any segment of a chain, read
|
|
18
|
+
* any segment of a chain, read off the one shell lexer by `shell-run-candidates.ts`, and answering `unreadable`
|
|
19
19
|
* for what it cannot read rather than silence). Precedence across behaviors is the upstream one,
|
|
20
20
|
* deny > ask > allow ({@link RULE_BEHAVIOR_PRECEDENCE}, the one table every adjudication reads).
|
|
21
21
|
*
|
|
@@ -161,15 +161,43 @@ export declare function callPathFaceOf(call: ToolCallLike): DenyFace | undefined
|
|
|
161
161
|
* it, whatever the call's tool is called (a caller's tool declaring a read path target is on the read face too)? */
|
|
162
162
|
export declare function faceRowsSpeakFor(face: DenyFace | undefined): boolean;
|
|
163
163
|
/**
|
|
164
|
-
* The tools whose rule content NAMES THE VALUE of one parameter instead of a command or a path —
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
164
|
+
* The tools whose rule content NAMES THE VALUE of one parameter instead of a command or a path — ONE grammar (the name grammar,
|
|
165
|
+
* match `name`), one row per tool, the row declaring the tool's SPELLING of it:
|
|
166
|
+
* · BARE — the reference's `Skill(<name>)` / `Skill(<prefix>:*)`: `param` is the argument the rule's content is read against,
|
|
167
|
+
* `params` every scalar argument the tool carries (the parameter spelling `Tool(<param>:<pattern>)` those names used to reach
|
|
168
|
+
* is retired for such a tool, see {@link parseNameRuleContent}). The Skill tool's real schema keys are pinned equal to
|
|
169
|
+
* `params` (test/backlog1318).
|
|
170
|
+
* · KEYED — `words` present: the reference's `Artifact(action:<word>)`, the content carrying the argument's own key. `params`
|
|
171
|
+
* is that one key; `words` is the closed set of values the tool itself declares for it (the row points at the tool's own
|
|
172
|
+
* set, never a copy), and a rule names exactly one of them — no prefix form, no `*`.
|
|
173
|
+
* Whatever the spelling, a rule of this grammar says "the call's value of `param` is this value", read by the one subject
|
|
174
|
+
* reader ({@link nameRuleSubjectOf}), the one predicate ({@link nameRuleAdmits}), the one precedence walk
|
|
175
|
+
* ({@link adjudicatePersistedNameRules}) and the one offer ({@link suggestRuleForName}).
|
|
168
176
|
*/
|
|
169
177
|
export declare const NAME_RULE_FACES: Readonly<Record<string, {
|
|
170
178
|
readonly param: string;
|
|
171
179
|
readonly params: readonly string[];
|
|
180
|
+
readonly words?: readonly string[];
|
|
172
181
|
}>>;
|
|
182
|
+
/**
|
|
183
|
+
* Why a call of a KEYED value tool that names no value for the key cannot be read against that key's deny / ask rules, or
|
|
184
|
+
* undefined (a bare-spelling tool, or no value tool). Every judge reads a call's arguments as the tool reads them
|
|
185
|
+
* (`argsAsTheToolReads`: its argument preparation, then the schema's coercions); a call that STILL names no value for the key
|
|
186
|
+
* runs a value no argument shows — a default filled in inside the tool's execution (a caller's tool mounted under the keyed
|
|
187
|
+
* name, or answering to it, with no argument preparation of its own). Whether a rule by the key reaches such a call is
|
|
188
|
+
* unknown, so every reader REFUSES it on the key's deny or ask rule — a refusal, never an ask a person could approve past a
|
|
189
|
+
* deny, and no allow by the key reaches it. A tool whose reading always names the key (the built-in `Artifact`: an absent
|
|
190
|
+
* action reads as `publish`) never meets this. A bare-spelling tool's call that names nothing is reached by nothing: no
|
|
191
|
+
* default stands behind it.
|
|
192
|
+
*/
|
|
193
|
+
export declare function keyedValueAbsentReason(tool: string): string | undefined;
|
|
194
|
+
/**
|
|
195
|
+
* Does the name grammar read this content of this tool? Every content of a bare-spelling tool; on a keyed tool only content
|
|
196
|
+
* keyed by its own parameter (`action:…` on `Artifact`) — another key (`Artifact(url:…)`) is a rule on another argument,
|
|
197
|
+
* which the persisted grammar has no form for and the caller policy reads with its generic parameter lane. Undefined grammar
|
|
198
|
+
* ⇒ false.
|
|
199
|
+
*/
|
|
200
|
+
export declare function nameGrammarReads(tool: string, content: string): boolean;
|
|
173
201
|
/**
|
|
174
202
|
* The tools whose rule content names the HOST of the URL one parameter carries — the reference's `WebFetch(domain:<host>)`
|
|
175
203
|
* form, one row per tool: `param` is the URL argument the host is read from ({@link domainRuleSubjectOf}). Read by
|
|
@@ -182,8 +210,9 @@ export declare const DOMAIN_RULE_FACES: Readonly<Record<string, {
|
|
|
182
210
|
* Which tools a rule may name — the model's tool set is DERIVED, never a hand-kept list: the command rule word
|
|
183
211
|
* ({@link COMMAND_RULE_TOOL}) takes the COMMAND grammar — the other shell-face tools take none of their own, their command is
|
|
184
212
|
* judged by the `Bash(…)` rules ({@link laneToolNamesOf}); every tool the catalog declares a path target for (Read / Edit / Write /
|
|
185
|
-
* Glob / NotebookEdit / …, `pathTargetOf`) takes the PATH grammar; the tools of {@link NAME_RULE_FACES} take the NAME grammar
|
|
186
|
-
* the tools of {@link DOMAIN_RULE_FACES} the
|
|
213
|
+
* Glob / NotebookEdit / …, `pathTargetOf`) takes the PATH grammar; the tools of {@link NAME_RULE_FACES} take the NAME grammar
|
|
214
|
+
* (each in its row's spelling: bare `Skill(<name>)`, keyed `Artifact(action:<word>)`); the tools of {@link DOMAIN_RULE_FACES} the
|
|
215
|
+
* DOMAIN grammar.
|
|
187
216
|
* A rule on any other tool has no content this model can judge and is refused (`unsupported.tool`). The tool set therefore
|
|
188
217
|
* moves with the catalog's declarations, exactly as the protective fences' covered set does.
|
|
189
218
|
*/
|
|
@@ -206,6 +235,10 @@ export declare function nameRuleSubjectOf(toolName: string, args: unknown): stri
|
|
|
206
235
|
* name, as a policy's rule does. Every lane that judges a call by a rule's word reads it here: the policy compiler, the
|
|
207
236
|
* person's stored rows, the org partition, the allow lists. Empty ⇒ no grammar speaks for the call. */
|
|
208
237
|
export declare function laneToolNamesOf(req: Pick<import("./tool-policy.js").ToolCallRequest, "toolName" | "face">): readonly string[];
|
|
238
|
+
/** The match words a persisted rule is stored with, spelled once: {@link PersistedRuleMatch} is derived from this
|
|
239
|
+
* tuple (each form is documented there). A store backend that checks a stored row's `match` reads this value
|
|
240
|
+
* rather than keeping its own list. */
|
|
241
|
+
export declare const PERSISTED_RULE_MATCHES: readonly ["exact", "prefix", "wildcard", "subpath", "path", "name", "name_prefix", "host", "subdomain"];
|
|
209
242
|
/**
|
|
210
243
|
* Match forms, one family per grammar (`prefix` and `wildcard` are one predicate):
|
|
211
244
|
* · `exact` — admits the one string the rule spells.
|
|
@@ -232,7 +265,7 @@ export declare function laneToolNamesOf(req: Pick<import("./tool-policy.js").Too
|
|
|
232
265
|
* · `host` / `subdomain` — the DOMAIN grammar ({@link parseDomainRuleContent}): one exact host, or every
|
|
233
266
|
* host below one domain.
|
|
234
267
|
*/
|
|
235
|
-
export type PersistedRuleMatch =
|
|
268
|
+
export type PersistedRuleMatch = (typeof PERSISTED_RULE_MATCHES)[number];
|
|
236
269
|
/**
|
|
237
270
|
* Where a rule applies — design/382 §4.1, the THREE consent dimensions.
|
|
238
271
|
*
|
|
@@ -277,9 +310,12 @@ export interface RuleDot {
|
|
|
277
310
|
actor: string;
|
|
278
311
|
counter: number;
|
|
279
312
|
}
|
|
313
|
+
/** The add provenance words, spelled once: {@link RuleAddOrigin} is derived from this tuple, and the sync wire's and
|
|
314
|
+
* the session overlay's add checks read it. */
|
|
315
|
+
export declare const RULE_ADD_ORIGINS: readonly ["user", "imported-cc", "starter"];
|
|
280
316
|
/** Where one add came from. Stored PER ADD — never folded across dots (a folded provenance cannot
|
|
281
317
|
* follow a dot that is later deleted on its own). */
|
|
282
|
-
export type RuleAddOrigin =
|
|
318
|
+
export type RuleAddOrigin = (typeof RULE_ADD_ORIGINS)[number];
|
|
283
319
|
/** One add of one logical rule, carrying its own dot and its own provenance. */
|
|
284
320
|
export interface RuleAdd {
|
|
285
321
|
dot: RuleDot;
|
|
@@ -355,6 +391,21 @@ export interface ParsedRule {
|
|
|
355
391
|
/** Upper bound on a rule text. Rules are human-authored one-liners; anything longer is a mistake or an
|
|
356
392
|
* attempt to make the store unreadable. */
|
|
357
393
|
export declare const MAX_RULE_TEXT_CHARS = 512;
|
|
394
|
+
/**
|
|
395
|
+
* The most code points of a rule's ORIGINAL text a notice's machine-read slot carries: twice the grammar's own bound. A text past
|
|
396
|
+
* {@link MAX_RULE_TEXT_CHARS} reads as no rule whatever follows its first characters, so the slot keeps room to show it as far as a person
|
|
397
|
+
* needs to recognise it and no further; every text a rule can be is carried whole. Not exported from the package root: a consumer reads
|
|
398
|
+
* the slot and never judges its length (it is not a relay that must refuse what the engine did not mint).
|
|
399
|
+
*/
|
|
400
|
+
export declare const NOTICE_RULE_TEXT_MAX: number;
|
|
401
|
+
/**
|
|
402
|
+
* A rule's (or allow-list entry's) text as a notice's machine-read slot carries it: the original string, untouched, when it is at most
|
|
403
|
+
* {@link NOTICE_RULE_TEXT_MAX} code points; a longer one is cut to that many and ends in the engine's one truncation mark
|
|
404
|
+
* ({@link INLINE_TRUNCATION_MARK}), so the longest slot is `NOTICE_RULE_TEXT_MAX + INLINE_TRUNCATION_MARK_CODEPOINTS` (1 025) code points.
|
|
405
|
+
* The ONE reading both `permission.allowed_tool_not_granted` (`detail.entries[].entry`) and `permission.rule_row_dropped`
|
|
406
|
+
* (`detail.rows[].rule`) hand their rule text over through — what spells it for a person is the sentence, never this slot.
|
|
407
|
+
*/
|
|
408
|
+
export declare function noticeRuleText(text: string): string;
|
|
358
409
|
/**
|
|
359
410
|
* Interpreter names whose PREFIX form is refused everywhere a rule can enter the store (minting,
|
|
360
411
|
* import, file reload, every backend). `Bash(node:*)` reads as one narrow-looking rule and means "run
|
|
@@ -418,7 +469,7 @@ export declare const BARE_INTERPRETER_NAMES: ReadonlySet<string>;
|
|
|
418
469
|
*
|
|
419
470
|
* Refusing the keyword puts the whole command outside the ALLOW lane, which is the honest answer: no rule
|
|
420
471
|
* can be minted for it and no allow matches it, so it asks. The DENY/ASK direction reads these shapes
|
|
421
|
-
* with its
|
|
472
|
+
* with its run-candidate view (`shell-run-candidates.ts`: a prefixing keyword is stripped so `do curl evil.example` runs
|
|
422
473
|
* `curl`; a `for`/`case` header, whose commands need a syntax tree, is `unreadable` and asks).
|
|
423
474
|
*
|
|
424
475
|
* `!` is here for the same reason with a sharper edge: it is a keyword that PREFIXES a real command, so
|
|
@@ -736,11 +787,14 @@ export declare function programRunReachOf(rule: Pick<PersistedRule, "match" | "c
|
|
|
736
787
|
/** The boolean face of {@link programRunReachOf}: `reached` only. The shadow view and the pins that
|
|
737
788
|
* assert a reach read this; a lane that must fail closed on `unreadable` reads the outcome. */
|
|
738
789
|
export declare function ruleReachesProgramRun(rule: Pick<PersistedRule, "match" | "command">, command: string): boolean;
|
|
790
|
+
/** The breadth-warning codes, spelled once: {@link EditedRuleBreadthWarning}'s `code` is derived from this tuple,
|
|
791
|
+
* and a consent record's `edited.warnings` stores exactly these words (its readback refuses any other). */
|
|
792
|
+
export declare const EDITED_RULE_BREADTH_WARNING_CODES: readonly ["broad_prefix", "compound_prefix"];
|
|
739
793
|
/** design/382 §3.3-5 — one BREADTH note about a rule a person is about to persist: which wide shape
|
|
740
794
|
* it has, said in words a surface can show beside the input line. A warning is never a refusal —
|
|
741
795
|
* the closed refusal set is untouched — and never adjudication input. */
|
|
742
796
|
export interface EditedRuleBreadthWarning {
|
|
743
|
-
readonly code:
|
|
797
|
+
readonly code: (typeof EDITED_RULE_BREADTH_WARNING_CODES)[number];
|
|
744
798
|
/** The baseline sentence (a surface may reword it, never invert it — the same contract as the
|
|
745
799
|
* §3.5 reason baselines). */
|
|
746
800
|
readonly message: string;
|
|
@@ -956,7 +1010,9 @@ export declare function commandCoverageOf(rules: readonly PersistedRule[], call:
|
|
|
956
1010
|
};
|
|
957
1011
|
/** The answer of {@link adjudicatePersistedRules}: which behavior decided, and the rule(s) it stands
|
|
958
1012
|
* on — a one-element set for a deny/ask (the first table-order rule of the deciding behavior that
|
|
959
|
-
* reaches the call
|
|
1013
|
+
* reaches the call; for a keyed tool's call naming no value for its key, a DENY standing on the first deny — else ask —
|
|
1014
|
+
* row of that key, which the call cannot be read against: {@link adjudicatePersistedNameRules}) and the allow
|
|
1015
|
+
* direction's coverage SET ({@link findAdmittingRule}). */
|
|
960
1016
|
export interface PersistedRuleVerdict {
|
|
961
1017
|
readonly behavior: RuleBehavior;
|
|
962
1018
|
readonly rules: readonly PersistedRule[];
|
|
@@ -1032,8 +1088,11 @@ export declare function adjudicatePersistedPathRules(rules: readonly PersistedRu
|
|
|
1032
1088
|
* ({@link NAME_RULE_FACES}), judged on the one string the call names (`nameRuleSubjectOf`). A rule of the tool speaks for the call
|
|
1033
1089
|
* when it is live, of this behavior, in scope ({@link eligiblePersisted}, the one predicate) and {@link nameRuleAdmits} the name; the
|
|
1034
1090
|
* first behavior in {@link RULE_BEHAVIORS_BY_PRECEDENCE} whose rules speak decides, the first table-order rule of it is the answer.
|
|
1035
|
-
* Every behavior is judged with the one predicate — a name has no "program run" the tightening direction could read wider
|
|
1036
|
-
*
|
|
1091
|
+
* Every behavior is judged with the one predicate — a name has no "program run" the tightening direction could read wider.
|
|
1092
|
+
* A call that names nothing (read as the tool reads it): on a bare-spelling tool it is reached by no rule; on a KEYED tool it
|
|
1093
|
+
* cannot be read against the key's rules ({@link keyedValueAbsentReason}) and is REFUSED — a deny verdict standing on the
|
|
1094
|
+
* first eligible deny row of the key, else its first ask row (an ask a person could approve would let a deny be approved
|
|
1095
|
+
* past) — and no allow row reaches it.
|
|
1037
1096
|
*/
|
|
1038
1097
|
export declare function adjudicatePersistedNameRules(rules: readonly PersistedRule[], call: {
|
|
1039
1098
|
tool: string;
|
|
@@ -1138,6 +1197,11 @@ export interface SegmentRuleSuggestion {
|
|
|
1138
1197
|
* show a reader something other than what they authorize. */
|
|
1139
1198
|
readonly segment: string;
|
|
1140
1199
|
}
|
|
1200
|
+
/** The batch member discriminants as a value: the `kind` words of {@link RuleOfferBatchMember}, both directions
|
|
1201
|
+
* fenced at compile time ({@link RuleOfferBatchMemberKindsCoverEveryArm} / {@link RuleOfferBatchMemberArmsCoverEveryKind}).
|
|
1202
|
+
* A consumer applying the degrade arm below (a member whose `kind` is outside this set drops the whole batch offer)
|
|
1203
|
+
* reads this value rather than keeping its own list. */
|
|
1204
|
+
export declare const RULE_OFFER_BATCH_MEMBER_KINDS: readonly ["command", "directoryRead"];
|
|
1141
1205
|
/**
|
|
1142
1206
|
* design/382 §2.3 (B3, BREAKING) — one member of a batch offer, a DISCRIMINATED union keyed by
|
|
1143
1207
|
* `kind`:
|
|
@@ -1166,6 +1230,10 @@ export type RuleOfferBatchMember = (SegmentRuleSuggestion & {
|
|
|
1166
1230
|
/** The (folded) cd segment this authorization came from — rendering context, never adjudication input. */
|
|
1167
1231
|
readonly segment: string;
|
|
1168
1232
|
};
|
|
1233
|
+
/** Compile fence: an arm whose `kind` is not in {@link RULE_OFFER_BATCH_MEMBER_KINDS} turns the argument into that word. */
|
|
1234
|
+
export type RuleOfferBatchMemberKindsCoverEveryArm = AssertAllKeysHandled<Exclude<RuleOfferBatchMember["kind"], (typeof RULE_OFFER_BATCH_MEMBER_KINDS)[number]>>;
|
|
1235
|
+
/** Compile fence, the other direction: a word in {@link RULE_OFFER_BATCH_MEMBER_KINDS} that no arm carries. */
|
|
1236
|
+
export type RuleOfferBatchMemberArmsCoverEveryKind = AssertAllKeysHandled<Exclude<(typeof RULE_OFFER_BATCH_MEMBER_KINDS)[number], RuleOfferBatchMember["kind"]>>;
|
|
1169
1237
|
/**
|
|
1170
1238
|
* design/375 §3.1 — ONE "don't ask again" option on an approval card. A CLOSED discriminated union
|
|
1171
1239
|
* (`kind`); consumers parse it per element, and an element whose `kind` they do not know is dropped
|
|
@@ -1229,6 +1297,10 @@ export type RuleOffer = {
|
|
|
1229
1297
|
*/
|
|
1230
1298
|
readonly uncoveredDetail?: readonly UncoveredSegmentDetail[];
|
|
1231
1299
|
};
|
|
1300
|
+
/** The uncovered-segment reason words, spelled once: {@link UncoveredSegmentDetail}'s `reason` is derived from this
|
|
1301
|
+
* tuple, {@link UNCOVERED_SEGMENT_REASON_BASELINE} is keyed by it, and a consent record's reason rows are read
|
|
1302
|
+
* against it. */
|
|
1303
|
+
export declare const UNCOVERED_SEGMENT_REASONS: readonly ["redirection", "no_rule_form", "cap_overflow"];
|
|
1232
1304
|
/** design/382 §3.5 — the closed reason set for one uncovered segment of a batch offer.
|
|
1233
1305
|
*
|
|
1234
1306
|
* · `"redirection"` — the segment carries a shell redirection: refused by discipline (#490 修①,
|
|
@@ -1251,7 +1323,7 @@ export interface UncoveredSegmentDetail {
|
|
|
1251
1323
|
/** The folded, trimmed segment text — raw post-rewrite command bytes, same contract as
|
|
1252
1324
|
* {@link SegmentRuleSuggestion.segment}. */
|
|
1253
1325
|
readonly segment: string;
|
|
1254
|
-
readonly reason:
|
|
1326
|
+
readonly reason: (typeof UNCOVERED_SEGMENT_REASONS)[number];
|
|
1255
1327
|
}
|
|
1256
1328
|
/**
|
|
1257
1329
|
* design/382 §3.5-2 — the BASELINE "why + outlet" sentence per uncovered-segment reason: the
|
|
@@ -3,13 +3,15 @@ import { shellWordsOf } from "../shell/shell-words.js";
|
|
|
3
3
|
import { parsePermissionRule } from "./permission-rule-syntax.js";
|
|
4
4
|
import { compileReadDeny } from "../tools/fs/read-deny.js";
|
|
5
5
|
import { carriesShellRedirection, parseLeadingCommandName, splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
|
|
6
|
-
import { inlineUntrusted } from "./untrusted-text.js";
|
|
6
|
+
import { INLINE_TRUNCATION_MARK, inlineUntrusted } from "./untrusted-text.js";
|
|
7
7
|
import { isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
|
|
8
8
|
import { heldValues } from "./tool-name-readings.js";
|
|
9
9
|
import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
|
|
10
10
|
import { bindPathRules, judgeRuleRows, rowsWhereTheyLead } from "./path-rule-binding.js";
|
|
11
|
-
import { readShellCommand } from "./shell-
|
|
11
|
+
import { readShellCommand } from "./shell-run-candidates.js";
|
|
12
12
|
import { lineDescriptorsOfText, readOnlyLineObjection, readOnlyShellVerdict } from "./read-only-shell.js";
|
|
13
|
+
import { ARTIFACT_ACTIONS } from "./artifact-host.js";
|
|
14
|
+
import { ARTIFACT_TOOL_NAME } from "./tool-catalog-entries.js";
|
|
13
15
|
export const RULE_BEHAVIORS = Object.freeze(["deny", "ask", "allow"]);
|
|
14
16
|
const RULE_BEHAVIOR_SET = new Set(RULE_BEHAVIORS);
|
|
15
17
|
export function isRuleBehavior(v) {
|
|
@@ -43,7 +45,25 @@ export function faceRowsSpeakFor(face) {
|
|
|
43
45
|
}
|
|
44
46
|
export const NAME_RULE_FACES = Object.freeze({
|
|
45
47
|
Skill: Object.freeze({ param: "skill", params: Object.freeze(["skill", "args"]) }),
|
|
48
|
+
[ARTIFACT_TOOL_NAME]: Object.freeze({ param: "action", params: Object.freeze(["action"]), words: ARTIFACT_ACTIONS }),
|
|
46
49
|
});
|
|
50
|
+
function keyedValueParamOf(tool) {
|
|
51
|
+
const face = Object.hasOwn(NAME_RULE_FACES, tool) ? NAME_RULE_FACES[tool] : undefined;
|
|
52
|
+
return face?.words !== undefined ? face.param : undefined;
|
|
53
|
+
}
|
|
54
|
+
export function keyedValueAbsentReason(tool) {
|
|
55
|
+
const key = keyedValueParamOf(tool);
|
|
56
|
+
return key === undefined ? undefined : `the ${tool} call names no ${key} as the tool reads it, so the ${key} it runs is not one its arguments show — a rule by ${key} cannot be read against it`;
|
|
57
|
+
}
|
|
58
|
+
export function nameGrammarReads(tool, content) {
|
|
59
|
+
if (ruleToolGrammarOf(tool) !== "name")
|
|
60
|
+
return false;
|
|
61
|
+
const key = keyedValueParamOf(tool);
|
|
62
|
+
if (key === undefined)
|
|
63
|
+
return true;
|
|
64
|
+
const colon = content.indexOf(":");
|
|
65
|
+
return colon > 0 && content.slice(0, colon).trim() === key;
|
|
66
|
+
}
|
|
47
67
|
export const DOMAIN_RULE_FACES = Object.freeze({
|
|
48
68
|
WebFetch: Object.freeze({ param: "url" }),
|
|
49
69
|
});
|
|
@@ -91,10 +111,26 @@ function ruleWordOf(name) {
|
|
|
91
111
|
return COMMAND_RULE_TOOL;
|
|
92
112
|
return ruleToolGrammarOf(name) !== undefined ? name : undefined;
|
|
93
113
|
}
|
|
114
|
+
export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "subpath", "path", "name", "name_prefix", "host", "subdomain"]);
|
|
94
115
|
function isCommandMatch(match) {
|
|
95
116
|
return match === "exact" || match === "prefix" || match === "wildcard";
|
|
96
117
|
}
|
|
118
|
+
export const RULE_ADD_ORIGINS = Object.freeze(["user", "imported-cc", "starter"]);
|
|
97
119
|
export const MAX_RULE_TEXT_CHARS = 512;
|
|
120
|
+
export const NOTICE_RULE_TEXT_MAX = 2 * MAX_RULE_TEXT_CHARS;
|
|
121
|
+
export function noticeRuleText(text) {
|
|
122
|
+
if (text.length <= NOTICE_RULE_TEXT_MAX)
|
|
123
|
+
return text;
|
|
124
|
+
let kept = 0;
|
|
125
|
+
let end = 0;
|
|
126
|
+
for (const ch of text) {
|
|
127
|
+
if (kept === NOTICE_RULE_TEXT_MAX)
|
|
128
|
+
return `${text.slice(0, end)}${INLINE_TRUNCATION_MARK}`;
|
|
129
|
+
end += ch.length;
|
|
130
|
+
kept += 1;
|
|
131
|
+
}
|
|
132
|
+
return text;
|
|
133
|
+
}
|
|
98
134
|
export const BARE_INTERPRETER_NAMES = new Set([
|
|
99
135
|
"node", "deno", "bun", "python", "python2", "python3", "perl", "ruby", "php", "osascript",
|
|
100
136
|
"java", "dotnet", "mono", "lua", "luajit", "julia", "Rscript", "tclsh", "groovy", "scala",
|
|
@@ -243,6 +279,13 @@ function ruleLaneShapeOf(command, reading) {
|
|
|
243
279
|
}
|
|
244
280
|
const CONTROL_CHARS_RE = /[\u0000-\u0008\u000A-\u001F\u007F-\u009F\p{Cf}\u2028\u2029]/u;
|
|
245
281
|
const CONTROL_CHARS_GLOBAL_RE = new RegExp(CONTROL_CHARS_RE.source, "gu");
|
|
282
|
+
const RULE_SPELLING_REJECT_BYTES = "<>";
|
|
283
|
+
const RULE_SPELLING_REJECT_RE = new RegExp(`[${RULE_SPELLING_REJECT_BYTES}]`);
|
|
284
|
+
function valueRuleFormsSentence() {
|
|
285
|
+
return Object.entries(NAME_RULE_FACES)
|
|
286
|
+
.map(([tool, face]) => (face.words !== undefined ? `${tool}(${face.param}:<${face.param}>) rules` : `${tool}(<${face.param}>) rules`))
|
|
287
|
+
.join(", ");
|
|
288
|
+
}
|
|
246
289
|
const DISCLOSED_RULE_TEXT_MAX_CHARS = 120;
|
|
247
290
|
export function escapeForDisclosure(value) {
|
|
248
291
|
let text;
|
|
@@ -293,13 +336,16 @@ export function parseRuleText(text, behavior) {
|
|
|
293
336
|
}
|
|
294
337
|
if (parsed.toolName !== COMMAND_RULE_TOOL) {
|
|
295
338
|
const grammar = ruleToolGrammarOf(parsed.toolName);
|
|
339
|
+
if (grammar === undefined) {
|
|
340
|
+
return reject("unsupported.tool", `only Bash command rules, rules on path-targeting tools (Read, Edit, Write, …), ${valueRuleFormsSentence()} and WebFetch domain rules have a rule form in this version (got "${parsed.toolName}")`);
|
|
341
|
+
}
|
|
342
|
+
if (!tighten && RULE_SPELLING_REJECT_RE.test(parsed.ruleContent)) {
|
|
343
|
+
return reject("invalid.grammar", `"${text}" carries a shell redirection byte (${[...RULE_SPELLING_REJECT_BYTES].map((b) => `\`${b}\``).join(" or ")}) — no path, name or host allow spells one, and the grant would not read as what it looks like`);
|
|
344
|
+
}
|
|
296
345
|
if (grammar === "name")
|
|
297
346
|
return parseNameRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
|
|
298
347
|
if (grammar === "domain")
|
|
299
348
|
return parseDomainRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
|
|
300
|
-
if (grammar !== "path") {
|
|
301
|
-
return reject("unsupported.tool", `only Bash command rules, rules on path-targeting tools (Read, Edit, Write, …), Skill name rules and WebFetch domain rules have a rule form in this version (got "${parsed.toolName}")`);
|
|
302
|
-
}
|
|
303
349
|
return parsePathRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
|
|
304
350
|
}
|
|
305
351
|
const content = parsed.ruleContent;
|
|
@@ -353,14 +399,18 @@ export function parseRuleText(text, behavior) {
|
|
|
353
399
|
export function formatRuleText(command, match, tool) {
|
|
354
400
|
if (match === "subpath")
|
|
355
401
|
return `${tool}(//${command.startsWith("/") ? command.slice(1) : command}/**)`;
|
|
356
|
-
if (match === "path"
|
|
402
|
+
if (match === "path")
|
|
357
403
|
return `${tool}(${command})`;
|
|
404
|
+
if (match === "name")
|
|
405
|
+
return `${tool}(${keyedValueParamOf(tool) !== undefined ? `${keyedValueParamOf(tool)}:` : ""}${command})`;
|
|
358
406
|
if (match === "host" || match === "subdomain")
|
|
359
407
|
return `${tool}(domain:${match === "subdomain" ? "*." : ""}${command})`;
|
|
360
408
|
return `${tool}(${command}${match === "prefix" || match === "name_prefix" ? ":*" : match === "wildcard" ? " *" : ""})`;
|
|
361
409
|
}
|
|
362
410
|
function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = true) {
|
|
363
411
|
const face = NAME_RULE_FACES[tool];
|
|
412
|
+
if (face?.words !== undefined)
|
|
413
|
+
return parseKeyedValueRuleContent(text, tool, content, behavior, { param: face.param, words: face.words });
|
|
364
414
|
const bare = content.startsWith("/") ? content.slice(1) : content;
|
|
365
415
|
const colon = bare.indexOf(":");
|
|
366
416
|
if (face !== undefined && colon > 0 && face.params.includes(bare.slice(0, colon).trim())) {
|
|
@@ -389,6 +439,24 @@ function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = t
|
|
|
389
439
|
}
|
|
390
440
|
return { rule };
|
|
391
441
|
}
|
|
442
|
+
function parseKeyedValueRuleContent(text, tool, content, behavior, face) {
|
|
443
|
+
const vocabulary = face.words.join(", ");
|
|
444
|
+
const colon = content.indexOf(":");
|
|
445
|
+
if (colon <= 0 || content.slice(0, colon).trim() !== face.param) {
|
|
446
|
+
return reject("invalid.grammar", `${tool} rules name the ${face.param}: ${tool}(${face.param}:<${face.param}>) with one of ${vocabulary} ("${text}") — a bare value, or a rule on another argument, has no rule form here`);
|
|
447
|
+
}
|
|
448
|
+
const word = content.slice(colon + 1).trim();
|
|
449
|
+
if (word === "") {
|
|
450
|
+
return reject("invalid.grammar", `the ${face.param} in "${text}" is empty — ${tool} rules name the ${face.param} exactly as the call carries it`);
|
|
451
|
+
}
|
|
452
|
+
if (word.includes("*")) {
|
|
453
|
+
return reject("unsupported.wildcard", `a \`*\` in the ${face.param} of "${text}" has no rule form — ${tool} rules name one ${face.param} (${vocabulary}); the whole tool is the bare ${tool} entry`);
|
|
454
|
+
}
|
|
455
|
+
if (!face.words.includes(word)) {
|
|
456
|
+
return reject("invalid.grammar", `"${word}" is not one of the ${face.param} values ${tool} declares (${vocabulary}) — the rule would never reach a call`);
|
|
457
|
+
}
|
|
458
|
+
return { rule: { behavior, rule: formatRuleText(word, "name", tool), tool, match: "name", command: word } };
|
|
459
|
+
}
|
|
392
460
|
export function nameRuleAdmits(rule, name) {
|
|
393
461
|
if (rule.match === "name")
|
|
394
462
|
return name === rule.command;
|
|
@@ -745,6 +813,7 @@ function admitsUnder(rule, command, reading) {
|
|
|
745
813
|
return false;
|
|
746
814
|
return folded === rule.command || folded.startsWith(rule.command + " ");
|
|
747
815
|
}
|
|
816
|
+
export const EDITED_RULE_BREADTH_WARNING_CODES = Object.freeze(["broad_prefix", "compound_prefix"]);
|
|
748
817
|
export function ruleBreadthWarningsOf(rule) {
|
|
749
818
|
if (rule.match !== "prefix" && rule.match !== "wildcard")
|
|
750
819
|
return [];
|
|
@@ -802,7 +871,7 @@ export function directoryIdentityOf(spelling, bases) {
|
|
|
802
871
|
const r = pathIdentityOf(spelling, pathFamilyOf(bases), bases.root ?? bases.cwd);
|
|
803
872
|
return r.ok ? r.identity : undefined;
|
|
804
873
|
}
|
|
805
|
-
const CD_TARGET_REJECT_CHARS =
|
|
874
|
+
const CD_TARGET_REJECT_CHARS = new RegExp(String.raw `[*?[\]{}$` + "`" + String.raw `\\'"\n\r` + RULE_SPELLING_REJECT_BYTES + "]");
|
|
806
875
|
function cdSegmentDirectoryOf(segment, opts) {
|
|
807
876
|
if (/[^\S \t]/.test(segment))
|
|
808
877
|
return undefined;
|
|
@@ -1033,8 +1102,16 @@ export function adjudicatePersistedPathRules(rules, call, views, anchorsOf, lead
|
|
|
1033
1102
|
return undefined;
|
|
1034
1103
|
}
|
|
1035
1104
|
export function adjudicatePersistedNameRules(rules, call) {
|
|
1036
|
-
if (call.name === undefined)
|
|
1105
|
+
if (call.name === undefined) {
|
|
1106
|
+
if (keyedValueAbsentReason(call.tool) === undefined)
|
|
1107
|
+
return undefined;
|
|
1108
|
+
for (const behavior of ["deny", "ask"]) {
|
|
1109
|
+
const row = rules.find((rule) => rule.match === "name" && eligiblePersisted(rule, call, behavior));
|
|
1110
|
+
if (row !== undefined)
|
|
1111
|
+
return { behavior: "deny", rules: [row] };
|
|
1112
|
+
}
|
|
1037
1113
|
return undefined;
|
|
1114
|
+
}
|
|
1038
1115
|
for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
|
|
1039
1116
|
for (const rule of rules) {
|
|
1040
1117
|
if (eligiblePersisted(rule, call, behavior) && nameRuleAdmits(rule, call.name))
|
|
@@ -1085,6 +1162,8 @@ export function segmentCoverageOf(command, rules, call) {
|
|
|
1085
1162
|
};
|
|
1086
1163
|
});
|
|
1087
1164
|
}
|
|
1165
|
+
export const RULE_OFFER_BATCH_MEMBER_KINDS = Object.freeze(["command", "directoryRead"]);
|
|
1166
|
+
export const UNCOVERED_SEGMENT_REASONS = Object.freeze(["redirection", "no_rule_form", "cap_overflow"]);
|
|
1088
1167
|
export const UNCOVERED_SEGMENT_REASON_BASELINE = {
|
|
1089
1168
|
redirection: "This part carries a redirection, so it is approved per use — no rule can cover this spelling. If you trust its redirection-free form, a rule can be written for that form (the card's edit row, or settings).",
|
|
1090
1169
|
no_rule_form: "No rule form exists for this part; it can be approved per use.",
|