@sema-agent/core 5.19.0 → 5.21.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 +102 -4
- package/dist/agents/agent-definition.d.ts +7 -0
- package/dist/agents/agent-transcript-tool.d.ts +30 -0
- package/dist/agents/builtin-agents.d.ts +60 -0
- package/dist/agents/cascade.d.ts +107 -0
- package/dist/agents/cumulative-stats.d.ts +56 -0
- package/dist/agents/observer.d.ts +162 -0
- package/dist/agents/peer-admission.d.ts +95 -0
- package/dist/agents/repair-loop.d.ts +226 -0
- package/dist/agents/retain-ledger.d.ts +141 -0
- package/dist/agents/roster-store.d.ts +105 -0
- package/dist/agents/roster-store.js +3 -0
- package/dist/agents/send-message-tool.d.ts +92 -0
- package/dist/agents/session-util.d.ts +5 -0
- package/dist/agents/subagent-steps.d.ts +66 -0
- package/dist/agents/subagent.d.ts +600 -0
- package/dist/agents/suspend-guard.d.ts +29 -0
- package/dist/agents/teacher.d.ts +75 -0
- package/dist/agents/team.d.ts +120 -1
- package/dist/agents/tool-filter.d.ts +34 -0
- package/dist/agents/verify.d.ts +198 -0
- package/dist/bench/metrics.d.ts +455 -0
- package/dist/brain/anthropic.d.ts +30 -0
- package/dist/brain/circuit-breaker.d.ts +33 -0
- package/dist/brain/circuit-breaker.js +14 -3
- package/dist/brain/context-overflow.d.ts +60 -3
- package/dist/brain/degrading.d.ts +67 -0
- package/dist/brain/errors.d.ts +42 -0
- package/dist/brain/failover.d.ts +15 -0
- package/dist/brain/media-degrade.d.ts +39 -0
- package/dist/brain/model-presets.d.ts +31 -0
- package/dist/brain/open-responses.d.ts +19 -0
- package/dist/brain/openai.d.ts +46 -0
- package/dist/brain/reasoning.d.ts +106 -1
- package/dist/brain/repetition.d.ts +83 -0
- package/dist/brain/request-params.d.ts +56 -0
- package/dist/brain/retry.d.ts +46 -0
- package/dist/brain/routing.d.ts +12 -0
- package/dist/brain/status-sink.d.ts +30 -1
- package/dist/brain/stream-engine.d.ts +147 -0
- package/dist/brain/stream-shared.d.ts +34 -0
- package/dist/brain/terminal-cause.d.ts +31 -0
- package/dist/brain/timeout.d.ts +108 -0
- package/dist/brain/timeout.js +11 -0
- package/dist/brain/tool-call-id.d.ts +20 -0
- package/dist/brain/tool-call-repair.d.ts +13 -0
- package/dist/config/catalog.d.ts +47 -0
- package/dist/config/defaults.d.ts +33 -0
- package/dist/core/a2a-task-state.d.ts +53 -0
- package/dist/core/a2a.d.ts +51 -0
- package/dist/core/arg-summary.d.ts +62 -0
- package/dist/core/ask-question.d.ts +272 -2
- package/dist/core/auto-compaction.d.ts +467 -0
- package/dist/core/auto-compaction.js +20 -6
- package/dist/core/auto-mode-prompt-assets.d.ts +8 -0
- package/dist/core/auto-mode-prompt.d.ts +27 -0
- package/dist/core/auto-mode.d.ts +54 -3
- package/dist/core/auto-promote.d.ts +100 -0
- package/dist/core/background-agent-store.d.ts +293 -0
- package/dist/core/background-agent-store.js +5 -0
- package/dist/core/background-shell.d.ts +110 -0
- package/dist/core/cache-break-detector.d.ts +34 -0
- package/dist/core/canonical-json.d.ts +57 -0
- package/dist/core/checkpoint-store.d.ts +1574 -16
- package/dist/core/compliance.d.ts +30 -0
- package/dist/core/consolidate-scope.d.ts +75 -0
- package/dist/core/context-edit.d.ts +99 -0
- package/dist/core/context-guard.d.ts +46 -0
- package/dist/core/exec-gate.d.ts +44 -0
- package/dist/core/exec-output-tail.d.ts +61 -0
- package/dist/core/file-snapshot-store.d.ts +104 -0
- package/dist/core/fs-write-gate-policy.d.ts +36 -0
- package/dist/core/git-worktree-env.d.ts +31 -0
- package/dist/core/governance-codes.d.ts +19 -0
- package/dist/core/hooks.d.ts +609 -1
- package/dist/core/human-input-projection.d.ts +37 -0
- package/dist/core/human-input-projection.js +13 -0
- package/dist/core/image-downsample.d.ts +74 -0
- package/dist/core/locked-config.d.ts +37 -0
- package/dist/core/lsp-diagnostics.d.ts +77 -0
- package/dist/core/lsp-protocol.d.ts +29 -0
- package/dist/core/lsp-session.d.ts +60 -1
- package/dist/core/lsp.d.ts +150 -1
- package/dist/core/mailbox-store.d.ts +57 -0
- package/dist/core/mailbox-store.js +2 -0
- package/dist/core/mcp.d.ts +385 -0
- package/dist/core/mcp.js +58 -11
- package/dist/core/media-byte-cap.d.ts +21 -0
- package/dist/core/memory-admission.d.ts +71 -0
- package/dist/core/memory-engine/content-origin.d.ts +64 -0
- package/dist/core/memory-engine/data-plane.d.ts +43 -0
- package/dist/core/memory-engine/dual-root.d.ts +24 -0
- package/dist/core/memory-engine/engine.d.ts +230 -0
- package/dist/core/memory-engine/engine.js +103 -35
- package/dist/core/memory-engine/file-backend.d.ts +155 -0
- package/dist/core/memory-engine/frontmatter.d.ts +16 -0
- package/dist/core/memory-engine/layout.d.ts +217 -0
- package/dist/core/memory-engine/memory-backend-contract.d.ts +12 -0
- package/dist/core/memory-engine/migrate.d.ts +9 -0
- package/dist/core/memory-engine/scan.d.ts +12 -0
- package/dist/core/memory-engine/scope-contract.d.ts +61 -1
- package/dist/core/memory-engine/sync-client.d.ts +87 -0
- package/dist/core/memory-engine/sync.d.ts +60 -0
- package/dist/core/memory-engine/tools.d.ts +41 -0
- package/dist/core/memory-engine/types.d.ts +188 -0
- package/dist/core/memory-recall.d.ts +141 -0
- package/dist/core/memory-vector.d.ts +20 -0
- package/dist/core/memory.d.ts +458 -0
- package/dist/core/message-utils.d.ts +6 -0
- package/dist/core/oracle-isolation.d.ts +69 -0
- package/dist/core/permission-rule-consent.d.ts +138 -0
- package/dist/core/permission-rule-model.d.ts +122 -0
- package/dist/core/permission-rule-store.d.ts +119 -3
- package/dist/core/permission-rules.d.ts +87 -1
- package/dist/core/present-plan-tool.d.ts +20 -0
- package/dist/core/pricing.d.ts +26 -0
- package/dist/core/property-harness.d.ts +86 -0
- package/dist/core/protocol-naming.d.ts +38 -0
- package/dist/core/protocol-table.d.ts +61 -0
- package/dist/core/push-queue.d.ts +1 -0
- package/dist/core/remote-env.d.ts +383 -1
- package/dist/core/retention-policy.d.ts +40 -0
- package/dist/core/retention-policy.js +21 -0
- package/dist/core/retention.d.ts +51 -0
- package/dist/core/roles.d.ts +59 -0
- package/dist/core/runner/active-skill-scope.d.ts +34 -0
- package/dist/core/runner/assemble-result.d.ts +134 -0
- package/dist/core/runner/compaction-call-options.d.ts +97 -1
- package/dist/core/runner/grounding-signal.d.ts +10 -0
- package/dist/core/runner/image.d.ts +17 -0
- package/dist/core/runner/image.js +29 -15
- package/dist/core/runner/memory-consolidation.d.ts +60 -0
- package/dist/core/runner/prepare-memory.d.ts +59 -0
- package/dist/core/runner/prepare-task.d.ts +1011 -2
- package/dist/core/runner/prepare-task.js +51 -14
- package/dist/core/runner/prompt-suggestions.d.ts +7 -0
- package/dist/core/runner/runtask.d.ts +304 -3
- package/dist/core/runner/runtask.js +17 -2
- package/dist/core/runner/session-file-state-replay.d.ts +49 -0
- package/dist/core/runner/session-rule-policy.d.ts +58 -0
- package/dist/core/runner/strict-output-schema.d.ts +18 -3
- package/dist/core/runner/synthetic-tools.d.ts +113 -0
- package/dist/core/runner/teardown-bounded.d.ts +34 -0
- package/dist/core/runner/tool-disclosure.d.ts +227 -0
- package/dist/core/runner/tool-output-projection.d.ts +6 -0
- package/dist/core/runner/turn-attachments.d.ts +634 -0
- package/dist/core/runner/usage-accounting.d.ts +32 -0
- package/dist/core/runtime.d.ts +9 -0
- package/dist/core/safe-notify.d.ts +64 -0
- package/dist/core/safety-axis-vocab.d.ts +23 -0
- package/dist/core/safety-merge-corpus.d.ts +37 -0
- package/dist/core/scheduler.d.ts +121 -0
- package/dist/core/secret-env.d.ts +32 -0
- package/dist/core/select-model.d.ts +15 -0
- package/dist/core/sensitive-path-policy.d.ts +42 -0
- package/dist/core/session-policy-store.d.ts +94 -0
- package/dist/core/session-reconcile.d.ts +80 -0
- package/dist/core/session-store.d.ts +85 -0
- package/dist/core/session.d.ts +153 -0
- package/dist/core/shared-memory/contract.d.ts +22 -0
- package/dist/core/shared-memory/normalize.d.ts +123 -2
- package/dist/core/shared-memory/tools.d.ts +14 -0
- package/dist/core/shared-memory/types.d.ts +105 -0
- package/dist/core/shutdown-debug.d.ts +6 -0
- package/dist/core/side-query.d.ts +38 -0
- package/dist/core/side-query.js +6 -1
- package/dist/core/skill-tool-specifier.d.ts +72 -0
- package/dist/core/skills-directory.d.ts +100 -1
- package/dist/core/spec-contract.d.ts +89 -0
- package/dist/core/store-contracts/background-agent-store-contract.d.ts +31 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +26 -0
- package/dist/core/store-contracts/contract-harness.d.ts +20 -0
- package/dist/core/store-contracts/contract-kit-version.d.ts +27 -0
- package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +8 -0
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +42 -0
- package/dist/core/store-contracts/tool-result-store-contract.d.ts +13 -0
- package/dist/core/strategy-store.d.ts +37 -0
- package/dist/core/stub-env.d.ts +7 -0
- package/dist/core/surrogate-safe-slice.d.ts +35 -0
- package/dist/core/task-notification.d.ts +202 -0
- package/dist/core/task-outcome.d.ts +53 -0
- package/dist/core/task-registry-agent.d.ts +337 -1
- package/dist/core/task-registry-agent.js +2 -0
- package/dist/core/task-registry-monitor.d.ts +12 -0
- package/dist/core/task-registry-shared.d.ts +540 -0
- package/dist/core/task-registry.d.ts +343 -0
- package/dist/core/task-registry.js +13 -2
- package/dist/core/task-tool-shape.d.ts +44 -0
- package/dist/core/tighten-task-spec.d.ts +21 -0
- package/dist/core/tool-detach.d.ts +21 -0
- package/dist/core/tool-errors.d.ts +131 -0
- package/dist/core/tool-errors.js +4 -0
- package/dist/core/tool-name-aliases.d.ts +27 -0
- package/dist/core/tool-policy.d.ts +555 -0
- package/dist/core/tool-policy.js +3 -0
- package/dist/core/tool-result-budget.d.ts +32 -0
- package/dist/core/tool-result-store.d.ts +174 -1
- package/dist/core/tools.d.ts +45 -0
- package/dist/core/trace.d.ts +323 -0
- package/dist/core/types.d.ts +3859 -2
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-text.d.ts +156 -0
- package/dist/core/usage-window-store.d.ts +95 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/warm-resume.d.ts +17 -0
- package/dist/core/wiring-manifest.d.ts +169 -0
- package/dist/core/with-retry.d.ts +24 -0
- package/dist/core/workflow-journal-store.d.ts +160 -0
- package/dist/core/workflow-run-store-contract.d.ts +25 -0
- package/dist/core/workflow-run-store.d.ts +119 -0
- package/dist/core/workflow-run-store.js +2 -0
- package/dist/engine/compaction/compaction.d.ts +256 -1
- package/dist/engine/compaction/utils.d.ts +94 -0
- package/dist/engine/execution-env/kill-tree.d.ts +29 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +94 -0
- package/dist/engine/harness/agent-harness.d.ts +116 -0
- package/dist/engine/harness/agent-harness.js +3 -14
- package/dist/engine/harness/messages.d.ts +15 -0
- package/dist/engine/harness/types.d.ts +464 -2
- package/dist/engine/llm/diagnostics.d.ts +4 -0
- package/dist/engine/llm/event-stream.d.ts +3 -0
- package/dist/engine/llm/index.d.ts +7 -0
- package/dist/engine/llm/types.d.ts +500 -3
- package/dist/engine/llm/validation.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +87 -2
- package/dist/engine/loop/runtime-deps.d.ts +7 -0
- package/dist/engine/loop/types.d.ts +424 -0
- package/dist/engine/lsp/frame-decoder.d.ts +13 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +107 -0
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +7 -0
- package/dist/engine/session/import-validate.d.ts +27 -0
- package/dist/engine/session/log-digest.d.ts +93 -0
- package/dist/engine/session/memory-repo.d.ts +6 -0
- package/dist/engine/session/memory-storage.d.ts +2 -0
- package/dist/engine/session/session.d.ts +75 -0
- package/dist/engine/session/storage-base.d.ts +8 -0
- package/dist/fixtures/index.d.ts +36 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +1 -2
- package/dist/internal/harness-types.d.ts +6 -0
- package/dist/internal/harness.d.ts +11 -0
- package/dist/internal/llm.d.ts +6 -0
- package/dist/orchestration/builtin-workflows.d.ts +53 -0
- package/dist/orchestration/dev-vm-script-runner.d.ts +19 -0
- package/dist/orchestration/goal.d.ts +57 -0
- package/dist/orchestration/goal.js +3 -0
- package/dist/orchestration/run-spec.d.ts +42 -0
- package/dist/orchestration/run-spec.js +4 -0
- package/dist/orchestration/run-workflow-tool.d.ts +169 -0
- package/dist/orchestration/workflow-governance.d.ts +61 -0
- package/dist/orchestration/workflow-meta.d.ts +28 -0
- package/dist/orchestration/workflow-observe.d.ts +60 -0
- package/dist/orchestration/workflow-primitives.d.ts +23 -1
- package/dist/orchestration/workflow-sandbox-conformance.d.ts +38 -0
- package/dist/orchestration/workflow-script-runner.d.ts +88 -0
- package/dist/orchestration/workflow-script-store.d.ts +98 -0
- package/dist/orchestration/workflow-size-guideline.d.ts +67 -0
- package/dist/orchestration/workflow-types.d.ts +169 -2
- package/dist/orchestration/workflow.d.ts +358 -0
- package/dist/orchestration/workflow.js +13 -2
- package/dist/prompt-assembly/artifact-store.d.ts +33 -0
- package/dist/prompt-assembly/artifact.d.ts +25 -0
- package/dist/prompt-assembly/assemble.d.ts +20 -0
- package/dist/prompt-assembly/composer.d.ts +29 -0
- package/dist/prompt-assembly/epoch.d.ts +55 -1
- package/dist/prompt-assembly/event-registry.d.ts +35 -0
- package/dist/prompt-assembly/explain.d.ts +12 -0
- package/dist/prompt-assembly/tool-catalog.d.ts +38 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +40 -0
- package/dist/prompt-assembly/types.d.ts +115 -0
- package/dist/prompts/coordinator.d.ts +27 -0
- package/dist/prompts/default.d.ts +539 -0
- package/dist/prompts/simple-sections.d.ts +45 -0
- package/dist/prompts/supervisor.d.ts +66 -0
- package/dist/scenarios/env.d.ts +28 -0
- package/dist/scenarios/full-body.d.ts +50 -0
- package/dist/scenarios/scenario-registry.d.ts +60 -0
- package/dist/scenarios/teacher-quickstart.d.ts +27 -0
- package/dist/server/http.d.ts +17 -0
- package/dist/stores/cc/lockfile.d.ts +6 -0
- package/dist/stores/cc/mailbox-store.d.ts +8 -0
- package/dist/stores/cc/roster-adapter.d.ts +4 -0
- package/dist/stores/cc/sidecar-transcript.d.ts +66 -0
- package/dist/stores/file/background-agent-store.d.ts +24 -0
- package/dist/stores/file/background-agent-store.js +2 -1
- package/dist/stores/file/checkpoint-store.d.ts +38 -0
- package/dist/stores/file/file-snapshot-store.d.ts +25 -0
- package/dist/stores/file/fs-atomic.d.ts +155 -0
- package/dist/stores/file/index.d.ts +89 -0
- package/dist/stores/file/mailbox-store.d.ts +36 -0
- package/dist/stores/file/mailbox-store.js +2 -0
- package/dist/stores/file/memory-store.d.ts +82 -0
- package/dist/stores/file/permission-rule-store.d.ts +79 -0
- package/dist/stores/file/session-policy-store.d.ts +28 -0
- package/dist/stores/file/session-store.d.ts +40 -0
- package/dist/stores/file/shared-ledger.d.ts +83 -0
- package/dist/stores/file/tool-result-store.d.ts +11 -0
- package/dist/stores/file/usage-window-store.d.ts +18 -0
- package/dist/stores/file/workflow-journal-store.d.ts +100 -0
- package/dist/stores/file/workflow-run-store.d.ts +11 -0
- package/dist/stores/file/workflow-run-store.js +2 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +282 -0
- package/dist/tools/fs/encoding.d.ts +60 -0
- package/dist/tools/fs/fs-bash.d.ts +133 -0
- package/dist/tools/fs/fs-pdf.d.ts +28 -0
- package/dist/tools/fs/fs-search-tools.d.ts +5 -0
- package/dist/tools/fs/fs-shared.d.ts +360 -0
- package/dist/tools/fs/fs-write.d.ts +16 -0
- package/dist/tools/fs/gh-rate-limit.d.ts +22 -0
- package/dist/tools/fs/index.d.ts +79 -0
- package/dist/tools/fs/notebook.d.ts +56 -0
- package/dist/tools/fs/pdf.d.ts +70 -0
- package/dist/tools/fs/pdf.js +8 -2
- package/dist/tools/fs/repo-map.d.ts +10 -0
- package/dist/tools/fs/safety.d.ts +376 -0
- package/dist/tools/fs/search.d.ts +131 -0
- package/dist/tools/loop-tick.d.ts +24 -0
- package/dist/tools/monitor.d.ts +55 -0
- package/dist/tools/scheduler-tools.d.ts +51 -0
- package/dist/tools/sql-adapters.d.ts +18 -0
- package/dist/tools/sql.d.ts +25 -0
- package/dist/tools/task-list.d.ts +77 -0
- package/dist/tools/todo.d.ts +8 -0
- package/dist/tools/web.d.ts +184 -0
- package/dist/tools/web.js +32 -5
- package/dist/tools/worktree.d.ts +81 -0
- package/package.json +2 -2
- package/dist/tools/gitea-issue.d.ts +0 -13
- package/dist/tools/gitea-issue.js +0 -75
|
@@ -1,15 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate-1 — oracleIsolation (design/77 §1): the CORE SEAM + assertion for the out-of-process grader boundary,
|
|
3
|
+
* the 4th auto-promote gate. The whole auto-promote tier is **mandate-OFF** (design/77 §5/§7); this file only
|
|
4
|
+
* makes the seam EXIST so that auto-accept (`fixed`) becomes implementable once the SERVICE supplies the real
|
|
5
|
+
* isolated grader environment (an E2B/Kata container). SAFE-tier stays `candidate_only` regardless.
|
|
6
|
+
*
|
|
7
|
+
* **What core owns vs what the service owns** (mirrors {@link import("./remote-env.js").RemoteExecutionEnv} —
|
|
8
|
+
* core owns the seam shape; service owns the backend):
|
|
9
|
+
* - CORE: the {@link GraderEnv} provenance contract, the {@link assertOracleIsolation} assertion (3 fail-closed
|
|
10
|
+
* checks), and the {@link OracleIsolationVerdict}.isolationClass tiering that CAPS a non-isolated grader at
|
|
11
|
+
* `candidate_only` (never auto-accept).
|
|
12
|
+
* - SERVICE: the actually-isolated grader env (E2B microVM with `capabilities.isolation === true`), minted by
|
|
13
|
+
* the trusted control plane through {@link import("./types.js").RunnerDeps.graderEnvFactory}.
|
|
14
|
+
*
|
|
15
|
+
* 🔴 **Honest residual (design/77 §1 Residual + §9):** the bash write-probe is point-in-time and
|
|
16
|
+
* necessary-not-sufficient — core, running *inside the worker*, cannot prove a mount is truly immutable (it
|
|
17
|
+
* only observes that one write failed once; a candidate can `mount -o remount,rw` after the probe). And
|
|
18
|
+
* `capabilities.isolation` is self-declared by the adapter (a lying adapter can claim `true`). The REAL
|
|
19
|
+
* protection is the OUT-OF-PROCESS structural boundary (check B) on a control-plane-provisioned adapter — which
|
|
20
|
+
* is exactly why auto-promote stays mandate-OFF until the deployment contract is verified end-to-end (Slice-3).
|
|
21
|
+
*/
|
|
1
22
|
import type { ExecutionEnv } from "../internal/harness-types.js";
|
|
23
|
+
/**
|
|
24
|
+
* The verdict of {@link assertOracleIsolation}. `protected` is the fail-closed AND of all three checks
|
|
25
|
+
* (identity ∧ structural-class-acceptable ∧ write-probe). `isolationClass` is the load-bearing tiering that the
|
|
26
|
+
* (OFF) auto-accept path consults:
|
|
27
|
+
* - `"out_of_process"` — the grader is a real isolated sandbox (a remote env with `capabilities.isolation ===
|
|
28
|
+
* true`). ONLY this class is eligible for auto-accept (still mandate-OFF).
|
|
29
|
+
* - `"in_process_probe_only"` — the grader is non-remote or non-isolated; the bash write-probe is the only
|
|
30
|
+
* signal and it is necessary-not-sufficient → this CAPS the run at `candidate_only`, NEVER auto-accept (the
|
|
31
|
+
* 2-of-3 asymmetry, design/77 §1/§6: the oracle axis's real protection is the out-of-process boundary).
|
|
32
|
+
*/
|
|
2
33
|
export interface OracleIsolationVerdict {
|
|
3
34
|
protected: boolean;
|
|
4
35
|
isolationClass: "out_of_process" | "in_process_probe_only";
|
|
36
|
+
/** Set when `protected:false` (or when the class is downgraded) — the fail-closed reason, for observability. */
|
|
5
37
|
reason?: string;
|
|
6
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The isolated grader environment, provisioned by the trusted control plane. `provenance` is settable ONLY at
|
|
41
|
+
* the {@link import("./types.js").RunnerDeps.graderEnvFactory} call site — it is NEVER reconstructed from
|
|
42
|
+
* worker-visible state (a worker that could mint a `{provenance:'control_plane'}` object would defeat the whole
|
|
43
|
+
* gate). `immutableOraclePaths` are the paths the oracle/spec is supposed to live behind (e.g. a hidden held-out
|
|
44
|
+
* test dir) that the worker must not be able to corrupt; the bash write-probe (check C) targets them.
|
|
45
|
+
*/
|
|
7
46
|
export interface GraderEnv {
|
|
47
|
+
/** The isolated env the oracle grades in (service-provisioned; a {@link import("./remote-env.js").RemoteExecutionEnv} in production). */
|
|
8
48
|
env: ExecutionEnv;
|
|
49
|
+
/** Paths the oracle must be protected behind — probed for writability through the WORKER's bash bypass surface. */
|
|
9
50
|
immutableOraclePaths: string[];
|
|
51
|
+
/** Provenance brand — ONLY the control-plane factory sets this. Never derived from worker state. */
|
|
10
52
|
provenance: "control_plane";
|
|
11
53
|
}
|
|
54
|
+
/** Options for {@link assertOracleIsolation}. */
|
|
12
55
|
export interface AssertOracleIsolationOptions {
|
|
56
|
+
/** Abort the write-probe(s); an abort/timeout during the probe is fail-closed (protected:false). */
|
|
13
57
|
signal?: AbortSignal;
|
|
14
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Assert that the grader env is isolated from the worker env so a positive oracle verdict is trustworthy
|
|
61
|
+
* (design/77 §1, the 4th auto-promote gate). THREE fail-closed checks — ANY failure ⇒ `protected:false`:
|
|
62
|
+
*
|
|
63
|
+
* **(A) IDENTITY** — `grader.env !== workerEnv` (reference inequality); when BOTH are remote envs, also require
|
|
64
|
+
* distinct `workspaceHandle().sandboxId`. A grader that IS the worker env (or the same sandbox) is grading in
|
|
65
|
+
* the very env the worker can corrupt.
|
|
66
|
+
*
|
|
67
|
+
* **(B) STRUCTURAL CLASS (the real protection)** — when `grader.env` is a {@link import("./remote-env.js").RemoteExecutionEnv}
|
|
68
|
+
* with `capabilities.isolation === true` ({@link isIsolated}, fail-closed default false) → `isolationClass =
|
|
69
|
+
* "out_of_process"` (the only class the OFF auto-accept path may consider). A non-remote / non-isolated grader →
|
|
70
|
+
* `"in_process_probe_only"`, which CAPS the run at `candidate_only` — never auto-accept. Note: a non-isolated
|
|
71
|
+
* grader is NOT itself a `protected:false` (the probe can still pass and the run can still be a legitimate
|
|
72
|
+
* `candidate_only`); the class is what gates auto-accept (the 2-of-3 asymmetry).
|
|
73
|
+
*
|
|
74
|
+
* **(C) BASH WRITE-PROBE (necessary-not-sufficient, cheap early signal)** — for each `immutableOraclePath`, run
|
|
75
|
+
* a write probe through the WORKER env's `exec` (the bash bypass surface — see {@link probeCommand}), against
|
|
76
|
+
* the path canonicalized via {@link canonicalizeTarget}. The probe PASSES only if the write returns a non-zero
|
|
77
|
+
* exit code or an exec error (read-only mount / permission denied / not supported). Any SUCCESS (RC 0) ⇒ the
|
|
78
|
+
* oracle dir is writable from the worker ⇒ `protected:false`. A probe THROW / abort / timeout ⇒ fail-closed
|
|
79
|
+
* (`protected:false`).
|
|
80
|
+
*
|
|
81
|
+
* The verdict is fail-closed: the function itself never throws — a thrown probe is caught and mapped to
|
|
82
|
+
* `protected:false` (so a flaky env can't turn into a silent pass).
|
|
83
|
+
*/
|
|
15
84
|
export declare function assertOracleIsolation(workerEnv: ExecutionEnv, grader: GraderEnv, opts?: AssertOracleIsolationOptions): Promise<OracleIsolationVerdict>;
|
|
@@ -1,10 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/179 §4/§7/§13 — the ONE consent protocol every rule takes to enter the store through the engine.
|
|
3
|
+
*
|
|
4
|
+
* Single card, settings import and starter batch all reduce to the same three steps:
|
|
5
|
+
*
|
|
6
|
+
* durable approval record (pending) → authenticated confirmation transfer (approved) → principal-bound redemption (redeemed)
|
|
7
|
+
*
|
|
8
|
+
* and only the third step reaches the core-private writer. A host holding a record id cannot redeem it
|
|
9
|
+
* while it is pending, cannot redeem another principal's record, and cannot redeem free text — the
|
|
10
|
+
* redemption face takes a ticket and nothing else, so there is no field to write a rule of one's own into.
|
|
11
|
+
*
|
|
12
|
+
* The record is DURABLE, not an in-memory ticket: a safety ask can park, and the approval that redeems it
|
|
13
|
+
* may arrive in another process at another time. An in-memory ticket loses exactly that case.
|
|
14
|
+
*
|
|
15
|
+
* ## Crash order, and why replay is safe
|
|
16
|
+
*
|
|
17
|
+
* The record moves to `redeemed` — carrying the dot it minted — BEFORE the writer applies. A crash in
|
|
18
|
+
* between leaves a redeemed record whose rule is not in the store; replaying the redemption re-applies the
|
|
19
|
+
* SAME delta with the SAME dot, which the store folds idempotently. One redemption mints exactly one dot,
|
|
20
|
+
* for as many replays as it takes. Deliberately re-adding a deleted rule is a different act: a new ask, a
|
|
21
|
+
* new record, a new dot.
|
|
22
|
+
*
|
|
23
|
+
* ## The boundary this does NOT claim
|
|
24
|
+
*
|
|
25
|
+
* The confirmation transfer is an in-process call. A deployment must reach it only from its authenticated
|
|
26
|
+
* approval channel, and a deployment that shares filesystem credentials with the user can bypass all of
|
|
27
|
+
* this by editing a file backend's file. That is the settings-file trust model, stated rather than
|
|
28
|
+
* defended against: for a file backend, host = user, no more and no less.
|
|
29
|
+
*/
|
|
1
30
|
import { type RuleScope, type RuleDot } from "./permission-rule-model.js";
|
|
2
31
|
import type { PermissionRuleStoreProvider } from "./permission-rule-store.js";
|
|
32
|
+
/** One candidate rule inside an approval record: the exact text and where it would apply. */
|
|
3
33
|
export interface RuleCandidate {
|
|
4
34
|
rule: string;
|
|
5
35
|
scope: RuleScope;
|
|
6
36
|
}
|
|
37
|
+
/** What the approval record is for — it decides the provenance the minted adds carry. */
|
|
7
38
|
export type RuleApprovalKind = "card" | "import" | "starter";
|
|
39
|
+
/** A durable record of one pending/settled human decision about minting rules. */
|
|
8
40
|
export interface RuleApprovalRecord {
|
|
9
41
|
id: string;
|
|
10
42
|
principal: string;
|
|
@@ -12,38 +44,89 @@ export interface RuleApprovalRecord {
|
|
|
12
44
|
state: "pending" | "approved" | "redeemed";
|
|
13
45
|
candidates: RuleCandidate[];
|
|
14
46
|
createdAt: string;
|
|
47
|
+
/** The ask this record was drawn from, for reconciliation. Advisory metadata; never adjudication input. */
|
|
15
48
|
toolCallId?: string;
|
|
16
49
|
boundInputHash?: string;
|
|
50
|
+
/** Monotonic revision of THIS record, bumped by every accepted transition. The compare-and-set key:
|
|
51
|
+
* comparing state alone cannot separate two different writes that both leave the state unchanged. */
|
|
17
52
|
rev: number;
|
|
53
|
+
/**
|
|
54
|
+
* Which candidate the person actually chose, recorded by the confirmation transfer.
|
|
55
|
+
*
|
|
56
|
+
* A card offers alternatives — the exact command and, sometimes, a broader prefix — and a human picks
|
|
57
|
+
* ONE. Without this, confirming the card would authorize every option on it: a host could redeem the
|
|
58
|
+
* exact ticket, then later redeem the wider prefix ticket, and the second rule would exist without
|
|
59
|
+
* anyone ever having agreed to it. Absent on batch records, where the confirmation is over the whole
|
|
60
|
+
* previewed list by construction.
|
|
61
|
+
*/
|
|
18
62
|
selectedCandidate?: number;
|
|
63
|
+
/** Dots already minted for this record, keyed by candidate index — the replay anchor. */
|
|
19
64
|
redeemedDots?: Record<number, RuleDot>;
|
|
20
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* The durable home of approval records. A separate record type on purpose: it is not a checkpoint row and
|
|
68
|
+
* must not evolve with one.
|
|
69
|
+
*/
|
|
21
70
|
export interface RuleApprovalRecordStore {
|
|
22
71
|
get(id: string): Promise<RuleApprovalRecord | undefined>;
|
|
72
|
+
/**
|
|
73
|
+
* Compare-and-set on the record's REVISION, not on its state.
|
|
74
|
+
*
|
|
75
|
+
* State alone is not a compare-and-set key: a batch's second candidate transitions redeemed → redeemed,
|
|
76
|
+
* so two concurrent retries would both find the state they expected, both mint a dot, and both write —
|
|
77
|
+
* one silently overwriting the other's `redeemedDots`, which is how one approval becomes two adds and a
|
|
78
|
+
* deleted rule comes back to life. `next.rev` must be `expectRev + 1`.
|
|
79
|
+
*/
|
|
23
80
|
cas(id: string, expectRev: number, next: RuleApprovalRecord): Promise<boolean>;
|
|
24
81
|
create(record: RuleApprovalRecord): Promise<void>;
|
|
25
82
|
}
|
|
83
|
+
/** Everything the consent lane needs wired. */
|
|
26
84
|
export interface RuleConsentDeps {
|
|
27
85
|
provider: PermissionRuleStoreProvider;
|
|
28
86
|
approvals: RuleApprovalRecordStore;
|
|
87
|
+
/** Injectable clock/id for deterministic tests; defaults are the real ones. */
|
|
29
88
|
now?: () => Date;
|
|
30
89
|
newId?: () => string;
|
|
31
90
|
}
|
|
91
|
+
/** In-memory approval records — the test backend and the reference CAS semantics. */
|
|
32
92
|
export declare class InMemoryRuleApprovalRecordStore implements RuleApprovalRecordStore {
|
|
33
93
|
private readonly rows;
|
|
34
94
|
get(id: string): Promise<RuleApprovalRecord | undefined>;
|
|
35
95
|
create(record: RuleApprovalRecord): Promise<void>;
|
|
36
96
|
cas(id: string, expectRev: number, next: RuleApprovalRecord): Promise<boolean>;
|
|
97
|
+
/** Test/inspection helper — how many records exist. The principal negative pins read this. */
|
|
37
98
|
size(): number;
|
|
38
99
|
}
|
|
100
|
+
/** An opaque, single-purpose redemption ticket. Its only content is which record and which candidate. */
|
|
39
101
|
export type RuleTicket = string;
|
|
40
102
|
export declare function mintRuleTicket(recordId: string, candidateIndex: number): RuleTicket;
|
|
103
|
+
/**
|
|
104
|
+
* Create the pending record behind ONE approval card, and the tickets its options would redeem.
|
|
105
|
+
*
|
|
106
|
+
* The candidates are DERIVED here, from the call, by the same function that produced the options shown on
|
|
107
|
+
* the card — they are not an argument. That is the difference between the stated invariant and a wish:
|
|
108
|
+
* while this took a candidate list, a surface could display an approval for `git status` and build a
|
|
109
|
+
* record containing `Bash(rm:*)`, and every later step would faithfully honor the record. There is now no
|
|
110
|
+
* field in which to write a rule of one's own, on this entry as on the redemption entry.
|
|
111
|
+
*
|
|
112
|
+
* `toolCallId` and `boundInputHash` are recorded so an auditor can tie the record back to the exact ask,
|
|
113
|
+
* and a command the lane cannot speak for yields no record and no tickets at all.
|
|
114
|
+
*
|
|
115
|
+
* What this does NOT claim: that the command named here is the command that was displayed. A surface that
|
|
116
|
+
* shows one thing and says another is the same-layer trust §5 already states plainly — in an in-process
|
|
117
|
+
* deployment the host IS the user. What it closes is the gap where the rule TEXT never came from the
|
|
118
|
+
* engine at all.
|
|
119
|
+
*/
|
|
41
120
|
export declare function prepareCardApproval(opts: {
|
|
42
121
|
principal: string;
|
|
122
|
+
/** The tool whose ask this card belongs to. Only the shell tool the rule lane speaks for is accepted. */
|
|
43
123
|
toolName: string;
|
|
124
|
+
/** The command as adjudicated — the same bytes the ask carried. */
|
|
44
125
|
command: string;
|
|
126
|
+
/** The ask's call id and argument digest, recorded for reconciliation. */
|
|
45
127
|
toolCallId?: string;
|
|
46
128
|
boundInputHash?: string;
|
|
129
|
+
/** Where a redeemed rule would apply. Defaults to global. */
|
|
47
130
|
scope?: RuleScope;
|
|
48
131
|
deps: RuleConsentDeps;
|
|
49
132
|
}): Promise<{
|
|
@@ -51,19 +134,34 @@ export declare function prepareCardApproval(opts: {
|
|
|
51
134
|
tickets: RuleTicket[];
|
|
52
135
|
candidates: RuleCandidate[];
|
|
53
136
|
} | undefined>;
|
|
137
|
+
/**
|
|
138
|
+
* The authenticated confirmation transfer: pending → approved.
|
|
139
|
+
*
|
|
140
|
+
* A deployment must reach this ONLY from the channel that actually carries a human decision — the same
|
|
141
|
+
* channel an approval card's yes travels. Nothing below this line can tell an authentic confirmation from
|
|
142
|
+
* a fabricated one, which is why the boundary is stated here rather than checked here.
|
|
143
|
+
*/
|
|
54
144
|
export declare function confirmRuleApproval(opts: {
|
|
55
145
|
approvalId: string;
|
|
56
146
|
principal: string;
|
|
147
|
+
/**
|
|
148
|
+
* REQUIRED for a card record: the index of the option the person chose. A card presents alternatives of
|
|
149
|
+
* different breadth, so "they said yes" is not an answer on its own — "they said yes to THIS one" is.
|
|
150
|
+
* Rejected on a batch record, whose confirmation covers the previewed list by construction.
|
|
151
|
+
*/
|
|
57
152
|
selectedCandidate?: number;
|
|
58
153
|
deps: RuleConsentDeps;
|
|
59
154
|
}): Promise<ConfirmResult>;
|
|
155
|
+
/** Why a confirmation did not land. A closed set so a host can branch (re-present, re-fetch, give up). */
|
|
60
156
|
export type ConfirmRefusalReason = "record_not_found" | "selection_missing" | "selection_invalid" | "selection_mismatch" | "batch_takes_no_selection" | "not_pending" | "conflict";
|
|
157
|
+
/** The confirmation outcome: landed, or refused with a named reason. */
|
|
61
158
|
export type ConfirmResult = {
|
|
62
159
|
ok: true;
|
|
63
160
|
} | {
|
|
64
161
|
ok: false;
|
|
65
162
|
reason: ConfirmRefusalReason;
|
|
66
163
|
};
|
|
164
|
+
/** What a redemption produced. `alreadyRedeemed` marks the replay path — the same dot, no second rule. */
|
|
67
165
|
export type RedeemResult = {
|
|
68
166
|
status: "redeemed";
|
|
69
167
|
rule: string;
|
|
@@ -75,18 +173,35 @@ export type RedeemResult = {
|
|
|
75
173
|
status: "refused";
|
|
76
174
|
reason: string;
|
|
77
175
|
};
|
|
176
|
+
/**
|
|
177
|
+
* Redeem one ticket into one persisted rule.
|
|
178
|
+
*
|
|
179
|
+
* Takes a ticket and a verified principal, and nothing else. There is deliberately no field carrying rule
|
|
180
|
+
* text: what gets persisted is what the engine put in the record when it drew the card, so a host cannot
|
|
181
|
+
* turn a click on `git status` into a rule for something else.
|
|
182
|
+
*/
|
|
78
183
|
export declare function redeemRuleTicket(opts: {
|
|
79
184
|
ticket: RuleTicket;
|
|
80
185
|
principal: string;
|
|
81
186
|
deps: RuleConsentDeps;
|
|
82
187
|
}): Promise<RedeemResult>;
|
|
188
|
+
/** The three user-editable settings layers this version reads. */
|
|
83
189
|
export type ImportedSettingsLayer = "userSettings" | "projectSettings" | "localSettings";
|
|
190
|
+
/** One layer to read. `root` is the canonical directory a project-layer rule is scoped to — taken from
|
|
191
|
+
* where the FILE actually lives, since the local layer is not always at a repository root. */
|
|
84
192
|
export interface CcImportLayer {
|
|
85
193
|
layer: ImportedSettingsLayer;
|
|
86
194
|
path: string;
|
|
87
195
|
root: string;
|
|
88
196
|
readFile: (path: string) => Promise<string | undefined>;
|
|
89
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* What the import WOULD do, shown before anyone confirms.
|
|
200
|
+
*
|
|
201
|
+
* `uncovered` is a two-key record, not a list: the two layers this version does not read are named in the
|
|
202
|
+
* TYPE, so an empty list, a missing member or a duplicate is not expressible. A preview that reported only
|
|
203
|
+
* the layers it read would be claiming completeness it does not have.
|
|
204
|
+
*/
|
|
90
205
|
export interface ImportPreview {
|
|
91
206
|
candidates: RuleCandidate[];
|
|
92
207
|
skipped: Array<{
|
|
@@ -103,6 +218,8 @@ export interface ImportPreview {
|
|
|
103
218
|
policySettings: "not-imported-v1";
|
|
104
219
|
};
|
|
105
220
|
}
|
|
221
|
+
/** What the import ACTUALLY did — a different moment and a different contract from the preview, because
|
|
222
|
+
* dedup, concurrency and redemption-time validation can all move an entry between the two. */
|
|
106
223
|
export interface ImportResult {
|
|
107
224
|
persisted: RuleCandidate[];
|
|
108
225
|
deduped: RuleCandidate[];
|
|
@@ -112,6 +229,13 @@ export interface ImportResult {
|
|
|
112
229
|
}>;
|
|
113
230
|
rev: number;
|
|
114
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Read the allow buckets of the user-editable settings layers and produce a preview plus a PENDING
|
|
234
|
+
* approval record. Nothing is stored until someone confirms that record and the batch is redeemed.
|
|
235
|
+
*
|
|
236
|
+
* Only the allow bucket is read. The deny/ask buckets are the tightening direction and have their own
|
|
237
|
+
* channel; importing them through a loosening lane would be the wrong door.
|
|
238
|
+
*/
|
|
115
239
|
export declare function prepareCcImport(opts: {
|
|
116
240
|
layers: CcImportLayer[];
|
|
117
241
|
principal: string;
|
|
@@ -120,7 +244,17 @@ export declare function prepareCcImport(opts: {
|
|
|
120
244
|
preview: ImportPreview;
|
|
121
245
|
approvalId: string;
|
|
122
246
|
}>;
|
|
247
|
+
/**
|
|
248
|
+
* The factory-reviewed starter set, offered when there is nothing to import.
|
|
249
|
+
*
|
|
250
|
+
* Every entry is an EXACT read-only command whose behavior does not depend on repository or environment
|
|
251
|
+
* configuration. The version-control family is deliberately absent: repository configuration can make a
|
|
252
|
+
* subcommand invoke an external helper, which is the same reason this repository already declined a
|
|
253
|
+
* subcommand allowlist for it. Nothing here takes effect until someone confirms the batch.
|
|
254
|
+
*/
|
|
123
255
|
export declare const STARTER_RULES: readonly string[];
|
|
256
|
+
/** Prepare the starter batch as a pending approval record. Nothing takes effect without confirmation;
|
|
257
|
+
* an unconfirmed deployment simply starts with an empty rule set. */
|
|
124
258
|
export declare function prepareStarterBatch(opts: {
|
|
125
259
|
principal: string;
|
|
126
260
|
deps: RuleConsentDeps;
|
|
@@ -128,6 +262,10 @@ export declare function prepareStarterBatch(opts: {
|
|
|
128
262
|
preview: RuleCandidate[];
|
|
129
263
|
approvalId: string;
|
|
130
264
|
}>;
|
|
265
|
+
/**
|
|
266
|
+
* Redeem an approved BATCH record (import or starter) — every candidate, one dot each, reporting what
|
|
267
|
+
* actually landed rather than what was previewed.
|
|
268
|
+
*/
|
|
131
269
|
export declare function redeemRuleBatch(opts: {
|
|
132
270
|
approvalId: string;
|
|
133
271
|
principal: string;
|
|
@@ -1,66 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/179 §2 — the persisted ALLOW rule model and its matcher.
|
|
3
|
+
*
|
|
4
|
+
* A persisted allow rule is the standing form of ONE recorded human approval: the person confirmed a
|
|
5
|
+
* command once on an approval card and asked not to be asked again, and the rule is what that answer
|
|
6
|
+
* becomes. This module owns the rule's SHAPE (text grammar, canonical spelling, scope) and its MATCH
|
|
7
|
+
* semantics; who may mint one, where it is stored, and where in the gate it is consumed live in
|
|
8
|
+
* `permission-rule-store.ts`, `permission-rule-consent.ts` and `hooks.ts` respectively.
|
|
9
|
+
*
|
|
10
|
+
* ## The floor: only a single simple command can ever be matched
|
|
11
|
+
*
|
|
12
|
+
* Everything a shell can use to run a SECOND program behind an admitted first one — operators,
|
|
13
|
+
* substitution, subshells, backgrounding, and **redirection** — puts the command outside this lane
|
|
14
|
+
* entirely: it falls back to the pre-existing chain and asks. That includes redirection specifically,
|
|
15
|
+
* which is a deliberate registered divergence from upstream (upstream strips redirections before
|
|
16
|
+
* matching; with this repo's write gate not covering the shell tool, stripping would turn a rule as
|
|
17
|
+
* innocuous as `Bash(ls)` into a licence for `ls > ~/.ssh/authorized_keys`).
|
|
18
|
+
*
|
|
19
|
+
* The floor is `parseLeadingCommandName` — the one simple-command parser already shared by the
|
|
20
|
+
* read-only classifier, the reversibility probe, the coarse command-name policy and the skill tool
|
|
21
|
+
* specifier. A second tokenizer would drift, and drift on a loosening face shows up as a bypass rather
|
|
22
|
+
* than as a test failure.
|
|
23
|
+
*
|
|
24
|
+
* ## Normalization order is load-bearing
|
|
25
|
+
*
|
|
26
|
+
* Whitespace folding runs AFTER the shell-construct rejection, never before: folding first would
|
|
27
|
+
* swallow a newline and glue two commands into one string that then reads as a single simple command.
|
|
28
|
+
* Only space/tab runs fold; there is no case folding, no alias expansion (no shell alias view exists
|
|
29
|
+
* here) and no leading-env-assignment stripping (`FOO=1 git status` simply does not match
|
|
30
|
+
* `Bash(git status:*)`) — the last two are strict-side divergences from upstream, registered as such.
|
|
31
|
+
*/
|
|
32
|
+
/** The one tool the v1 rule lane speaks for. The field exists on the rule so v2 can widen without a shape change. */
|
|
1
33
|
export type PersistedRuleTool = "Bash";
|
|
34
|
+
/** v1 match forms. `"wildcard"` is reserved for v2 and is not a value this version ever produces. */
|
|
2
35
|
export type PersistedRuleMatch = "exact" | "prefix";
|
|
36
|
+
/**
|
|
37
|
+
* Where a rule applies. A rule read out of a project-local settings layer applies ONLY inside that
|
|
38
|
+
* project's canonical root — project A's approval is not project B's. User-layer, starter and
|
|
39
|
+
* user-minted rules are global.
|
|
40
|
+
*
|
|
41
|
+
* `root` is expected to be already canonicalized (realpath) by whoever constructs the scope; the
|
|
42
|
+
* matcher compares with word-boundary containment, never a bare `startsWith` (`/a` must not reach `/ab`).
|
|
43
|
+
*/
|
|
3
44
|
export type RuleScope = {
|
|
4
45
|
kind: "global";
|
|
5
46
|
} | {
|
|
6
47
|
kind: "project";
|
|
7
48
|
root: string;
|
|
8
49
|
};
|
|
50
|
+
/** The immutable causal identity of one add: a replica identity plus a monotonic counter. */
|
|
9
51
|
export interface RuleDot {
|
|
10
52
|
actor: string;
|
|
11
53
|
counter: number;
|
|
12
54
|
}
|
|
55
|
+
/** Where one add came from. Stored PER ADD — never folded across dots (a folded provenance cannot
|
|
56
|
+
* follow a dot that is later deleted on its own). */
|
|
13
57
|
export type RuleAddOrigin = "user" | "imported-cc" | "starter";
|
|
58
|
+
/** One add of one logical rule, carrying its own dot and its own provenance. */
|
|
14
59
|
export interface RuleAdd {
|
|
15
60
|
dot: RuleDot;
|
|
16
61
|
origin: RuleAddOrigin;
|
|
17
62
|
createdAt: string;
|
|
18
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* A persisted ALLOW rule for one tool.
|
|
66
|
+
*
|
|
67
|
+
* `adds` is a real set, not a scalar: two concurrently approved records for the same (rule, scope)
|
|
68
|
+
* each redeem their own dot, and a rule is live iff at least one of its adds survives the tombstones.
|
|
69
|
+
* Organization rules have the opposite polarity (deny/ask only) and never enter this type.
|
|
70
|
+
*/
|
|
19
71
|
export interface PersistedAllowRule {
|
|
72
|
+
/** Canonical rule text: `Bash(git status)` (exact) or `Bash(git status:*)` (prefix). */
|
|
20
73
|
rule: string;
|
|
21
74
|
tool: PersistedRuleTool;
|
|
22
75
|
match: PersistedRuleMatch;
|
|
76
|
+
/** What the matcher compares against — parsed out of `rule`, stored so no consumer re-parses. */
|
|
23
77
|
command: string;
|
|
24
78
|
scope: RuleScope;
|
|
25
79
|
adds: RuleAdd[];
|
|
26
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* A delete intent, in observed-remove form: it carries EVERY add dot for its (rule, scope) that was
|
|
83
|
+
* visible to the deleting replica at delete time. A single-dot tombstone cannot delete cleanly —
|
|
84
|
+
* concurrent replicas add the same rule text under different dots, and a tombstone that names only one
|
|
85
|
+
* of them lets the deleted rule come back. The merge criterion is dot IDENTITY, not a causal-order
|
|
86
|
+
* comparison (single dots across replicas have no order to compare): an add is live iff its dot appears
|
|
87
|
+
* in no tombstone for the same (rule, scope).
|
|
88
|
+
*/
|
|
27
89
|
export interface RuleTombstone {
|
|
28
90
|
rule: string;
|
|
29
91
|
scope: RuleScope;
|
|
30
92
|
removedDots: RuleDot[];
|
|
93
|
+
/** The delete operation's own dot — the operation's persistent identity. */
|
|
31
94
|
deletedBy: RuleDot;
|
|
32
95
|
}
|
|
96
|
+
/** Why a rule text was refused. Codes are stable so an import report can group by them. */
|
|
33
97
|
export type RuleRejectCode = "invalid.grammar" | "invalid.empty_command" | "invalid.not_simple_command" | "invalid.bare_interpreter_prefix" | "invalid.unbalanced_quotes" | "invalid.too_long" | "unsupported.tool" | "unsupported.wildcard";
|
|
34
98
|
export interface RuleReject {
|
|
35
99
|
code: RuleRejectCode;
|
|
36
100
|
message: string;
|
|
37
101
|
}
|
|
102
|
+
/** The parsed, canonical shape of one rule text — everything but the scope and the provenance. */
|
|
38
103
|
export interface ParsedAllowRule {
|
|
39
104
|
rule: string;
|
|
40
105
|
tool: PersistedRuleTool;
|
|
41
106
|
match: PersistedRuleMatch;
|
|
42
107
|
command: string;
|
|
43
108
|
}
|
|
109
|
+
/** Upper bound on a rule text. Rules are human-authored one-liners; anything longer is a mistake or an
|
|
110
|
+
* attempt to make the store unreadable. */
|
|
44
111
|
export declare const MAX_RULE_TEXT_CHARS = 512;
|
|
112
|
+
/**
|
|
113
|
+
* Interpreter names whose PREFIX form is refused everywhere a rule can enter the store (minting,
|
|
114
|
+
* import, file reload, every backend). `Bash(node:*)` reads as one narrow-looking rule and means "run
|
|
115
|
+
* any program you like" — that is not a shape a single click can be understood to have authorized.
|
|
116
|
+
*
|
|
117
|
+
* The check is on argv[0] of the prefix, not on the prefix being exactly one word: `node -e:*` is the
|
|
118
|
+
* same licence spelled longer. Deliberately strict-side — it costs the ability to persist a rule like
|
|
119
|
+
* `python manage.py migrate:*` (which an import reports as skipped rather than dropping silently), and
|
|
120
|
+
* an EXACT rule naming a whole interpreter command line stays legal, since it authorizes one command.
|
|
121
|
+
*/
|
|
45
122
|
export declare const BARE_INTERPRETER_NAMES: ReadonlySet<string>;
|
|
123
|
+
/**
|
|
124
|
+
* Parse one rule text into its canonical shape, or refuse it with a reason.
|
|
125
|
+
*
|
|
126
|
+
* This is the ONE validator every entry point shares — minting a suggestion, importing another
|
|
127
|
+
* product's settings, reloading a store file, and any future backend. A shape refused here cannot enter
|
|
128
|
+
* the store through any door.
|
|
129
|
+
*/
|
|
46
130
|
export declare function parseAllowRuleText(text: string): {
|
|
47
131
|
rule: ParsedAllowRule;
|
|
48
132
|
} | {
|
|
49
133
|
reject: RuleReject;
|
|
50
134
|
};
|
|
135
|
+
/** The canonical spelling of a rule for a (already folded) command and match form. */
|
|
51
136
|
export declare function formatAllowRuleText(command: string, match: PersistedRuleMatch): string;
|
|
137
|
+
/**
|
|
138
|
+
* Does this rule's command pattern admit `command`?
|
|
139
|
+
*
|
|
140
|
+
* `command` is the raw tool argument: the floor and the folding happen HERE, in that order, so no
|
|
141
|
+
* caller can accidentally match a folded compound. Returns false for anything that is not a single
|
|
142
|
+
* simple command — including every redirection form.
|
|
143
|
+
*/
|
|
52
144
|
export declare function ruleAdmitsCommand(rule: Pick<PersistedAllowRule, "match" | "command">, command: string): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Is `path` inside (or equal to) `root`? Word-boundary containment on the path separator, so `/a` does
|
|
147
|
+
* not contain `/ab`. Both sides are expected to be canonical already.
|
|
148
|
+
*/
|
|
53
149
|
export declare function pathWithinRoot(path: string, root: string): boolean;
|
|
150
|
+
/** Does a rule's scope cover a task running in `cwd`? A project rule needs a cwd to compare against;
|
|
151
|
+
* without one it covers nothing (fail-closed). */
|
|
54
152
|
export declare function scopeCoversCwd(scope: RuleScope, cwd: string | undefined): boolean;
|
|
153
|
+
/** Is this rule live — i.e. does it still carry at least one add? Deleted adds are removed by the store
|
|
154
|
+
* when tombstones are applied, so a rule with an empty `adds` is a rule that no longer exists. */
|
|
55
155
|
export declare function isRuleLive(rule: PersistedAllowRule): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Find the rule that admits this command for a task in `cwd`, or `undefined`.
|
|
158
|
+
*
|
|
159
|
+
* Order is defined only for reporting (the first match wins and its text is what the decision names);
|
|
160
|
+
* no rule is stronger than another — they are all the same one answer, "the person already said yes to
|
|
161
|
+
* this".
|
|
162
|
+
*/
|
|
56
163
|
export declare function findAdmittingRule(rules: readonly PersistedAllowRule[], call: {
|
|
57
164
|
tool: string;
|
|
58
165
|
command: string;
|
|
59
166
|
cwd: string | undefined;
|
|
60
167
|
}): PersistedAllowRule | undefined;
|
|
168
|
+
/** A rule the engine offers to mint, as carried on an approval card. */
|
|
61
169
|
export interface RuleSuggestion {
|
|
170
|
+
/** The canonical rule text the host would redeem. */
|
|
62
171
|
rule: string;
|
|
63
172
|
match: PersistedRuleMatch;
|
|
173
|
+
/** The command pattern in the rule, for a host that wants to render it without re-parsing. */
|
|
64
174
|
command: string;
|
|
65
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* The 1-2 candidates offered on an approval card for `command`.
|
|
178
|
+
*
|
|
179
|
+
* The exact form (this whole command line) is always offered when the command is matchable at all. A
|
|
180
|
+
* prefix form is offered only when it is a shape a person can read as narrow: the upstream multi-word
|
|
181
|
+
* convention (`git status`, `npm run`) is preferred over a bare program name, and no prefix at all is
|
|
182
|
+
* offered for an interpreter head — that candidate would be refused by the validator anyway, and
|
|
183
|
+
* offering a candidate the store will not accept is worse than offering one fewer.
|
|
184
|
+
*
|
|
185
|
+
* Returns an empty array for anything the rule lane cannot speak for (compounds, redirections,
|
|
186
|
+
* substitutions) — the card then simply carries no "don't ask again" option, which is the honest answer.
|
|
187
|
+
*/
|
|
66
188
|
export declare function suggestRulesForCommand(command: string): RuleSuggestion[];
|