@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
|
@@ -5,17 +5,15 @@ import type { ToolCallRequest } from "../../core/tool-policy.js";
|
|
|
5
5
|
* recursive option (removing an empty home or the task root is still removing it). */
|
|
6
6
|
export declare const SHELL_REMOVAL_COMMANDS: readonly ["rm", "rmdir"];
|
|
7
7
|
export type ShellRemovalCommand = (typeof SHELL_REMOVAL_COMMANDS)[number];
|
|
8
|
-
/** What makes a removal target dangerous, in the order a card names it.
|
|
9
|
-
|
|
10
|
-
export type DangerousRemovalKind = "filesystem_root" | "home" | "workspace" | "top_level" | "runtime_script";
|
|
8
|
+
/** What makes a removal target dangerous, in the order a card names it. */
|
|
9
|
+
export type DangerousRemovalKind = "filesystem_root" | "home" | "workspace" | "top_level";
|
|
11
10
|
/** The protected locations a removal is judged against. `homes`: the environment's declared home and every other spelling
|
|
12
11
|
* of it (its canonical one); `workspace`: the task root, the working directory, the additional working directories. */
|
|
13
12
|
export interface RemovalSeat {
|
|
14
13
|
readonly homes: readonly string[];
|
|
15
14
|
readonly workspace: readonly string[];
|
|
16
15
|
}
|
|
17
|
-
/** One dangerous removal a command makes: the program
|
|
18
|
-
* to), the operand as written, the target as established (the script word as written, for `runtime_script`). */
|
|
16
|
+
/** One dangerous removal a command makes: the removal program, the operand as written, the target as established. */
|
|
19
17
|
export interface DangerousRemoval {
|
|
20
18
|
readonly kind: DangerousRemovalKind;
|
|
21
19
|
readonly program: string;
|
|
@@ -30,33 +28,64 @@ export interface DangerousRemoval {
|
|
|
30
28
|
* directory (one segment below the root). `undefined` for a relative path or any other target.
|
|
31
29
|
*/
|
|
32
30
|
export declare function dangerousRemovalOf(path: string, seat: RemovalSeat): DangerousRemovalKind | undefined;
|
|
33
|
-
/** What one removal finding asks: a protected location (`dangerous_delete`
|
|
34
|
-
*
|
|
31
|
+
/** What one removal finding asks: a protected location (`dangerous_delete`; `untrackedFrom` — the segment the working
|
|
32
|
+
* directory stops being tracked at, where the target is relative and was judged where the text stood then); a recursive
|
|
33
|
+
* forced removal whose target the
|
|
34
|
+
* guard cannot verify (`unverifiable_delete`) — an operand the shell rewrites from a value the plan does not settle
|
|
35
|
+
* (`rewritten`), the operands of a removal its argv stream fills in (`stream`), or an operand relative to the directory a
|
|
36
|
+
* launcher option moved the removal into (`relocated`); or a shell handed text the engine cannot read
|
|
37
|
+
* (`unverifiable_shell_text`) — a text a program produces (`produced`: what a pipe or a `<(…)` carries, an argv a stream
|
|
38
|
+
* fills in, a substitution), or a text nobody read (`unread`: one the lexer could not read at all, {@link unreadPlan}, or a
|
|
39
|
+
* segment whose program the walk could not name, {@link programUnread}). */
|
|
35
40
|
export type RemovalFinding = (DangerousRemoval & {
|
|
36
41
|
readonly guard: "dangerous_delete";
|
|
42
|
+
readonly untrackedFrom?: string;
|
|
37
43
|
}) | {
|
|
38
44
|
readonly guard: "unverifiable_delete";
|
|
39
45
|
readonly program: string;
|
|
40
46
|
readonly operand: string;
|
|
41
|
-
readonly
|
|
47
|
+
readonly source: "rewritten" | "stream" | "relocated";
|
|
48
|
+
} | {
|
|
49
|
+
readonly guard: "unverifiable_shell_text";
|
|
50
|
+
readonly program: string;
|
|
51
|
+
readonly text: string;
|
|
52
|
+
readonly source: "produced" | "unread";
|
|
42
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* The names a removal target may be built on and still be verified: a root the SHELL provides, never one a command's
|
|
56
|
+
* output fed — `rm -rf "$TMPDIR"/<literal path below it>` is not asked about. `TMPDIR` is the reference's carve-out (its
|
|
57
|
+
* rule exempts "a well-known-safe temp pattern"); `HOME` is sema's (a registered divergence, looser than the reference,
|
|
58
|
+
* whose rule names `$HOME` as the blast radius: it is a root the shell provides too). A declared home is read by the
|
|
59
|
+
* value table itself — `$HOME` is then its value, and the target is judged where it lands; `PWD` is no root of this kind:
|
|
60
|
+
* its value is where the working-directory thread says the command stands.
|
|
61
|
+
*/
|
|
62
|
+
export declare const KNOWN_SAFE_ROOTS: readonly string[];
|
|
43
63
|
/**
|
|
44
64
|
* The removal findings of an established plan — ONE walk, nested plans included (a `bash -c` / `sh -c` text, a substitution
|
|
45
65
|
* body), two words. For every `rm` / `rmdir` segment, each of its OPERANDS (every write target that is not one of its
|
|
46
66
|
* option words: a word some implementation's reading of the program's row writes, {@link writeReadings}) as the plan
|
|
47
67
|
* established it on the WRITE face (the entry the program unlinks, and the deny-floor shadow of a home word the plan did
|
|
48
|
-
* not place) is judged by {@link dangerousRemovalOf}
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* of
|
|
68
|
+
* not place) is judged by {@link dangerousRemovalOf}. In a recursive forced removal ({@link recursiveForce}), an operand
|
|
69
|
+
* the shell rewrites from a value the plan's table does not settle — a name with no value here or one set in a form the
|
|
70
|
+
* table cannot read, a substitution, a parameter operator, a positional parameter — is an unverifiable target, save a
|
|
71
|
+
* literal path below a known-safe root the shell still provides there ({@link belowKnownSafeRoot}); so are the operands
|
|
72
|
+
* of a removal its argv stream fills in (`… | xargs rm -rf`), and an operand the plan cannot place — in a removal a
|
|
73
|
+
* launcher moved into another base, or in text such a removal hands a shell (`env -C <dir> rm -rf build`). A relative
|
|
74
|
+
* operand past a directory move the working-directory thread cannot follow is judged where the text stood when it lost
|
|
75
|
+
* track (the reference resolves it against the original working directory: KL-1403-b), and a card it earns says so. A `read` fills its name from the input: a value
|
|
76
|
+
* the table does not settle (stricter than the reference, whose rule counts `read VAR` as an in-window assignment). A
|
|
77
|
+
* name the table settles is its value, so `D=./build; rm -rf "$D/../build"` is judged
|
|
78
|
+
* where it lands, as `rm -rf ./build` is. A shell handed a text the engine cannot read — one a program produces
|
|
79
|
+
* ({@link runtimeScriptOf}), or one nobody read ({@link unreadPlan}, {@link programUnread}) — is an unreadable text of its
|
|
80
|
+
* own. The read-face copy of an operand is not judged: it follows a link the removal does not follow.
|
|
53
81
|
*/
|
|
54
|
-
export declare function removalFindingsOf(established: EstablishedPlan, seat: RemovalSeat): RemovalFinding[];
|
|
55
|
-
/** Does
|
|
56
|
-
*
|
|
82
|
+
export declare function removalFindingsOf(established: EstablishedPlan, seat: RemovalSeat, safeRoots?: readonly string[], enclosingRelocated?: boolean): RemovalFinding[];
|
|
83
|
+
/** Does this plan (nested plans included) run a removal program, hand a shell a script built when it runs, or hold text
|
|
84
|
+
* nobody could read ({@link unreadPlan}, {@link programUnread})? The guard establishes nothing for a command that does none. */
|
|
57
85
|
export declare function planRunsRemoval(plan: EstablishedPlan["plan"]): boolean;
|
|
58
86
|
/** What the removal guard asks for one command: the guard word and its sentence's account — the first protected location
|
|
59
|
-
* (the program, the location, the target), else the first
|
|
87
|
+
* (the program, the location, the target), else the first target the guard cannot verify, else the first shell text the
|
|
88
|
+
* engine cannot read; `undefined` when none. */
|
|
60
89
|
export interface RemovalVerdict {
|
|
61
90
|
readonly guard: RemovalFinding["guard"];
|
|
62
91
|
readonly text: string;
|
|
@@ -72,12 +101,16 @@ export interface RemovalGuardSeat {
|
|
|
72
101
|
readonly additionalRoots?: () => readonly string[];
|
|
73
102
|
}
|
|
74
103
|
/**
|
|
75
|
-
* The
|
|
76
|
-
* removal program
|
|
77
|
-
* the
|
|
78
|
-
*
|
|
79
|
-
*
|
|
104
|
+
* The removal guard's READING of one command — undefined when no segment of it (nested shell text included) runs a
|
|
105
|
+
* removal program or hands a shell a script built when it runs, and every text in it was read, so a command that removes
|
|
106
|
+
* nothing costs the gate no suspension (a text the lexer could not read is not one: bash may run the lines before its
|
|
107
|
+
* syntax error, and the guard reads no prefix of it). Otherwise `judge` establishes the plan the read
|
|
108
|
+
* station plans (the same planner, the same value table, the same write-target reading) lexically against the request's
|
|
109
|
+
* `cwd` and the seat's home, and — where an environment is seated — through its resolver too, and returns what either
|
|
110
|
+
* establishment finds ({@link removalFindingsOf}: the protected-location word first, then the unverifiable target, then the
|
|
111
|
+
* unreadable text).
|
|
112
|
+
* `safeRoots`: the known-safe roots ({@link KNOWN_SAFE_ROOTS}, with a deployment's own added by the policy).
|
|
80
113
|
*/
|
|
81
|
-
export declare function removalReadingOf(command: string, req: Pick<ToolCallRequest, "cwd" | "root">, seat: RemovalGuardSeat | undefined): {
|
|
114
|
+
export declare function removalReadingOf(command: string, req: Pick<ToolCallRequest, "cwd" | "root">, seat: RemovalGuardSeat | undefined, safeRoots?: readonly string[]): {
|
|
82
115
|
judge(signal?: AbortSignal): Promise<RemovalVerdict | undefined>;
|
|
83
116
|
} | undefined;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { planShellReads } from "./shell-read-plan.js";
|
|
2
2
|
import { literalTextOf } from "../../shell/lex.js";
|
|
3
3
|
import { createShellReadEstablisher, establishShellReadsLexically } from "./shell-read-establish.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isAbsoluteForFamily, isWinFormPath, normalizeAbsPathLexically } from "./safety.js";
|
|
5
5
|
import { launchedCommands, launchedReadings, reentryText, scriptFileAt, spellLaunched } from "./shell-reentry-text.js";
|
|
6
|
-
import {
|
|
6
|
+
import { readProgramPosition } from "./bash-program-position.js";
|
|
7
7
|
import { bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
|
|
8
8
|
import { programArgs } from "./shell-read-write-targets.js";
|
|
9
9
|
import { implementationsOf, programRow, runsShellText, selectedOptions, writeReadings } from "./program-read-model.js";
|
|
@@ -25,16 +25,16 @@ function driveOf(path, key) {
|
|
|
25
25
|
return isWinFormPath(path) && key.length >= 2 && key[1] === ":" ? key.slice(0, 2) : "";
|
|
26
26
|
}
|
|
27
27
|
export function dangerousRemovalOf(path, seat) {
|
|
28
|
-
if (!
|
|
28
|
+
if (!isAbsoluteForFamily("own", path))
|
|
29
29
|
return undefined;
|
|
30
30
|
const k = removalKey(path);
|
|
31
31
|
const rest = k.slice(driveOf(path, k).length);
|
|
32
32
|
if (rest === "/" || /^\/\/[^/]+\/[^/]+$/.test(k))
|
|
33
33
|
return "filesystem_root";
|
|
34
|
-
if (seat.homes.some((h) =>
|
|
34
|
+
if (seat.homes.some((h) => isAbsoluteForFamily("own", h) && removalKey(h) === k))
|
|
35
35
|
return "home";
|
|
36
36
|
const prefix = k.endsWith("/") ? k : `${k}/`;
|
|
37
|
-
if (seat.workspace.some((w) =>
|
|
37
|
+
if (seat.workspace.some((w) => isAbsoluteForFamily("own", w) && (removalKey(w) === k || removalKey(w).startsWith(prefix))))
|
|
38
38
|
return "workspace";
|
|
39
39
|
if (/^\/[^/]+$/.test(rest))
|
|
40
40
|
return "top_level";
|
|
@@ -56,53 +56,17 @@ function removalKindOf(read, seat) {
|
|
|
56
56
|
}
|
|
57
57
|
return read.established === "unestablishable" && tildeNamesAHome(read.candidate.spelled) ? "home" : undefined;
|
|
58
58
|
}
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const [head, name] = segment.argv;
|
|
71
|
-
if (isShellSyntaxWord(head, LOOP_LIST_HEADS) && name !== undefined && NAME.test(name.folded)) {
|
|
72
|
-
const at = segment.argv.findIndex((w, i) => i > 1 && w.folded === "in");
|
|
73
|
-
const words = at < 0 ? [undefined] : segment.argv.slice(at + 1).map((w) => literalTextOf(w.pieces));
|
|
74
|
-
for (const v of words.length > 0 ? words : [undefined])
|
|
75
|
-
bind(name.folded, v);
|
|
76
|
-
}
|
|
77
|
-
if (head !== undefined && NAME_BINDERS.has(head.folded))
|
|
78
|
-
for (const w of segment.argv.slice(1))
|
|
79
|
-
if (w.assignment === undefined && NAME.test(w.folded))
|
|
80
|
-
bind(w.folded, undefined);
|
|
81
|
-
}
|
|
82
|
-
return out;
|
|
83
|
-
}
|
|
84
|
-
const BOUND_SPELLINGS_MAX = 64;
|
|
85
|
-
function boundSpellingsOf(word, bindings) {
|
|
86
|
-
const named = word.pieces.filter((q) => q.literal === undefined);
|
|
87
|
-
if (named.length === 0 || !named.some((q) => q.expansion?.kind === "parameter" && q.expansion.name !== undefined && bindings.has(q.expansion.name)))
|
|
88
|
-
return undefined;
|
|
89
|
-
let spellings = [""];
|
|
90
|
-
for (const q of word.pieces) {
|
|
91
|
-
if (q.literal !== undefined) {
|
|
92
|
-
spellings = spellings.map((sp) => sp + q.literal);
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
const name = q.expansion?.kind === "parameter" && (q.expansion.body === undefined || q.expansion.body === q.expansion.name) ? q.expansion.name : undefined;
|
|
96
|
-
const values = name !== undefined ? bindings.get(name) : undefined;
|
|
97
|
-
if (name === undefined || values === undefined)
|
|
98
|
-
return { unreadable: q.expansion?.name ?? word.raw };
|
|
99
|
-
if (values.some((v) => v === undefined || (q.quotedBy === undefined && /\s/.test(v))))
|
|
100
|
-
return { unreadable: name };
|
|
101
|
-
spellings = spellings.flatMap((sp) => values.map((v) => sp + v));
|
|
102
|
-
if (spellings.length > BOUND_SPELLINGS_MAX)
|
|
103
|
-
return { unreadable: name };
|
|
104
|
-
}
|
|
105
|
-
return { spellings };
|
|
59
|
+
export const KNOWN_SAFE_ROOTS = Object.freeze(["TMPDIR", "HOME"]);
|
|
60
|
+
function belowKnownSafeRoot(word, safe, segment) {
|
|
61
|
+
const [head, ...rest] = word.pieces;
|
|
62
|
+
const e = head?.expansion;
|
|
63
|
+
const name = e?.kind === "parameter" && (e.body === undefined || e.body === e.name) ? e.name : undefined;
|
|
64
|
+
if (name === undefined || !safe.has(name) || segment.rebound === "all" || segment.rebound.has(name))
|
|
65
|
+
return false;
|
|
66
|
+
if (rest.some((p) => p.literal === undefined))
|
|
67
|
+
return false;
|
|
68
|
+
const below = rest.map((p) => p.literal).join("");
|
|
69
|
+
return /^\/[^*?[\]]+$/.test(below) && !below.split("/").includes("..");
|
|
106
70
|
}
|
|
107
71
|
function recursiveForce(row, args) {
|
|
108
72
|
return implementationsOf(row).some((view) => {
|
|
@@ -110,9 +74,11 @@ function recursiveForce(row, args) {
|
|
|
110
74
|
return selected.some((o) => o.selects === "traversal") && selected.some((o) => o.mode === "forced");
|
|
111
75
|
});
|
|
112
76
|
}
|
|
113
|
-
export function removalFindingsOf(established, seat) {
|
|
77
|
+
export function removalFindingsOf(established, seat, safeRoots = KNOWN_SAFE_ROOTS, enclosingRelocated = false) {
|
|
78
|
+
if (unreadPlan(established.plan))
|
|
79
|
+
return [{ guard: "unverifiable_shell_text", program: "bash", text: established.plan.unreadable, source: "unread" }];
|
|
114
80
|
const out = [];
|
|
115
|
-
const
|
|
81
|
+
const safe = new Set(safeRoots);
|
|
116
82
|
for (const segment of established.plan.segments) {
|
|
117
83
|
const program = segment.verb?.basename;
|
|
118
84
|
const row = program !== undefined && REMOVAL_COMMAND_SET.has(program) ? programRow(program) : undefined;
|
|
@@ -121,45 +87,41 @@ export function removalFindingsOf(established, seat) {
|
|
|
121
87
|
const spelled = args.map((w) => w.spelled);
|
|
122
88
|
const removed = new Set(writeReadings(row, spelled).flatMap((r) => r.written.map((w) => w.at)));
|
|
123
89
|
const options = new Set(args.filter((_, at) => !removed.has(at)).map((w) => w.raw));
|
|
90
|
+
const forced = recursiveForce(row, spelled);
|
|
91
|
+
if (forced && segment.reentry === "argv")
|
|
92
|
+
out.push({ guard: "unverifiable_delete", program, operand: segment.text.trim(), source: "stream" });
|
|
93
|
+
const relocated = enclosingRelocated || readProgramPosition(segment.argv).launcherMovesBase;
|
|
124
94
|
for (const read of established.reads.get(segment) ?? []) {
|
|
125
95
|
if (read.candidate.face !== "write" || options.has(read.candidate.raw))
|
|
126
96
|
continue;
|
|
127
97
|
const kind = removalKindOf(read, seat);
|
|
128
98
|
if (kind !== undefined) {
|
|
129
|
-
|
|
99
|
+
const untracked = read.candidate.bases === "unmodelled" ? established.plan.segments.find((s) => s.bases === "unmodelled")?.text.trim() : undefined;
|
|
100
|
+
out.push({ guard: "dangerous_delete", kind, program, operand: read.candidate.raw, target: read.target ?? read.candidate.floorSpelled ?? read.candidate.spelled, ...(untracked !== undefined ? { untrackedFrom: untracked } : {}) });
|
|
130
101
|
continue;
|
|
131
102
|
}
|
|
132
|
-
if (read.established !== "unestablishable")
|
|
103
|
+
if (!forced || read.established !== "unestablishable" || (read.candidate.expands !== "unresolvable" && !relocated))
|
|
133
104
|
continue;
|
|
134
105
|
const word = segment.argv.find((w) => w.raw === read.candidate.raw);
|
|
135
|
-
|
|
136
|
-
if (bound === undefined)
|
|
106
|
+
if (word !== undefined && belowKnownSafeRoot(word, safe, segment))
|
|
137
107
|
continue;
|
|
138
|
-
|
|
139
|
-
if (recursiveForce(row, spelled))
|
|
140
|
-
out.push({ guard: "unverifiable_delete", program, operand: read.candidate.raw, name: bound.unreadable });
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const bases = segment.bases === "unmodelled" ? (read.candidate.floorBases ?? []) : segment.bases;
|
|
144
|
-
for (const spelled of bound.spellings) {
|
|
145
|
-
const views = isAbsolutePathForm(spelled) ? [spelled] : bases.map((base) => `${base}/${spelled}`);
|
|
146
|
-
for (const view of views) {
|
|
147
|
-
const hit = dangerousRemovalOf(view, seat);
|
|
148
|
-
if (hit === undefined)
|
|
149
|
-
continue;
|
|
150
|
-
out.push({ guard: "dangerous_delete", kind: hit, program, operand: read.candidate.raw, target: normalizeAbsPathLexically(view) });
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
108
|
+
out.push({ guard: "unverifiable_delete", program, operand: read.candidate.raw, source: read.candidate.expands === "unresolvable" ? "rewritten" : "relocated" });
|
|
154
109
|
}
|
|
155
110
|
}
|
|
156
111
|
const script = runtimeScriptOf(segment);
|
|
157
112
|
if (script !== undefined && program !== undefined)
|
|
158
|
-
out.push({ guard: "
|
|
113
|
+
out.push({ guard: "unverifiable_shell_text", program, text: script, source: "produced" });
|
|
114
|
+
if (programUnread(segment))
|
|
115
|
+
out.push({ guard: "unverifiable_shell_text", program: segment.launcherChain[0] ?? segment.text.trim(), text: segment.text.trim(), source: "unread" });
|
|
116
|
+
const handedRelocated = enclosingRelocated || readProgramPosition(segment.argv).launcherMovesBase;
|
|
159
117
|
for (const nested of segment.nested) {
|
|
118
|
+
if (unreadPlan(nested)) {
|
|
119
|
+
out.push({ guard: "unverifiable_shell_text", program: program ?? segment.text.trim(), text: segment.text.trim(), source: "unread" });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
160
122
|
const sub = established.nested.get(nested);
|
|
161
123
|
if (sub !== undefined)
|
|
162
|
-
out.push(...removalFindingsOf(sub, seat));
|
|
124
|
+
out.push(...removalFindingsOf(sub, seat, safeRoots, handedRelocated));
|
|
163
125
|
}
|
|
164
126
|
}
|
|
165
127
|
return out;
|
|
@@ -187,38 +149,54 @@ function runtimeScriptOf(segment) {
|
|
|
187
149
|
const named = pos.program !== undefined && pos.refusal === undefined ? [{ shell: program.basename, words: segment.argv.slice(pos.program.index + 1).map((_, k) => spell(pos.program.index + 1 + k)) }] : [];
|
|
188
150
|
const launched = launchedCommands(segment.argv, pos).flatMap((c) => {
|
|
189
151
|
const shell = launchedReadings(segment.argv, c).shell;
|
|
190
|
-
return shell === undefined ? [] : spellLaunched(segment.argv, c.args, { word: spell })
|
|
152
|
+
return shell === undefined ? [] : [{ shell, words: spellLaunched(segment.argv, c.args, { word: spell }) }];
|
|
191
153
|
});
|
|
192
154
|
const texts = [...named, ...launched].flatMap(({ shell, words }) => [reentryText(shell, words), ...(segment.stdin ?? []).map((s) => reentryText(shell, words, s.literal ?? `${MARK}${s.from}`))]);
|
|
193
155
|
const text = texts.find((t) => t?.includes(MARK) === true);
|
|
194
156
|
return text === undefined ? undefined : text.split(MARK).join("");
|
|
195
157
|
}
|
|
158
|
+
const unreadPlan = (plan) => plan.unreadable !== undefined && plan.segments.length === 0;
|
|
159
|
+
const programUnread = (s) => s.declines.includes("unreadable_program_position");
|
|
196
160
|
export function planRunsRemoval(plan) {
|
|
197
|
-
return plan.segments.some((s) => (s.verb !== undefined && REMOVAL_COMMAND_SET.has(s.verb.basename)) || runtimeScriptOf(s) !== undefined || s.nested.some(planRunsRemoval));
|
|
161
|
+
return unreadPlan(plan) || plan.segments.some((s) => (s.verb !== undefined && REMOVAL_COMMAND_SET.has(s.verb.basename)) || runtimeScriptOf(s) !== undefined || programUnread(s) || s.nested.some(planRunsRemoval));
|
|
198
162
|
}
|
|
199
163
|
const DANGEROUS_REMOVAL_SENTENCE = {
|
|
200
164
|
filesystem_root: (p, t) => `\`${p}\` would remove the filesystem root (${t})`,
|
|
201
165
|
home: (p, t) => `\`${p}\` would remove a home directory (${t})`,
|
|
202
166
|
workspace: (p, t) => `\`${p}\` would remove a workspace directory — the task root, the working directory or an additional working directory — or one of its ancestors (${t})`,
|
|
203
167
|
top_level: (p, t) => `\`${p}\` would remove a top-level system directory (${t})`,
|
|
204
|
-
|
|
168
|
+
};
|
|
169
|
+
const UNVERIFIABLE_REMOVAL_SENTENCE = {
|
|
170
|
+
rewritten: (p, o) => `\`${p}\` recursive+force target ${o} is rewritten by the shell from a value this command does not settle (a name with no value here or one it sets in a form the guard cannot read, a substitution, a parameter operator) — the deleted path cannot be verified from this command`,
|
|
171
|
+
stream: (p, o) => `\`${p}\` recursive+force takes its targets from the stream it is fed (${o}) — the deleted paths cannot be verified from this command`,
|
|
172
|
+
relocated: (p, o) => `\`${p}\` recursive+force target ${o} is relative to a directory a launcher option moved the program into, which this command does not settle — the deleted path cannot be verified from this command`,
|
|
173
|
+
};
|
|
174
|
+
const UNREADABLE_TEXT_SENTENCE = {
|
|
175
|
+
produced: (p, t) => `\`${p}\` runs shell text that is built only when the command runs (${t}) — the text this shell will run cannot be read by the engine, so whether it removes anything is unknown`,
|
|
176
|
+
unread: (p, t) => `\`${p}\` runs shell text this guard cannot read (${t}) — the text this shell will run cannot be read by the engine, so whether it removes anything is unknown`,
|
|
205
177
|
};
|
|
206
178
|
function verdictOf(findings) {
|
|
207
|
-
for (const f of findings)
|
|
208
|
-
if (f.guard
|
|
209
|
-
|
|
179
|
+
for (const f of findings) {
|
|
180
|
+
if (f.guard !== "dangerous_delete")
|
|
181
|
+
continue;
|
|
182
|
+
const untracked = f.untrackedFrom === undefined ? "" : ` — the working directory is not tracked from \`${inlineUntrusted(f.untrackedFrom, 120)}\` on (a directory change before it cannot be followed), so this target was judged where the command stood when tracking stopped`;
|
|
183
|
+
return { guard: f.guard, text: `${DANGEROUS_REMOVAL_SENTENCE[f.kind](f.program, inlineUntrusted(f.target, 200))}${untracked}` };
|
|
184
|
+
}
|
|
210
185
|
for (const f of findings)
|
|
211
186
|
if (f.guard === "unverifiable_delete")
|
|
212
|
-
return { guard: f.guard, text:
|
|
187
|
+
return { guard: f.guard, text: UNVERIFIABLE_REMOVAL_SENTENCE[f.source](f.program, inlineUntrusted(f.operand, 200)) };
|
|
188
|
+
for (const f of findings)
|
|
189
|
+
if (f.guard === "unverifiable_shell_text")
|
|
190
|
+
return { guard: f.guard, text: UNREADABLE_TEXT_SENTENCE[f.source](f.program, inlineUntrusted(f.text, 200)) };
|
|
213
191
|
return undefined;
|
|
214
192
|
}
|
|
215
193
|
const REMOVAL_RESOLVE_TIMEOUT_MS = 10_000;
|
|
216
|
-
export function removalReadingOf(command, req, seat) {
|
|
194
|
+
export function removalReadingOf(command, req, seat, safeRoots = KNOWN_SAFE_ROOTS) {
|
|
217
195
|
const homeDir = seat?.env?.homeDir;
|
|
218
196
|
const cwd = req.cwd ?? req.root ?? "";
|
|
219
197
|
const roots = req.root !== undefined ? [req.root] : cwd !== "" ? [cwd] : [];
|
|
220
198
|
const boundary = { roots, ...(cwd !== "" ? { cwd } : {}), ...(homeDir !== undefined ? { homeDir } : {}) };
|
|
221
|
-
const plan = planShellReads(command, { readers: new Set(bashClassifyDefaultAllow()), cwd, roots, ...(homeDir !== undefined ? { homeDir } : {}) });
|
|
199
|
+
const plan = planShellReads(command, { readers: new Set(bashClassifyDefaultAllow()), cwd, roots, cdPath: seat?.env?.cdPath ?? "unknown", ...(homeDir !== undefined ? { homeDir } : {}) });
|
|
222
200
|
if (!planRunsRemoval(plan))
|
|
223
201
|
return undefined;
|
|
224
202
|
return {
|
|
@@ -229,11 +207,11 @@ export function removalReadingOf(command, req, seat) {
|
|
|
229
207
|
homes: [...(homeDir !== undefined ? [homeDir] : []), ...(resolved !== undefined && resolved.ok ? [resolved.value] : [])],
|
|
230
208
|
workspace: [...(req.root !== undefined ? [req.root] : []), ...(req.cwd !== undefined ? [req.cwd] : []), ...(seat?.additionalRoots?.() ?? [])],
|
|
231
209
|
};
|
|
232
|
-
const lexical = removalFindingsOf(establishShellReadsLexically(plan, boundary), removalSeat);
|
|
210
|
+
const lexical = removalFindingsOf(establishShellReadsLexically(plan, boundary), removalSeat, safeRoots);
|
|
233
211
|
if (lexical.some((f) => f.guard === "dangerous_delete") || env === undefined || roots.length === 0)
|
|
234
212
|
return verdictOf(lexical);
|
|
235
213
|
const established = await resolvedOrUndefined(createShellReadEstablisher(env, roots, { rootsCanonical: true }).establish(plan, boundary, signal), signal);
|
|
236
|
-
return verdictOf(established === undefined ? lexical : [...removalFindingsOf(established, removalSeat), ...lexical]);
|
|
214
|
+
return verdictOf(established === undefined ? lexical : [...removalFindingsOf(established, removalSeat, safeRoots), ...lexical]);
|
|
237
215
|
},
|
|
238
216
|
};
|
|
239
217
|
}
|
|
@@ -10,7 +10,8 @@ import { protectivePathTargetOf, type ToolCallLike } from "../../core/tool-regis
|
|
|
10
10
|
* hole) or fail-closed (deny a legitimate write). Single source.
|
|
11
11
|
*
|
|
12
12
|
* The `path` arm is deliberately BROADER than what Read/Edit/Write accept: their schema requires
|
|
13
|
-
* `file_path`
|
|
13
|
+
* `file_path` (fs-shared.ts FILE_PATH_PARAMS; Write alone repairs the `path` spelling onto it BEFORE validation,
|
|
14
|
+
* `coerceWriteArguments`, so a gate never sees the alias there), but this reader is
|
|
14
15
|
* shared with gates that judge OTHER tool vocabularies — Glob's real `path` parameter, deployment-authored tools —
|
|
15
16
|
* and a gate must never see LESS than the tool it is confining might act on. So the asymmetry is the design: the
|
|
16
17
|
* schema decides what a caller may send, this decides what gets judged, and this side over-reads on purpose.
|
|
@@ -114,21 +115,12 @@ export interface FsViolation {
|
|
|
114
115
|
message: string;
|
|
115
116
|
}
|
|
116
117
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* coordinate leaves every other copy judging the same string differently, and this predicate gates a
|
|
122
|
-
* security fence (frozen-path deny, readonly-boundary containment) as well as URI key minting.
|
|
123
|
-
* Callers that need a family the predicate does not cover must widen it HERE, not locally.
|
|
124
|
-
*
|
|
125
|
-
* The UNC arm (`\\host\share\…`) is the shape that drift produced: held privately in one consumer, it
|
|
126
|
-
* made that consumer resolve a UNC path as absolute while a sibling prefixed it with the task root —
|
|
127
|
-
* two URI keys for one file. Folding it in here settles the disagreement in the direction the rest of
|
|
128
|
-
* this file takes: a literal `\\host\share\…` is win-form (see {@link isWinFormPath}) and `within()`
|
|
129
|
-
* lands it outside every POSIX/drive root, i.e. a location the working directory does not contain.
|
|
118
|
+
* Is a spelling relative in EVERY family — neither `/`- nor `\`-rooted and carrying no drive letter (not even the
|
|
119
|
+
* drive-relative `C:x`)? The tamper check of a recorded relative key reads this (a record that is absolute, rooted or
|
|
120
|
+
* drive-bearing in ANY family is refused there): the opposite stance from {@link isAbsoluteForFamily}, which answers
|
|
121
|
+
* one family's question, so it is a separate, named predicate rather than a mode of that one.
|
|
130
122
|
*/
|
|
131
|
-
export declare function
|
|
123
|
+
export declare function relativeInEveryFamily(p: string): boolean;
|
|
132
124
|
/** Does this absolute spelling name the opening process's STANDARD INPUT (`/dev/stdin`, `/dev/fd/0`, `/proc/<pid>/fd/0`),
|
|
133
125
|
* folded lexically first (`/dev/./stdin`, `/dev//fd/0`)? */
|
|
134
126
|
export declare function namesStandardInput(path: string): boolean;
|
|
@@ -306,13 +298,19 @@ export declare function pathFamilyOf(bases: {
|
|
|
306
298
|
* reads it. The ONE UNC recognizer behind {@link isAbsoluteForFamily}, {@link pathIdentityOf} and
|
|
307
299
|
* {@link joinForFamily}. */
|
|
308
300
|
export declare function isUncSpelling(p: string): boolean;
|
|
309
|
-
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
301
|
+
/** Where a reading takes its family from: a tree's ({@link PathFamily}), or `"own"` — an address with no tree to place
|
|
302
|
+
* it reads its OWN spelling as the family source (the {@link pathFamilyOf} of that spelling), so a treeless `C:\x` and a
|
|
303
|
+
* treeless `/x` are each read in their own family rather than one of them being declared relative. */
|
|
304
|
+
export type PathFamilySource = PathFamily | "own";
|
|
305
|
+
/** The family a {@link PathFamilySource} names for one spelling. */
|
|
306
|
+
export declare function familyFor(source: PathFamilySource, p: string): PathFamily;
|
|
307
|
+
/**
|
|
308
|
+
* FULLY QUALIFIED under the family — the ONE absoluteness predicate. win: drive-rooted (`C:\`/`C:/`) or UNC (a host AND a
|
|
309
|
+
* share: a bare `\\x` names no share and is no address) — a `/x` / `\x` is rooted-not-qualified and joins the base's drive
|
|
310
|
+
* or share root ({@link joinForFamily}), a `C:rel` is drive-relative. posix: `/`-rooted. `"own"`: the spelling's own
|
|
311
|
+
* family ({@link familyFor}) — the reading of an address no tree places.
|
|
312
|
+
*/
|
|
313
|
+
export declare function isAbsoluteForFamily(family: PathFamilySource, p: string): boolean;
|
|
316
314
|
/** The refusal words of {@link win32NamespaceScreen} — a closed set (docs/CLOSED-SETS.md). */
|
|
317
315
|
export declare const WIN32_NAMESPACE_REFUSALS: readonly ["device_namespace", "unmappable_namespace", "unc_on_posix_tree", "drive_relative"];
|
|
318
316
|
export type Win32NamespaceRefusal = (typeof WIN32_NAMESPACE_REFUSALS)[number];
|
|
@@ -436,6 +434,14 @@ export declare function foldTrailingSeparators(spelled: string, family: PathFami
|
|
|
436
434
|
* share root, anything else under the base with the base's own separator. posix = `/`-join.
|
|
437
435
|
*/
|
|
438
436
|
export declare function joinForFamily(family: PathFamily, base: string, rel: string): string;
|
|
437
|
+
/**
|
|
438
|
+
* THE anchor step of every path reading: a spelling fully qualified under the family stands as spelled; any other is joined
|
|
439
|
+
* to `base` ({@link joinForFamily}, unfolded). One definition, so "already absolute, therefore do not prepend a base" is
|
|
440
|
+
* decided in one place for every reader — the path prelude (`readPath`, effective-path-target.ts), the canonicalizer, a
|
|
441
|
+
* link's target against its parent, a search tool's root-relative output. Callers that need the namespace screen or an
|
|
442
|
+
* identity read the prelude; this is only its anchor.
|
|
443
|
+
*/
|
|
444
|
+
export declare function anchorSpelling(family: PathFamily, base: string, spelled: string): string;
|
|
439
445
|
/** The family-aware LEXICAL view of a possibly-relative spelling against a resolution base (no I/O,
|
|
440
446
|
* `.`/`..` folded via {@link normalizeAbsPathLexically}): the SECOND deny-judgment view. It asks
|
|
441
447
|
* "does the NAME land on a guarded path", complementing the canonical
|
|
@@ -456,6 +462,7 @@ export declare function resolveKey(env: ExecutionEnv, rootCanonical: string, pat
|
|
|
456
462
|
readonly homeViews?: HomeViewSource;
|
|
457
463
|
matchTarget(canonicalKey: string, lexicalView?: string, opts?: {
|
|
458
464
|
readonly aliasResolved?: boolean;
|
|
465
|
+
readonly hops?: readonly string[];
|
|
459
466
|
}): {
|
|
460
467
|
pattern: string;
|
|
461
468
|
matchedView?: string;
|
package/dist/tools/fs/safety.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readDenySentence } from "./read-deny.js";
|
|
3
3
|
import { homeViewsOf } from "./read-deny-home.js";
|
|
4
|
+
import { skipsRootsContainment } from "./containment-face.js";
|
|
4
5
|
import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
|
|
5
6
|
import { pathTargetValue, protectivePathTargetOf } from "../../core/tool-registry.js";
|
|
6
|
-
import {
|
|
7
|
+
import { readPath } from "../../core/effective-path-target.js";
|
|
7
8
|
import { withGateTimeout, DEFAULT_GATE_AWAIT_TIMEOUT_MS } from "../../core/gate-bounded-await.js";
|
|
8
9
|
import { thrownText } from "../../core/thrown-value.js";
|
|
9
10
|
export function fileArgPath(args) {
|
|
@@ -25,8 +26,8 @@ export function sha256(content) {
|
|
|
25
26
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
26
27
|
}
|
|
27
28
|
const SEP = "/";
|
|
28
|
-
export function
|
|
29
|
-
return p.startsWith("/") || /^[A-Za-z]
|
|
29
|
+
export function relativeInEveryFamily(p) {
|
|
30
|
+
return !(p.startsWith("/") || p.startsWith("\\") || /^[A-Za-z]:/.test(p));
|
|
30
31
|
}
|
|
31
32
|
function nativeSepOf(p) {
|
|
32
33
|
return isWinFormPath(p) && p.includes("\\") ? "\\" : "/";
|
|
@@ -314,8 +315,11 @@ export function pathFamilyOf(bases) {
|
|
|
314
315
|
export function isUncSpelling(p) {
|
|
315
316
|
return /^[\\/]{2}[^\\/]+[\\/]+[^\\/]/.test(p);
|
|
316
317
|
}
|
|
318
|
+
export function familyFor(source, p) {
|
|
319
|
+
return source === "own" ? (isWinFormPath(p) ? "win" : "posix") : source;
|
|
320
|
+
}
|
|
317
321
|
export function isAbsoluteForFamily(family, p) {
|
|
318
|
-
return family === "win" ? /^[A-Za-z]:[\\/]/.test(p) || isUncSpelling(p) : p.startsWith("/");
|
|
322
|
+
return familyFor(family, p) === "win" ? /^[A-Za-z]:[\\/]/.test(p) || isUncSpelling(p) : p.startsWith("/");
|
|
319
323
|
}
|
|
320
324
|
export const WIN32_NAMESPACE_REFUSALS = Object.freeze(["device_namespace", "unmappable_namespace", "unc_on_posix_tree", "drive_relative"]);
|
|
321
325
|
export const WIN32_NAMESPACE_REFUSAL_TEXT = {
|
|
@@ -445,9 +449,11 @@ export function joinForFamily(family, base, rel) {
|
|
|
445
449
|
}
|
|
446
450
|
return `${base.replace(/[\\/]+$/, "")}${base.includes("\\") ? "\\" : "/"}${rel}`;
|
|
447
451
|
}
|
|
452
|
+
export function anchorSpelling(family, base, spelled) {
|
|
453
|
+
return isAbsoluteForFamily(family, spelled) ? spelled : joinForFamily(family, base, spelled);
|
|
454
|
+
}
|
|
448
455
|
export function lexicalViewOf(spelled, base) {
|
|
449
|
-
|
|
450
|
-
return normalizeAbsPathLexically(isAbsoluteForFamily(family, spelled) ? spelled : joinForFamily(family, base, spelled));
|
|
456
|
+
return normalizeAbsPathLexically(anchorSpelling(pathFamilyOf({ root: base }), base, spelled));
|
|
451
457
|
}
|
|
452
458
|
function within(rootCanonical, p) {
|
|
453
459
|
if (!rootCanonical)
|
|
@@ -462,7 +468,7 @@ function within(rootCanonical, p) {
|
|
|
462
468
|
return pN.identity.startsWith(rootN.identity.endsWith(SEP) ? rootN.identity : rootN.identity + SEP);
|
|
463
469
|
}
|
|
464
470
|
export async function resolveKey(env, rootCanonical, path, signal, baseCwd, additionalRoots, exactFileReadExemption, readDeny, containmentFace) {
|
|
465
|
-
const views = await
|
|
471
|
+
const views = await readPath(env, path, baseCwd ?? rootCanonical, { root: rootCanonical, ...(baseCwd !== undefined ? { cwd: baseCwd } : {}) }, { intent: "resolve", views: readDeny !== undefined ? "chain" : "canonical" }, signal);
|
|
466
472
|
const canon = views.canonical ?? (await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootCanonical }), baseCwd));
|
|
467
473
|
if (!canon.ok)
|
|
468
474
|
return { ok: false, violation: { code: "invalid", message: canon.message } };
|
|
@@ -473,7 +479,7 @@ export async function resolveKey(env, rootCanonical, path, signal, baseCwd, addi
|
|
|
473
479
|
}
|
|
474
480
|
if (readDeny !== undefined) {
|
|
475
481
|
const judged = { aliasResolved: canon.aliasResolved === true, hops: views.hops };
|
|
476
|
-
const lexical = views.lexical?.kind === "path" ? spellingOfIdentity(views.lexical.identity,
|
|
482
|
+
const lexical = views.lexical?.kind === "path" ? spellingOfIdentity(views.lexical.identity, views.lexical.family) : undefined;
|
|
477
483
|
let hit = readDeny.matchTarget(key, lexical, judged);
|
|
478
484
|
if (hit === null) {
|
|
479
485
|
const homeView = await homeViewsOf(env, readDeny, signal).of(key);
|
|
@@ -493,7 +499,7 @@ export async function resolveKey(env, rootCanonical, path, signal, baseCwd, addi
|
|
|
493
499
|
};
|
|
494
500
|
}
|
|
495
501
|
}
|
|
496
|
-
if (containmentFace
|
|
502
|
+
if (skipsRootsContainment(containmentFace)) {
|
|
497
503
|
if (isUncPath(key) && isWinFormPath(key) && !within(rootCanonical, key) && !additionalRootsCanonical?.some((r) => within(r, key))) {
|
|
498
504
|
return {
|
|
499
505
|
ok: false,
|
|
@@ -528,7 +534,7 @@ export async function canonicalizeTarget(env, path, signal, family, baseCwd) {
|
|
|
528
534
|
}
|
|
529
535
|
const unprefixed = nativeUncSpellingOf(family, screened.spelled);
|
|
530
536
|
const spelled = family === "posix" && unprefixed.startsWith("//") ? unprefixed.replace(/^\/+/, "/") : unprefixed;
|
|
531
|
-
const target = baseCwd
|
|
537
|
+
const target = baseCwd ? anchorSpelling(family, baseCwd, spelled) : spelled;
|
|
532
538
|
let thrownByProbe;
|
|
533
539
|
let seenThrow = false;
|
|
534
540
|
const probe = probeRealTarget(env, path, target, family, signal).catch((e) => {
|
|
@@ -593,7 +599,7 @@ async function probeRealTarget(env, path, target, family, signal) {
|
|
|
593
599
|
};
|
|
594
600
|
}
|
|
595
601
|
const hopSpelling = family === "win" ? screenedTarget.spelled : link.value;
|
|
596
|
-
const linkTarget =
|
|
602
|
+
const linkTarget = anchorSpelling(family, parentDir(abs), hopSpelling);
|
|
597
603
|
const reabs = await env.absolutePath(linkTarget, signal);
|
|
598
604
|
if (!reabs.ok) {
|
|
599
605
|
return { ok: false, message: `cannot resolve symlink target "${linkTarget}" for "${path}": ${reabs.error.message}`, unresolvedSymlink: true };
|