@sema-agent/core 7.51.2 → 7.52.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 +504 -2
- package/dist/agents/agent-transcript-tool.d.ts +1 -1
- package/dist/agents/cascade.js +4 -0
- package/dist/agents/cumulative-stats.d.ts +3 -0
- package/dist/agents/cumulative-stats.js +3 -0
- package/dist/agents/list-agents-tool.d.ts +1 -1
- package/dist/agents/read-inbox-tool.d.ts +1 -1
- package/dist/agents/repair-loop.js +4 -0
- package/dist/agents/send-message-tool.d.ts +11 -16
- package/dist/agents/send-message-tool.js +11 -13
- package/dist/agents/subagent.js +6 -4
- package/dist/agents/tool-filter.d.ts +40 -13
- package/dist/agents/tool-filter.js +28 -14
- package/dist/bench/metrics.d.ts +1 -0
- package/dist/brain/anthropic.js +16 -12
- package/dist/brain/model-presets.d.ts +4 -5
- package/dist/brain/model-presets.js +4 -4
- package/dist/brain/open-responses.js +16 -12
- package/dist/brain/openai.js +16 -12
- package/dist/brain/request-params.d.ts +12 -33
- package/dist/brain/request-params.js +5 -2
- package/dist/brain/request-route.d.ts +9 -8
- package/dist/brain/request-route.js +1 -0
- package/dist/brain/route-adjudicator.d.ts +117 -47
- package/dist/brain/route-adjudicator.js +171 -42
- package/dist/brain/route-conformance.js +2 -2
- package/dist/brain/status-sink.d.ts +18 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/core/allow-entry.d.ts +40 -8
- package/dist/core/allow-entry.js +23 -16
- package/dist/core/auto-compaction.d.ts +23 -20
- package/dist/core/auto-compaction.js +37 -16
- package/dist/core/auto-promote.d.ts +2 -1
- package/dist/core/auto-promote.js +2 -2
- package/dist/core/caller-tool-name.d.ts +9 -0
- package/dist/core/caller-tool-name.js +13 -0
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +36 -12
- package/dist/core/checkpoint-store.js +1 -1
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +2 -1
- package/dist/core/credential-vocabulary.d.ts +33 -8
- package/dist/core/credential-vocabulary.js +32 -7
- package/dist/core/decision-receipt.d.ts +2 -2
- package/dist/core/delegation-frames.d.ts +9 -2
- package/dist/core/delegation-name.d.ts +18 -0
- package/dist/core/delegation-name.js +47 -0
- package/dist/core/engine-capabilities.d.ts +8 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +36 -19
- package/dist/core/frontmatter-fence.d.ts +10 -0
- package/dist/core/frontmatter-fence.js +6 -0
- package/dist/core/gate-fold.js +5 -23
- package/dist/core/gate-lanes.js +356 -270
- package/dist/core/governance-codes.d.ts +9 -4
- package/dist/core/governance-codes.js +9 -3
- package/dist/core/hooks.d.ts +276 -734
- package/dist/core/hooks.js +97 -192
- package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
- package/dist/{tools/fs → core}/image-admission.js +24 -6
- package/dist/core/mcp-connection-pool.d.ts +4 -1
- package/dist/core/mcp-connection-pool.js +7 -2
- package/dist/core/mcp-probe.d.ts +12 -9
- package/dist/core/mcp-probe.js +2 -2
- package/dist/core/mcp-server-spec.d.ts +16 -0
- package/dist/core/mcp.d.ts +11 -7
- package/dist/core/mcp.js +11 -5
- package/dist/core/memory-engine/data-plane.js +5 -1
- package/dist/core/memory-engine/distiller.js +16 -2
- package/dist/core/memory-engine/engine.d.ts +8 -11
- package/dist/core/memory-engine/engine.js +9 -5
- package/dist/core/memory-engine/frontmatter.d.ts +6 -0
- package/dist/core/memory-engine/frontmatter.js +19 -16
- package/dist/core/memory.d.ts +2 -2
- package/dist/core/mode-policy.d.ts +4 -0
- package/dist/core/mode-policy.js +7 -5
- package/dist/core/model-seat.d.ts +4 -1
- package/dist/core/mods/dollar.d.ts +109 -0
- package/dist/core/mods/dollar.js +154 -0
- package/dist/core/mods/host.d.ts +23 -0
- package/dist/core/mods/host.js +21 -0
- package/dist/core/mods/kernel/budget.d.ts +43 -0
- package/dist/core/mods/kernel/budget.js +83 -0
- package/dist/core/mods/kernel/dispatch.d.ts +100 -0
- package/dist/core/mods/kernel/dispatch.js +701 -0
- package/dist/core/mods/kernel/events.d.ts +242 -0
- package/dist/core/mods/kernel/events.js +210 -0
- package/dist/core/mods/kernel/index.d.ts +6 -0
- package/dist/core/mods/kernel/index.js +5 -0
- package/dist/core/mods/kernel/receive.d.ts +65 -0
- package/dist/core/mods/kernel/receive.js +123 -0
- package/dist/core/mods/kernel/registration.d.ts +131 -0
- package/dist/core/mods/kernel/registration.js +102 -0
- package/dist/core/mods/kernel/site-rules.d.ts +84 -0
- package/dist/core/mods/kernel/site-rules.js +104 -0
- package/dist/core/mods/stations.d.ts +350 -0
- package/dist/core/mods/stations.js +444 -0
- package/dist/core/own-property-receipt.js +11 -23
- package/dist/core/parent-spec-seat.d.ts +18 -7
- package/dist/core/parent-spec-seat.js +6 -2
- package/dist/core/path-identity.js +2 -1
- package/dist/core/permission-rule-syntax.d.ts +15 -0
- package/dist/core/permission-rule-syntax.js +24 -16
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +26 -15
- package/dist/core/prompt-section-seat.d.ts +15 -27
- package/dist/core/prompt-section-seat.js +73 -40
- package/dist/core/protocol-http-auth.d.ts +3 -1
- package/dist/core/protocol-http-auth.js +2 -0
- package/dist/core/reminder-mint.d.ts +2 -2
- package/dist/core/remote-env.d.ts +24 -12
- package/dist/core/remote-env.js +1 -0
- package/dist/core/resume-startup-cause.d.ts +16 -1
- package/dist/core/resume-startup-cause.js +4 -1
- package/dist/core/retired-keys.d.ts +4 -0
- package/dist/core/retired-keys.js +3 -0
- package/dist/core/runner/announce-once-ledger.d.ts +31 -1
- package/dist/core/runner/announce-once-ledger.js +22 -0
- package/dist/core/runner/assemble-result.d.ts +9 -0
- package/dist/core/runner/assemble-result.js +3 -1
- package/dist/core/runner/call-hook-stations.d.ts +23 -27
- package/dist/core/runner/call-hook-stations.js +58 -42
- package/dist/core/runner/caller-stop-gate.d.ts +25 -0
- package/dist/core/runner/caller-stop-gate.js +7 -0
- package/dist/core/runner/compaction-seams.d.ts +8 -22
- package/dist/core/runner/compaction-seams.js +28 -53
- package/dist/core/runner/construction-doors.js +11 -1
- package/dist/core/runner/contracts.d.ts +85 -83
- package/dist/core/runner/credential-origin-seat.d.ts +2 -0
- package/dist/core/runner/credential-origin-seat.js +19 -0
- package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
- package/dist/core/runner/gate-exit.d.ts +47 -36
- package/dist/core/runner/gate-exit.js +8 -2
- package/dist/core/runner/image.js +1 -2
- package/dist/core/runner/inline-image.js +1 -1
- package/dist/core/runner/notice-seat-input.d.ts +11 -0
- package/dist/core/runner/notice-seat-input.js +1 -0
- package/dist/core/runner/person-path-rows.js +1 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
- package/dist/core/runner/prepare-ask-lane.js +6 -15
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
- package/dist/core/runner/prepare-config-doors.d.ts +5 -0
- package/dist/core/runner/prepare-config-doors.js +9 -0
- package/dist/core/runner/prepare-defer-classify.js +3 -4
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
- package/dist/core/runner/prepare-gate-stations.js +11 -10
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
- package/dist/core/runner/prepare-inherited-gate.js +8 -21
- package/dist/core/runner/prepare-memory-engine-session.js +1 -1
- package/dist/core/runner/prepare-park-ask.js +10 -14
- package/dist/core/runner/prepare-policy-chain.d.ts +17 -15
- package/dist/core/runner/prepare-policy-chain.js +95 -100
- package/dist/core/runner/prepare-project-context.js +7 -10
- package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
- package/dist/core/runner/prepare-prompt-assembly.js +134 -101
- package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
- package/dist/core/runner/prepare-prompt-inputs.js +5 -7
- package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
- package/dist/core/runner/prepare-protocol-tools.js +2 -2
- package/dist/core/runner/prepare-safety-scan.js +6 -10
- package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
- package/dist/core/runner/prepare-suspend-saga.js +7 -5
- package/dist/core/runner/prepare-task.js +33 -15
- package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
- package/dist/core/runner/prepare-turn-wiring.js +29 -27
- package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
- package/dist/core/runner/prepare-wiring-manifest.js +7 -7
- package/dist/core/runner/resume-apply.d.ts +5 -2
- package/dist/core/runner/resume-apply.js +24 -42
- package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
- package/dist/core/runner/resume-checkpoint-screen.js +9 -1
- package/dist/core/runner/resume-preflight.js +38 -16
- package/dist/core/runner/retired-runner-deps.js +3 -1
- package/dist/core/runner/run-brain-sinks.d.ts +3 -2
- package/dist/core/runner/run-brain-sinks.js +5 -1
- package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
- package/dist/core/runner/run-compaction-machinery.js +7 -33
- package/dist/core/runner/run-harness-handlers.js +4 -2
- package/dist/core/runner/run-identity-wiring.d.ts +3 -1
- package/dist/core/runner/run-identity-wiring.js +3 -2
- package/dist/core/runner/run-leg.js +62 -84
- package/dist/core/runner/run-notification-lane.js +1 -0
- package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
- package/dist/core/runner/run-recovery-lanes.js +11 -10
- package/dist/core/runner/run-settle-and-teardown.js +9 -28
- package/dist/core/runner/run-stop-and-final-verify.js +18 -25
- package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
- package/dist/core/runner/run-terminal-adoption.js +33 -22
- package/dist/core/runner/run-tool-mount-facts.js +5 -3
- package/dist/core/runner/run-turn-boundary.js +23 -34
- package/dist/core/runner/runtask.d.ts +6 -7
- package/dist/core/runner/runtask.js +14 -72
- package/dist/core/runner/session-rule-policy.d.ts +5 -5
- package/dist/core/runner/session-rule-policy.js +13 -8
- package/dist/core/runner/side-call-account.d.ts +68 -0
- package/dist/core/runner/side-call-account.js +61 -0
- package/dist/core/runner/stream-halt-verbs.js +12 -17
- package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
- package/dist/core/runner/stream-steer-verb.d.ts +1 -1
- package/dist/core/runner/stream-steer-verb.js +20 -38
- package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
- package/dist/core/runner/sub-agent-deny-lists.js +228 -71
- package/dist/core/runner/tool-end-body.d.ts +5 -6
- package/dist/core/runner/tool-end-body.js +6 -4
- package/dist/core/runner/tool-not-executed.d.ts +15 -0
- package/dist/core/runner/tool-not-executed.js +30 -0
- package/dist/core/runner/tool-occurrence.d.ts +8 -7
- package/dist/core/runner/tool-occurrence.js +5 -5
- package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
- package/dist/core/runner-deps.d.ts +20 -28
- package/dist/core/safety-merge-corpus.js +18 -2
- package/dist/core/secret-env.d.ts +88 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +52 -16
- package/dist/core/session-policy-store.js +118 -28
- package/dist/core/session-reconcile.d.ts +4 -0
- package/dist/core/session-reconcile.js +2 -1
- package/dist/core/shell-read-silence.js +3 -32
- package/dist/core/side-query.d.ts +5 -3
- package/dist/core/skill-grant.d.ts +3 -2
- package/dist/core/skill-grant.js +17 -9
- package/dist/core/skills-directory.d.ts +31 -11
- package/dist/core/skills-directory.js +655 -104
- package/dist/core/task-event.d.ts +13 -3
- package/dist/core/task-result.d.ts +8 -0
- package/dist/core/task-spec.d.ts +20 -19
- package/dist/core/task-stream.d.ts +14 -4
- package/dist/core/tighten-task-spec.d.ts +4 -3
- package/dist/core/tighten-task-spec.js +2 -4
- package/dist/core/tool-check-reading.d.ts +74 -0
- package/dist/core/tool-check-reading.js +80 -0
- package/dist/core/tool-policy.d.ts +59 -18
- package/dist/core/tool-policy.js +77 -12
- package/dist/core/tool-registry.d.ts +20 -0
- package/dist/core/tool-registry.js +4 -0
- package/dist/core/tool-spec.d.ts +10 -6
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +51 -32
- package/dist/core/unc-spelling.d.ts +10 -0
- package/dist/core/unc-spelling.js +3 -0
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-egress.js +4 -1
- package/dist/core/user-attachment.d.ts +12 -2
- package/dist/core/user-attachment.js +17 -2
- package/dist/core/wiring-manifest.d.ts +14 -13
- package/dist/core/wiring-manifest.js +2 -2
- package/dist/engine/compaction/utils.js +1 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
- package/dist/engine/execution-env/symlink-chain.js +1 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/engine/harness/agent-harness.js +3 -3
- package/dist/engine/harness/types.d.ts +17 -16
- package/dist/engine/harness/types.js +7 -1
- package/dist/engine/llm/types.d.ts +12 -7
- package/dist/engine/loop/agent-loop.js +1 -1
- package/dist/engine/loop/types.d.ts +1 -1
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/import-validate.js +3 -10
- package/dist/engine/session/session.d.ts +4 -4
- package/dist/engine/session/session.js +4 -4
- package/dist/index.d.ts +20 -16
- package/dist/index.js +13 -11
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/run-spec.d.ts +4 -6
- package/dist/orchestration/run-spec.js +39 -63
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow.d.ts +11 -3
- package/dist/orchestration/workflow.js +11 -2
- package/dist/prompt-assembly/artifact-store.d.ts +37 -56
- package/dist/prompt-assembly/artifact-store.js +31 -49
- package/dist/prompt-assembly/artifact.d.ts +89 -76
- package/dist/prompt-assembly/artifact.js +167 -146
- package/dist/prompt-assembly/assemble.d.ts +34 -29
- package/dist/prompt-assembly/assemble.js +33 -220
- package/dist/prompt-assembly/bands.d.ts +20 -0
- package/dist/prompt-assembly/bands.js +18 -0
- package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
- package/dist/prompt-assembly/catalog/constitution.js +69 -0
- package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
- package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
- package/dist/prompt-assembly/catalog/index.d.ts +11 -0
- package/dist/prompt-assembly/catalog/index.js +59 -0
- package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
- package/dist/prompt-assembly/catalog/modes.js +137 -0
- package/dist/prompt-assembly/catalog/role.d.ts +10 -0
- package/dist/prompt-assembly/catalog/role.js +157 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
- package/dist/prompt-assembly/catalog/schema.js +25 -0
- package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
- package/dist/prompt-assembly/catalog/sections.js +98 -0
- package/dist/prompt-assembly/catalog/team.d.ts +4 -0
- package/dist/prompt-assembly/catalog/team.js +258 -0
- package/dist/prompt-assembly/composer.d.ts +24 -28
- package/dist/prompt-assembly/composer.js +124 -46
- package/dist/prompt-assembly/epoch.d.ts +89 -79
- package/dist/prompt-assembly/epoch.js +115 -142
- package/dist/prompt-assembly/explain.d.ts +58 -34
- package/dist/prompt-assembly/explain.js +21 -24
- package/dist/prompt-assembly/overlay.d.ts +127 -0
- package/dist/prompt-assembly/overlay.js +301 -0
- package/dist/prompt-assembly/renderers.d.ts +21 -0
- package/dist/prompt-assembly/renderers.js +39 -0
- package/dist/prompt-assembly/section-usage.d.ts +3 -3
- package/dist/prompt-assembly/section-usage.js +1 -1
- package/dist/prompt-assembly/select.d.ts +56 -0
- package/dist/prompt-assembly/select.js +69 -0
- package/dist/prompt-assembly/sugar.d.ts +31 -0
- package/dist/prompt-assembly/sugar.js +20 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
- package/dist/prompt-assembly/types.d.ts +45 -148
- package/dist/prompts/coordinator.d.ts +2 -21
- package/dist/prompts/coordinator.js +3 -255
- package/dist/prompts/cowork.d.ts +27 -16
- package/dist/prompts/cowork.js +2 -23
- package/dist/prompts/default.d.ts +38 -500
- package/dist/prompts/default.js +20 -285
- package/dist/prompts/supervisor.d.ts +6 -71
- package/dist/prompts/supervisor.js +6 -117
- package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
- package/dist/tools/fs/dir-entries.d.ts +4 -2
- package/dist/tools/fs/dir-entries.js +16 -4
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +22 -11
- package/dist/tools/fs/fs-pdf.js +17 -13
- package/dist/tools/fs/fs-read.js +4 -3
- package/dist/tools/fs/fs-shared.d.ts +1 -10
- package/dist/tools/fs/fs-shared.js +3 -4
- package/dist/tools/fs/fs-write.js +2 -1
- package/dist/tools/fs/notebook.js +1 -1
- package/dist/tools/fs/pdf.d.ts +20 -10
- package/dist/tools/fs/pdf.js +19 -10
- package/dist/tools/fs/program-read-model.d.ts +68 -8
- package/dist/tools/fs/program-read-model.js +112 -33
- package/dist/tools/fs/program-read-model.rows.js +30 -10
- package/dist/tools/fs/read-deny-home.d.ts +25 -9
- package/dist/tools/fs/read-deny-home.js +28 -27
- package/dist/tools/fs/read-deny.d.ts +41 -11
- package/dist/tools/fs/read-deny.js +51 -5
- package/dist/tools/fs/safety.d.ts +4 -26
- package/dist/tools/fs/safety.js +10 -32
- package/dist/tools/fs/search.js +15 -10
- package/dist/tools/fs/shell-read-argv.d.ts +3 -2
- package/dist/tools/fs/shell-read-argv.js +118 -38
- package/dist/tools/fs/shell-read-establish.d.ts +5 -5
- package/dist/tools/fs/shell-read-establish.js +11 -61
- package/dist/tools/fs/shell-read-identity.js +6 -3
- package/dist/tools/fs/shell-read-judge.d.ts +13 -15
- package/dist/tools/fs/shell-read-judge.js +18 -4
- package/dist/tools/fs/shell-read-plan.d.ts +2 -2
- package/dist/tools/web.js +1 -1
- package/package.json +13 -1
- package/test/export-surface.snapshot.json +242 -99
- package/wire-changes/core@7.52.0.json +5859 -0
- package/dist/core/hook-seats.d.ts +0 -66
- package/dist/core/hook-seats.js +0 -37
- package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
- package/dist/prompt-assembly/packs/sema-default.js +0 -147
- package/dist/prompts/simple-sections.d.ts +0 -58
- package/dist/prompts/simple-sections.js +0 -43
|
@@ -21,10 +21,12 @@
|
|
|
21
21
|
* - **a row keeps its grammar**: the write judge stamps the grammar it read the entries in ({@link SESSION_RULES_GRAMMAR}), and
|
|
22
22
|
* every station reads a row's entries in the grammar the row carries ({@link readSessionRulesShape}) — a row stored before
|
|
23
23
|
* the stamp keeps the reading it was written under, so an upgrade never turns a stored entry into a grant.
|
|
24
|
+
* - **rules are read as rules or not at all**: rules whose shape cannot be read, or whose toolDeny holds an entry no grammar
|
|
25
|
+
* reads ({@link unreadableDenyEntries}), refuse every call until an operator writes them afresh — never read as fewer.
|
|
24
26
|
* - **NOT A SANDBOX**: a shell command rule reads the command line the call carries (every program run in its connector
|
|
25
27
|
* chain); the real isolation boundary is the executionEnv, never these rules.
|
|
26
28
|
*/
|
|
27
|
-
import { compileAllowList, type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
|
|
29
|
+
import { compileAllowList, type AllowEntryInert, type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
|
|
28
30
|
/** The per-session rules. All optional; an absent bucket imposes no constraint of that kind. */
|
|
29
31
|
export interface SessionPermissionRules {
|
|
30
32
|
/** If set, ONLY the calls these entries admit are allowed — a narrowing allowlist: a call no entry admits is refused.
|
|
@@ -51,11 +53,16 @@ export type SessionRuleEntryField = {
|
|
|
51
53
|
/**
|
|
52
54
|
* The rule grammar this engine reads a session's rule entries in: the `grammar` the write judge stamps on every rules value it
|
|
53
55
|
* judges ({@link judgeSessionRulesWrite}), so every store that writes through it keeps the stamp on the row. It counts
|
|
54
|
-
* revisions of what an entry MEANS and moves only when that changes
|
|
55
|
-
* a bucket's entries were tool names compared as written, so an allow entry the
|
|
56
|
-
* (`Bash(rm:*)`, `Bash(*)`) named no tool and admitted nothing
|
|
56
|
+
* revisions of what an entry MEANS and moves only when that changes:
|
|
57
|
+
* - 0, the reading before the one entry reader: a bucket's entries were tool names compared as written, so an allow entry the
|
|
58
|
+
* reader now reads as speaking for another name (`Bash(rm:*)`, `Bash(*)`) named no tool and admitted nothing — and so did
|
|
59
|
+
* `*`, one more name no tool carried, which voided nothing beside it;
|
|
60
|
+
* - 1, the one entry reader taking its tool name from the lenient rule parser (trimmed before a `(`, any character but a
|
|
61
|
+
* blank, a parenthesis or a control / format character);
|
|
62
|
+
* - 2, the rule syntax's tool-name head (`ruleSpellingOf`): a name is read as written, in the characters names are minted in.
|
|
63
|
+
* An entry this grammar reads as no rule in a row an earlier grammar wrote reads as `earlier_grammar` ({@link compileSessionRuleBucket}).
|
|
57
64
|
*/
|
|
58
|
-
export declare const SESSION_RULES_GRAMMAR =
|
|
65
|
+
export declare const SESSION_RULES_GRAMMAR = 2;
|
|
59
66
|
/** {@link SessionPermissionRules} plus the monotonic `rev` the store stamps (the OCC key). */
|
|
60
67
|
export interface StoredSessionRules extends SessionPermissionRules {
|
|
61
68
|
rev: number;
|
|
@@ -111,7 +118,8 @@ export declare class SessionPolicyError extends Error {
|
|
|
111
118
|
export interface SessionPolicyStoreOptions {
|
|
112
119
|
/** The names this deployment mounts its OWN tools under — the roster the store reads a rule entry's tool name against
|
|
113
120
|
* (`readRuleEntry`): a retired name declared here is an ordinary name to the write and to the loosening check alike.
|
|
114
|
-
* Absent = none.
|
|
121
|
+
* Absent = none. Each name keeps the caller tool name contract a deployment tool's name keeps at the mount
|
|
122
|
+
* (`callerToolNameProblem`: a name a rule can name, no `__`); one that does not is refused at construction. */
|
|
115
123
|
mountedToolNames?: Iterable<string>;
|
|
116
124
|
}
|
|
117
125
|
/** A store's declared roster from {@link SessionPolicyStoreOptions.mountedToolNames} — the one reader both bundled stores
|
|
@@ -205,20 +213,46 @@ export declare function readSessionRulesShape(value: unknown): {
|
|
|
205
213
|
* stored form is `..`-free and the tighten diff compares the form the runtime resolves. Both bundled stores and a durable twin
|
|
206
214
|
* stamp exactly this, so a structuredClone and a JSON round-trip of it are byte-identical. */
|
|
207
215
|
export declare function normalizeRules(rules: unknown): SessionPermissionRules;
|
|
216
|
+
/**
|
|
217
|
+
* THE ONE ANSWER to "can these rules' denies be read?": the `toolDeny` entries the list reading ({@link compileSessionRuleBucket},
|
|
218
|
+
* in the rules' own grammar) reads as no rule at all. Rules whose deny bucket holds one cannot be read, and rules that cannot be
|
|
219
|
+
* read are never read as fewer: they refuse every call (the run-time layer) and a non-operator write over them is a loosening
|
|
220
|
+
* ({@link loosenReasons}) — until an operator writes them afresh, and the write judge refuses such an entry. Unlike a definition
|
|
221
|
+
* file a plugin ships, which its user cannot edit, a session's rules are written by the session's own operator and can be
|
|
222
|
+
* rewritten, so a deny nobody can read costs one write instead of quietly guarding nothing. An entry the grammar reads that names
|
|
223
|
+
* no mounted tool (or `*`) is not here: it names nothing and refuses nothing, the rest of the bucket refusing as written.
|
|
224
|
+
* `roster` as in {@link compileSessionRuleBucket}; it never changes this answer (a spelling is read before a name is judged).
|
|
225
|
+
*/
|
|
226
|
+
export declare function unreadableDenyEntries(rules: SessionRulesInGrammar, roster?: ReadonlySet<string>): AllowEntryInert[];
|
|
208
227
|
/**
|
|
209
228
|
* THE LIST READING of one rule bucket of a rules value the shape reading read — what the run-time layer enforces, the tighten
|
|
210
229
|
* comparison compares and the write judge refuses on: `compileAllowList` over the one entry reader, in the bucket's direction,
|
|
211
|
-
* against `roster` when given, in the grammar the value is written in. An entry its grammar reads as nothing
|
|
212
|
-
*
|
|
230
|
+
* against `roster` when given, in the grammar the value is written in. An entry its grammar reads as nothing
|
|
231
|
+
* ({@link earlierGrammarReads}) is moved to the entries that read as nothing, reason `earlier_grammar`, before the list is
|
|
232
|
+
* compiled: it is one entry that reads as nothing, and the list beside it is read as written. The move is an ENTRY's reading and
|
|
233
|
+
* never the list's: an allow list a `*` voids (in any spelling the voiding test reads as `*`, `* (*)` included) is voided in
|
|
234
|
+
* every grammar from 1 on, so in such a row the list is read whole, as written — only grammar 0, where `*` was one more name, takes
|
|
235
|
+
* it out alone.
|
|
213
236
|
*/
|
|
214
237
|
export declare function compileSessionRuleBucket(rules: SessionRulesInGrammar, field: SessionRuleEntryField, roster?: ReadonlySet<string>): ReturnType<typeof compileAllowList>;
|
|
215
|
-
/**
|
|
216
|
-
*
|
|
217
|
-
|
|
238
|
+
/**
|
|
239
|
+
* The advisory for the entries of one bucket a stored row's earlier grammar reads as nothing ({@link compileSessionRuleBucket},
|
|
240
|
+
* `earlier_grammar`; `grammar` = the row's): the session, the bucket, the entries as written, what they do and the way out. An
|
|
241
|
+
* entry this engine's grammar reads as no rule is named with the reader's word — in `toolAllow` it admits nothing, in `toolDeny`
|
|
242
|
+
* the rules cannot be read and every call is refused ({@link unreadableDenyEntries}); the way out is a rewrite leaving it out
|
|
243
|
+
* (the write judge refuses an entry it cannot read). A grammar-0 allow entry that named no tool there is told as it always was:
|
|
244
|
+
* to make a parenthesized one the rule it now spells, or (`*`, which is no allow rule in any grammar) to leave it out.
|
|
245
|
+
*/
|
|
246
|
+
export declare function earlierGrammarAdvisory(sessionId: string, entries: readonly string[], field: SessionRuleEntryField, grammar: number): string;
|
|
247
|
+
/** The clause a run-time refusal carries when the allowlist it judged by holds entries its earlier grammar reads as nothing
|
|
248
|
+
* ({@link compileSessionRuleBucket}; `grammar` = the row's): the grammar the rules are stored in, then which entries name no tool
|
|
249
|
+
* there (grammar 0: `*` by its spelling, the parenthesized ones counted) and how many this engine's grammar reads as no rule —
|
|
250
|
+
* counted, since a stored entry's own text is data and never reaches a sentence the model reads. Empty when none. */
|
|
251
|
+
export declare function earlierGrammarRefusalClause(entries: readonly string[], grammar: number): string;
|
|
218
252
|
/** A layer's delivery of the entries its snapshots' earlier grammar reads as nothing ({@link compileSessionRuleBucket},
|
|
219
|
-
* `earlier_grammar`)
|
|
220
|
-
* throwing sink never changes what the snapshot enforces. */
|
|
221
|
-
export declare function earlierGrammarAnnouncer(sink: ((entries: readonly string[]) => void) | undefined): (entries: readonly string[]) => void;
|
|
253
|
+
* `earlier_grammar`), one bucket at a time with the grammar its row is written in, to `sink`: once per distinct set per layer
|
|
254
|
+
* (the ledger is this closure's), an empty set is no news, and a throwing sink never changes what the snapshot enforces. */
|
|
255
|
+
export declare function earlierGrammarAnnouncer(sink: ((entries: readonly string[], field: SessionRuleEntryField, grammar: number) => void) | undefined): (entries: readonly string[], field: SessionRuleEntryField, grammar: number) => void;
|
|
222
256
|
/**
|
|
223
257
|
* THE ONE WRITE JUDGE every store applies before it stamps a row (both bundled stores, and a durable backend at the same point):
|
|
224
258
|
* the rules to stamp, or a {@link SessionPolicyError}, in this order — `conflict` (a stale `expectedRev` against `prior`'s rev),
|
|
@@ -234,9 +268,11 @@ export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null,
|
|
|
234
268
|
};
|
|
235
269
|
/**
|
|
236
270
|
* Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op), over the EFFECTIVE
|
|
237
|
-
* entries of each rule bucket — the list as the run-time layer enforces it ({@link effectiveEntries}).
|
|
271
|
+
* entries of each rule bucket — the list as the run-time layer enforces it ({@link effectiveEntries}). Rules whose toolDeny holds
|
|
272
|
+
* an entry no grammar reads ({@link unreadableDenyEntries}) refuse every call: as `next` they loosen nothing, and as `prior`
|
|
273
|
+
* every readable `next` loosens them (one reason, naming the entries). Any other entry that reads as
|
|
238
274
|
* nothing is in neither side's effect, so removing one relaxes nothing and adding one widens nothing (the way a session's owner
|
|
239
|
-
* takes out an entry that names nothing); an allowlist a `*` voids admits nothing, so dropping the `*` widens. toolDeny: removing an effective entry loosens. toolAllow: removing the list
|
|
275
|
+
* takes out an entry that names nothing); an allowlist a `*` voids (in this engine's grammar; in grammar 0 a `*` is one such entry beside the rest) admits nothing, so dropping the `*` widens. toolDeny: removing an effective entry loosens. toolAllow: removing the list
|
|
240
276
|
* (→ everything allowed) or adding an effective entry outside the prior set loosens; prior-undefined → any defined next only
|
|
241
277
|
* narrows. allowDirs: removing confinement, or adding a dir not within some prior dir, loosens (coarse raw containment).
|
|
242
278
|
* Entries compare as written, each side's in the grammar that side is written in (a stored row's entry that its grammar reads as
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
2
|
import { isWinFormPath } from "../tools/fs/safety.js";
|
|
3
|
-
import { NAMESPACED_NAME_SHAPES
|
|
4
|
-
import {
|
|
3
|
+
import { NAMESPACED_NAME_SHAPES } from "./protocol-table.js";
|
|
4
|
+
import { callerToolNameProblem } from "./caller-tool-name.js";
|
|
5
|
+
import { compileAllowList, compiledEntryName, inertEntryPhrase, readRuleEntry, renderListEntry, rootFacingToolNames } from "./allow-entry.js";
|
|
5
6
|
import { valueTypeLabel } from "./tool-name-list.js";
|
|
6
7
|
const SESSION_RULE_BUCKETS = { toolAllow: "allow", toolDeny: "deny", allowDirs: "dirs" };
|
|
7
8
|
const BUCKETS = Object.keys(SESSION_RULE_BUCKETS);
|
|
8
9
|
const ENTRY_FIELDS = BUCKETS.filter((f) => SESSION_RULE_BUCKETS[f] !== "dirs");
|
|
9
10
|
const STORE_STAMPS = new Set(["rev", "gen", "grammar"]);
|
|
10
|
-
export const SESSION_RULES_GRAMMAR =
|
|
11
|
+
export const SESSION_RULES_GRAMMAR = 2;
|
|
11
12
|
export class SessionPolicyError extends Error {
|
|
12
13
|
code;
|
|
13
14
|
detail;
|
|
@@ -29,10 +30,11 @@ export function readMountedToolNames(value) {
|
|
|
29
30
|
refuse("must be an iterable of tool names (an array or a Set), not a single value");
|
|
30
31
|
const names = new Set();
|
|
31
32
|
for (const n of value) {
|
|
32
|
-
if (typeof n !== "string"
|
|
33
|
-
refuse(`holds ${JSON.stringify(n) ?? String(n)}, which is not a
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (typeof n !== "string")
|
|
34
|
+
refuse(`holds ${JSON.stringify(n) ?? String(n)}, which is not a tool name`);
|
|
35
|
+
const problem = callerToolNameProblem(n);
|
|
36
|
+
if (problem !== undefined)
|
|
37
|
+
refuse(`holds ${JSON.stringify(n)}: ${problem}`);
|
|
36
38
|
names.add(n);
|
|
37
39
|
}
|
|
38
40
|
return names;
|
|
@@ -116,34 +118,103 @@ function canonicalRules(read) {
|
|
|
116
118
|
}
|
|
117
119
|
return out;
|
|
118
120
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
+
function earlierGrammarReads(grammar, field, entry, reading) {
|
|
122
|
+
if (grammar >= SESSION_RULES_GRAMMAR)
|
|
123
|
+
return false;
|
|
124
|
+
if (reading.kind === "inert" && reading.reason === "grammar")
|
|
125
|
+
return true;
|
|
126
|
+
if (grammar !== 0 || SESSION_RULE_BUCKETS[field] !== "allow")
|
|
127
|
+
return false;
|
|
128
|
+
return reading.kind === "row" ? compiledEntryName(reading.compiled) !== entry : reading.reason === "wildcard_all";
|
|
129
|
+
}
|
|
130
|
+
function readsAsNoRule(entry, field) {
|
|
131
|
+
const reading = readRuleEntry(entry, SESSION_RULE_BUCKETS[field]);
|
|
132
|
+
return reading.kind === "inert" && reading.reason === "grammar";
|
|
133
|
+
}
|
|
134
|
+
const NO_RULE_REASONS = new Set(["grammar", "earlier_grammar"]);
|
|
135
|
+
export function unreadableDenyEntries(rules, roster) {
|
|
136
|
+
return compileSessionRuleBucket(rules, "toolDeny", roster).inert.filter((i) => NO_RULE_REASONS.has(i.reason));
|
|
137
|
+
}
|
|
138
|
+
function readsAsWildcard(entry) {
|
|
139
|
+
return compileAllowList([entry], "allow").inert.some((i) => i.reason === "wildcard_all");
|
|
140
|
+
}
|
|
141
|
+
const STAR_IS_NO_ALLOW_RULE = "it is no allow rule in any grammar and a write holding it is refused";
|
|
142
|
+
function starWayOut(onlyStars, others, left) {
|
|
143
|
+
return onlyStars
|
|
144
|
+
? `"*" is no allow rule in any grammar (a write holding it is refused); to drop it, write the session's rules afresh without it ${REWRITE_THROUGH}; ${left}`
|
|
145
|
+
: `${others}, leaving "*" out — ${STAR_IS_NO_ALLOW_RULE}; ${left}`;
|
|
121
146
|
}
|
|
122
147
|
export function compileSessionRuleBucket(rules, field, roster) {
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
148
|
+
const direction = SESSION_RULE_BUCKETS[field];
|
|
149
|
+
const entries = rules[field] ?? [];
|
|
150
|
+
const earlier = new Set(entries.filter((e) => earlierGrammarReads(rules.grammar, field, e, readRuleEntry(e, direction, roster))));
|
|
151
|
+
const whole = compileAllowList(entries, direction, roster);
|
|
152
|
+
const voided = direction === "allow" && whole.inert.some((i) => i.reason === "wildcard_all");
|
|
153
|
+
if (earlier.size === 0 || (voided && rules.grammar > 0))
|
|
154
|
+
return whole;
|
|
155
|
+
const list = compileAllowList(entries.filter((e) => !earlier.has(e)), direction, roster);
|
|
156
|
+
return { rows: list.rows, inert: [...list.inert, ...entries.filter((e) => earlier.has(e)).map((entry) => ({ entry, reason: "earlier_grammar" }))] };
|
|
129
157
|
}
|
|
130
158
|
const ADVISORY_ENTRIES_SHOWN = 5;
|
|
131
|
-
|
|
132
|
-
const one = entries.length === 1;
|
|
133
|
-
const shown = entries.slice(0, ADVISORY_ENTRIES_SHOWN).map(renderListEntry).join(", ");
|
|
159
|
+
function shownEntries(entries, render) {
|
|
134
160
|
const rest = entries.length > ADVISORY_ENTRIES_SHOWN ? ` and ${entries.length - ADVISORY_ENTRIES_SHOWN} more` : "";
|
|
135
|
-
return (
|
|
136
|
-
|
|
161
|
+
return `${entries.slice(0, ADVISORY_ENTRIES_SHOWN).map(render).join(", ")}${rest}`;
|
|
162
|
+
}
|
|
163
|
+
const REWRITE_THROUGH = `through this engine (an operator write of a new value without the stored row's rev; the write stamps grammar ${SESSION_RULES_GRAMMAR})`;
|
|
164
|
+
export function earlierGrammarAdvisory(sessionId, entries, field, grammar) {
|
|
165
|
+
const noRule = entries.filter((e) => readsAsNoRule(e, field));
|
|
166
|
+
const namedNoTool = entries.filter((e) => !readsAsNoRule(e, field));
|
|
167
|
+
const sentences = [...(namedNoTool.length > 0 ? [namedNoToolInGrammar0(namedNoTool)] : []), ...(noRule.length > 0 ? [noRuleInThisGrammar(noRule, field, grammar)] : [])];
|
|
168
|
+
return `session permission rules of session ${JSON.stringify(sessionId)}: ${sentences.join(" ")}`;
|
|
169
|
+
}
|
|
170
|
+
function namedNoToolInGrammar0(entries) {
|
|
171
|
+
const one = entries.length === 1;
|
|
172
|
+
const wildcard = entries.some(readsAsWildcard);
|
|
173
|
+
const rules = entries.some((e) => !readsAsWildcard(e));
|
|
174
|
+
const left = `left as stored, ${one ? "it keeps" : "they keep"} admitting nothing.`;
|
|
175
|
+
const way = !wildcard
|
|
176
|
+
? `To make ${one ? "it an allow rule" : "them allow rules"}, write the session's rules afresh ${REWRITE_THROUGH}; ${left}`
|
|
177
|
+
: starWayOut(!rules, `To make the parenthesized ones allow rules, write the session's rules afresh ${REWRITE_THROUGH}`, left);
|
|
178
|
+
return (`toolAllow ${shownEntries(entries, renderListEntry)} ${one ? "was" : "were"} stored before the rule grammar stamp, when session rules compared an entry as a tool name as written, so ${wildcard ? 'an entry that is no bare tool name (a parenthesized rule, or "*")' : "a parenthesized entry"} named no tool — ` +
|
|
179
|
+
`${one ? "it admits" : "they admit"} nothing, as when written${wildcard ? "; the rest of the list is read as written, not voided" : ""}. ${way}`);
|
|
180
|
+
}
|
|
181
|
+
function noRuleInThisGrammar(entries, field, grammar) {
|
|
182
|
+
const one = entries.length === 1;
|
|
183
|
+
const stored = `${field} ${shownEntries(entries, (entry) => inertEntryPhrase({ entry, reason: "grammar" }))} ${one ? "was" : "were"} stored in rule grammar ${grammar}${grammar === 0 ? ", before the rule grammar stamp" : ""}, and this engine's rule grammar (${SESSION_RULES_GRAMMAR}) reads ${one ? "it" : "them"} as no rule`;
|
|
184
|
+
const way = `write the session's rules afresh ${REWRITE_THROUGH}, leaving ${one ? "it" : "them"} out or spelling the tool's name as a rule spells one — the write judge refuses an entry it cannot read`;
|
|
185
|
+
if (SESSION_RULE_BUCKETS[field] === "deny") {
|
|
186
|
+
return `${stored}: rules whose toolDeny holds an entry no grammar reads cannot be read, so every call is refused until they are rewritten. To lift that, ${way}, and a non-operator write cannot loosen rules that refuse every call.`;
|
|
187
|
+
}
|
|
188
|
+
const stars = entries.filter(readsAsWildcard).length;
|
|
189
|
+
const left = `left as stored, ${one ? "it keeps" : "they keep"} admitting nothing.`;
|
|
190
|
+
const wayOut = stars === 0
|
|
191
|
+
? `To allow what ${one ? "it" : "they"} meant, ${way}; ${left}`
|
|
192
|
+
: starWayOut(stars === entries.length, `To allow what the others meant, write the session's rules afresh ${REWRITE_THROUGH}, spelling each tool's name as a rule spells one`, left);
|
|
193
|
+
return `${stored} — ${one ? "it admits" : "they admit"} nothing, and the rest of the list is read as written. ${wayOut}`;
|
|
194
|
+
}
|
|
195
|
+
export function earlierGrammarRefusalClause(entries, grammar) {
|
|
196
|
+
if (entries.length === 0)
|
|
197
|
+
return "";
|
|
198
|
+
const noRule = entries.filter((e) => readsAsNoRule(e, "toolAllow")).length;
|
|
199
|
+
const namedNoTool = entries.filter((e) => !readsAsNoRule(e, "toolAllow"));
|
|
200
|
+
const stars = namedNoTool.filter(readsAsWildcard).length;
|
|
201
|
+
const others = namedNoTool.length - stars;
|
|
202
|
+
const named = [...(stars > 0 ? ['"*"'] : []), ...(others > 0 ? [`${others} parenthesized ${others === 1 ? "entry" : "entries"}`] : [])];
|
|
203
|
+
const clauses = [
|
|
204
|
+
...(namedNoTool.length > 0 ? [`where ${named.join(" and ")} ${namedNoTool.length === 1 ? "names no tool and admits" : "name no tool and admit"} nothing`] : []),
|
|
205
|
+
...(noRule > 0 ? [`${namedNoTool.length > 0 ? "and " : "where "}${noRule} ${noRule === 1 ? "entry is" : "entries are"} no rule in this engine's rule grammar (${SESSION_RULES_GRAMMAR}) and ${noRule === 1 ? "admits" : "admit"} nothing`] : []),
|
|
206
|
+
];
|
|
207
|
+
return `; the session's rules are stored in rule grammar ${grammar}, ${clauses.join(" ")}`;
|
|
137
208
|
}
|
|
138
209
|
export function earlierGrammarAnnouncer(sink) {
|
|
139
210
|
const announced = new Set();
|
|
140
|
-
return (entries) => {
|
|
141
|
-
const key = JSON.stringify(entries);
|
|
211
|
+
return (entries, field, grammar) => {
|
|
212
|
+
const key = JSON.stringify([field, grammar, entries]);
|
|
142
213
|
if (sink === undefined || entries.length === 0 || announced.has(key))
|
|
143
214
|
return;
|
|
144
215
|
announced.add(key);
|
|
145
216
|
try {
|
|
146
|
-
sink(entries);
|
|
217
|
+
sink(entries, field, grammar);
|
|
147
218
|
}
|
|
148
219
|
catch {
|
|
149
220
|
}
|
|
@@ -153,23 +224,36 @@ function effectiveEntries(rules, field, mountedToolNames) {
|
|
|
153
224
|
return compileSessionRuleBucket(rules, field, mountedToolNames).rows.map((r) => r.entry);
|
|
154
225
|
}
|
|
155
226
|
function assertEntriesRead(rules, mountedToolNames) {
|
|
227
|
+
const known = rootFacingToolNames(mountedToolNames);
|
|
156
228
|
for (const field of ENTRY_FIELDS) {
|
|
157
229
|
for (const entry of rules[field] ?? []) {
|
|
158
|
-
const reading = readRuleEntry(entry, SESSION_RULE_BUCKETS[field],
|
|
159
|
-
const read =
|
|
230
|
+
const reading = readRuleEntry(entry, SESSION_RULE_BUCKETS[field], known);
|
|
231
|
+
const read = earlierGrammarReads(rules.grammar, field, entry, reading) ? { kind: "inert", reason: "earlier_grammar" } : reading;
|
|
160
232
|
if (read.kind === "row")
|
|
161
233
|
continue;
|
|
162
234
|
const { kind: _kind, ...why } = read;
|
|
163
235
|
void _kind;
|
|
164
236
|
const way = why.reason === "earlier_grammar"
|
|
165
|
-
?
|
|
166
|
-
|
|
237
|
+
? readsAsNoRule(entry, field)
|
|
238
|
+
? ` These rules are written in grammar ${rules.grammar} (a stored row), and this engine's grammar (${SESSION_RULES_GRAMMAR}) reads this entry as no rule (grammar) — ` +
|
|
239
|
+
(field === "toolAllow" && readsAsWildcard(entry) ? `leave the entry out: it spells "*", and ${STAR_IS_NO_ALLOW_RULE}.` : "leave the entry out, or spell the tool's name as a rule spells one.")
|
|
240
|
+
: ` These rules are written in grammar ${rules.grammar} (a stored row from before the grammar stamp), in which this entry names no tool.` +
|
|
241
|
+
(readsAsWildcard(entry)
|
|
242
|
+
? ` It is no allow rule in grammar ${SESSION_RULES_GRAMMAR} either (a write holding it is refused) — leave the entry out.`
|
|
243
|
+
: ` To make it an allow rule, write the rules afresh — a value without the stored row's rev; the write stamps grammar ${SESSION_RULES_GRAMMAR} — or leave the entry out.`)
|
|
244
|
+
: why.verdict === "retired" || why.reason === "near_miss"
|
|
167
245
|
? ` If this deployment mounts its own tool under ${renderListEntry(entry)}, declare it in the store's mountedToolNames.`
|
|
168
246
|
: why.currentName === undefined
|
|
169
247
|
? ` An entry is a tool name (Read; a protocol tool is named ${NAMESPACED_NAME_SHAPES}), a protocol namespace (mcp__<server>), or a rule in its tool's own grammar (Bash(<command>), Edit(<path>), Skill(<name>), WebFetch(domain:<host>)).`
|
|
170
248
|
: "";
|
|
171
249
|
const sentence = why.guidance !== undefined ? ` — ${why.guidance}` : "";
|
|
172
|
-
|
|
250
|
+
const does = SESSION_RULE_BUCKETS[field] === "allow"
|
|
251
|
+
? "admits nothing as written"
|
|
252
|
+
: NO_RULE_REASONS.has(why.reason)
|
|
253
|
+
? "refuses every call as stored (rules whose toolDeny holds an entry no grammar reads cannot be read) — a write holding it is refused"
|
|
254
|
+
: "refuses nothing as written";
|
|
255
|
+
const carriedBy = why.reason === "near_miss" && why.currentName !== undefined && !mountedToolNames.has(why.currentName) ? { carriedBy: "catalog" } : {};
|
|
256
|
+
throw new SessionPolicyError("inert_entry", `session rules ${field} holds ${inertEntryPhrase({ entry, ...why, ...carriedBy })}, which ${does}${sentence}; nothing was written.${way}`, { field, entry, ...why });
|
|
173
257
|
}
|
|
174
258
|
}
|
|
175
259
|
}
|
|
@@ -201,6 +285,12 @@ export function loosenReasons(priorValue, nextValue, mountedToolNames = NO_DECLA
|
|
|
201
285
|
};
|
|
202
286
|
const prior = shapeOf("prior", priorValue);
|
|
203
287
|
const next = shapeOf("next", nextValue);
|
|
288
|
+
if (unreadableDenyEntries(next, mountedToolNames).length > 0)
|
|
289
|
+
return [];
|
|
290
|
+
const unreadPrior = unreadableDenyEntries(prior, mountedToolNames);
|
|
291
|
+
if (unreadPrior.length > 0) {
|
|
292
|
+
return [`the prior rules refuse every call — their toolDeny holds ${shownEntries(unreadPrior.map((i) => i.entry), (entry) => inertEntryPhrase({ entry, reason: "grammar" }))}, which no grammar reads`];
|
|
293
|
+
}
|
|
204
294
|
const reasons = [];
|
|
205
295
|
const keptDeny = new Set(effectiveEntries(next, "toolDeny", mountedToolNames));
|
|
206
296
|
for (const x of effectiveEntries(prior, "toolDeny", mountedToolNames))
|
|
@@ -51,6 +51,9 @@ export interface OrphanToolCall {
|
|
|
51
51
|
* message and `errorCode` on the synthetic `tool_end` frame, so a consumer never parses the text. Two-valued
|
|
52
52
|
* (a third value needs a cross-repo slip, see {@link reconcileInterruptedSession}). */
|
|
53
53
|
export type ReconciledErrorKind = "interrupted_never_started" | "interrupted_outcome_unknown";
|
|
54
|
+
/** The class that is positive evidence the call never ran: closed with no start observed. The synthetic `tool_end` frame's
|
|
55
|
+
* `notExecuted` (`reconciledEnd`) and the persisted close's `details.notExecuted` both read it — one table, two faces. */
|
|
56
|
+
export declare const closeNeverExecuted: (errorKind: ReconciledErrorKind) => boolean;
|
|
54
57
|
/** One orphan closed by {@link reconcileInterruptedSession}, with everything the caller needs to mint the
|
|
55
58
|
* matching stream frame WITHOUT re-reading the session. */
|
|
56
59
|
export type RecoveredOrphan = OrphanToolCall & {
|
|
@@ -153,6 +156,7 @@ export declare function isInterruptedClose(message: {
|
|
|
153
156
|
/** The `toolResult` message for one {@link InterruptedResult} — the persisted and the request-view closes are this one shape. */
|
|
154
157
|
export declare function interruptedResultMessage(result: InterruptedResult, timestamp: number): ToolResultMessage<{
|
|
155
158
|
errorKind: ReconciledErrorKind;
|
|
159
|
+
notExecuted?: true;
|
|
156
160
|
}>;
|
|
157
161
|
/**
|
|
158
162
|
* Reconcile a resumed session's active branch: close any orphan tool calls with a synthetic
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readMember } from "./thrown-value.js";
|
|
2
|
+
export const closeNeverExecuted = (errorKind) => errorKind === "interrupted_never_started";
|
|
2
3
|
const INTERRUPTED_UNKNOWN = "[INTERRUPTED] The previous run ended before this tool call's result was recorded. Its outcome " +
|
|
3
4
|
"is UNKNOWN — the action may have completed, partially completed, or never run. Do not assume " +
|
|
4
5
|
"success or failure. Before relying on it: if it is safe to read, query the current state to " +
|
|
@@ -119,7 +120,7 @@ export function interruptedResultMessage(result, timestamp) {
|
|
|
119
120
|
toolCallId: result.toolCallId,
|
|
120
121
|
toolName: result.toolName,
|
|
121
122
|
content: [{ type: "text", text: result.text }],
|
|
122
|
-
details: { errorKind: result.errorKind },
|
|
123
|
+
details: { errorKind: result.errorKind, ...(closeNeverExecuted(result.errorKind) ? { notExecuted: true } : {}) },
|
|
123
124
|
isError: true,
|
|
124
125
|
timestamp,
|
|
125
126
|
};
|
|
@@ -2,6 +2,7 @@ import { types } from "node:util";
|
|
|
2
2
|
import { dispositionsOfRow, SHELL_READ_FINDINGS } from "../tools/fs/shell-read-judge.js";
|
|
3
3
|
import { receiveContract } from "./decision-receipt.js";
|
|
4
4
|
import { egressThrownText } from "./untrusted-egress.js";
|
|
5
|
+
import { receive } from "./mods/kernel/receive.js";
|
|
5
6
|
const DISPOSITIONS = ["refuse", "mandated", "ordinary", "vouch"];
|
|
6
7
|
const MEMBER_KINDS = {
|
|
7
8
|
disposition: "word",
|
|
@@ -20,37 +21,7 @@ const MEMBER_KINDS = {
|
|
|
20
21
|
admit: "admission",
|
|
21
22
|
};
|
|
22
23
|
const VERDICT_MEMBERS = Object.keys(MEMBER_KINDS);
|
|
23
|
-
const
|
|
24
|
-
function ownedData(value, depth, path) {
|
|
25
|
-
if (value === null || value === undefined || typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
26
|
-
return { ok: true, value };
|
|
27
|
-
if (typeof value !== "object" || depth >= DATA_DEPTH || types.isProxy(value) || path.has(value))
|
|
28
|
-
return { ok: false };
|
|
29
|
-
const list = Array.isArray(value);
|
|
30
|
-
const proto = Object.getPrototypeOf(value);
|
|
31
|
-
if (list ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
|
|
32
|
-
return { ok: false };
|
|
33
|
-
const own = Object.getOwnPropertyDescriptors(value);
|
|
34
|
-
path.add(value);
|
|
35
|
-
try {
|
|
36
|
-
const out = list ? [] : {};
|
|
37
|
-
for (const key of Object.keys(own)) {
|
|
38
|
-
if (list && key === "length")
|
|
39
|
-
continue;
|
|
40
|
-
const d = own[key];
|
|
41
|
-
if (!Object.hasOwn(d, "value") || d.enumerable !== true)
|
|
42
|
-
return { ok: false };
|
|
43
|
-
const inner = ownedData(d.value, depth + 1, path);
|
|
44
|
-
if (!inner.ok)
|
|
45
|
-
return { ok: false };
|
|
46
|
-
Object.defineProperty(out, key, { value: inner.value, enumerable: true, writable: true, configurable: true });
|
|
47
|
-
}
|
|
48
|
-
return { ok: true, value: out };
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
path.delete(value);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
24
|
+
const OWNED_DATA_MODE = Object.freeze({ accessors: "refuse", depth: 8, others: "refuse", isProxy: (v) => types.isProxy(v) });
|
|
54
25
|
function receivedAnswer(answer, call) {
|
|
55
26
|
if (typeof answer !== "object" || answer === null)
|
|
56
27
|
return undefined;
|
|
@@ -65,7 +36,7 @@ function receivedAnswer(answer, call) {
|
|
|
65
36
|
if (kind === "word" ? typeof v !== "string" : kind === "mark" ? v !== true : kind === "face" ? v !== "write" : kind === "admission" ? typeof v !== "function" || types.isProxy(v) : false)
|
|
66
37
|
return undefined;
|
|
67
38
|
if (kind === "record") {
|
|
68
|
-
const data =
|
|
39
|
+
const data = receive(v, OWNED_DATA_MODE);
|
|
69
40
|
if (!data.ok || typeof data.value !== "object" || data.value === null || Array.isArray(data.value))
|
|
70
41
|
return undefined;
|
|
71
42
|
v = data.value;
|
|
@@ -63,9 +63,11 @@ export interface SideQuerySpec {
|
|
|
63
63
|
* Per-model auth — MIRRORS {@link TaskSpec.getApiKeyAndHeaders} (same signature, resolved per
|
|
64
64
|
* call against the RESOLVED model, exactly like the task path's per-call hook). The brain
|
|
65
65
|
* contract is the route pairing law (route-adjudicator.ts): a per-model credential (this hook,
|
|
66
|
-
* or
|
|
67
|
-
*
|
|
68
|
-
*
|
|
66
|
+
* or a credential header on `Model.headers`) rides to the origin it first went out to and is
|
|
67
|
+
* refused anywhere else (`route.credential_origin_mismatch`); an answer handing the key beside
|
|
68
|
+
* the lane's own key header is refused as one verdict (`route.credential_answer_conflict`); the
|
|
69
|
+
* deployment credential rides only to the deployment's own root — a model carrying a root of its
|
|
70
|
+
* own is refused (`route.credential_mismatch` / `route.credential_missing`), never silently followed.
|
|
69
71
|
* Historically the fallback was unconditional (`options.apiKey ?? config.apiKey`, with a model's
|
|
70
72
|
* own `baseUrl` outranking the brain's), so a side query routed to a model carrying its own
|
|
71
73
|
* `baseUrl` + per-model key sent the GATEWAY key to the per-model (possibly external) URL — the
|
|
@@ -69,8 +69,9 @@ export declare function skillGrantBindingMembers(call: {
|
|
|
69
69
|
* bytes — the row stays pending: deny it, then activate again) and the resumed execution boundary (`site: "execution"`, the
|
|
70
70
|
* bytes that will run, after every station that can rewrite them — the approval is spent: the call does not run). The
|
|
71
71
|
* sentence says which side of the digest differs, read off the row's own arguments: the arguments name another skill than
|
|
72
|
-
* the card's (`row.args`), or the card's skill no longer
|
|
73
|
-
*
|
|
72
|
+
* the card's (`row.args`), or the card's skill's lists no longer bind to the card's digest (or it is not served) — its
|
|
73
|
+
* definition changed, or this engine reads an entry of it differently than the engine that parked the row (an entry that
|
|
74
|
+
* reads as nothing: the digest is the list reader's reading, not the text). Names the skills, never a path; says the way out.
|
|
74
75
|
*/
|
|
75
76
|
export declare function parkedSkillApprovalRefusal(row: {
|
|
76
77
|
readonly toolName: string;
|
package/dist/core/skill-grant.js
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import { wireOf } from "./tool-name-readings.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
3
3
|
import { mountedToolPermissionSeat } from "./mode-policy.js";
|
|
4
4
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
5
5
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
6
6
|
import { NOT_BINDABLE } from "./skill-grant-binding.js";
|
|
7
7
|
import { readToolNameList } from "./tool-name-list.js";
|
|
8
|
-
import { allowingRows, autoStripped, compileAllowList, readRuleEntry, inertEntryPhrase, judgeAllowEntries, nonListValueLabel,
|
|
8
|
+
import { allowingRows, autoStripped, compileAllowList, readRuleEntry, inertEntryPhrase, judgeAllowEntries, nonListValueLabel, renderListEntry, spelledListEntry } from "./allow-entry.js";
|
|
9
9
|
export const SKILL_GRANT_ORIGINS = Object.freeze(["model", "user", "inherited"]);
|
|
10
10
|
const FROZEN_SKILL_GRANT_ORIGINS = new Set(["model", "inherited"]);
|
|
11
11
|
export function skillNamedBy(args, skills) {
|
|
12
12
|
const named = args?.skill;
|
|
13
13
|
return typeof named === "string" ? skills.find((s) => s.name === named) : undefined;
|
|
14
14
|
}
|
|
15
|
+
function readsAsNothingLabel(inert) {
|
|
16
|
+
return `<reads as nothing (${inert.reason}): ${spelledListEntry(inert.entry)}>`;
|
|
17
|
+
}
|
|
15
18
|
function skillGrantBinding(skill) {
|
|
16
|
-
const canonical = (entries
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const p =
|
|
19
|
+
const canonical = (entries, behavior) => {
|
|
20
|
+
const read = compileAllowList((entries ?? []), behavior);
|
|
21
|
+
const spelled = read.rows.map((r) => {
|
|
22
|
+
const p = ruleSpellingOf(r.entry);
|
|
23
|
+
if (p === undefined)
|
|
24
|
+
return readsAsNothingLabel({ entry: r.entry, reason: "grammar" });
|
|
20
25
|
return p.ruleContent === undefined ? p.toolName : `${p.toolName}(${p.ruleContent})`;
|
|
21
|
-
})
|
|
22
|
-
|
|
26
|
+
});
|
|
27
|
+
const unread = read.inert.map((i) => (i.valueType !== undefined ? i.entry : readsAsNothingLabel(i)));
|
|
28
|
+
return [...new Set([...spelled, ...unread])].sort();
|
|
29
|
+
};
|
|
30
|
+
const boundGrants = { skill: skill?.name ?? null, allowedTools: canonical(skill?.allowedTools, "allow"), disallowedTools: canonical(skill?.disallowedTools, "deny") };
|
|
23
31
|
return { boundGrantDigest: boundInputHashOf(boundGrants), boundGrants };
|
|
24
32
|
}
|
|
25
33
|
export function skillGrantDigest(skill) {
|
|
@@ -43,7 +51,7 @@ export function parkedSkillApprovalRefusal(row, args, skills, skillToolName, sit
|
|
|
43
51
|
? `this parked Skill activation of "${chosen}" carries no digest of the allowed-tools / disallowed-tools it was asked under (it was parked before Skill approvals bound them), so it cannot cover what the activation grants or refuses`
|
|
44
52
|
: nameIn(args) !== nameIn(row.args)
|
|
45
53
|
? `this Skill approval was given on the card for "${shown(nameIn(row.args))}", and ${site === "claim" ? "this decision selects" : "the call would now activate"} "${chosen}" — not the Skill that card asked about, so the approval does not cover activating it`
|
|
46
|
-
: `
|
|
54
|
+
: `the allowed-tools / disallowed-tools of "${chosen}", as this resume serves it (one it does not load counts as absent), no longer bind to the digest this Skill approval was asked under — its definition changed, or this engine reads one of its entries differently than the engine that approved it (an entry that reads as nothing) — so the approval does not cover what activating it would now grant or refuse`;
|
|
47
55
|
return site === "claim"
|
|
48
56
|
? `${why} — refusing pre-CAS (nothing ran; the checkpoint stays pending). Decide this checkpoint as deny and re-activate it: the new card shows the current lists.`
|
|
49
57
|
: `The approved Skill call was not executed: ${why}. This approval is spent — to use "${chosen}", have the model activate it again: the new card shows its lists.`;
|
|
@@ -18,25 +18,45 @@
|
|
|
18
18
|
* directory third parties can write to is the same as pasting their text into the system prompt. The
|
|
19
19
|
* loader validates SHAPE (spec conformance), never intent.
|
|
20
20
|
*
|
|
21
|
-
* **`allowed-tools` / `disallowed-tools` are COPIED, never judged.** Each frontmatter list reaches
|
|
21
|
+
* **`allowed-tools` / `disallowed-tools` are COPIED, never judged for what they mean.** Each frontmatter list reaches
|
|
22
22
|
* {@link SkillSpec.allowedTools} / {@link SkillSpec.disallowedTools} entry by entry as the author wrote it — no
|
|
23
|
-
* intersection with a roster, no projection onto a tool name, no
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* intersection with a roster, no projection onto a tool name, no de-duplication. Each entry must be a rule SPELLING — the
|
|
24
|
+
* rule syntax's own test, `ruleSpellingOf` (a tool name, then at most one parenthesized rule) — or the skill is refused;
|
|
25
|
+
* what a spelled entry means (a tool, a namespace, a command rule, a path, a wildcard that grants nothing) is read in ONE
|
|
26
|
+
* place, the run's grant table, which announces what it cannot read. The deny list has a second spelling,
|
|
27
|
+
* `disallowedTools`, read where `disallowed-tools` is absent or null (YAML's null is the key's absence); a hyphen key with
|
|
28
|
+
* a value is the list and the alias is not looked at. The allow list has none. **Each list is read whole by ONE scanner,
|
|
29
|
+
* {@link scanValue} (its JSDoc states the rules), or the skill is refused by name (`no_frontmatter`)** — never installed
|
|
30
|
+
* shorter: a `disallowed-tools` that lost entries is a looser skill than the one its author wrote. The scanner reads YAML's
|
|
31
|
+
* structure as YAML does — a comment, a flow's `,` / `]` and a mapping's `: ` are YAML's inside a rule's parentheses too —
|
|
32
|
+
* and departs from what YAML and the reference's reader give only where the list is cut into entries, on purpose: the cut
|
|
33
|
+
* counts a rule's parentheses by depth and refuses an entry whose parentheses are not one group closing it — unmatched
|
|
34
|
+
* either way, or followed by more text (the reference's zone is boolean and installs such an entry); a `*` entry is
|
|
35
|
+
* copied, the list not collapsed to `*` alone; tab-indented block items are read (the reference's retry reads them too);
|
|
36
|
+
* and a block scalar's text is cut line by line — each line of a literal one (`|`) as written, a folded one (`>`) after
|
|
37
|
+
* YAML's folding, so `>` lines that hold one rule read as that rule (YAML keeps the line breaks of `|` in one string, which
|
|
38
|
+
* the reference installs as one entry no rule reads).
|
|
26
39
|
*
|
|
27
|
-
* Frontmatter is
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
40
|
+
* Frontmatter is read by a deliberately closed subset reader (no YAML dependency — the runtime dependency budget is a
|
|
41
|
+
* standing constraint): top-level key lines ({@link yamlKeyLineOf}), `#` comment lines, block scalars (`|` / `>` with their
|
|
42
|
+
* indentation and chomping indicators), the tool lists, and nested blocks (a mapping or a sequence under a key with nothing
|
|
43
|
+
* on its line) skipped wholesale but for a line that opens a tool-list key (below). A key other than the tool lists keeps its
|
|
44
|
+
* value as written on its line (a value that is one quoted scalar, the scanner's own judgment, is that scalar decoded, over
|
|
45
|
+
* every line it runs); a value of it that opens a quote, a `[` or a `{` and does not
|
|
46
|
+
* close it refuses the skill too — the same scanner, over the same lines, says so. Every value takes its own lines and no
|
|
47
|
+
* more, and every line no value takes is judged in one place: an indented `key:` line under a value that is not a nested
|
|
48
|
+
* block — plain, quoted, a flow, a block scalar — refuses the skill naming both keys (YAML reads no key there), and a
|
|
49
|
+
* flush-left line that is no key line refuses it naming the line (YAML reads no key there either). A tool list is read only
|
|
50
|
+
* as a key of the frontmatter, written flush left: a tool-list key written in another key's nested block (at any depth), or
|
|
51
|
+
* after another key's closing quote or `]` on its line, or as a key of a flow mapping (or a pair in a flow sequence) that
|
|
52
|
+
* value holds, refuses the skill naming both keys — its list would be lost.
|
|
33
53
|
*/
|
|
34
54
|
import type { SkillSpec } from "./types.js";
|
|
35
55
|
/** Why one skill directory (or one of its parts) was not taken as-declared. */
|
|
36
56
|
export type SkillsDirectoryWarningCode =
|
|
37
57
|
/** The directory holds no `SKILL.md`. */
|
|
38
58
|
"no_skill_file"
|
|
39
|
-
/** `SKILL.md` has no well-formed `---` fenced frontmatter block. */
|
|
59
|
+
/** `SKILL.md` has no well-formed `---` fenced frontmatter block: no opening fence, an opening fence never closed by a whole-line `---`, a tool list (`allowed-tools` / `disallowed-tools`) whose value the reader cannot read as a list, a key whose value opens a quote, a `[` or a `{` it does not close, a value that is not a nested block followed by an indented `key:` line, a tool-list key written anywhere but flush left (in a nested block, or after a closing quote or `]` on a key's line), or a flush-left line that is no key line. */
|
|
40
60
|
| "no_frontmatter"
|
|
41
61
|
/** Required `name` absent. */
|
|
42
62
|
| "missing_name"
|