@sema-agent/core 7.51.1 → 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 +550 -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.d.ts +5 -0
- package/dist/agents/subagent.js +7 -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 +42 -8
- package/dist/core/allow-entry.js +24 -17
- 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/locked-config.d.ts +26 -5
- package/dist/core/locked-config.js +31 -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 +53 -7
- package/dist/core/mcp-server-spec.js +84 -1
- package/dist/core/mcp.d.ts +11 -7
- package/dist/core/mcp.js +27 -9
- 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 +18 -16
- package/dist/core/runner/prepare-policy-chain.js +98 -101
- 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 +6 -4
- package/dist/core/runner/session-rule-policy.js +17 -10
- 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 +93 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +93 -17
- package/dist/core/session-policy-store.js +178 -25
- 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/types.d.ts +2 -1
- package/dist/core/types.js +1 -0
- 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 +24 -17
- package/dist/index.js +15 -12
- 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/stores/file/session-policy-store.d.ts +3 -3
- 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 +256 -99
- package/wire-changes/core@7.51.1.json +5 -5
- package/wire-changes/core@7.51.2.json +293 -0
- 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
|
@@ -18,10 +18,15 @@
|
|
|
18
18
|
* (`readRuleEntry`, allow-entry.ts): a tool name, a protocol namespace, a shell command rule (`Bash(git push:*)`), a
|
|
19
19
|
* path rule, a name or host rule. Entries are stored as written; every station that validates, compares, matches or
|
|
20
20
|
* names one reads that reader's answer ({@link judgeSessionRulesWrite}, {@link loosenReasons}, the run-time layer).
|
|
21
|
+
* - **a row keeps its grammar**: the write judge stamps the grammar it read the entries in ({@link SESSION_RULES_GRAMMAR}), and
|
|
22
|
+
* every station reads a row's entries in the grammar the row carries ({@link readSessionRulesShape}) — a row stored before
|
|
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.
|
|
21
26
|
* - **NOT A SANDBOX**: a shell command rule reads the command line the call carries (every program run in its connector
|
|
22
27
|
* chain); the real isolation boundary is the executionEnv, never these rules.
|
|
23
28
|
*/
|
|
24
|
-
import { type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
|
|
29
|
+
import { compileAllowList, type AllowEntryInert, type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
|
|
25
30
|
/** The per-session rules. All optional; an absent bucket imposes no constraint of that kind. */
|
|
26
31
|
export interface SessionPermissionRules {
|
|
27
32
|
/** If set, ONLY the calls these entries admit are allowed — a narrowing allowlist: a call no entry admits is refused.
|
|
@@ -45,6 +50,19 @@ declare const SESSION_RULE_BUCKETS: {
|
|
|
45
50
|
export type SessionRuleEntryField = {
|
|
46
51
|
[K in keyof typeof SESSION_RULE_BUCKETS]: (typeof SESSION_RULE_BUCKETS)[K] extends "dirs" ? never : K;
|
|
47
52
|
}[keyof typeof SESSION_RULE_BUCKETS];
|
|
53
|
+
/**
|
|
54
|
+
* The rule grammar this engine reads a session's rule entries in: the `grammar` the write judge stamps on every rules value it
|
|
55
|
+
* judges ({@link judgeSessionRulesWrite}), so every store that writes through it keeps the stamp on the row. It counts
|
|
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}).
|
|
64
|
+
*/
|
|
65
|
+
export declare const SESSION_RULES_GRAMMAR = 2;
|
|
48
66
|
/** {@link SessionPermissionRules} plus the monotonic `rev` the store stamps (the OCC key). */
|
|
49
67
|
export interface StoredSessionRules extends SessionPermissionRules {
|
|
50
68
|
rev: number;
|
|
@@ -53,7 +71,15 @@ export interface StoredSessionRules extends SessionPermissionRules {
|
|
|
53
71
|
* can tell a recreated row from a continuation of the one it enforces whatever `rev` the new row wears.
|
|
54
72
|
* Absent = generation 0 (a backend that predates the mark, or a row written before it). */
|
|
55
73
|
gen?: number;
|
|
74
|
+
/** The rule grammar the row's entries are read in ({@link SESSION_RULES_GRAMMAR}), stamped by the write judge. Absent =
|
|
75
|
+
* a row stored before the stamp: grammar 0. */
|
|
76
|
+
grammar?: number;
|
|
56
77
|
}
|
|
78
|
+
/** A rules value as the shape reading hands it on ({@link readSessionRulesShape}): its buckets, and the grammar its entries
|
|
79
|
+
* are read in — resolved, so a store that serves it on beside its own counters serves the row's grammar with it. */
|
|
80
|
+
export type SessionRulesInGrammar = SessionPermissionRules & {
|
|
81
|
+
grammar: number;
|
|
82
|
+
};
|
|
57
83
|
/** Options for {@link SessionPolicyStore.putRules}. */
|
|
58
84
|
export interface PutRulesOptions {
|
|
59
85
|
/** CAS: reject the write if the stored `rev` is not this (optimistic concurrency). Omit to skip the check. */
|
|
@@ -92,7 +118,8 @@ export declare class SessionPolicyError extends Error {
|
|
|
92
118
|
export interface SessionPolicyStoreOptions {
|
|
93
119
|
/** The names this deployment mounts its OWN tools under — the roster the store reads a rule entry's tool name against
|
|
94
120
|
* (`readRuleEntry`): a retired name declared here is an ordinary name to the write and to the loosening check alike.
|
|
95
|
-
* 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. */
|
|
96
123
|
mountedToolNames?: Iterable<string>;
|
|
97
124
|
}
|
|
98
125
|
/** A store's declared roster from {@link SessionPolicyStoreOptions.mountedToolNames} — the one reader both bundled stores
|
|
@@ -169,13 +196,14 @@ declare function lexWithin(root: string, p: string): boolean;
|
|
|
169
196
|
* THE ONE SHAPE READING of a rules value — what every station that takes one reads it through: the write (incoming rules),
|
|
170
197
|
* a store's read-back of its own rows, the tighten comparison's prior, the run-time layer's served row. A rules value is an
|
|
171
198
|
* object whose keys are buckets ({@link SessionPermissionRules}); each bucket present is a list of strings (`allowDirs`: of
|
|
172
|
-
* non-blank strings — a directory is no blank). A bucket's TYPE is all this reads: what an entry means is the entry reader's
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* `["r","m"]`), and a key outside the buckets is
|
|
199
|
+
* non-blank strings — a directory is no blank). A bucket's TYPE is all this reads: what an entry means is the entry reader's,
|
|
200
|
+
* in the grammar the value is written in ({@link grammarOf}), handed on as `grammar`. The store's counters (`rev`, `gen`) are
|
|
201
|
+
* passed over, and a key holding `undefined` holds nothing. Anything else is a fault, named by key and by TYPE, never by value —
|
|
202
|
+
* a bare string is never one rule and never its characters (`"rm"` spread into `["r","m"]`), and a key outside the buckets is
|
|
203
|
+
* never dropped (a dropped deny is a deny nobody enforces).
|
|
176
204
|
*/
|
|
177
205
|
export declare function readSessionRulesShape(value: unknown): {
|
|
178
|
-
rules:
|
|
206
|
+
rules: SessionRulesInGrammar;
|
|
179
207
|
} | {
|
|
180
208
|
fault: SessionRuleShapeRefusal;
|
|
181
209
|
sentence: string;
|
|
@@ -185,25 +213,73 @@ export declare function readSessionRulesShape(value: unknown): {
|
|
|
185
213
|
* stored form is `..`-free and the tighten diff compares the form the runtime resolves. Both bundled stores and a durable twin
|
|
186
214
|
* stamp exactly this, so a structuredClone and a JSON round-trip of it are byte-identical. */
|
|
187
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[];
|
|
227
|
+
/**
|
|
228
|
+
* THE LIST READING of one rule bucket of a rules value the shape reading read — what the run-time layer enforces, the tighten
|
|
229
|
+
* comparison compares and the write judge refuses on: `compileAllowList` over the one entry reader, in the bucket's direction,
|
|
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.
|
|
236
|
+
*/
|
|
237
|
+
export declare function compileSessionRuleBucket(rules: SessionRulesInGrammar, field: SessionRuleEntryField, roster?: ReadonlySet<string>): ReturnType<typeof compileAllowList>;
|
|
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;
|
|
252
|
+
/** A layer's delivery of the entries its snapshots' earlier grammar reads as nothing ({@link compileSessionRuleBucket},
|
|
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;
|
|
188
256
|
/**
|
|
189
257
|
* THE ONE WRITE JUDGE every store applies before it stamps a row (both bundled stores, and a durable backend at the same point):
|
|
190
258
|
* the rules to stamp, or a {@link SessionPolicyError}, in this order — `conflict` (a stale `expectedRev` against `prior`'s rev),
|
|
191
|
-
* `invalid_shape` ({@link normalizeRules}), `inert_entry` (an entry
|
|
192
|
-
* rule that names nothing guards nothing whoever writes it), then, for a non-operator write
|
|
193
|
-
* `loosen_forbidden` ({@link loosenReasons}) — or `corrupt` when that prior cannot be read as rules,
|
|
194
|
-
* reads as fewer. `
|
|
259
|
+
* `invalid_shape` ({@link normalizeRules}), `inert_entry` (an entry that reads as nothing in the grammar the rules are written
|
|
260
|
+
* in; operator writes included — a rule that names nothing guards nothing whoever writes it), then, for a non-operator write
|
|
261
|
+
* over an existing row, `loosen_forbidden` ({@link loosenReasons}) — or `corrupt` when that prior cannot be read as rules,
|
|
262
|
+
* which the comparison never reads as fewer. The rules to stamp carry `grammar` = {@link SESSION_RULES_GRAMMAR}: what passed
|
|
263
|
+
* the judge means the same in this engine's grammar, and THIS is the one place the stamp is minted, so every store that
|
|
264
|
+
* stores what the judge returns keeps it. `mountedToolNames` = the store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}).
|
|
195
265
|
*/
|
|
196
|
-
export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionPermissionRules
|
|
266
|
+
export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionPermissionRules & {
|
|
267
|
+
grammar: number;
|
|
268
|
+
};
|
|
197
269
|
/**
|
|
198
270
|
* Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op), over the EFFECTIVE
|
|
199
|
-
* 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
|
|
200
274
|
* nothing is in neither side's effect, so removing one relaxes nothing and adding one widens nothing (the way a session's owner
|
|
201
|
-
* 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
|
|
202
276
|
* (→ everything allowed) or adding an effective entry outside the prior set loosens; prior-undefined → any defined next only
|
|
203
277
|
* narrows. allowDirs: removing confinement, or adding a dir not within some prior dir, loosens (coarse raw containment).
|
|
204
|
-
* Entries compare as written
|
|
205
|
-
* is no
|
|
206
|
-
*
|
|
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
|
|
279
|
+
* nothing is in no effect, so writing it in this engine's grammar widens). Both sides are read through
|
|
280
|
+
* {@link readSessionRulesShape} first: a side it cannot read (a key that is no bucket — a retired one included) throws
|
|
281
|
+
* `invalid_shape` rather than compare without it. `mountedToolNames` = the store's declaration
|
|
282
|
+
* ({@link SessionPolicyStoreOptions.mountedToolNames}; empty when it has none) — the write judge's too.
|
|
207
283
|
*/
|
|
208
284
|
export declare function loosenReasons(priorValue: SessionPermissionRules, nextValue: SessionPermissionRules, mountedToolNames?: ReadonlySet<string>): string[];
|
|
209
285
|
/**
|
|
@@ -1,12 +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
|
-
const STORE_STAMPS = new Set(["rev", "gen"]);
|
|
10
|
+
const STORE_STAMPS = new Set(["rev", "gen", "grammar"]);
|
|
11
|
+
export const SESSION_RULES_GRAMMAR = 2;
|
|
10
12
|
export class SessionPolicyError extends Error {
|
|
11
13
|
code;
|
|
12
14
|
detail;
|
|
@@ -28,10 +30,11 @@ export function readMountedToolNames(value) {
|
|
|
28
30
|
refuse("must be an iterable of tool names (an array or a Set), not a single value");
|
|
29
31
|
const names = new Set();
|
|
30
32
|
for (const n of value) {
|
|
31
|
-
if (typeof n !== "string"
|
|
32
|
-
refuse(`holds ${JSON.stringify(n) ?? String(n)}, which is not a
|
|
33
|
-
|
|
34
|
-
|
|
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}`);
|
|
35
38
|
names.add(n);
|
|
36
39
|
}
|
|
37
40
|
return names;
|
|
@@ -49,13 +52,32 @@ function lexWithin(root, p) {
|
|
|
49
52
|
return true;
|
|
50
53
|
return np === nr || np.startsWith(nr + "/");
|
|
51
54
|
}
|
|
55
|
+
function grammarOf(value) {
|
|
56
|
+
const stamp = value.grammar;
|
|
57
|
+
if (stamp === undefined)
|
|
58
|
+
return { grammar: value.rev !== undefined ? 0 : SESSION_RULES_GRAMMAR };
|
|
59
|
+
if (typeof stamp !== "number" || !Number.isInteger(stamp) || stamp < 0) {
|
|
60
|
+
const got = typeof stamp === "number" ? "a number that is not a whole number of 0 or more" : valueTypeLabel(stamp);
|
|
61
|
+
return { got, sentence: `session rules carry a grammar stamp that is ${got} — the stamp is the whole number of the rule grammar the rules were written in (this engine reads grammar ${SESSION_RULES_GRAMMAR})` };
|
|
62
|
+
}
|
|
63
|
+
if (stamp > SESSION_RULES_GRAMMAR) {
|
|
64
|
+
return {
|
|
65
|
+
got: `${stamp}, a grammar newer than this engine's (${SESSION_RULES_GRAMMAR})`,
|
|
66
|
+
sentence: `session rules were written in rule grammar ${stamp}, newer than this engine reads (grammar ${SESSION_RULES_GRAMMAR}) — refusing rather than read their entries in an older grammar; an engine that reads grammar ${stamp} reads them`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return { grammar: stamp };
|
|
70
|
+
}
|
|
52
71
|
export function readSessionRulesShape(value) {
|
|
53
72
|
const fault = (field, got, sentence) => ({ fault: { field, got }, sentence });
|
|
54
73
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
55
74
|
const got = Array.isArray(value) ? "an array" : valueTypeLabel(value);
|
|
56
75
|
return fault("", got, `session rules must be an object of rule buckets (${BUCKETS.join(", ")}), got ${got}`);
|
|
57
76
|
}
|
|
58
|
-
const
|
|
77
|
+
const read = grammarOf(value);
|
|
78
|
+
if ("got" in read)
|
|
79
|
+
return fault("grammar", read.got, read.sentence);
|
|
80
|
+
const rules = { grammar: read.grammar };
|
|
59
81
|
for (const [key, v] of Object.entries(value)) {
|
|
60
82
|
if (STORE_STAMPS.has(key) || v === undefined)
|
|
61
83
|
continue;
|
|
@@ -78,36 +100,160 @@ export function readSessionRulesShape(value) {
|
|
|
78
100
|
return { rules };
|
|
79
101
|
}
|
|
80
102
|
export function normalizeRules(rules) {
|
|
103
|
+
return canonicalRules(shapeOrRefuse(rules));
|
|
104
|
+
}
|
|
105
|
+
function shapeOrRefuse(rules) {
|
|
81
106
|
const read = readSessionRulesShape(rules);
|
|
82
107
|
if ("fault" in read)
|
|
83
108
|
throw new SessionPolicyError("invalid_shape", read.sentence, read.fault);
|
|
109
|
+
return read.rules;
|
|
110
|
+
}
|
|
111
|
+
function canonicalRules(read) {
|
|
84
112
|
const out = {};
|
|
85
113
|
for (const field of BUCKETS) {
|
|
86
|
-
const list = read
|
|
114
|
+
const list = read[field];
|
|
87
115
|
if (list === undefined)
|
|
88
116
|
continue;
|
|
89
117
|
out[field] = SESSION_RULE_BUCKETS[field] === "dirs" ? list.map((d) => posix.normalize(d).replace(/\/+$/, "") || "/") : [...list];
|
|
90
118
|
}
|
|
91
119
|
return out;
|
|
92
120
|
}
|
|
93
|
-
function
|
|
94
|
-
|
|
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}`;
|
|
146
|
+
}
|
|
147
|
+
export function compileSessionRuleBucket(rules, field, roster) {
|
|
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" }))] };
|
|
157
|
+
}
|
|
158
|
+
const ADVISORY_ENTRIES_SHOWN = 5;
|
|
159
|
+
function shownEntries(entries, render) {
|
|
160
|
+
const rest = entries.length > ADVISORY_ENTRIES_SHOWN ? ` and ${entries.length - ADVISORY_ENTRIES_SHOWN} more` : "";
|
|
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(" ")}`;
|
|
208
|
+
}
|
|
209
|
+
export function earlierGrammarAnnouncer(sink) {
|
|
210
|
+
const announced = new Set();
|
|
211
|
+
return (entries, field, grammar) => {
|
|
212
|
+
const key = JSON.stringify([field, grammar, entries]);
|
|
213
|
+
if (sink === undefined || entries.length === 0 || announced.has(key))
|
|
214
|
+
return;
|
|
215
|
+
announced.add(key);
|
|
216
|
+
try {
|
|
217
|
+
sink(entries, field, grammar);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function effectiveEntries(rules, field, mountedToolNames) {
|
|
224
|
+
return compileSessionRuleBucket(rules, field, mountedToolNames).rows.map((r) => r.entry);
|
|
95
225
|
}
|
|
96
226
|
function assertEntriesRead(rules, mountedToolNames) {
|
|
227
|
+
const known = rootFacingToolNames(mountedToolNames);
|
|
97
228
|
for (const field of ENTRY_FIELDS) {
|
|
98
229
|
for (const entry of rules[field] ?? []) {
|
|
99
|
-
const
|
|
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;
|
|
100
232
|
if (read.kind === "row")
|
|
101
233
|
continue;
|
|
102
234
|
const { kind: _kind, ...why } = read;
|
|
103
235
|
void _kind;
|
|
104
|
-
const way = why.
|
|
105
|
-
?
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
:
|
|
236
|
+
const way = why.reason === "earlier_grammar"
|
|
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"
|
|
245
|
+
? ` If this deployment mounts its own tool under ${renderListEntry(entry)}, declare it in the store's mountedToolNames.`
|
|
246
|
+
: why.currentName === undefined
|
|
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>)).`
|
|
248
|
+
: "";
|
|
109
249
|
const sentence = why.guidance !== undefined ? ` — ${why.guidance}` : "";
|
|
110
|
-
|
|
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 });
|
|
111
257
|
}
|
|
112
258
|
}
|
|
113
259
|
}
|
|
@@ -115,8 +261,9 @@ export function judgeSessionRulesWrite(prior, rules, opts, mountedToolNames = NO
|
|
|
115
261
|
if (opts?.expectedRev !== undefined && (prior?.rev ?? 0) !== opts.expectedRev) {
|
|
116
262
|
throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${prior?.rev ?? 0})`);
|
|
117
263
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
264
|
+
const read = shapeOrRefuse(rules);
|
|
265
|
+
const clean = canonicalRules(read);
|
|
266
|
+
assertEntriesRead(read, mountedToolNames);
|
|
120
267
|
if (prior !== null && opts?.operator !== true) {
|
|
121
268
|
const stored = readSessionRulesShape(prior);
|
|
122
269
|
if ("fault" in stored) {
|
|
@@ -127,7 +274,7 @@ export function judgeSessionRulesWrite(prior, rules, opts, mountedToolNames = NO
|
|
|
127
274
|
throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
|
|
128
275
|
}
|
|
129
276
|
}
|
|
130
|
-
return clean;
|
|
277
|
+
return { ...clean, grammar: SESSION_RULES_GRAMMAR };
|
|
131
278
|
}
|
|
132
279
|
export function loosenReasons(priorValue, nextValue, mountedToolNames = NO_DECLARED_TOOLS) {
|
|
133
280
|
const shapeOf = (side, value) => {
|
|
@@ -138,17 +285,23 @@ export function loosenReasons(priorValue, nextValue, mountedToolNames = NO_DECLA
|
|
|
138
285
|
};
|
|
139
286
|
const prior = shapeOf("prior", priorValue);
|
|
140
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
|
+
}
|
|
141
294
|
const reasons = [];
|
|
142
|
-
const keptDeny = new Set(effectiveEntries("toolDeny",
|
|
143
|
-
for (const x of effectiveEntries("toolDeny",
|
|
295
|
+
const keptDeny = new Set(effectiveEntries(next, "toolDeny", mountedToolNames));
|
|
296
|
+
for (const x of effectiveEntries(prior, "toolDeny", mountedToolNames))
|
|
144
297
|
if (!keptDeny.has(x))
|
|
145
298
|
reasons.push(`removed toolDeny ${renderListEntry(x)}`);
|
|
146
299
|
if (prior.toolAllow !== undefined) {
|
|
147
300
|
if (next.toolAllow === undefined)
|
|
148
301
|
reasons.push("removed toolAllow allowlist (all allowed)");
|
|
149
302
|
else {
|
|
150
|
-
const had = new Set(effectiveEntries("toolAllow",
|
|
151
|
-
for (const x of effectiveEntries("toolAllow",
|
|
303
|
+
const had = new Set(effectiveEntries(prior, "toolAllow", mountedToolNames));
|
|
304
|
+
for (const x of effectiveEntries(next, "toolAllow", mountedToolNames))
|
|
152
305
|
if (!had.has(x))
|
|
153
306
|
reasons.push(`widened toolAllow with ${renderListEntry(x)}`);
|
|
154
307
|
}
|
|
@@ -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;
|