@sema-agent/core 7.48.0 → 7.49.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 +418 -0
- package/README.md +2 -2
- package/dist/agents/cascade.d.ts +2 -5
- package/dist/agents/child-failure.d.ts +20 -0
- package/dist/agents/child-failure.js +28 -0
- package/dist/agents/cross-session-ref.d.ts +11 -3
- package/dist/agents/cross-session-ref.js +6 -0
- package/dist/agents/cumulative-stats.d.ts +12 -15
- package/dist/agents/cumulative-stats.js +1 -1
- package/dist/agents/extra-tool-names.js +1 -1
- package/dist/agents/inherited-tool-pool.d.ts +17 -13
- package/dist/agents/inherited-tool-pool.js +4 -3
- package/dist/agents/peer-session-drain.d.ts +3 -2
- package/dist/agents/peer-session-drain.js +5 -4
- package/dist/agents/send-message-tool.js +2 -9
- package/dist/agents/subagent.d.ts +10 -29
- package/dist/agents/subagent.js +65 -53
- package/dist/agents/suspend-guard.d.ts +1 -1
- package/dist/agents/tool-filter.d.ts +76 -65
- package/dist/agents/tool-filter.js +122 -92
- package/dist/agents/verify.d.ts +4 -7
- package/dist/agents/verify.js +6 -7
- package/dist/bench/metrics.d.ts +1 -2
- package/dist/brain/anthropic.js +133 -79
- package/dist/brain/errors.d.ts +53 -6
- package/dist/brain/errors.js +22 -2
- package/dist/brain/input-too-long.d.ts +40 -19
- package/dist/brain/input-too-long.js +3 -16
- package/dist/brain/open-responses.js +19 -10
- package/dist/brain/openai.js +123 -32
- package/dist/brain/provider-dialect.d.ts +214 -0
- package/dist/brain/provider-dialect.js +186 -0
- package/dist/brain/request-params.d.ts +27 -8
- package/dist/brain/request-params.js +7 -12
- package/dist/brain/request-route.d.ts +84 -0
- package/dist/brain/request-route.js +41 -0
- package/dist/brain/retry.d.ts +8 -0
- package/dist/brain/retry.js +10 -4
- package/dist/brain/route-adjudicator.d.ts +7 -0
- package/dist/brain/route-adjudicator.js +5 -1
- package/dist/brain/status-sink.d.ts +20 -0
- package/dist/brain/status-sink.js +11 -0
- package/dist/brain/stream-engine.d.ts +52 -1
- package/dist/brain/stream-engine.js +81 -24
- package/dist/brain/stream-shared.d.ts +57 -11
- package/dist/brain/stream-shared.js +28 -2
- package/dist/core/a2a.d.ts +1 -0
- package/dist/core/agent-config-dir.js +3 -2
- package/dist/core/allow-entry.d.ts +21 -12
- package/dist/core/allow-entry.js +38 -31
- package/dist/core/arg-summary.d.ts +17 -24
- package/dist/core/arg-summary.js +169 -68
- package/dist/core/ask-origin.d.ts +42 -24
- package/dist/core/ask-origin.js +18 -13
- package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
- package/dist/core/auto-mode-prompt-assets.js +1 -1
- package/dist/core/auto-mode-prompt.d.ts +11 -1
- package/dist/core/auto-mode-prompt.js +13 -10
- package/dist/core/auto-mode-rebuild.d.ts +7 -0
- package/dist/core/auto-mode-rebuild.js +4 -1
- package/dist/core/canonical-json.d.ts +3 -0
- package/dist/core/canonical-json.js +2 -1
- package/dist/core/checkpoint-requirements.d.ts +892 -0
- package/dist/core/checkpoint-requirements.js +333 -0
- package/dist/core/checkpoint-store.d.ts +94 -65
- package/dist/core/checkpoint-store.js +14 -26
- package/dist/core/child-clamp-seats.d.ts +228 -69
- package/dist/core/child-clamp-seats.js +211 -17
- package/dist/core/classifier-unavailable-arm.d.ts +5 -1
- package/dist/core/classifier-unavailable-arm.js +3 -3
- package/dist/core/compliance.d.ts +3 -0
- package/dist/core/compliance.js +9 -4
- package/dist/core/credential-vocabulary.d.ts +140 -0
- package/dist/core/credential-vocabulary.js +131 -0
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/effective-path-target.d.ts +73 -16
- package/dist/core/effective-path-target.js +63 -33
- package/dist/core/engine-notice.d.ts +32 -7
- package/dist/core/file-history-store.js +2 -2
- package/dist/core/fs-write-gate-policy.d.ts +4 -0
- package/dist/core/fs-write-gate-policy.js +22 -5
- package/dist/core/gate-bounded-await.d.ts +28 -13
- package/dist/core/gate-bounded-await.js +41 -4
- package/dist/core/gate-fold.d.ts +1 -1
- package/dist/core/gate-fold.js +24 -25
- package/dist/core/gate-lanes.js +16 -15
- package/dist/core/gate-outcome.d.ts +20 -5
- package/dist/core/gate-outcome.js +5 -0
- package/dist/core/governance-codes.d.ts +11 -4
- package/dist/core/governance-codes.js +14 -3
- package/dist/core/hooks.d.ts +4 -0
- package/dist/core/locked-config.d.ts +8 -0
- package/dist/core/locked-config.js +14 -2
- package/dist/core/lsp.js +3 -5
- package/dist/core/mailbox-store.d.ts +3 -4
- package/dist/core/mcp-connection-pool.d.ts +75 -0
- package/dist/core/mcp-connection-pool.js +128 -0
- package/dist/core/mcp-failure.d.ts +2 -1
- package/dist/core/mcp-failure.js +13 -2
- package/dist/core/mcp.d.ts +4 -0
- package/dist/core/mcp.js +70 -43
- package/dist/core/memory-engine/content-origin.js +2 -14
- package/dist/core/memory-engine/engine.d.ts +32 -22
- package/dist/core/memory-engine/engine.js +103 -55
- package/dist/core/memory-engine/file-backend.d.ts +13 -30
- package/dist/core/memory-engine/file-backend.js +37 -252
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/memory-engine/layout.d.ts +139 -9
- package/dist/core/memory-engine/layout.js +350 -7
- package/dist/core/memory-engine/scope-contract.d.ts +10 -0
- package/dist/core/memory-engine/scope-contract.js +8 -0
- package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
- package/dist/core/memory-engine/session-incarnation.js +5 -2
- package/dist/core/memory-engine/types.d.ts +10 -1
- package/dist/core/memory.js +5 -17
- package/dist/core/model-seat.d.ts +4 -4
- package/dist/core/model-seat.js +1 -1
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +19 -0
- package/dist/core/namespaced-rule-name.d.ts +29 -0
- package/dist/core/namespaced-rule-name.js +52 -0
- package/dist/core/park-selfcheck.d.ts +1 -1
- package/dist/core/park-selfcheck.js +16 -6
- package/dist/core/path-guard-rows.d.ts +99 -0
- package/dist/core/path-guard-rows.js +45 -0
- package/dist/core/path-identity.d.ts +22 -16
- package/dist/core/path-identity.js +10 -10
- package/dist/core/path-rule-binding.d.ts +2 -1
- package/dist/core/path-rule-binding.js +3 -1
- package/dist/core/pause-registry.d.ts +16 -2
- package/dist/core/pause-registry.js +6 -0
- package/dist/core/permission-rule-consent.js +0 -7
- package/dist/core/permission-rule-model.d.ts +68 -24
- package/dist/core/permission-rule-model.js +105 -24
- package/dist/core/permission-rule-store.js +2 -2
- package/dist/core/permission-rules.d.ts +3 -30
- package/dist/core/permission-rules.js +4 -54
- package/dist/core/read-root-candidate.d.ts +1 -3
- package/dist/core/read-root-candidate.js +4 -15
- package/dist/core/real-approval.d.ts +4 -3
- package/dist/core/retired-keys.d.ts +13 -0
- package/dist/core/retired-keys.js +14 -0
- package/dist/core/roles.d.ts +2 -3
- package/dist/core/roles.js +15 -6
- package/dist/core/runner/assemble-result.d.ts +0 -7
- package/dist/core/runner/assemble-result.js +19 -31
- package/dist/core/runner/caller-roster.d.ts +78 -0
- package/dist/core/runner/caller-roster.js +219 -0
- package/dist/core/runner/checkpoint-scope.d.ts +16 -16
- package/dist/core/runner/checkpoint-scope.js +2 -2
- package/dist/core/runner/child-tree.js +2 -2
- package/dist/core/runner/contracts.d.ts +68 -35
- package/dist/core/runner/decide-continuation.d.ts +51 -3
- package/dist/core/runner/decide-continuation.js +76 -21
- package/dist/core/runner/denial-limit-arms.d.ts +17 -5
- package/dist/core/runner/denial-limit-arms.js +11 -9
- package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
- package/dist/core/runner/extra-body-dropped-seat.js +22 -0
- package/dist/core/runner/finish-reason-seat.d.ts +9 -0
- package/dist/core/runner/finish-reason-seat.js +40 -0
- package/dist/core/runner/gate-exit.d.ts +44 -32
- package/dist/core/runner/gate-exit.js +112 -25
- package/dist/core/runner/inherited-ask-grants.js +6 -4
- package/dist/core/runner/mcp-redial.d.ts +3 -3
- package/dist/core/runner/mcp-redial.js +3 -4
- package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
- package/dist/core/runner/permission-rule-lanes.js +5 -4
- package/dist/core/runner/prepare-artifact.d.ts +4 -0
- package/dist/core/runner/prepare-artifact.js +2 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
- package/dist/core/runner/prepare-ask-lane.js +12 -6
- package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
- package/dist/core/runner/prepare-boundary-parks.js +4 -7
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
- package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
- package/dist/core/runner/prepare-config-doors.d.ts +9 -9
- package/dist/core/runner/prepare-config-doors.js +10 -5
- package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
- package/dist/core/runner/prepare-defer-classify.js +5 -3
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
- package/dist/core/runner/prepare-delegation-surface.js +3 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
- package/dist/core/runner/prepare-gate-stations.js +9 -7
- package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
- package/dist/core/runner/prepare-hands-readface.js +7 -7
- package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
- package/dist/core/runner/prepare-inherited-gate.js +14 -15
- package/dist/core/runner/prepare-listings.d.ts +7 -24
- package/dist/core/runner/prepare-listings.js +4 -5
- package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
- package/dist/core/runner/prepare-memory-engine-session.js +4 -3
- package/dist/core/runner/prepare-memory.d.ts +6 -0
- package/dist/core/runner/prepare-memory.js +13 -13
- package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
- package/dist/core/runner/prepare-offload-wrappers.js +3 -3
- package/dist/core/runner/prepare-park-ask.d.ts +7 -8
- package/dist/core/runner/prepare-park-ask.js +17 -10
- package/dist/core/runner/prepare-policy-chain.js +24 -44
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +3 -4
- package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
- package/dist/core/runner/prepare-protocol-tools.js +3 -4
- package/dist/core/runner/prepare-run-refs.d.ts +3 -0
- package/dist/core/runner/prepare-run-refs.js +2 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
- package/dist/core/runner/prepare-safety-scan.js +1 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
- package/dist/core/runner/prepare-suspend-saga.js +28 -12
- package/dist/core/runner/prepare-task.d.ts +1 -1
- package/dist/core/runner/prepare-task.js +67 -56
- package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
- package/dist/core/runner/prepare-wiring-manifest.js +5 -8
- package/dist/core/runner/resume-apply.js +7 -14
- package/dist/core/runner/resume-checkpoint-screen.js +11 -4
- package/dist/core/runner/resume-internals-and-config.js +2 -2
- package/dist/core/runner/resume-policy-outcome.js +5 -32
- package/dist/core/runner/resume-review-outcome.js +4 -1
- package/dist/core/runner/retired-runner-deps.d.ts +6 -0
- package/dist/core/runner/retired-runner-deps.js +10 -1
- package/dist/core/runner/run-brain-sinks.d.ts +4 -0
- package/dist/core/runner/run-brain-sinks.js +5 -2
- package/dist/core/runner/run-harness-handlers.js +27 -34
- package/dist/core/runner/run-leg.d.ts +0 -2
- package/dist/core/runner/run-leg.js +32 -33
- package/dist/core/runner/run-terminal-adoption.js +4 -3
- package/dist/core/runner/runtask.js +8 -8
- package/dist/core/runner/session-rule-policy.js +5 -5
- package/dist/core/runner/stream-settle-backstop.js +7 -4
- package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
- package/dist/core/runner/sub-agent-deny-lists.js +111 -80
- package/dist/core/runner/terminal-account.d.ts +48 -0
- package/dist/core/runner/terminal-account.js +186 -0
- package/dist/core/runner/tool-end-body.d.ts +54 -17
- package/dist/core/runner/tool-end-body.js +27 -2
- package/dist/core/runner/tool-face-overlay.d.ts +3 -1
- package/dist/core/runner/tool-face-overlay.js +6 -4
- package/dist/core/runner/tool-occurrence.d.ts +106 -0
- package/dist/core/runner/tool-occurrence.js +123 -0
- package/dist/core/runner/tool-progress-lane.d.ts +16 -8
- package/dist/core/runner/tool-progress-lane.js +25 -34
- package/dist/core/runner/turn-attachments.d.ts +1 -1
- package/dist/core/runner/write-protection-station.d.ts +21 -9
- package/dist/core/runner/write-protection-station.js +20 -17
- package/dist/core/runner-deps.d.ts +12 -22
- package/dist/core/secret-env.d.ts +16 -5
- package/dist/core/secret-env.js +23 -63
- package/dist/core/sensitive-path-policy.d.ts +19 -18
- package/dist/core/sensitive-path-policy.js +73 -49
- package/dist/core/session-reconcile.d.ts +3 -0
- package/dist/core/session-reconcile.js +5 -4
- package/dist/core/session-store.d.ts +5 -0
- package/dist/core/session-store.js +12 -0
- package/dist/core/session.d.ts +13 -0
- package/dist/core/shared-memory/normalize.d.ts +17 -4
- package/dist/core/shared-memory/normalize.js +7 -7
- package/dist/core/shell-lexer.js +5 -4
- package/dist/core/skill-grant.js +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
- package/dist/core/stub-env.d.ts +2 -0
- package/dist/core/stub-env.js +1 -0
- package/dist/core/synthetic-read-verdict.js +2 -2
- package/dist/core/task-event.d.ts +59 -5
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +42 -9
- package/dist/core/terminal-cause.d.ts +94 -11
- package/dist/core/tighten-task-spec.d.ts +5 -4
- package/dist/core/tighten-task-spec.js +20 -25
- package/dist/core/tool-catalog-entries.d.ts +5 -7
- package/dist/core/tool-catalog-entries.js +14 -15
- package/dist/core/tool-face.d.ts +47 -0
- package/dist/core/tool-face.js +5 -0
- package/dist/core/tool-model-gate.d.ts +5 -5
- package/dist/core/tool-name-aliases.d.ts +26 -2
- package/dist/core/tool-name-aliases.js +13 -0
- package/dist/core/tool-name-list.d.ts +30 -0
- package/dist/core/tool-name-list.js +55 -0
- package/dist/core/tool-name-readings.d.ts +1 -9
- package/dist/core/tool-name-readings.js +1 -3
- package/dist/core/tool-policy.d.ts +195 -85
- package/dist/core/tool-policy.js +165 -380
- package/dist/core/tool-registry.d.ts +44 -1
- package/dist/core/tool-registry.js +26 -0
- package/dist/core/tool-roster.d.ts +19 -1
- package/dist/core/tool-roster.js +21 -2
- package/dist/core/tool-spec-members.d.ts +45 -0
- package/dist/core/tool-spec-members.js +169 -0
- package/dist/core/tool-spec.d.ts +46 -8
- package/dist/core/tools.d.ts +6 -2
- package/dist/core/tools.js +8 -4
- package/dist/core/untrusted-egress.js +29 -26
- package/dist/core/untrusted-text.d.ts +13 -10
- package/dist/core/untrusted-text.js +6 -44
- package/dist/core/workflow-journal-store.d.ts +4 -1
- package/dist/core/write-protect.d.ts +65 -29
- package/dist/core/write-protect.js +235 -177
- package/dist/engine/compaction/compaction.d.ts +2 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
- package/dist/engine/execution-env/node-execution-env.js +16 -38
- package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
- package/dist/engine/execution-env/symlink-chain.js +32 -0
- package/dist/engine/harness/types.d.ts +7 -0
- package/dist/engine/llm/tool-turns.d.ts +3 -0
- package/dist/engine/llm/tool-turns.js +1 -0
- package/dist/engine/llm/types.d.ts +59 -23
- package/dist/engine/loop/agent-loop.js +16 -2
- package/dist/engine/session/memory-repo.js +11 -3
- package/dist/engine/session/repo-utils.d.ts +8 -0
- package/dist/engine/session/repo-utils.js +13 -0
- package/dist/index.d.ts +20 -23
- package/dist/index.js +17 -20
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/governance-baseline-validity.js +1 -1
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +16 -35
- package/dist/orchestration/run-workflow-tool.js +38 -78
- package/dist/orchestration/workflow-governance.d.ts +21 -25
- package/dist/orchestration/workflow-governance.js +38 -78
- package/dist/orchestration/workflow-primitives.d.ts +7 -22
- package/dist/orchestration/workflow-primitives.js +5 -12
- package/dist/orchestration/workflow-types.d.ts +1 -1
- package/dist/orchestration/workflow.d.ts +61 -11
- package/dist/orchestration/workflow.js +189 -117
- package/dist/scenarios/full-body.d.ts +1 -1
- package/dist/shell/lex.d.ts +0 -21
- package/dist/shell/lex.js +250 -68
- package/dist/shell/shell-words.d.ts +35 -0
- package/dist/shell/shell-words.js +65 -0
- package/dist/stores/file/checkpoint-store.d.ts +5 -5
- package/dist/stores/file/checkpoint-store.js +5 -2
- package/dist/stores/file/index.d.ts +0 -8
- package/dist/stores/file/index.js +0 -12
- package/dist/stores/file/session-store.d.ts +3 -0
- package/dist/stores/file/session-store.js +44 -19
- package/dist/tools/artifact/artifact-tool.js +2 -0
- package/dist/tools/fs/bash-program-position.d.ts +10 -3
- package/dist/tools/fs/bash-program-position.js +21 -9
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/bash-readonly-classifier.js +8 -7
- package/dist/tools/fs/containment-face.d.ts +4 -0
- package/dist/tools/fs/containment-face.js +3 -0
- package/dist/tools/fs/fs-shared.d.ts +10 -0
- package/dist/tools/fs/fs-shared.js +18 -0
- package/dist/tools/fs/fs-write.js +4 -2
- package/dist/tools/fs/index.d.ts +1 -1
- package/dist/tools/fs/program-read-model.d.ts +39 -18
- package/dist/tools/fs/program-read-model.js +29 -19
- package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
- package/dist/tools/fs/program-read-model.rows.js +5 -5
- package/dist/tools/fs/read-deny-home.d.ts +33 -47
- package/dist/tools/fs/read-deny-home.js +35 -49
- package/dist/tools/fs/read-deny.d.ts +23 -22
- package/dist/tools/fs/read-deny.js +114 -89
- package/dist/tools/fs/removal-targets.d.ts +56 -23
- package/dist/tools/fs/removal-targets.js +65 -87
- package/dist/tools/fs/safety.d.ts +29 -22
- package/dist/tools/fs/safety.js +17 -11
- package/dist/tools/fs/search.js +10 -8
- package/dist/tools/fs/shell-read-argv.js +1 -1
- package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
- package/dist/tools/fs/shell-read-bindings.js +324 -86
- package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
- package/dist/tools/fs/shell-read-builtins.js +43 -0
- package/dist/tools/fs/shell-read-check.d.ts +15 -1
- package/dist/tools/fs/shell-read-check.js +51 -42
- package/dist/tools/fs/shell-read-establish.js +6 -6
- package/dist/tools/fs/shell-read-functions.d.ts +22 -0
- package/dist/tools/fs/shell-read-functions.js +29 -0
- package/dist/tools/fs/shell-read-judge.d.ts +48 -18
- package/dist/tools/fs/shell-read-judge.js +33 -4
- package/dist/tools/fs/shell-read-plan.d.ts +12 -6
- package/dist/tools/fs/shell-read-plan.js +118 -113
- package/dist/tools/fs/shell-read-start.d.ts +29 -6
- package/dist/tools/fs/shell-read-start.js +16 -9
- package/dist/tools/fs/shell-read-word.js +4 -0
- package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
- package/dist/tools/fs/shell-read-workdir.js +281 -0
- package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
- package/dist/tools/fs/shell-read-write-targets.js +14 -5
- package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
- package/dist/tools/fs/shell-reentry-text.js +3 -11
- package/dist/tools/task-list.d.ts +1 -2
- package/dist/tools/web.js +1 -1
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +107 -143
- package/wire-changes/core@7.49.0.json +4765 -0
- package/dist/agents/teacher.d.ts +0 -159
- package/dist/agents/teacher.js +0 -577
- package/dist/agents/team.d.ts +0 -226
- package/dist/agents/team.js +0 -257
- package/dist/core/strategy-store.d.ts +0 -238
- package/dist/core/strategy-store.js +0 -253
- package/dist/orchestration/builtin-workflows.d.ts +0 -133
- package/dist/orchestration/builtin-workflows.js +0 -159
- package/dist/scenarios/env.d.ts +0 -40
- package/dist/scenarios/env.js +0 -39
- package/dist/scenarios/scenario-registry.d.ts +0 -110
- package/dist/scenarios/scenario-registry.js +0 -140
- package/dist/scenarios/teacher-quickstart.d.ts +0 -40
- package/dist/scenarios/teacher-quickstart.js +0 -12
- package/dist/stores/cc/lockfile.d.ts +0 -10
- package/dist/stores/cc/lockfile.js +0 -98
- package/dist/stores/cc/mailbox-store.d.ts +0 -20
- package/dist/stores/cc/mailbox-store.js +0 -255
- package/dist/stores/cc/roster-adapter.d.ts +0 -12
- package/dist/stores/cc/roster-adapter.js +0 -117
- package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
- package/dist/stores/cc/sidecar-transcript.js +0 -264
- package/dist/stores/cc/task-list-store.d.ts +0 -2
- package/dist/stores/cc/task-list-store.js +0 -125
- package/dist/stores/file/strategy-store.d.ts +0 -97
- package/dist/stores/file/strategy-store.js +0 -341
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHETHER A BUILTIN RUNS BY ITS NAME — a binding of the same ordered thread as a variable's or a function's
|
|
3
|
+
* (`shell-read-bindings.ts`). `enable -n NAME` disables a builtin (the word then finds no command: "command not found"),
|
|
4
|
+
* `enable NAME` enables it again, and every builtin starts a text enabled. The thread keeps this the way it keeps a
|
|
5
|
+
* function's definition, under a key neither a variable nor a function can spell, and a reader at a segment asks the one
|
|
6
|
+
* ordered rule ({@link BuiltinState}) before it takes a word for the builtin of its name; this module is the vocabulary of
|
|
7
|
+
* that key and the reading of an `enable` into bindings.
|
|
8
|
+
*/
|
|
9
|
+
import type { LexedWord } from "../../shell/lex.js";
|
|
10
|
+
import type { Binding, BoundValue, NameState } from "./shell-read-bindings.js";
|
|
11
|
+
/** What a builtin is at a reader: `enabled` — it certainly runs by its name; `disabled` — it certainly does not (`enable -n`:
|
|
12
|
+
* "command not found"); `maybe` — an `enable` that may not have run, a call that may have run one, one for every builtin. */
|
|
13
|
+
export type BuiltinState = "enabled" | "maybe" | "disabled";
|
|
14
|
+
/** The builtin whose words are read into the availability of the others. */
|
|
15
|
+
export declare const ENABLE_BUILTIN = "enable";
|
|
16
|
+
export declare const builtinKey: (name: string) => string;
|
|
17
|
+
export declare const isBuiltinKey: (name: string) => boolean;
|
|
18
|
+
/** Every builtin at once (`enable -f FILE` loads one over any name; a name the text computes may be any). */
|
|
19
|
+
export declare const BUILTINS_ALL: string;
|
|
20
|
+
export declare const BUILTIN_ENABLED: NameState;
|
|
21
|
+
export declare const isDisabledValue: (v: BoundValue) => boolean;
|
|
22
|
+
/** What the shell finds looking up each word of a chain it runs as builtins (`builtin` / `command` in front, then the name): the
|
|
23
|
+
* same reading ({@link BuiltinState}) for every word, since the chain runs no further than its first word that finds no command —
|
|
24
|
+
* one that certainly does not run makes the chain certainly not run, and one that may not makes it so. `ask` absent: all enabled. */
|
|
25
|
+
export declare function chainAvailability(words: readonly string[], ask: ((builtin: string) => BuiltinState) | undefined): BuiltinState;
|
|
26
|
+
/** Does an entry (or a made-unreadable name) with this name speak for `name`: no name (every name), the name itself, or —
|
|
27
|
+
* for a builtin's key — every builtin at once. */
|
|
28
|
+
export declare const speaksFor: (subject: string | undefined, name: string) => boolean;
|
|
29
|
+
/** The bindings an `enable [-a] [-dnps] [-f FILE] [NAME…]` makes: `enable NAME` enables each name, `enable -n NAME` disables it
|
|
30
|
+
* (a literal name: its key); any other switch — `-f FILE` loads a builtin over any name, `-d`, the listing switches — or a
|
|
31
|
+
* name the text computes may change every builtin; a bare listing (`enable`, `enable -n`) changes none. The words in front of
|
|
32
|
+
* the first name are read as the shell reads them: a word opening with `-` (but a lone `-`, which is a name) is switches, `--`
|
|
33
|
+
* ends them, and a letter outside the switches is an invalid option — the shell rejects the whole call (status 2) and nothing
|
|
34
|
+
* changes, whatever else the call says; a word after the first name is a name. */
|
|
35
|
+
export declare function enableBindings(args: readonly LexedWord[]): Binding[];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const ENABLE_BUILTIN = "enable";
|
|
2
|
+
const BUILTIN_KEY_PREFIX = "[]";
|
|
3
|
+
export const builtinKey = (name) => BUILTIN_KEY_PREFIX + name;
|
|
4
|
+
export const isBuiltinKey = (name) => name.startsWith(BUILTIN_KEY_PREFIX);
|
|
5
|
+
export const BUILTINS_ALL = builtinKey("*");
|
|
6
|
+
export const BUILTIN_ENABLED = [{ spelled: "enabled" }];
|
|
7
|
+
const BUILTIN_DISABLED = [{ spelled: "disabled" }];
|
|
8
|
+
export const isDisabledValue = (v) => v.spelled === "disabled";
|
|
9
|
+
export function chainAvailability(words, ask) {
|
|
10
|
+
const states = words.map((w) => ask?.(w) ?? "enabled");
|
|
11
|
+
return states.includes("disabled") ? "disabled" : states.includes("maybe") ? "maybe" : "enabled";
|
|
12
|
+
}
|
|
13
|
+
export const speaksFor = (subject, name) => subject === undefined || subject === name || (subject === BUILTINS_ALL && isBuiltinKey(name));
|
|
14
|
+
const ENABLE_SWITCHES = "adfnps";
|
|
15
|
+
export function enableBindings(args) {
|
|
16
|
+
const computed = (w) => w.pieces.some((p) => p.expansion !== undefined || p.literal === undefined);
|
|
17
|
+
let disable = false;
|
|
18
|
+
let other = false;
|
|
19
|
+
let at = 0;
|
|
20
|
+
for (; at < args.length; at++) {
|
|
21
|
+
const w = args[at];
|
|
22
|
+
if (w.folded === "--") {
|
|
23
|
+
at++;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
if (computed(w) || w.folded.length < 2 || !w.folded.startsWith("-"))
|
|
27
|
+
break;
|
|
28
|
+
for (const letter of w.folded.slice(1)) {
|
|
29
|
+
if (!ENABLE_SWITCHES.includes(letter))
|
|
30
|
+
return [];
|
|
31
|
+
if (letter === "f")
|
|
32
|
+
return [{ name: BUILTINS_ALL, state: "unknown" }];
|
|
33
|
+
if (letter === "n")
|
|
34
|
+
disable = true;
|
|
35
|
+
else
|
|
36
|
+
other = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const names = args.slice(at);
|
|
40
|
+
if (other || names.some(computed))
|
|
41
|
+
return [{ name: BUILTINS_ALL, state: "unknown" }];
|
|
42
|
+
return names.map((w) => ({ name: builtinKey(w.folded), state: disable ? BUILTIN_DISABLED : BUILTIN_ENABLED }));
|
|
43
|
+
}
|
|
@@ -44,6 +44,9 @@ export type ShellReadRefusalDetails = NonNullable<ReturnType<typeof violationDet
|
|
|
44
44
|
export interface ShellReadVerdict {
|
|
45
45
|
readonly disposition: ShellReadDisposition;
|
|
46
46
|
readonly finding: ShellReadFinding;
|
|
47
|
+
/** `write` ⇔ the finding is about where the command WRITES — its witness is a write target (a redirection's, or a word
|
|
48
|
+
* the program's row writes), judged against the write roots. Absent ⇒ a read's finding (or `vouch`). */
|
|
49
|
+
readonly face?: "write";
|
|
47
50
|
/** `refuse`: the refusal text (`Error (<tool>): …`, the five-segment deny sentence or the containment
|
|
48
51
|
* sentence). `mandated` / `ordinary`: the ACCOUNT of the finding, for the approval card's disclosure seat
|
|
49
52
|
* (never the ask's own message — that is the auto-mode classifier's input). Absent for `vouch`. */
|
|
@@ -58,7 +61,7 @@ export interface ShellReadVerdict {
|
|
|
58
61
|
/** A FACT beside the disposition, for the mode that answers a file command itself: every write target this command
|
|
59
62
|
* names was established inside the boundary's WRITE roots at every seat the program writes it (the entry, where it
|
|
60
63
|
* lands, or both — `WriteSeat`, shell-read-write-targets.ts), off the write-protection table and off every deployment
|
|
61
|
-
* write
|
|
64
|
+
* guarded write row (the leg's one write judgment, `ShellReadBoundary.writeTargetJudge`), no removal (`rm` / `rmdir`) targets a
|
|
62
65
|
* write root or a directory containing one, every program the command runs is in its text (no `sed -f` file, no
|
|
63
66
|
* re-entered shell, no unreadable text), and the line's order keeps each segment's establishment true
|
|
64
67
|
* ({@link lineOrderPlaced}). Absent ⇒ not established — the mode does not answer; the station's own table is unchanged. */
|
|
@@ -67,6 +70,11 @@ export interface ShellReadVerdict {
|
|
|
67
70
|
* the table's own judge, as a write tool's target is — so the command's ask carries that table's class, whichever
|
|
68
71
|
* mode reads it. Absent ⇒ no target the station established is covered (or no judge is mounted). */
|
|
69
72
|
readonly writeProtected?: true;
|
|
73
|
+
/** A FACT beside the disposition: a target this command's program WRITES was placed and the environment could not say
|
|
74
|
+
* where it leads ({@link EstablishedRead.unresolved}) — the reading's `target_unresolved` fact, the same one a write
|
|
75
|
+
* tool's unresolved target states, so the command's ask carries that class (no automatic lane answers it; the shell face
|
|
76
|
+
* releases it under bypassPermissions). Absent ⇒ every written target resolved, or was not placed for another reason. */
|
|
77
|
+
readonly targetUnresolved?: true;
|
|
70
78
|
/** A FACT beside `writesPlaced`'s absence: the command is one of the mode's file commands whose writes the station would
|
|
71
79
|
* have placed but for an OPTION WORD on a `cp` / `mv` segment ({@link UNPLACED_OPTIONS}: any word starting with `-` — the
|
|
72
80
|
* reference implementation's rule, "with flags requires manual approval to ensure path safety"). Names the first such
|
|
@@ -149,6 +157,12 @@ export interface ShellReadStationOptions {
|
|
|
149
157
|
/** The home views of the placement the boundary's deny seats were compiled at (read-deny-home.ts) — the Runner's. Absent,
|
|
150
158
|
* the declared home alone. */
|
|
151
159
|
readonly homeViews?: HomeViewSource;
|
|
160
|
+
/** The engine's account of a REFUSED admission, called with the call's id before the refusal is handed back to the tool
|
|
161
|
+
* — the one trusted channel by which a refusal after the gate reaches the call's record (the tool's result is the
|
|
162
|
+
* tool's own word, and a refusal must not read as a tool failure under the gate's `allowed`). Every shell tool this
|
|
163
|
+
* station admits (both Bash faces, Monitor) refuses through {@link ShellReadStation.admit}, so this is the one
|
|
164
|
+
* place to hear it. The Runner's; absent outside one. */
|
|
165
|
+
readonly onAdmissionRefused?: (toolCallId: string) => void;
|
|
152
166
|
}
|
|
153
167
|
/** What a consumer hands {@link judgeShellRead}: the command as written, the environment whose filesystem
|
|
154
168
|
* answers where its words land (symlinks resolved, patterns expanded — a `NodeExecutionEnv` over a real
|
|
@@ -9,10 +9,11 @@ import { createShellReadEstablisher, establishShellReadsLexically } from "./shel
|
|
|
9
9
|
import { declaredReadDenyHomes, homeViewsAt } from "./read-deny-home.js";
|
|
10
10
|
import { programArgs } from "./shell-read-write-targets.js";
|
|
11
11
|
import { programRow, selectedOptions } from "./program-read-model.js";
|
|
12
|
-
import { allFindings, decideShellRead, denyRowAtOrBelow, foldShellReadFindings, judgeEstablishedPlan, readRootGrantCandidateOf, stationGuards, } from "./shell-read-judge.js";
|
|
12
|
+
import { allFindings, anyFaceGuards, decideShellRead, denyRowAtOrBelow, foldShellReadFindings, judgeEstablishedPlan, readRootGrantCandidateOf, stationGuards, writeFaceGuards, } from "./shell-read-judge.js";
|
|
13
13
|
import { seatOf } from "../../core/tool-name-readings.js";
|
|
14
14
|
import { normalizeReadRootCandidate } from "../../core/read-root-candidate.js";
|
|
15
15
|
import { argvOf, lexShellText } from "../../shell/lex.js";
|
|
16
|
+
import { assertNoRetiredShellReadBoundaryKeys } from "../../core/retired-keys.js";
|
|
16
17
|
function containmentDetails(paths) {
|
|
17
18
|
return { type: "readonly_out_of_root", code: "readonly_out_of_root", paths };
|
|
18
19
|
}
|
|
@@ -75,7 +76,7 @@ function writesPlacedOf(judged, boundary) {
|
|
|
75
76
|
if (f.read.candidate.face !== "write")
|
|
76
77
|
continue;
|
|
77
78
|
const target = f.read.target;
|
|
78
|
-
if (f.read.established === "unestablishable" || target === undefined || isPattern(f) ||
|
|
79
|
+
if (f.read.established === "unestablishable" || target === undefined || isPattern(f) || f.kind !== "none" || (f.containment !== "inside" && f.containment !== "open"))
|
|
79
80
|
return false;
|
|
80
81
|
if (beneath && denyRowAtOrBelow(f.read, boundary, !REMOVAL_COMMANDS.has(verb)))
|
|
81
82
|
return false;
|
|
@@ -97,10 +98,9 @@ function unplacedOptionsOf(judged) {
|
|
|
97
98
|
}
|
|
98
99
|
return undefined;
|
|
99
100
|
}
|
|
100
|
-
async function writeGuardsOf(judged, boundary,
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
if (protection === undefined && guard === undefined)
|
|
101
|
+
async function writeGuardsOf(judged, boundary, env, signal) {
|
|
102
|
+
const judge = boundary.writeTargetJudge;
|
|
103
|
+
if (judge === undefined)
|
|
104
104
|
return { unplaced: false, writeProtected: false };
|
|
105
105
|
const { runs, concurrent } = runOrderOf(judged);
|
|
106
106
|
const asKey = new Map();
|
|
@@ -155,25 +155,17 @@ async function writeGuardsOf(judged, boundary, placed, env, signal) {
|
|
|
155
155
|
const dir = fold(parentOf(view));
|
|
156
156
|
return writtenBy.some((paths, i) => i !== at && (i < at || concurrent) && paths.some((w) => withinAnyRoot([fold(w)], dir) || withinAnyRoot([dir], fold(w))));
|
|
157
157
|
};
|
|
158
|
-
const answer = async (
|
|
159
|
-
try {
|
|
160
|
-
return (await ask()) === true;
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
return undefined;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
158
|
+
const answer = async (view, key) => (await judge(view, { ...(signal !== undefined ? { signal } : {}), ...(key ? { canonical: true } : {}) })) ?? "clear";
|
|
166
159
|
const asked = [...[...asKey].map(([view, v]) => [view, v, true]), ...[...asSpelling].filter(([view]) => !landingKeys.has(view)).map(([view, v]) => [view, v, false])];
|
|
167
160
|
let unplaced = false;
|
|
168
161
|
let writeProtected = false;
|
|
169
162
|
for (const [view, v, key] of asked) {
|
|
170
163
|
if (!(await writable(view, v)) && !changedBefore(view, v.at))
|
|
171
164
|
continue;
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
if (covered === true)
|
|
165
|
+
const found = await answer(view, key);
|
|
166
|
+
if (found === "protected")
|
|
175
167
|
writeProtected = true;
|
|
176
|
-
if (
|
|
168
|
+
if (found !== "clear")
|
|
177
169
|
unplaced = true;
|
|
178
170
|
if (writeProtected)
|
|
179
171
|
break;
|
|
@@ -206,15 +198,21 @@ const CAUSE_SPELLING_MAX = 64;
|
|
|
206
198
|
function fitCause(cause) {
|
|
207
199
|
return cause.replace(/^"([^"]*)"/, (_m, spelling) => `"${fitMiddle(spelling, CAUSE_SPELLING_MAX)}"`);
|
|
208
200
|
}
|
|
209
|
-
function findingAccount(fold,
|
|
210
|
-
const allowed = fitMiddle(roots.length > 0 ? roots.join(", ") : "(none)", ACCOUNT_PART_MAX);
|
|
201
|
+
function findingAccount(fold, boundary) {
|
|
211
202
|
const w = fold.witness;
|
|
203
|
+
const writes = w?.read.candidate.face === "write";
|
|
204
|
+
const roots = writes ? (boundary.writeRoots ?? []) : boundary.roots;
|
|
205
|
+
const allowed = fitMiddle(roots.length > 0 ? roots.join(", ") : "(none)", ACCOUNT_PART_MAX);
|
|
212
206
|
switch (fold.finding) {
|
|
213
207
|
case "outside_roots":
|
|
214
|
-
return
|
|
208
|
+
return `${writes ? "writes" : "reads"} outside the allowed directories for this session: "${fitMiddle(w?.read.target ?? "?", ACCOUNT_PART_MAX)}" is not under ${allowed} ${NOT_AUTO_ALLOWED}`;
|
|
215
209
|
case "unestablishable":
|
|
210
|
+
if (writes && boundary.writeRoots === undefined)
|
|
211
|
+
return `this command writes "${fitMiddle(w.read.target ?? w.read.candidate.spelled, ACCOUNT_PART_MAX)}", and this session declares no directories it may write ${NOT_AUTO_ALLOWED}`;
|
|
212
|
+
if (writes)
|
|
213
|
+
return `where this command writes could not be established${w.read.cause !== undefined ? `: ${fitCause(w.read.cause)}` : ""}, so it cannot be confirmed to write only inside the allowed directories for this session (${allowed}) ${NOT_AUTO_ALLOWED}`;
|
|
216
214
|
if (w?.read.cause !== undefined)
|
|
217
|
-
return `where this command reads could not be established: ${fitCause(w.read.cause)}, so it cannot be confirmed ${face === "open" ? "not to read a path the deny list names" : `to read only inside the allowed directories for this session (${allowed})`} ${NOT_AUTO_ALLOWED}`;
|
|
215
|
+
return `where this command reads could not be established: ${fitCause(w.read.cause)}, so it cannot be confirmed ${boundary.face === "open" ? "not to read a path the deny list names" : `to read only inside the allowed directories for this session (${allowed})`} ${NOT_AUTO_ALLOWED}`;
|
|
218
216
|
if (fold.declined?.baseMove !== undefined)
|
|
219
217
|
return `"${fitMiddle(fold.declined.text.trim(), ACCOUNT_PART_MAX)}" moves the working directory somewhere this command's text does not settle ${NOT_AUTO_ALLOWED}`;
|
|
220
218
|
if (fold.declined?.unreadProgramText !== undefined)
|
|
@@ -243,7 +241,7 @@ function outsidePathsOf(judged) {
|
|
|
243
241
|
}
|
|
244
242
|
return out;
|
|
245
243
|
}
|
|
246
|
-
function refusalOf(tool, fold,
|
|
244
|
+
function refusalOf(tool, fold, boundary, paths) {
|
|
247
245
|
const w = fold.witness;
|
|
248
246
|
if (fold.finding === "deny_hit" && w?.deny !== undefined) {
|
|
249
247
|
const target = w.read.target ?? w.deny.matchedView;
|
|
@@ -252,7 +250,7 @@ function refusalOf(tool, fold, roots, paths, face) {
|
|
|
252
250
|
const sentence = `Error (${tool}): ${core} ${write ? WRITE_DENY_REFUSAL_NOTE : DENY_REFUSAL_NOTE}`;
|
|
253
251
|
return { sentence, ...(write ? {} : { details: violationDetails({ code: "read_path_denied", message: sentence, target, pattern: w.deny.pattern }) }) };
|
|
254
252
|
}
|
|
255
|
-
const account = findingAccount(fold,
|
|
253
|
+
const account = findingAccount(fold, boundary);
|
|
256
254
|
const sentence = `Error (${tool}): ${account}. ${BASH_READONLY_CONFINEMENT_NOTE}`;
|
|
257
255
|
return { sentence, details: findingDetails(fold.finding, paths) };
|
|
258
256
|
}
|
|
@@ -268,37 +266,40 @@ function verdictOf(tool, seat, fold, judged, boundary) {
|
|
|
268
266
|
return { disposition, finding: fold.finding };
|
|
269
267
|
const paths = outsidePathsOf(judged);
|
|
270
268
|
const unestablished = fold.unestablished === true ? { unestablished: true } : {};
|
|
269
|
+
const writes = fold.witness?.read.candidate.face === "write" ? { face: "write" } : {};
|
|
271
270
|
if (disposition === "refuse") {
|
|
272
|
-
const r = refusalOf(tool, fold, boundary
|
|
273
|
-
return { disposition, finding: fold.finding, sentence: r.sentence, ...(r.details !== undefined ? { details: r.details } : {}) };
|
|
271
|
+
const r = refusalOf(tool, fold, boundary, paths);
|
|
272
|
+
return { disposition, finding: fold.finding, ...writes, sentence: r.sentence, ...(r.details !== undefined ? { details: r.details } : {}) };
|
|
274
273
|
}
|
|
275
274
|
const grant = fold.finding === "outside_roots" ? readRootGrantCandidateOf(judged, boundary) : undefined;
|
|
276
275
|
const containment = fold.finding === "outside_roots" || fold.finding === "unestablishable";
|
|
277
276
|
return {
|
|
278
277
|
disposition,
|
|
279
278
|
finding: fold.finding,
|
|
279
|
+
...writes,
|
|
280
280
|
...unestablished,
|
|
281
|
-
sentence: findingAccount(fold, boundary
|
|
281
|
+
sentence: findingAccount(fold, boundary),
|
|
282
282
|
...(disposition === "mandated" && containment ? { details: findingDetails(fold.finding, paths) } : {}),
|
|
283
283
|
...(grant !== undefined ? { readRootCandidate: { dir: grant.dir, ...(grant.covers !== undefined ? { covers: grant.covers } : {}), clearsThisAsk: true } } : {}),
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
|
-
function declarationsOf(boundary, readers) {
|
|
287
|
-
return { readers, cwd: boundary.cwd ?? boundary.roots[0] ?? "", roots: boundary.roots, ...(boundary.homeDir !== undefined ? { homeDir: boundary.homeDir } : {}), ...(boundary.face !== undefined ? { face: boundary.face } : {}) };
|
|
286
|
+
function declarationsOf(boundary, readers, cdPath) {
|
|
287
|
+
return { readers, cwd: boundary.cwd ?? boundary.roots[0] ?? "", roots: boundary.roots, cdPath, ...(boundary.homeDir !== undefined ? { homeDir: boundary.homeDir } : {}), ...(boundary.face !== undefined ? { face: boundary.face } : {}) };
|
|
288
288
|
}
|
|
289
289
|
function judgePlan(plan, established, boundary) {
|
|
290
290
|
return judgeEstablishedPlan(plan, established.reads, boundary, (n) => {
|
|
291
291
|
const nested = established.nested.get(n);
|
|
292
|
-
return nested === undefined ? { plan: n, segments: [], guards: stationGuards(boundary) } : judgePlan(n, nested, boundary);
|
|
292
|
+
return nested === undefined ? { plan: n, segments: [], guards: stationGuards(boundary), writeGuards: writeFaceGuards(boundary) } : judgePlan(n, nested, boundary);
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
async function applyAlsoInside(judged, boundary) {
|
|
296
296
|
const alsoInside = boundary.alsoInside;
|
|
297
297
|
if (alsoInside === undefined)
|
|
298
298
|
return judged;
|
|
299
|
+
const outsideRead = (f) => f.kind === "outside_roots" && f.read.candidate.face !== "write" && f.read.target !== undefined;
|
|
299
300
|
const rescued = new Set();
|
|
300
301
|
for (const f of allFindings(judged)) {
|
|
301
|
-
if (f
|
|
302
|
+
if (!outsideRead(f) || rescued.has(f.read.target))
|
|
302
303
|
continue;
|
|
303
304
|
if (await alsoInside(f.read.target))
|
|
304
305
|
rescued.add(f.read.target);
|
|
@@ -308,28 +309,31 @@ async function applyAlsoInside(judged, boundary) {
|
|
|
308
309
|
const rewrite = (j) => ({
|
|
309
310
|
plan: j.plan,
|
|
310
311
|
guards: j.guards,
|
|
312
|
+
writeGuards: j.writeGuards,
|
|
311
313
|
segments: j.segments.map((s) => ({
|
|
312
314
|
segment: s.segment,
|
|
313
|
-
findings: s.findings.map((f) => (f
|
|
315
|
+
findings: s.findings.map((f) => (outsideRead(f) && rescued.has(f.read.target) ? { ...f, kind: "none", containment: "inside" } : f)),
|
|
314
316
|
nested: s.nested.map(rewrite),
|
|
315
317
|
})),
|
|
316
318
|
});
|
|
317
319
|
return rewrite(judged);
|
|
318
320
|
}
|
|
319
321
|
async function runShellReadStation(tool, command, declared, seat, readers, establisher, env, signal) {
|
|
320
|
-
const plan = planShellReads(command, declarationsOf(declared, readers));
|
|
322
|
+
const plan = planShellReads(command, declarationsOf(declared, readers, env.cdPath));
|
|
321
323
|
const established = await establisher.establish(plan, declared, signal);
|
|
322
324
|
const boundary = { ...declared, roots: established.roots };
|
|
323
325
|
const judged = await applyAlsoInside(judgePlan(plan, established, boundary), boundary);
|
|
324
326
|
const judgedVerdict = verdictOf(tool, seat, foldShellReadFindings(judged), judged, boundary);
|
|
325
327
|
const unplaced = unplacedOptionsOf(judged);
|
|
326
328
|
const placed = writesPlacedOf(judged, boundary);
|
|
327
|
-
const guarded = judgedVerdict.disposition === "refuse" ? { unplaced: false, writeProtected: false } : await writeGuardsOf(judged, boundary,
|
|
328
|
-
const
|
|
329
|
+
const guarded = judgedVerdict.disposition === "refuse" ? { unplaced: false, writeProtected: false } : await writeGuardsOf(judged, boundary, env, signal);
|
|
330
|
+
const unresolved = judgedVerdict.disposition !== "refuse" && runOrderOf(judged).runs.some((s) => s.findings.some((f) => f.read.written !== undefined && f.read.unresolved === true));
|
|
331
|
+
const facts = { ...judgedVerdict, ...(guarded.writeProtected ? { writeProtected: true } : {}), ...(unresolved ? { targetUnresolved: true } : {}) };
|
|
329
332
|
const verdict = placed && !guarded.unplaced ? { ...facts, writesPlaced: true } : unplaced !== undefined ? { ...facts, unplacedOptions: unplaced } : facts;
|
|
330
333
|
return { plan, established, judged, boundary, verdict };
|
|
331
334
|
}
|
|
332
335
|
export async function judgeShellRead(input) {
|
|
336
|
+
assertNoRetiredShellReadBoundaryKeys(input.boundary);
|
|
333
337
|
const readers = input.readers ?? new Set(bashClassifyDefaultAllow());
|
|
334
338
|
const run = await runShellReadStation(input.tool ?? "Bash", input.command, input.boundary, input.seat, readers, createShellReadEstablisher(input.env, input.boundary.roots, { homeViews: homeViewsAt(input.env, input.readDenyHomes === undefined ? declaredReadDenyHomes(input.env) : checkedReadDenyHomes(input.readDenyHomes)) }), input.env, input.signal);
|
|
335
339
|
const offer = normalizeReadRootCandidate(run.verdict.readRootCandidate);
|
|
@@ -341,8 +345,9 @@ export async function judgeShellRead(input) {
|
|
|
341
345
|
};
|
|
342
346
|
}
|
|
343
347
|
export function judgeShellReadLexically(command, boundary, seat, opts) {
|
|
348
|
+
assertNoRetiredShellReadBoundaryKeys(boundary);
|
|
344
349
|
const readers = opts?.readers ?? new Set(bashClassifyDefaultAllow());
|
|
345
|
-
const plan = planShellReads(command, declarationsOf(boundary, readers));
|
|
350
|
+
const plan = planShellReads(command, declarationsOf(boundary, readers, []));
|
|
346
351
|
const established = establishShellReadsLexically(plan, boundary);
|
|
347
352
|
const judged = judgePlan(plan, established, boundary);
|
|
348
353
|
return verdictOf(opts?.tool ?? "Bash", seat, foldShellReadFindings(judged), judged, boundary);
|
|
@@ -393,7 +398,7 @@ export function createShellReadStation(opts) {
|
|
|
393
398
|
const member = memberOf(call) ?? toolName;
|
|
394
399
|
const seat = opts.seatOf(member);
|
|
395
400
|
const boundary = opts.boundaryOf(member, undefined);
|
|
396
|
-
if (boundary === undefined || !(
|
|
401
|
+
if (boundary === undefined || !(anyFaceGuards(boundary) || boundary.writeTargetJudge !== undefined))
|
|
397
402
|
return { disposition: "vouch", finding: "none" };
|
|
398
403
|
const fold = { finding: "unestablishable" };
|
|
399
404
|
const disposition = decideShellRead("unestablishable", seat);
|
|
@@ -411,8 +416,12 @@ export function createShellReadStation(opts) {
|
|
|
411
416
|
if (a === undefined)
|
|
412
417
|
return undefined;
|
|
413
418
|
admissions.delete(toolCallId);
|
|
419
|
+
const refused = (r) => {
|
|
420
|
+
opts.onAdmissionRefused?.(toolCallId);
|
|
421
|
+
return r;
|
|
422
|
+
};
|
|
414
423
|
if (a.toolName !== toolName || a.command !== command) {
|
|
415
|
-
return errorResult(`Error (${toolName}): the command that would run is not the one the read station judged for this call — refused before the spawn; re-submit it as a fresh call.`, containmentDetails([]));
|
|
424
|
+
return refused(errorResult(`Error (${toolName}): the command that would run is not the one the read station judged for this call — refused before the spawn; re-submit it as a fresh call.`, containmentDetails([])));
|
|
416
425
|
}
|
|
417
426
|
const establisher = establisherFor(a.boundary.roots);
|
|
418
427
|
const { changed } = await establisher.reestablish(a.established, a.boundary, signal);
|
|
@@ -422,13 +431,13 @@ export function createShellReadStation(opts) {
|
|
|
422
431
|
const judged = await applyAlsoInside(judgePlan(a.established.plan, again, a.boundary), a.boundary);
|
|
423
432
|
const fold = foldShellReadFindings(judged);
|
|
424
433
|
if (fold.finding === "deny_hit") {
|
|
425
|
-
const r = refusalOf(toolName, fold, a.boundary
|
|
426
|
-
return errorResult(r.sentence, r.details);
|
|
434
|
+
const r = refusalOf(toolName, fold, a.boundary, outsidePathsOf(judged));
|
|
435
|
+
return refused(errorResult(r.sentence, r.details));
|
|
427
436
|
}
|
|
428
437
|
const moved = changed[0];
|
|
429
438
|
const identity = { finding: "identity_changed", witness: { read: moved, kind: "none", containment: "unknown" } };
|
|
430
|
-
const r = refusalOf(toolName, identity, a.boundary
|
|
431
|
-
return errorResult(r.sentence, containmentDetails(changed.map((c) => c.target).filter((t) => t !== undefined)));
|
|
439
|
+
const r = refusalOf(toolName, identity, a.boundary, []);
|
|
440
|
+
return refused(errorResult(r.sentence, containmentDetails(changed.map((c) => c.target).filter((t) => t !== undefined))));
|
|
432
441
|
};
|
|
433
442
|
return { check, admit };
|
|
434
443
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { anchorSpelling, normalizeAbsPathLexically, pathFamilyOf } from "./safety.js";
|
|
2
2
|
import { boundaryIsTheSandbox, canonicalPathIsAuthoritative } from "../../core/remote-env.js";
|
|
3
3
|
import { resolveOperandSpellings } from "./bash-readonly-classifier.js";
|
|
4
4
|
import { judgedCandidatesOf, sedBackupNames } from "./shell-read-write-targets.js";
|
|
@@ -224,7 +224,7 @@ export function createShellReadEstablisher(env, readRoots, opts) {
|
|
|
224
224
|
const entry = await env.fileInfo(at, signal);
|
|
225
225
|
const link = entry.ok && entry.value.kind === "symlink" && linkHops > 0 ? await env.readLink?.(at, signal) : undefined;
|
|
226
226
|
if (link?.ok === true) {
|
|
227
|
-
const landing =
|
|
227
|
+
const landing = anchorSpelling(family, parts.slice(0, -1).join("/") || "/", link.value);
|
|
228
228
|
return await resolve([landing, ...[...tail].reverse()].join("/"), signal, true, linkHops - 1);
|
|
229
229
|
}
|
|
230
230
|
if (entry.ok || entry.error.code !== "not_found")
|
|
@@ -416,11 +416,11 @@ export function createShellReadEstablisher(env, readRoots, opts) {
|
|
|
416
416
|
const real = canonicalKnown !== undefined ? { value: canonicalKnown, exact: true } : seat === "entry" ? await entryOf(spelling, signal) : await resolve(spelling, signal, candidate.face === "write");
|
|
417
417
|
if (real === undefined) {
|
|
418
418
|
if (resolverIsAuthoritative)
|
|
419
|
-
return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads
|
|
419
|
+
return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads`, unresolved: true };
|
|
420
420
|
return { candidate, established: "lexical", target: normalizeAbsPathLexically(spelling), spellings, ...(origin !== undefined ? { origin } : {}) };
|
|
421
421
|
}
|
|
422
422
|
if (!real.exact && real.missing !== true) {
|
|
423
|
-
return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads
|
|
423
|
+
return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads`, unresolved: true };
|
|
424
424
|
}
|
|
425
425
|
return { candidate, established, target: real.value, spellings, ...(origin !== undefined ? { origin } : {}) };
|
|
426
426
|
};
|
|
@@ -451,7 +451,7 @@ export function createShellReadEstablisher(env, readRoots, opts) {
|
|
|
451
451
|
if (link?.ok !== true)
|
|
452
452
|
return undefined;
|
|
453
453
|
const dir = name.slice(0, name.search(/[^/]*$/));
|
|
454
|
-
name =
|
|
454
|
+
name = dir === "" ? link.value : anchorSpelling(family, dir, link.value);
|
|
455
455
|
}
|
|
456
456
|
return undefined;
|
|
457
457
|
};
|
|
@@ -558,7 +558,7 @@ export function createShellReadEstablisher(env, readRoots, opts) {
|
|
|
558
558
|
};
|
|
559
559
|
for (const [i, lexical] of lexicalReadsOf(candidate, boundary).entries()) {
|
|
560
560
|
if (lexical.established !== "lexical") {
|
|
561
|
-
out.push(lexical);
|
|
561
|
+
out.push(writtenAt(lexical, writtenSeats, writtenSeats.written.includes("landing") ? "landing" : "entry"));
|
|
562
562
|
continue;
|
|
563
563
|
}
|
|
564
564
|
const folded = lexical.target;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE FUNCTION READING — whether a command's program word is a function of the text where a reader stands. A function is a
|
|
3
|
+
* binding of its name in the binding thread (`shell-read-bindings.ts`: defined by its head, unbound by an `unset`, visible
|
|
4
|
+
* and certain by the one ordered rule); this module asks that thread's {@link FunctionState} for the word the shell looks up
|
|
5
|
+
* first, and names what the answer means for the walk. The text's functions (`functions`) are only the CANDIDATE names: the
|
|
6
|
+
* ones its definition heads show, and the ones a literal `eval` text defines.
|
|
7
|
+
*/
|
|
8
|
+
import { type LexedWord } from "../../shell/lex.js";
|
|
9
|
+
import { type FunctionState } from "./shell-read-bindings.js";
|
|
10
|
+
/** Does a command name one of the text's `functions` where a reader stands: the word the shell looks up first — the program,
|
|
11
|
+
* or the launcher in front of it (`command`, `nice`, … are words like any, and a function of their name runs instead) —
|
|
12
|
+
* `named`: it is a function's name and defined there (bash looks functions up before builtins: it shadows a builtin of the
|
|
13
|
+
* name); `maybe`: it may be one (the definition may not have run, the word is one the text computes, or an external
|
|
14
|
+
* launcher stands in front of a program word that may be); `undefined`: it is not (`builtin` / `command` skip functions).
|
|
15
|
+
* The whole word names a function (`x/f` included: a `/` tells a function from a path in no way). `stateOf`: what a
|
|
16
|
+
* function of a name is there ({@link BindingTable.functionState}) — the binding thread's one rule; absent, any
|
|
17
|
+
* definition may be the one (the question put before the walk, where no reader stands yet). */
|
|
18
|
+
export type FunctionCall = "named" | "maybe";
|
|
19
|
+
export declare function functionCallOf(argv: readonly LexedWord[], functions: ReadonlySet<string>, stateOf?: (name: string) => FunctionState): FunctionCall | undefined;
|
|
20
|
+
/** The functions a literal `eval` text defines — the shell that runs it is this one, so they stand after it like any: names
|
|
21
|
+
* the text's own definition heads do not show (a text the plan cannot read hands none; the names it makes unknown are all). */
|
|
22
|
+
export declare function evalDefinedFunctions(argvs: readonly (readonly LexedWord[])[], depth?: number): string[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { lexShell, literalTextOf } from "../../shell/lex.js";
|
|
2
|
+
import { readProgramPosition } from "./bash-program-position.js";
|
|
3
|
+
import { SPECIAL_BUILTINS, projectSegment } from "./shell-read-bindings.js";
|
|
4
|
+
import { reentryText } from "./shell-reentry-text.js";
|
|
5
|
+
export function functionCallOf(argv, functions, stateOf = () => "maybe") {
|
|
6
|
+
const pos = readProgramPosition(argv);
|
|
7
|
+
const headAt = pos.launchers[0]?.index ?? pos.program?.index;
|
|
8
|
+
if (headAt === undefined || functions.size === 0)
|
|
9
|
+
return undefined;
|
|
10
|
+
const stateOfWord = (w) => (w.pieces.some((p) => p.expansion !== undefined || p.literal === undefined) ? ([...functions].some((name) => stateOf(name) !== "absent") ? "maybe" : "absent") : functions.has(w.folded) ? stateOf(w.folded) : "absent");
|
|
11
|
+
const headWord = argv[headAt];
|
|
12
|
+
const head = stateOfWord(headWord);
|
|
13
|
+
if (head !== "absent")
|
|
14
|
+
return head === "defined" && !SPECIAL_BUILTINS.has(headWord.folded) ? "named" : "maybe";
|
|
15
|
+
if (pos.program === undefined || pos.launchers.length === 0 || pos.launchers.some((l) => l.name === "builtin" || l.name === "command"))
|
|
16
|
+
return undefined;
|
|
17
|
+
return stateOfWord(argv[pos.program.index]) === "absent" ? undefined : "maybe";
|
|
18
|
+
}
|
|
19
|
+
export function evalDefinedFunctions(argvs, depth = 0) {
|
|
20
|
+
return depth > 4 ? [] : argvs.flatMap((argv) => {
|
|
21
|
+
const pos = readProgramPosition(argv);
|
|
22
|
+
const words = pos.program?.basename === "eval" ? argv.slice(pos.program.index + 1).map((w) => literalTextOf(w.pieces)) : [];
|
|
23
|
+
const lexed = words.length > 0 && words.every((t) => t !== undefined) ? lexShell(reentryText("eval", words) ?? "") : undefined;
|
|
24
|
+
if (lexed === undefined || "parseFailure" in lexed)
|
|
25
|
+
return [];
|
|
26
|
+
const inner = lexed.segments.map((segment) => projectSegment(segment));
|
|
27
|
+
return [...inner.flatMap((p) => p.defines), ...evalDefinedFunctions(inner.map((p) => p.argv), depth + 1)];
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* of a candidate that could not be established), then containment over the canonical target alone. The
|
|
9
9
|
* deny judge is the boundary's own `denyMatch` closure (`readDenySeat`, read-deny.ts) — the same function
|
|
10
10
|
* object every read face mounts; nothing here compiles a pattern or re-derives a view set. The containment
|
|
11
|
-
* judge is `withinAnyRoot` over the roots the boundary declares
|
|
11
|
+
* judge is `withinAnyRoot` over the roots the boundary declares — a WRITE target's over the write roots, at the
|
|
12
|
+
* seat it is written, under the write face in force ({@link judgeWrite}).
|
|
12
13
|
* · `foldShellReadFindings` folds a command's findings — every segment, every nested plan — to ONE word by
|
|
13
14
|
* the priority `identity_changed > deny_hit > outside_roots > unestablishable > declined > none`.
|
|
14
15
|
* · `SHELL_READ_DECISION` is the total table (finding × seat → disposition) that is the ONLY source of
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
import type { BashReadonlyRootBoundary } from "./bash-readonly-classifier.js";
|
|
23
24
|
import type { ReadCandidate, ShellReadPlan, ShellReadSegment } from "./shell-read-plan.js";
|
|
24
25
|
import type { WriteSeat } from "./shell-read-write-targets.js";
|
|
26
|
+
import { type ContainmentFace } from "./containment-face.js";
|
|
25
27
|
import type { AssertAllKeysHandled } from "../../core/ask-origin.js";
|
|
26
28
|
/** WHAT the judge found about a command — a closed set, registered in `docs/CLOSED-SETS.md`. */
|
|
27
29
|
export declare const SHELL_READ_FINDINGS: readonly ["deny_hit", "outside_roots", "unestablishable", "declined", "identity_changed", "none"];
|
|
@@ -122,21 +124,27 @@ export interface ShellReadBoundary extends BashReadonlyRootBoundary {
|
|
|
122
124
|
/** The read table's rows AT or BELOW a path (`readDenySeat`'s `denyAtOrBelow`, read-deny.ts): read by the station's
|
|
123
125
|
* `writesPlaced` fact only, through {@link denyRowAtOrBelow}. */
|
|
124
126
|
readonly denyAtOrBelow?: (resolvedPath: string) => string | null;
|
|
125
|
-
/** The WRITE roots (canonical) —
|
|
126
|
-
*
|
|
127
|
-
* `
|
|
127
|
+
/** The WRITE roots (canonical) — the task root and every additional working directory, never a read-only grant: where a
|
|
128
|
+
* command's write targets must land ({@link judgeRead}'s write face: a target written outside them is an
|
|
129
|
+
* `outside_roots` finding), and so where a mode that accepts file commands may place them. Absent ⇒ where a write may
|
|
130
|
+
* land is unknown: a written target is `unestablishable` and nothing is placed. */
|
|
128
131
|
readonly writeRoots?: readonly string[];
|
|
129
|
-
/** The
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
readonly
|
|
132
|
+
/** The WRITE face in force — the same seat the structured write tools read (the run's resolved write face, read once per
|
|
133
|
+
* judgment where the boundary is built): `"open"` skips the write roots' containment for a finding, as it skips the
|
|
134
|
+
* structured tools' root fence ({@link skipsRootsContainment}; absent ⇒ roots). Placement still reads the write roots:
|
|
135
|
+
* a mode accepting file commands accepts them inside its own directories, whatever the face. */
|
|
136
|
+
readonly writeFace?: ContainmentFace;
|
|
137
|
+
/** The leg's ONE write judgment of an ABSOLUTE write target — the one a write tool's target meets (its spelling, its real
|
|
138
|
+
* target and every hop of its symlink chain, under the leg's one exempt root), over the write-protection TABLE and the
|
|
139
|
+
* deployment's guarded write rows (`ToolPolicy.writeGuardRows`, every policy of the leg's chain) together; `canonical` ⇔
|
|
140
|
+
* the path is a key the station established (judged as a key, never as a spelling). `"protected"`: the table covers it —
|
|
141
|
+
* the target is not placed and the verdict says so ({@link import("./shell-read-check.js").ShellReadVerdict.writeProtected});
|
|
142
|
+
* `"guarded"`: a deployment row covers it — the target is not placed; `undefined`: nothing covers it. Read by the
|
|
143
|
+
* station's write facts only. A judgment that throws fails the station's answer (the caller's fail-closed arm). */
|
|
144
|
+
readonly writeTargetJudge?: (target: string, opts?: {
|
|
134
145
|
readonly signal?: AbortSignal;
|
|
135
146
|
readonly canonical?: true;
|
|
136
|
-
}) => Promise<
|
|
137
|
-
/** The deployment's write-target guard (`ToolPolicy.writeTargetGuard`, every policy of the leg's chain): `true` ⇔ a
|
|
138
|
-
* policy guards this ABSOLUTE write target. Read by the station's `writesPlaced` fact only: a guarded target is not placed. */
|
|
139
|
-
readonly writeTargetGuard?: (target: string, signal?: AbortSignal) => Promise<boolean>;
|
|
147
|
+
}) => Promise<"protected" | "guarded" | undefined>;
|
|
140
148
|
}
|
|
141
149
|
/** How far one candidate got before it was judged. `lexical` = the folded spelling is the judged target
|
|
142
150
|
* (an isolated sandbox, or a resolver that declares no authority and did not answer); `canonical` = the
|
|
@@ -163,6 +171,11 @@ export interface EstablishedRead {
|
|
|
163
171
|
readonly walked?: true;
|
|
164
172
|
/** Why the candidate could not be established. */
|
|
165
173
|
readonly cause?: string;
|
|
174
|
+
/** The candidate's spelling WAS placed and the environment could not resolve where it leads (the whole chain unanswered by
|
|
175
|
+
* a resolver that declares itself authoritative, or a component it could not follow) — the reading's
|
|
176
|
+
* `target_unresolved` fact, as opposed to a text the station cannot read (a rewritten word, an unsettled working
|
|
177
|
+
* directory). Present only on an `unestablishable` read. */
|
|
178
|
+
readonly unresolved?: true;
|
|
166
179
|
/** WHERE the command's program writes this target — present only on a write target the program itself writes, at the
|
|
167
180
|
* seat this read was established at (`landing`: through the path's links; `entry`: the directory entry, a link there
|
|
168
181
|
* not followed). Absent on a read, and on a write-face target the program only names (a copy's source). `child`: the
|
|
@@ -235,16 +248,30 @@ export interface JudgedSegment {
|
|
|
235
248
|
export interface JudgedPlan {
|
|
236
249
|
readonly plan: ShellReadPlan;
|
|
237
250
|
readonly segments: readonly JudgedSegment[];
|
|
238
|
-
/** {@link stationGuards} of the boundary the plan was judged against. */
|
|
251
|
+
/** {@link stationGuards} of the boundary the plan was judged against — what a READ finding folds by. */
|
|
239
252
|
readonly guards: boolean;
|
|
253
|
+
/** {@link writeFaceGuards} of the same boundary — what a WRITE target's finding folds by. */
|
|
254
|
+
readonly writeGuards: boolean;
|
|
240
255
|
}
|
|
241
256
|
/**
|
|
242
257
|
* Does the station GUARD anything for this boundary — a closed face (roots: containment is what it guards; an absent
|
|
243
258
|
* face reads as roots, as {@link containmentOf} does), or a deny table with rows (`denyMatch` is present exactly then)?
|
|
244
259
|
* Where it guards nothing (an open face, no deny row), a read it could not establish has nothing to be confirmed
|
|
245
|
-
* against: no finding. The deny judgment itself is untouched (with no row there is nothing to hit).
|
|
260
|
+
* against: no finding. The deny judgment itself is untouched (with no row there is nothing to hit). The READ face's
|
|
261
|
+
* question only: a write target folds by {@link writeFaceGuards}.
|
|
246
262
|
*/
|
|
247
263
|
export declare function stationGuards(boundary: Pick<ShellReadBoundary, "face" | "denyMatch">): boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Does the station guard anything on the WRITE face — the same shape for the write seats: the write face in force runs
|
|
266
|
+
* the roots-containment step ({@link skipsRootsContainment}, the structured write tools' reading; absent ⇒ roots), or
|
|
267
|
+
* the write deny table has rows. Where it guards nothing (an open write face, no write row), a write target the station
|
|
268
|
+
* could not establish has nothing to be confirmed against — as a structured write under that face is not fenced.
|
|
269
|
+
*/
|
|
270
|
+
export declare function writeFaceGuards(boundary: Pick<ShellReadBoundary, "writeFace" | "writeDenyMatch">): boolean;
|
|
271
|
+
/** Does the station guard anything on EITHER face? What a finding about the text's own structure folds by — a text the
|
|
272
|
+
* lexer could not read, a program it cannot name, program text it does not read, a working directory it lost, a text the
|
|
273
|
+
* shell re-enters — since what such a text reads AND what it writes are both unknown. */
|
|
274
|
+
export declare function anyFaceGuards(boundary: Pick<ShellReadBoundary, "face" | "denyMatch" | "writeFace" | "writeDenyMatch">): boolean;
|
|
248
275
|
/** The command-level fold and the finding that produced it. `witness` is absent for `none`; for `declined`
|
|
249
276
|
* the segment that declined stands in for a candidate. */
|
|
250
277
|
export interface ShellReadFold {
|
|
@@ -256,8 +283,10 @@ export interface ShellReadFold {
|
|
|
256
283
|
}
|
|
257
284
|
/**
|
|
258
285
|
* Fold a judged plan to ONE finding by {@link SHELL_READ_FINDING_RANK}, over every segment and every nested
|
|
259
|
-
* plan. Where the station guards nothing
|
|
260
|
-
*
|
|
286
|
+
* plan. Where the station guards nothing an `unestablishable` or `declined` finding is no finding: there is nothing to
|
|
287
|
+
* confirm it against. Which guard is asked is the face the finding is about — a read candidate's by the read face's
|
|
288
|
+
* ({@link JudgedPlan.guards}), a write target's by the write face's ({@link JudgedPlan.writeGuards}), and a finding about
|
|
289
|
+
* the text's own structure (no candidate stands in for it) by either ({@link anyFaceGuards}). Two structural rules ride the fold:
|
|
261
290
|
* · a plan the lexer refused (`unreadable`) is `unestablishable`, unconditionally — a quote-blind re-read
|
|
262
291
|
* yields more words, never zero, so an unreadable text is never "no candidates";
|
|
263
292
|
* · a SHELL re-entry segment (`sh -c S`, `eval S`, `env -S 'S'`, a launcher handing text to a shell) is at
|
|
@@ -289,7 +318,8 @@ export declare function deepestCommonDir(a: string, b: string): string | undefin
|
|
|
289
318
|
* The ONE directory whose addition to the declared roots would answer every `outside_roots` finding of a
|
|
290
319
|
* command — offered only when re-judging the same established reads with that root added leaves no
|
|
291
320
|
* finding at all (MEASURED, not argued: a deny row or an unestablishable candidate beside the out-of-root
|
|
292
|
-
* one leaves the ask standing whatever root is granted, and then nothing is offered
|
|
321
|
+
* one leaves the ask standing whatever root is granted, and then nothing is offered; so does a target written outside
|
|
322
|
+
* the WRITE roots, which no read root answers — a read grant grants no write). `exact` ⇒ the value
|
|
293
323
|
* is the one path the command NAMED, offered verbatim, because the containing directory would hand out the
|
|
294
324
|
* home or a root. A `..` spelling gets no offer: the folded and the kernel reading of it can disagree about
|
|
295
325
|
* whether the target is back inside, which no replay of the text settles.
|