@sema-agent/core 7.48.0 → 7.49.1
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 +457 -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 +21 -28
- package/dist/core/arg-summary.js +235 -88
- 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 +43 -11
- 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 +21 -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 +29 -36
- 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/tool-turn-repair-seat.d.ts +28 -4
- package/dist/core/runner/tool-turn-repair-seat.js +36 -13
- 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/shell-wrapper-projection.js +1 -1
- 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 +12 -4
- package/dist/tools/fs/bash-program-position.js +32 -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 +46 -20
- package/dist/tools/fs/program-read-model.js +50 -20
- package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
- package/dist/tools/fs/program-read-model.rows.js +16 -15
- 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 +67 -43
- 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 +51 -18
- package/dist/tools/fs/shell-read-judge.js +40 -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, decideShellReadForRedirects, 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,21 @@ function outsidePathsOf(judged) {
|
|
|
243
241
|
}
|
|
244
242
|
return out;
|
|
245
243
|
}
|
|
246
|
-
function
|
|
244
|
+
function outsideFindingsAreRedirections(judged) {
|
|
245
|
+
const walk = (plan) => plan.segments.every((s) => {
|
|
246
|
+
const redirected = new Set(s.segment.redirects.flatMap((r) => (r.direction === "out" && r.target !== undefined ? [r.target.raw] : [])));
|
|
247
|
+
const slotWrites = new Set(s.segment.reads.filter((c) => c.provenance === "write_redirect_target").map((c) => c.raw));
|
|
248
|
+
for (const f of s.findings) {
|
|
249
|
+
if (f.kind !== "outside_roots")
|
|
250
|
+
continue;
|
|
251
|
+
if (f.read.written === undefined || f.read.candidate.provenance !== "write_redirect_target" || !redirected.has(f.read.candidate.raw) || slotWrites.has(f.read.candidate.raw))
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
return s.nested.every(walk);
|
|
255
|
+
});
|
|
256
|
+
return walk(judged);
|
|
257
|
+
}
|
|
258
|
+
function refusalOf(tool, fold, boundary, paths) {
|
|
247
259
|
const w = fold.witness;
|
|
248
260
|
if (fold.finding === "deny_hit" && w?.deny !== undefined) {
|
|
249
261
|
const target = w.read.target ?? w.deny.matchedView;
|
|
@@ -252,7 +264,7 @@ function refusalOf(tool, fold, roots, paths, face) {
|
|
|
252
264
|
const sentence = `Error (${tool}): ${core} ${write ? WRITE_DENY_REFUSAL_NOTE : DENY_REFUSAL_NOTE}`;
|
|
253
265
|
return { sentence, ...(write ? {} : { details: violationDetails({ code: "read_path_denied", message: sentence, target, pattern: w.deny.pattern }) }) };
|
|
254
266
|
}
|
|
255
|
-
const account = findingAccount(fold,
|
|
267
|
+
const account = findingAccount(fold, boundary);
|
|
256
268
|
const sentence = `Error (${tool}): ${account}. ${BASH_READONLY_CONFINEMENT_NOTE}`;
|
|
257
269
|
return { sentence, details: findingDetails(fold.finding, paths) };
|
|
258
270
|
}
|
|
@@ -263,42 +275,46 @@ const DENY_REFUSAL_NOTE = "The read boundary judges the paths this command names
|
|
|
263
275
|
"a file the command reaches without naming it is judged only where a segment deny row (built in, or the deployment's " +
|
|
264
276
|
"own pattern) reaches into the tree a recursive read walks.";
|
|
265
277
|
function verdictOf(tool, seat, fold, judged, boundary) {
|
|
266
|
-
const
|
|
278
|
+
const redirectsOnly = fold.finding === "outside_roots" && fold.unestablished !== true && outsideFindingsAreRedirections(judged);
|
|
279
|
+
const disposition = decideShellReadForRedirects(fold.finding, seat, redirectsOnly);
|
|
267
280
|
if (disposition === "vouch")
|
|
268
281
|
return { disposition, finding: fold.finding };
|
|
269
282
|
const paths = outsidePathsOf(judged);
|
|
270
283
|
const unestablished = fold.unestablished === true ? { unestablished: true } : {};
|
|
284
|
+
const writes = fold.witness?.read.candidate.face === "write" ? { face: "write" } : {};
|
|
271
285
|
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 } : {}) };
|
|
286
|
+
const r = refusalOf(tool, fold, boundary, paths);
|
|
287
|
+
return { disposition, finding: fold.finding, ...writes, sentence: r.sentence, ...(r.details !== undefined ? { details: r.details } : {}) };
|
|
274
288
|
}
|
|
275
289
|
const grant = fold.finding === "outside_roots" ? readRootGrantCandidateOf(judged, boundary) : undefined;
|
|
276
290
|
const containment = fold.finding === "outside_roots" || fold.finding === "unestablishable";
|
|
277
291
|
return {
|
|
278
292
|
disposition,
|
|
279
293
|
finding: fold.finding,
|
|
294
|
+
...writes,
|
|
280
295
|
...unestablished,
|
|
281
|
-
sentence: findingAccount(fold, boundary
|
|
296
|
+
sentence: findingAccount(fold, boundary),
|
|
282
297
|
...(disposition === "mandated" && containment ? { details: findingDetails(fold.finding, paths) } : {}),
|
|
283
298
|
...(grant !== undefined ? { readRootCandidate: { dir: grant.dir, ...(grant.covers !== undefined ? { covers: grant.covers } : {}), clearsThisAsk: true } } : {}),
|
|
284
299
|
};
|
|
285
300
|
}
|
|
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 } : {}) };
|
|
301
|
+
function declarationsOf(boundary, readers, cdPath) {
|
|
302
|
+
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
303
|
}
|
|
289
304
|
function judgePlan(plan, established, boundary) {
|
|
290
305
|
return judgeEstablishedPlan(plan, established.reads, boundary, (n) => {
|
|
291
306
|
const nested = established.nested.get(n);
|
|
292
|
-
return nested === undefined ? { plan: n, segments: [], guards: stationGuards(boundary) } : judgePlan(n, nested, boundary);
|
|
307
|
+
return nested === undefined ? { plan: n, segments: [], guards: stationGuards(boundary), writeGuards: writeFaceGuards(boundary) } : judgePlan(n, nested, boundary);
|
|
293
308
|
});
|
|
294
309
|
}
|
|
295
310
|
async function applyAlsoInside(judged, boundary) {
|
|
296
311
|
const alsoInside = boundary.alsoInside;
|
|
297
312
|
if (alsoInside === undefined)
|
|
298
313
|
return judged;
|
|
314
|
+
const outsideRead = (f) => f.kind === "outside_roots" && f.read.candidate.face !== "write" && f.read.target !== undefined;
|
|
299
315
|
const rescued = new Set();
|
|
300
316
|
for (const f of allFindings(judged)) {
|
|
301
|
-
if (f
|
|
317
|
+
if (!outsideRead(f) || rescued.has(f.read.target))
|
|
302
318
|
continue;
|
|
303
319
|
if (await alsoInside(f.read.target))
|
|
304
320
|
rescued.add(f.read.target);
|
|
@@ -308,28 +324,31 @@ async function applyAlsoInside(judged, boundary) {
|
|
|
308
324
|
const rewrite = (j) => ({
|
|
309
325
|
plan: j.plan,
|
|
310
326
|
guards: j.guards,
|
|
327
|
+
writeGuards: j.writeGuards,
|
|
311
328
|
segments: j.segments.map((s) => ({
|
|
312
329
|
segment: s.segment,
|
|
313
|
-
findings: s.findings.map((f) => (f
|
|
330
|
+
findings: s.findings.map((f) => (outsideRead(f) && rescued.has(f.read.target) ? { ...f, kind: "none", containment: "inside" } : f)),
|
|
314
331
|
nested: s.nested.map(rewrite),
|
|
315
332
|
})),
|
|
316
333
|
});
|
|
317
334
|
return rewrite(judged);
|
|
318
335
|
}
|
|
319
336
|
async function runShellReadStation(tool, command, declared, seat, readers, establisher, env, signal) {
|
|
320
|
-
const plan = planShellReads(command, declarationsOf(declared, readers));
|
|
337
|
+
const plan = planShellReads(command, declarationsOf(declared, readers, env.cdPath));
|
|
321
338
|
const established = await establisher.establish(plan, declared, signal);
|
|
322
339
|
const boundary = { ...declared, roots: established.roots };
|
|
323
340
|
const judged = await applyAlsoInside(judgePlan(plan, established, boundary), boundary);
|
|
324
341
|
const judgedVerdict = verdictOf(tool, seat, foldShellReadFindings(judged), judged, boundary);
|
|
325
342
|
const unplaced = unplacedOptionsOf(judged);
|
|
326
343
|
const placed = writesPlacedOf(judged, boundary);
|
|
327
|
-
const guarded = judgedVerdict.disposition === "refuse" ? { unplaced: false, writeProtected: false } : await writeGuardsOf(judged, boundary,
|
|
328
|
-
const
|
|
344
|
+
const guarded = judgedVerdict.disposition === "refuse" ? { unplaced: false, writeProtected: false } : await writeGuardsOf(judged, boundary, env, signal);
|
|
345
|
+
const unresolved = judgedVerdict.disposition !== "refuse" && runOrderOf(judged).runs.some((s) => s.findings.some((f) => f.read.written !== undefined && f.read.unresolved === true));
|
|
346
|
+
const facts = { ...judgedVerdict, ...(guarded.writeProtected ? { writeProtected: true } : {}), ...(unresolved ? { targetUnresolved: true } : {}) };
|
|
329
347
|
const verdict = placed && !guarded.unplaced ? { ...facts, writesPlaced: true } : unplaced !== undefined ? { ...facts, unplacedOptions: unplaced } : facts;
|
|
330
348
|
return { plan, established, judged, boundary, verdict };
|
|
331
349
|
}
|
|
332
350
|
export async function judgeShellRead(input) {
|
|
351
|
+
assertNoRetiredShellReadBoundaryKeys(input.boundary);
|
|
333
352
|
const readers = input.readers ?? new Set(bashClassifyDefaultAllow());
|
|
334
353
|
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
354
|
const offer = normalizeReadRootCandidate(run.verdict.readRootCandidate);
|
|
@@ -341,8 +360,9 @@ export async function judgeShellRead(input) {
|
|
|
341
360
|
};
|
|
342
361
|
}
|
|
343
362
|
export function judgeShellReadLexically(command, boundary, seat, opts) {
|
|
363
|
+
assertNoRetiredShellReadBoundaryKeys(boundary);
|
|
344
364
|
const readers = opts?.readers ?? new Set(bashClassifyDefaultAllow());
|
|
345
|
-
const plan = planShellReads(command, declarationsOf(boundary, readers));
|
|
365
|
+
const plan = planShellReads(command, declarationsOf(boundary, readers, []));
|
|
346
366
|
const established = establishShellReadsLexically(plan, boundary);
|
|
347
367
|
const judged = judgePlan(plan, established, boundary);
|
|
348
368
|
return verdictOf(opts?.tool ?? "Bash", seat, foldShellReadFindings(judged), judged, boundary);
|
|
@@ -393,7 +413,7 @@ export function createShellReadStation(opts) {
|
|
|
393
413
|
const member = memberOf(call) ?? toolName;
|
|
394
414
|
const seat = opts.seatOf(member);
|
|
395
415
|
const boundary = opts.boundaryOf(member, undefined);
|
|
396
|
-
if (boundary === undefined || !(
|
|
416
|
+
if (boundary === undefined || !(anyFaceGuards(boundary) || boundary.writeTargetJudge !== undefined))
|
|
397
417
|
return { disposition: "vouch", finding: "none" };
|
|
398
418
|
const fold = { finding: "unestablishable" };
|
|
399
419
|
const disposition = decideShellRead("unestablishable", seat);
|
|
@@ -411,8 +431,12 @@ export function createShellReadStation(opts) {
|
|
|
411
431
|
if (a === undefined)
|
|
412
432
|
return undefined;
|
|
413
433
|
admissions.delete(toolCallId);
|
|
434
|
+
const refused = (r) => {
|
|
435
|
+
opts.onAdmissionRefused?.(toolCallId);
|
|
436
|
+
return r;
|
|
437
|
+
};
|
|
414
438
|
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([]));
|
|
439
|
+
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
440
|
}
|
|
417
441
|
const establisher = establisherFor(a.boundary.roots);
|
|
418
442
|
const { changed } = await establisher.reestablish(a.established, a.boundary, signal);
|
|
@@ -422,13 +446,13 @@ export function createShellReadStation(opts) {
|
|
|
422
446
|
const judged = await applyAlsoInside(judgePlan(a.established.plan, again, a.boundary), a.boundary);
|
|
423
447
|
const fold = foldShellReadFindings(judged);
|
|
424
448
|
if (fold.finding === "deny_hit") {
|
|
425
|
-
const r = refusalOf(toolName, fold, a.boundary
|
|
426
|
-
return errorResult(r.sentence, r.details);
|
|
449
|
+
const r = refusalOf(toolName, fold, a.boundary, outsidePathsOf(judged));
|
|
450
|
+
return refused(errorResult(r.sentence, r.details));
|
|
427
451
|
}
|
|
428
452
|
const moved = changed[0];
|
|
429
453
|
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)));
|
|
454
|
+
const r = refusalOf(toolName, identity, a.boundary, []);
|
|
455
|
+
return refused(errorResult(r.sentence, containmentDetails(changed.map((c) => c.target).filter((t) => t !== undefined))));
|
|
432
456
|
};
|
|
433
457
|
return { check, admit };
|
|
434
458
|
}
|
|
@@ -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
|
+
}
|