@sema-agent/core 7.48.0 → 7.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +418 -0
- package/README.md +2 -2
- package/dist/agents/cascade.d.ts +2 -5
- package/dist/agents/child-failure.d.ts +20 -0
- package/dist/agents/child-failure.js +28 -0
- package/dist/agents/cross-session-ref.d.ts +11 -3
- package/dist/agents/cross-session-ref.js +6 -0
- package/dist/agents/cumulative-stats.d.ts +12 -15
- package/dist/agents/cumulative-stats.js +1 -1
- package/dist/agents/extra-tool-names.js +1 -1
- package/dist/agents/inherited-tool-pool.d.ts +17 -13
- package/dist/agents/inherited-tool-pool.js +4 -3
- package/dist/agents/peer-session-drain.d.ts +3 -2
- package/dist/agents/peer-session-drain.js +5 -4
- package/dist/agents/send-message-tool.js +2 -9
- package/dist/agents/subagent.d.ts +10 -29
- package/dist/agents/subagent.js +65 -53
- package/dist/agents/suspend-guard.d.ts +1 -1
- package/dist/agents/tool-filter.d.ts +76 -65
- package/dist/agents/tool-filter.js +122 -92
- package/dist/agents/verify.d.ts +4 -7
- package/dist/agents/verify.js +6 -7
- package/dist/bench/metrics.d.ts +1 -2
- package/dist/brain/anthropic.js +133 -79
- package/dist/brain/errors.d.ts +53 -6
- package/dist/brain/errors.js +22 -2
- package/dist/brain/input-too-long.d.ts +40 -19
- package/dist/brain/input-too-long.js +3 -16
- package/dist/brain/open-responses.js +19 -10
- package/dist/brain/openai.js +123 -32
- package/dist/brain/provider-dialect.d.ts +214 -0
- package/dist/brain/provider-dialect.js +186 -0
- package/dist/brain/request-params.d.ts +27 -8
- package/dist/brain/request-params.js +7 -12
- package/dist/brain/request-route.d.ts +84 -0
- package/dist/brain/request-route.js +41 -0
- package/dist/brain/retry.d.ts +8 -0
- package/dist/brain/retry.js +10 -4
- package/dist/brain/route-adjudicator.d.ts +7 -0
- package/dist/brain/route-adjudicator.js +5 -1
- package/dist/brain/status-sink.d.ts +20 -0
- package/dist/brain/status-sink.js +11 -0
- package/dist/brain/stream-engine.d.ts +52 -1
- package/dist/brain/stream-engine.js +81 -24
- package/dist/brain/stream-shared.d.ts +57 -11
- package/dist/brain/stream-shared.js +28 -2
- package/dist/core/a2a.d.ts +1 -0
- package/dist/core/agent-config-dir.js +3 -2
- package/dist/core/allow-entry.d.ts +21 -12
- package/dist/core/allow-entry.js +38 -31
- package/dist/core/arg-summary.d.ts +17 -24
- package/dist/core/arg-summary.js +169 -68
- package/dist/core/ask-origin.d.ts +42 -24
- package/dist/core/ask-origin.js +18 -13
- package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
- package/dist/core/auto-mode-prompt-assets.js +1 -1
- package/dist/core/auto-mode-prompt.d.ts +11 -1
- package/dist/core/auto-mode-prompt.js +13 -10
- package/dist/core/auto-mode-rebuild.d.ts +7 -0
- package/dist/core/auto-mode-rebuild.js +4 -1
- package/dist/core/canonical-json.d.ts +3 -0
- package/dist/core/canonical-json.js +2 -1
- package/dist/core/checkpoint-requirements.d.ts +892 -0
- package/dist/core/checkpoint-requirements.js +333 -0
- package/dist/core/checkpoint-store.d.ts +94 -65
- package/dist/core/checkpoint-store.js +14 -26
- package/dist/core/child-clamp-seats.d.ts +228 -69
- package/dist/core/child-clamp-seats.js +211 -17
- package/dist/core/classifier-unavailable-arm.d.ts +5 -1
- package/dist/core/classifier-unavailable-arm.js +3 -3
- package/dist/core/compliance.d.ts +3 -0
- package/dist/core/compliance.js +9 -4
- package/dist/core/credential-vocabulary.d.ts +140 -0
- package/dist/core/credential-vocabulary.js +131 -0
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/effective-path-target.d.ts +73 -16
- package/dist/core/effective-path-target.js +63 -33
- package/dist/core/engine-notice.d.ts +32 -7
- package/dist/core/file-history-store.js +2 -2
- package/dist/core/fs-write-gate-policy.d.ts +4 -0
- package/dist/core/fs-write-gate-policy.js +22 -5
- package/dist/core/gate-bounded-await.d.ts +28 -13
- package/dist/core/gate-bounded-await.js +41 -4
- package/dist/core/gate-fold.d.ts +1 -1
- package/dist/core/gate-fold.js +24 -25
- package/dist/core/gate-lanes.js +16 -15
- package/dist/core/gate-outcome.d.ts +20 -5
- package/dist/core/gate-outcome.js +5 -0
- package/dist/core/governance-codes.d.ts +11 -4
- package/dist/core/governance-codes.js +14 -3
- package/dist/core/hooks.d.ts +4 -0
- package/dist/core/locked-config.d.ts +8 -0
- package/dist/core/locked-config.js +14 -2
- package/dist/core/lsp.js +3 -5
- package/dist/core/mailbox-store.d.ts +3 -4
- package/dist/core/mcp-connection-pool.d.ts +75 -0
- package/dist/core/mcp-connection-pool.js +128 -0
- package/dist/core/mcp-failure.d.ts +2 -1
- package/dist/core/mcp-failure.js +13 -2
- package/dist/core/mcp.d.ts +4 -0
- package/dist/core/mcp.js +70 -43
- package/dist/core/memory-engine/content-origin.js +2 -14
- package/dist/core/memory-engine/engine.d.ts +32 -22
- package/dist/core/memory-engine/engine.js +103 -55
- package/dist/core/memory-engine/file-backend.d.ts +13 -30
- package/dist/core/memory-engine/file-backend.js +37 -252
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/memory-engine/layout.d.ts +139 -9
- package/dist/core/memory-engine/layout.js +350 -7
- package/dist/core/memory-engine/scope-contract.d.ts +10 -0
- package/dist/core/memory-engine/scope-contract.js +8 -0
- package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
- package/dist/core/memory-engine/session-incarnation.js +5 -2
- package/dist/core/memory-engine/types.d.ts +10 -1
- package/dist/core/memory.js +5 -17
- package/dist/core/model-seat.d.ts +4 -4
- package/dist/core/model-seat.js +1 -1
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +19 -0
- package/dist/core/namespaced-rule-name.d.ts +29 -0
- package/dist/core/namespaced-rule-name.js +52 -0
- package/dist/core/park-selfcheck.d.ts +1 -1
- package/dist/core/park-selfcheck.js +16 -6
- package/dist/core/path-guard-rows.d.ts +99 -0
- package/dist/core/path-guard-rows.js +45 -0
- package/dist/core/path-identity.d.ts +22 -16
- package/dist/core/path-identity.js +10 -10
- package/dist/core/path-rule-binding.d.ts +2 -1
- package/dist/core/path-rule-binding.js +3 -1
- package/dist/core/pause-registry.d.ts +16 -2
- package/dist/core/pause-registry.js +6 -0
- package/dist/core/permission-rule-consent.js +0 -7
- package/dist/core/permission-rule-model.d.ts +68 -24
- package/dist/core/permission-rule-model.js +105 -24
- package/dist/core/permission-rule-store.js +2 -2
- package/dist/core/permission-rules.d.ts +3 -30
- package/dist/core/permission-rules.js +4 -54
- package/dist/core/read-root-candidate.d.ts +1 -3
- package/dist/core/read-root-candidate.js +4 -15
- package/dist/core/real-approval.d.ts +4 -3
- package/dist/core/retired-keys.d.ts +13 -0
- package/dist/core/retired-keys.js +14 -0
- package/dist/core/roles.d.ts +2 -3
- package/dist/core/roles.js +15 -6
- package/dist/core/runner/assemble-result.d.ts +0 -7
- package/dist/core/runner/assemble-result.js +19 -31
- package/dist/core/runner/caller-roster.d.ts +78 -0
- package/dist/core/runner/caller-roster.js +219 -0
- package/dist/core/runner/checkpoint-scope.d.ts +16 -16
- package/dist/core/runner/checkpoint-scope.js +2 -2
- package/dist/core/runner/child-tree.js +2 -2
- package/dist/core/runner/contracts.d.ts +68 -35
- package/dist/core/runner/decide-continuation.d.ts +51 -3
- package/dist/core/runner/decide-continuation.js +76 -21
- package/dist/core/runner/denial-limit-arms.d.ts +17 -5
- package/dist/core/runner/denial-limit-arms.js +11 -9
- package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
- package/dist/core/runner/extra-body-dropped-seat.js +22 -0
- package/dist/core/runner/finish-reason-seat.d.ts +9 -0
- package/dist/core/runner/finish-reason-seat.js +40 -0
- package/dist/core/runner/gate-exit.d.ts +44 -32
- package/dist/core/runner/gate-exit.js +112 -25
- package/dist/core/runner/inherited-ask-grants.js +6 -4
- package/dist/core/runner/mcp-redial.d.ts +3 -3
- package/dist/core/runner/mcp-redial.js +3 -4
- package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
- package/dist/core/runner/permission-rule-lanes.js +5 -4
- package/dist/core/runner/prepare-artifact.d.ts +4 -0
- package/dist/core/runner/prepare-artifact.js +2 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
- package/dist/core/runner/prepare-ask-lane.js +12 -6
- package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
- package/dist/core/runner/prepare-boundary-parks.js +4 -7
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
- package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
- package/dist/core/runner/prepare-config-doors.d.ts +9 -9
- package/dist/core/runner/prepare-config-doors.js +10 -5
- package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
- package/dist/core/runner/prepare-defer-classify.js +5 -3
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
- package/dist/core/runner/prepare-delegation-surface.js +3 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
- package/dist/core/runner/prepare-gate-stations.js +9 -7
- package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
- package/dist/core/runner/prepare-hands-readface.js +7 -7
- package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
- package/dist/core/runner/prepare-inherited-gate.js +14 -15
- package/dist/core/runner/prepare-listings.d.ts +7 -24
- package/dist/core/runner/prepare-listings.js +4 -5
- package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
- package/dist/core/runner/prepare-memory-engine-session.js +4 -3
- package/dist/core/runner/prepare-memory.d.ts +6 -0
- package/dist/core/runner/prepare-memory.js +13 -13
- package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
- package/dist/core/runner/prepare-offload-wrappers.js +3 -3
- package/dist/core/runner/prepare-park-ask.d.ts +7 -8
- package/dist/core/runner/prepare-park-ask.js +17 -10
- package/dist/core/runner/prepare-policy-chain.js +24 -44
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +3 -4
- package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
- package/dist/core/runner/prepare-protocol-tools.js +3 -4
- package/dist/core/runner/prepare-run-refs.d.ts +3 -0
- package/dist/core/runner/prepare-run-refs.js +2 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
- package/dist/core/runner/prepare-safety-scan.js +1 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
- package/dist/core/runner/prepare-suspend-saga.js +28 -12
- package/dist/core/runner/prepare-task.d.ts +1 -1
- package/dist/core/runner/prepare-task.js +67 -56
- package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
- package/dist/core/runner/prepare-wiring-manifest.js +5 -8
- package/dist/core/runner/resume-apply.js +7 -14
- package/dist/core/runner/resume-checkpoint-screen.js +11 -4
- package/dist/core/runner/resume-internals-and-config.js +2 -2
- package/dist/core/runner/resume-policy-outcome.js +5 -32
- package/dist/core/runner/resume-review-outcome.js +4 -1
- package/dist/core/runner/retired-runner-deps.d.ts +6 -0
- package/dist/core/runner/retired-runner-deps.js +10 -1
- package/dist/core/runner/run-brain-sinks.d.ts +4 -0
- package/dist/core/runner/run-brain-sinks.js +5 -2
- package/dist/core/runner/run-harness-handlers.js +27 -34
- package/dist/core/runner/run-leg.d.ts +0 -2
- package/dist/core/runner/run-leg.js +32 -33
- package/dist/core/runner/run-terminal-adoption.js +4 -3
- package/dist/core/runner/runtask.js +8 -8
- package/dist/core/runner/session-rule-policy.js +5 -5
- package/dist/core/runner/stream-settle-backstop.js +7 -4
- package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
- package/dist/core/runner/sub-agent-deny-lists.js +111 -80
- package/dist/core/runner/terminal-account.d.ts +48 -0
- package/dist/core/runner/terminal-account.js +186 -0
- package/dist/core/runner/tool-end-body.d.ts +54 -17
- package/dist/core/runner/tool-end-body.js +27 -2
- package/dist/core/runner/tool-face-overlay.d.ts +3 -1
- package/dist/core/runner/tool-face-overlay.js +6 -4
- package/dist/core/runner/tool-occurrence.d.ts +106 -0
- package/dist/core/runner/tool-occurrence.js +123 -0
- package/dist/core/runner/tool-progress-lane.d.ts +16 -8
- package/dist/core/runner/tool-progress-lane.js +25 -34
- package/dist/core/runner/turn-attachments.d.ts +1 -1
- package/dist/core/runner/write-protection-station.d.ts +21 -9
- package/dist/core/runner/write-protection-station.js +20 -17
- package/dist/core/runner-deps.d.ts +12 -22
- package/dist/core/secret-env.d.ts +16 -5
- package/dist/core/secret-env.js +23 -63
- package/dist/core/sensitive-path-policy.d.ts +19 -18
- package/dist/core/sensitive-path-policy.js +73 -49
- package/dist/core/session-reconcile.d.ts +3 -0
- package/dist/core/session-reconcile.js +5 -4
- package/dist/core/session-store.d.ts +5 -0
- package/dist/core/session-store.js +12 -0
- package/dist/core/session.d.ts +13 -0
- package/dist/core/shared-memory/normalize.d.ts +17 -4
- package/dist/core/shared-memory/normalize.js +7 -7
- package/dist/core/shell-lexer.js +5 -4
- package/dist/core/skill-grant.js +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
- package/dist/core/stub-env.d.ts +2 -0
- package/dist/core/stub-env.js +1 -0
- package/dist/core/synthetic-read-verdict.js +2 -2
- package/dist/core/task-event.d.ts +59 -5
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +42 -9
- package/dist/core/terminal-cause.d.ts +94 -11
- package/dist/core/tighten-task-spec.d.ts +5 -4
- package/dist/core/tighten-task-spec.js +20 -25
- package/dist/core/tool-catalog-entries.d.ts +5 -7
- package/dist/core/tool-catalog-entries.js +14 -15
- package/dist/core/tool-face.d.ts +47 -0
- package/dist/core/tool-face.js +5 -0
- package/dist/core/tool-model-gate.d.ts +5 -5
- package/dist/core/tool-name-aliases.d.ts +26 -2
- package/dist/core/tool-name-aliases.js +13 -0
- package/dist/core/tool-name-list.d.ts +30 -0
- package/dist/core/tool-name-list.js +55 -0
- package/dist/core/tool-name-readings.d.ts +1 -9
- package/dist/core/tool-name-readings.js +1 -3
- package/dist/core/tool-policy.d.ts +195 -85
- package/dist/core/tool-policy.js +165 -380
- package/dist/core/tool-registry.d.ts +44 -1
- package/dist/core/tool-registry.js +26 -0
- package/dist/core/tool-roster.d.ts +19 -1
- package/dist/core/tool-roster.js +21 -2
- package/dist/core/tool-spec-members.d.ts +45 -0
- package/dist/core/tool-spec-members.js +169 -0
- package/dist/core/tool-spec.d.ts +46 -8
- package/dist/core/tools.d.ts +6 -2
- package/dist/core/tools.js +8 -4
- package/dist/core/untrusted-egress.js +29 -26
- package/dist/core/untrusted-text.d.ts +13 -10
- package/dist/core/untrusted-text.js +6 -44
- package/dist/core/workflow-journal-store.d.ts +4 -1
- package/dist/core/write-protect.d.ts +65 -29
- package/dist/core/write-protect.js +235 -177
- package/dist/engine/compaction/compaction.d.ts +2 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
- package/dist/engine/execution-env/node-execution-env.js +16 -38
- package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
- package/dist/engine/execution-env/symlink-chain.js +32 -0
- package/dist/engine/harness/types.d.ts +7 -0
- package/dist/engine/llm/tool-turns.d.ts +3 -0
- package/dist/engine/llm/tool-turns.js +1 -0
- package/dist/engine/llm/types.d.ts +59 -23
- package/dist/engine/loop/agent-loop.js +16 -2
- package/dist/engine/session/memory-repo.js +11 -3
- package/dist/engine/session/repo-utils.d.ts +8 -0
- package/dist/engine/session/repo-utils.js +13 -0
- package/dist/index.d.ts +20 -23
- package/dist/index.js +17 -20
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/governance-baseline-validity.js +1 -1
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +16 -35
- package/dist/orchestration/run-workflow-tool.js +38 -78
- package/dist/orchestration/workflow-governance.d.ts +21 -25
- package/dist/orchestration/workflow-governance.js +38 -78
- package/dist/orchestration/workflow-primitives.d.ts +7 -22
- package/dist/orchestration/workflow-primitives.js +5 -12
- package/dist/orchestration/workflow-types.d.ts +1 -1
- package/dist/orchestration/workflow.d.ts +61 -11
- package/dist/orchestration/workflow.js +189 -117
- package/dist/scenarios/full-body.d.ts +1 -1
- package/dist/shell/lex.d.ts +0 -21
- package/dist/shell/lex.js +250 -68
- package/dist/shell/shell-words.d.ts +35 -0
- package/dist/shell/shell-words.js +65 -0
- package/dist/stores/file/checkpoint-store.d.ts +5 -5
- package/dist/stores/file/checkpoint-store.js +5 -2
- package/dist/stores/file/index.d.ts +0 -8
- package/dist/stores/file/index.js +0 -12
- package/dist/stores/file/session-store.d.ts +3 -0
- package/dist/stores/file/session-store.js +44 -19
- package/dist/tools/artifact/artifact-tool.js +2 -0
- package/dist/tools/fs/bash-program-position.d.ts +10 -3
- package/dist/tools/fs/bash-program-position.js +21 -9
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/bash-readonly-classifier.js +8 -7
- package/dist/tools/fs/containment-face.d.ts +4 -0
- package/dist/tools/fs/containment-face.js +3 -0
- package/dist/tools/fs/fs-shared.d.ts +10 -0
- package/dist/tools/fs/fs-shared.js +18 -0
- package/dist/tools/fs/fs-write.js +4 -2
- package/dist/tools/fs/index.d.ts +1 -1
- package/dist/tools/fs/program-read-model.d.ts +39 -18
- package/dist/tools/fs/program-read-model.js +29 -19
- package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
- package/dist/tools/fs/program-read-model.rows.js +5 -5
- package/dist/tools/fs/read-deny-home.d.ts +33 -47
- package/dist/tools/fs/read-deny-home.js +35 -49
- package/dist/tools/fs/read-deny.d.ts +23 -22
- package/dist/tools/fs/read-deny.js +114 -89
- package/dist/tools/fs/removal-targets.d.ts +56 -23
- package/dist/tools/fs/removal-targets.js +65 -87
- package/dist/tools/fs/safety.d.ts +29 -22
- package/dist/tools/fs/safety.js +17 -11
- package/dist/tools/fs/search.js +10 -8
- package/dist/tools/fs/shell-read-argv.js +1 -1
- package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
- package/dist/tools/fs/shell-read-bindings.js +324 -86
- package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
- package/dist/tools/fs/shell-read-builtins.js +43 -0
- package/dist/tools/fs/shell-read-check.d.ts +15 -1
- package/dist/tools/fs/shell-read-check.js +51 -42
- package/dist/tools/fs/shell-read-establish.js +6 -6
- package/dist/tools/fs/shell-read-functions.d.ts +22 -0
- package/dist/tools/fs/shell-read-functions.js +29 -0
- package/dist/tools/fs/shell-read-judge.d.ts +48 -18
- package/dist/tools/fs/shell-read-judge.js +33 -4
- package/dist/tools/fs/shell-read-plan.d.ts +12 -6
- package/dist/tools/fs/shell-read-plan.js +118 -113
- package/dist/tools/fs/shell-read-start.d.ts +29 -6
- package/dist/tools/fs/shell-read-start.js +16 -9
- package/dist/tools/fs/shell-read-word.js +4 -0
- package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
- package/dist/tools/fs/shell-read-workdir.js +281 -0
- package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
- package/dist/tools/fs/shell-read-write-targets.js +14 -5
- package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
- package/dist/tools/fs/shell-reentry-text.js +3 -11
- package/dist/tools/task-list.d.ts +1 -2
- package/dist/tools/web.js +1 -1
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +107 -143
- package/wire-changes/core@7.49.0.json +4765 -0
- package/dist/agents/teacher.d.ts +0 -159
- package/dist/agents/teacher.js +0 -577
- package/dist/agents/team.d.ts +0 -226
- package/dist/agents/team.js +0 -257
- package/dist/core/strategy-store.d.ts +0 -238
- package/dist/core/strategy-store.js +0 -253
- package/dist/orchestration/builtin-workflows.d.ts +0 -133
- package/dist/orchestration/builtin-workflows.js +0 -159
- package/dist/scenarios/env.d.ts +0 -40
- package/dist/scenarios/env.js +0 -39
- package/dist/scenarios/scenario-registry.d.ts +0 -110
- package/dist/scenarios/scenario-registry.js +0 -140
- package/dist/scenarios/teacher-quickstart.d.ts +0 -40
- package/dist/scenarios/teacher-quickstart.js +0 -12
- package/dist/stores/cc/lockfile.d.ts +0 -10
- package/dist/stores/cc/lockfile.js +0 -98
- package/dist/stores/cc/mailbox-store.d.ts +0 -20
- package/dist/stores/cc/mailbox-store.js +0 -255
- package/dist/stores/cc/roster-adapter.d.ts +0 -12
- package/dist/stores/cc/roster-adapter.js +0 -117
- package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
- package/dist/stores/cc/sidecar-transcript.js +0 -264
- package/dist/stores/cc/task-list-store.d.ts +0 -2
- package/dist/stores/cc/task-list-store.js +0 -125
- package/dist/stores/file/strategy-store.d.ts +0 -97
- package/dist/stores/file/strategy-store.js +0 -341
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* the segment it sits in ({@link rebasePlan}), unless it starts in a working directory of its own.
|
|
6
6
|
*/
|
|
7
7
|
import type { LexedSegment } from "../../shell/lex.js";
|
|
8
|
-
import type { BindingTable, FloorValue, NameReader } from "./shell-read-bindings.js";
|
|
8
|
+
import type { BindingTable, FloorValue, InheritedFunctions, NameReader } from "./shell-read-bindings.js";
|
|
9
9
|
import type { TargetUser } from "./program-read-model.js";
|
|
10
10
|
import type { StdinText } from "./shell-reentry-text.js";
|
|
11
11
|
import type { ReadCandidate, ShellReadPlan } from "./shell-read-plan.js";
|
|
12
12
|
/** Where a nested text starts its names: a substitution body inherits the table of the segment it sits in (a subshell
|
|
13
|
-
* holds every variable); a text the shell runs later or elsewhere (a code body, `eval`'s
|
|
14
|
-
* starts from the text's own start — every name unknown, `HOME` the declared home unless
|
|
15
|
-
* anywhere. `boundAnywhere` answers for the enclosing texts. */
|
|
13
|
+
* holds every variable of the shell that forks it); a text the shell runs later or elsewhere (a code body, `eval`'s
|
|
14
|
+
* text, a child shell's `-c`) starts from the text's own start — every name unknown, `HOME` the declared home unless
|
|
15
|
+
* the enclosing text binds it anywhere. `boundAnywhere` answers for the enclosing texts. */
|
|
16
16
|
export interface PlanStart {
|
|
17
17
|
readonly initial: NameReader;
|
|
18
18
|
readonly boundAnywhere: (name: string) => boolean;
|
|
@@ -23,9 +23,30 @@ export interface PlanStart {
|
|
|
23
23
|
/** The working directories it starts in, where they are not the enclosing segment's (a login shell starts in its user's
|
|
24
24
|
* home; `"unmodelled"` — a home the text does not settle). */
|
|
25
25
|
readonly bases?: readonly string[] | "unmodelled";
|
|
26
|
+
/** The thread of the shell that runs the text, where the text runs in that shell or a fork of it ({@link HeldThread}). */
|
|
27
|
+
readonly thread?: HeldThread;
|
|
28
|
+
/** What the shell around the text holds where the text runs in that shell or a fork of it — a literal `eval` text, a
|
|
29
|
+
* substitution body, a text run later (a `trap` action): each function and builtin key as it stands there
|
|
30
|
+
* ({@link InheritedFunctions}). A text a new process runs (a child shell's `-c`) holds none: every builtin enabled, no function. */
|
|
31
|
+
readonly names?: InheritedFunctions;
|
|
26
32
|
/** What arrives on its own standard input (the input of the shell running it). */
|
|
27
33
|
readonly stdin?: readonly StdinText[];
|
|
28
34
|
}
|
|
35
|
+
/** The thread of the shell that runs a text, at the segment that hands it on: a literal `eval` text is not a new plan but the next
|
|
36
|
+
* segment of the same thread, so it starts in all of it and hands the working directory and the physical mode back; a substitution
|
|
37
|
+
* body (a fork) and a text run later start in what the shell holds and hand nothing back. `functions`: the names a call may mean
|
|
38
|
+
* (the shell's definition heads and the ones an `eval` text defines), `unread` those whose bodies the nested plan has not read — an
|
|
39
|
+
* `eval` text reads the shell's bodies through the names they bind, every other text has none of them in front of it. */
|
|
40
|
+
export interface HeldThread {
|
|
41
|
+
readonly physical: boolean;
|
|
42
|
+
readonly functions: ReadonlySet<string>;
|
|
43
|
+
readonly unread: ReadonlySet<string>;
|
|
44
|
+
}
|
|
45
|
+
/** What a shell holds at a segment, for a text that runs in it or in a fork of it ({@link PlanStart.names}): each function and builtin key
|
|
46
|
+
* as it stands there, and the names a call to one of its functions may change — the ones the text reads a call of: an `eval` text names
|
|
47
|
+
* the shell's functions (`bound`: what a call to one of them changes); a substitution body and a text run later read none of their bodies
|
|
48
|
+
* (the shell's functions are {@link HeldThread.unread} there: a call to one makes every name unknown), so they name none. */
|
|
49
|
+
export declare function heldAt(table: BindingTable, bound?: InheritedFunctions["bound"]): InheritedFunctions;
|
|
29
50
|
/** What a segment says of the start of a text it hands a shell to run later: the deny floors of the words it holds as
|
|
30
51
|
* written, the user the shell runs as ({@link TargetUser}), and the shell's standard input. */
|
|
31
52
|
export interface NestedStart {
|
|
@@ -52,6 +73,8 @@ export declare function stdinFeeds(segments: readonly LexedSegment[], start: rea
|
|
|
52
73
|
* $(cat secret)`), so its relative candidates are re-based onto the ENCLOSING segment's bases — every base the
|
|
53
74
|
* nested text derived from the start is carried over by prefix, an unsettled enclosing base leaves every
|
|
54
75
|
* relative candidate unplaced, and absolute candidates are untouched. Recursive: a nested plan's own nested plans
|
|
55
|
-
* derive from the same start.
|
|
76
|
+
* derive from the same start. A candidate left unplaced is judged where the text stood when it lost track (its floor,
|
|
77
|
+
* {@link ReadCandidate.floorBases}): the places the nested text was in move with it like its bases, and an enclosing segment that
|
|
78
|
+
* lost its base itself hands on its own floor (`floor`).
|
|
56
79
|
*/
|
|
57
|
-
export declare function rebasePlan(plan: ShellReadPlan, from: string, to: ReadCandidate["bases"]): ShellReadPlan;
|
|
80
|
+
export declare function rebasePlan(plan: ShellReadPlan, from: string, to: ReadCandidate["bases"], floor?: readonly string[]): ShellReadPlan;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const NO_NAMES = new Set();
|
|
2
|
+
export function heldAt(table, bound = NO_NAMES) {
|
|
3
|
+
return { start: (key) => table.stateOf(key), bound };
|
|
4
|
+
}
|
|
1
5
|
const ROOT_HOMES = ["/root", "/var/root"];
|
|
2
6
|
export function startedAt(later, at) {
|
|
3
7
|
if (at === undefined)
|
|
@@ -58,24 +62,27 @@ export function stdinFeeds(segments, start, piped) {
|
|
|
58
62
|
};
|
|
59
63
|
return fedAt;
|
|
60
64
|
}
|
|
61
|
-
export function rebasePlan(plan, from, to) {
|
|
65
|
+
export function rebasePlan(plan, from, to, floor) {
|
|
62
66
|
if (to !== "unmodelled" && to.length === 1 && to[0] === from)
|
|
63
67
|
return plan;
|
|
64
68
|
const fromSep = from.endsWith("/") || from.endsWith("\\") ? from : `${from}/`;
|
|
65
|
-
const
|
|
66
|
-
if (to === "unmodelled")
|
|
67
|
-
return [];
|
|
69
|
+
const moveInto = (into, b) => {
|
|
68
70
|
if (b === from)
|
|
69
|
-
return [...
|
|
71
|
+
return [...into];
|
|
70
72
|
if (b.startsWith(fromSep))
|
|
71
|
-
return
|
|
73
|
+
return into.map((t) => `${t.endsWith("/") || t.endsWith("\\") ? t : `${t}/`}${b.slice(fromSep.length)}`);
|
|
72
74
|
return [b];
|
|
73
75
|
};
|
|
76
|
+
const moveBase = (b) => (to === "unmodelled" ? [] : moveInto(to, b));
|
|
77
|
+
const floorAt = to === "unmodelled" ? floor : to;
|
|
78
|
+
const moveFloor = (bases, into) => [...new Set(bases.flatMap((b) => moveInto(into, b)))];
|
|
74
79
|
const moveCandidate = (c) => {
|
|
75
|
-
if (c.bases === "unmodelled"
|
|
80
|
+
if (c.bases === "unmodelled")
|
|
81
|
+
return c.floorBases === undefined || floorAt === undefined ? c : { ...c, floorBases: moveFloor(c.floorBases, floorAt) };
|
|
82
|
+
if (c.bases.length === 0)
|
|
76
83
|
return c;
|
|
77
84
|
if (to === "unmodelled")
|
|
78
|
-
return { ...c, bases: "unmodelled" };
|
|
85
|
+
return floor === undefined ? { ...c, bases: "unmodelled" } : { ...c, bases: "unmodelled", floorBases: moveFloor(c.bases, floor) };
|
|
79
86
|
return { ...c, bases: [...new Set(c.bases.flatMap(moveBase))] };
|
|
80
87
|
};
|
|
81
88
|
return {
|
|
@@ -86,7 +93,7 @@ export function rebasePlan(plan, from, to) {
|
|
|
86
93
|
redirects: s.redirects.map((r) => (r.target === undefined ? r : { ...r, target: moveCandidate(r.target) })),
|
|
87
94
|
...(s.baseMove !== undefined ? { baseMove: { target: typeof s.baseMove.target === "string" ? s.baseMove.target : moveCandidate(s.baseMove.target) } } : {}),
|
|
88
95
|
bases: s.bases === "unmodelled" || to === "unmodelled" ? "unmodelled" : [...new Set(s.bases.flatMap(moveBase))],
|
|
89
|
-
nested: s.nested.map((n) => rebasePlan(n, from, to)),
|
|
96
|
+
nested: s.nested.map((n) => rebasePlan(n, from, to, floor)),
|
|
90
97
|
})),
|
|
91
98
|
};
|
|
92
99
|
}
|
|
@@ -85,6 +85,10 @@ function overlaidTable(table, overlay) {
|
|
|
85
85
|
isInteger: (name) => table.isInteger(name),
|
|
86
86
|
ifsTouched: table.ifsTouched,
|
|
87
87
|
homeTouched: table.homeTouched,
|
|
88
|
+
rebound: table.rebound,
|
|
89
|
+
providedByShell: (name) => table.providedByShell(name),
|
|
90
|
+
functionState: (name) => table.functionState(name),
|
|
91
|
+
builtinState: (name) => table.builtinState(name),
|
|
88
92
|
floorOf: (name) => {
|
|
89
93
|
const held = table.stateOf(name);
|
|
90
94
|
return overlay.get(name) !== "unknown" || held === "unknown" ? table.floorOf(name) : [...table.floorOf(name), ...held.map(floorOf)];
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE WORKING-DIRECTORY THREAD — where a command stands at each of its segments, and what `PWD` / `OLDPWD` hold there.
|
|
3
|
+
* ONE walk (`shell-read-plan.ts` drives it, segment by segment, beside the binding thread) over a CLOSED word set that
|
|
4
|
+
* moves the base ({@link BaseShift}): `cd` in its forms — a bare relative target searched through `CDPATH` first, `cd -`
|
|
5
|
+
* going to `OLDPWD`, a bare `cd` to `HOME`, `-P` / `set -P` leaving `PWD` a spelling the text does not hold — `pushd` /
|
|
6
|
+
* `popd` with the stack the text pushed, and a literal `eval` text read as the text it is, in this shell. Any other text
|
|
7
|
+
* that may move the base where the plan cannot follow it (`source`, a non-literal `eval`, a computed program word, a
|
|
8
|
+
* call into a function body or a code body that may move, an option the stack reading does not follow) LOSES it: what
|
|
9
|
+
* stands after it cannot be placed. A move inside a subshell, a pipeline member or a function body nobody called is read
|
|
10
|
+
* as a move too — a candidate SET, more places, never fewer (a move may also fail: the unmoved place stays a candidate
|
|
11
|
+
* unless an `&&` run proves the move ran).
|
|
12
|
+
*/
|
|
13
|
+
import type { Bare } from "./shell-read-argv.js";
|
|
14
|
+
import type { BindingTable, WorkingDirectoryState } from "./shell-read-bindings.js";
|
|
15
|
+
import type { Read } from "./shell-read-word.js";
|
|
16
|
+
import { type LexedWord } from "../../shell/lex.js";
|
|
17
|
+
import { type FunctionCall } from "./shell-read-functions.js";
|
|
18
|
+
/** A working directory the shell could be standing in, with the latest move this candidate assumes did NOT run
|
|
19
|
+
* (`skippedCd`), and what the shell holds there: `pwd` — what `PWD` spells (absent: not the base's spelling — a physical
|
|
20
|
+
* move, or a start the plan does not place), `old` — what `OLDPWD` holds (absent: what the shell inherited, unknown),
|
|
21
|
+
* `stack` — the directories the text pushed and has not popped, top first (absent: the text changed the stack in a way
|
|
22
|
+
* this walk does not follow; what lies below the text's own entries is the shell's, unknown). */
|
|
23
|
+
export interface BaseCandidate {
|
|
24
|
+
readonly base: string;
|
|
25
|
+
readonly skippedCd: number;
|
|
26
|
+
readonly pwd?: string;
|
|
27
|
+
readonly old?: string;
|
|
28
|
+
readonly dirStack?: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/** How one segment moves the base — the closed word set. `cd`: a `cd` (`push`: a `pushd DIR`) to a target, the
|
|
31
|
+
* declared-home spelling of an unsettled target for the deny floor; `swap`: `pushd` with no directory; `pop`: `popd`;
|
|
32
|
+
* `stack`: the stack changes and the base does not (`pushd -n`, `popd -n`, `dirs -c`); `eval`: a literal `eval` text;
|
|
33
|
+
* `lost`: a move this walk does not follow. */
|
|
34
|
+
export type BaseShift = {
|
|
35
|
+
readonly kind: "cd";
|
|
36
|
+
readonly target: Bare | "home" | "previous";
|
|
37
|
+
readonly physical: boolean;
|
|
38
|
+
readonly push?: true;
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: "swap" | "pop" | "stack" | "lost";
|
|
41
|
+
} | {
|
|
42
|
+
readonly kind: "eval";
|
|
43
|
+
readonly text: string;
|
|
44
|
+
};
|
|
45
|
+
/** What the walk asks of the plan around it: the declared home (a `~` target), whether the text binds a name anywhere
|
|
46
|
+
* (`OLDPWD` — then `cd -` reads the table's value; `PWD` — then a move's `OLDPWD` is not the base the candidate stood
|
|
47
|
+
* in), and where a literal `eval` text started at a candidate ends ({@link WorkdirThread.end}). */
|
|
48
|
+
export interface WorkdirContext {
|
|
49
|
+
readonly homeDir?: string;
|
|
50
|
+
readonly textBinds: (name: string) => boolean;
|
|
51
|
+
readonly evalEnd: (text: string, from: BaseCandidate, table: BindingTable) => {
|
|
52
|
+
readonly end: readonly BaseCandidate[] | "unmodelled";
|
|
53
|
+
readonly physical: boolean;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** One segment's move as the walk applies it: the shift, whether an `&&` run proves it ran, the first segment of the
|
|
57
|
+
* reader's `&&` run, the names there, and whether `cdable_vars` may be on (a bare name may be a variable's value). */
|
|
58
|
+
export interface ShiftAt {
|
|
59
|
+
readonly shift: BaseShift;
|
|
60
|
+
readonly provable: boolean;
|
|
61
|
+
readonly andRunStart: number;
|
|
62
|
+
readonly table: CommandTable;
|
|
63
|
+
readonly cdable: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** The names at a `cd` segment with the command's own prefix assignments in force (`HOME=/ cd`), and which names those are. */
|
|
66
|
+
export interface CommandTable extends BindingTable {
|
|
67
|
+
readonly commandSets: ReadonlySet<string>;
|
|
68
|
+
}
|
|
69
|
+
export declare class WorkdirThread {
|
|
70
|
+
/** The start is where the shell stands (not a placeholder a nested plan is re-based from): `PWD` reads it. */
|
|
71
|
+
private readonly readable;
|
|
72
|
+
private candidates;
|
|
73
|
+
private lostAt;
|
|
74
|
+
/** The reachable bases at the moment the walk lost track — the deny floor of a word it cannot place. */
|
|
75
|
+
private floors;
|
|
76
|
+
/** `set -P` (or `-o physical`) may be on — a text it began in, an `eval` text that set it — : a later move leaves `PWD` a physical
|
|
77
|
+
* spelling the text does not hold, and a `..` in a target resolves links the plan cannot see. */
|
|
78
|
+
private physical;
|
|
79
|
+
/** The last segment whose move was applied or lost track ({@link WorkingDirectoryState.movedAt}). */
|
|
80
|
+
private movedAt;
|
|
81
|
+
constructor(start: readonly BaseCandidate[] | readonly string[] | "unmodelled",
|
|
82
|
+
/** The start is where the shell stands (not a placeholder a nested plan is re-based from): `PWD` reads it. */
|
|
83
|
+
readable: boolean,
|
|
84
|
+
/** The physical mode may already be on where the text starts (an `eval` text run in a shell that set it). */
|
|
85
|
+
startsPhysical?: boolean);
|
|
86
|
+
get lost(): boolean;
|
|
87
|
+
get floorBases(): readonly string[] | undefined;
|
|
88
|
+
/** The candidates a reader whose `&&` run starts at `andRunStart` may stand in. */
|
|
89
|
+
reachable(andRunStart: number): readonly BaseCandidate[];
|
|
90
|
+
/** Where the text ends: every candidate, or `unmodelled`. */
|
|
91
|
+
end(): readonly BaseCandidate[] | "unmodelled";
|
|
92
|
+
/** What `PWD` / `OLDPWD` hold for a reader whose `&&` run starts at `andRunStart`. */
|
|
93
|
+
where(andRunStart: number): WorkingDirectoryState;
|
|
94
|
+
/** `set -P` / `set -o physical`: from here on a move's `PWD` is a physical spelling. */
|
|
95
|
+
setPhysical(): void;
|
|
96
|
+
/** Where the text ends the physical mode may be on (an `eval` text hands it back to the shell that ran it). */
|
|
97
|
+
get mayBePhysical(): boolean;
|
|
98
|
+
/** Stop placing anything from segment `at` on: the reachable bases (and a target's floor spellings) become the deny floor. */
|
|
99
|
+
lose(at: number, reachable: readonly BaseCandidate[], floorTargets?: readonly string[]): void;
|
|
100
|
+
/** Apply segment `si`'s move. */
|
|
101
|
+
shift(si: number, at: ShiftAt, ctx: WorkdirContext): void;
|
|
102
|
+
/** Where one candidate goes: the places (the CDPATH entries a bare target is searched through, then the target itself),
|
|
103
|
+
* each with the `PWD`, `OLDPWD` and stack the move leaves — or `lost`. */
|
|
104
|
+
private movedFrom;
|
|
105
|
+
}
|
|
106
|
+
/** Does bash search `CDPATH` for this `cd` target: not an absolute one, not `.` / `..`, not one starting `./` / `../`? */
|
|
107
|
+
export declare function searchesCdPath(dir: string): boolean;
|
|
108
|
+
/** The base shift of a `cd` / `pushd` / `popd` / `dirs` segment, read off its arguments as the program receives them (and
|
|
109
|
+
* the move the plan reports for it: a `cd` target, or `unmodelled` for a stack move it cannot name), or `undefined` for
|
|
110
|
+
* another program. `targetOf` turns the target word into its candidate (and records it as a read where the caller does). */
|
|
111
|
+
export declare function baseShiftOf(program: string, args: readonly Read[], targetOf: (r: Read) => Bare): {
|
|
112
|
+
shift: BaseShift;
|
|
113
|
+
reported?: Bare | "home" | "previous" | "unmodelled";
|
|
114
|
+
} | undefined;
|
|
115
|
+
/** May this text move the base of the shell that runs it: a program of the closed set or one running text in this shell,
|
|
116
|
+
* a computed program word, a call into one of `functions` — or text the lexer cannot read? */
|
|
117
|
+
export declare function textMayMove(text: string, functions: ReadonlySet<string>): boolean;
|
|
118
|
+
/** Does this text turn the physical mode on in the shell that runs it? An `eval` text that does is no text to skip. */
|
|
119
|
+
export declare function textSetsPhysical(text: string): boolean;
|
|
120
|
+
/** The table a `cd` / `pushd` reads `HOME`, `CDPATH` and `OLDPWD` from: the segment's own, with each prefix assignment in
|
|
121
|
+
* front of the builtin (`HOME=/ cd`, `CDPATH=/ cd usr`) holding its value there — it is in the builtin's environment. A
|
|
122
|
+
* prefix value naming an earlier prefix's name (`HOME=/ CDPATH=$HOME cd usr`) reads it as bash 5 does (the earlier
|
|
123
|
+
* assignment applied) and as bash 3.2 does (the value before the command): both. */
|
|
124
|
+
export declare function withCommandEnvironment(table: BindingTable, argv: readonly LexedWord[]): CommandTable;
|
|
125
|
+
/** Does this text call one of `functions` (a program word naming one) — or is it text the lexer cannot read? */
|
|
126
|
+
export declare function textCalls(text: string, functions: ReadonlySet<string>): boolean;
|
|
127
|
+
/** May a segment with these words move the base ({@link textMayMove})? */
|
|
128
|
+
export declare function segmentMayMove(argv: readonly LexedWord[], functions: ReadonlySet<string>): boolean;
|
|
129
|
+
/** What the walk reads of a whole text before it walks it: the text's functions (the names a program word can be one of;
|
|
130
|
+
* whether it IS one at a segment is the binding thread's answer, `callAt`); per segment whether it may move the base
|
|
131
|
+
* ({@link segmentMayMove}), may call a function, runs code bodies later (`trap`, `alias`, …), turns on the physical mode
|
|
132
|
+
* (`set -P`, `set -o physical`); whether a function body may move the base; where `cdable_vars` goes on. */
|
|
133
|
+
export interface TextMoves {
|
|
134
|
+
readonly functions: ReadonlySet<string>;
|
|
135
|
+
/** The names whose bodies the plan has not read (an `eval` text defined them). */
|
|
136
|
+
readonly unread: ReadonlySet<string>;
|
|
137
|
+
readonly mayMove: readonly boolean[];
|
|
138
|
+
/** Segment `si` may call a function (read before the walk, to bound where bodies run). */
|
|
139
|
+
readonly mayCallAt: readonly boolean[];
|
|
140
|
+
/** Segment `si` may run a function body: it may call one, hands a code body to run later, or runs text the plan does not read. */
|
|
141
|
+
readonly runsBodyAt: readonly boolean[];
|
|
142
|
+
readonly bodiesAt: readonly (readonly string[])[];
|
|
143
|
+
readonly physicalAt: readonly boolean[];
|
|
144
|
+
readonly cdableFrom: number;
|
|
145
|
+
readonly bodyMoves: boolean;
|
|
146
|
+
/** The function segment `si`'s program word names where the walk stands (`table`) — {@link functionCallOf}. */
|
|
147
|
+
callAt(si: number, table: BindingTable): FunctionCall | undefined;
|
|
148
|
+
/** The segment may call a function a literal `eval` text defined: its body is not read — it may move the base and bind any name. */
|
|
149
|
+
callsUnread(si: number, table: BindingTable): boolean;
|
|
150
|
+
/** The segment runs a body that may move the base where the walk cannot follow it: a call when a body may move or whose body is not read, a code body that may move or call one. */
|
|
151
|
+
losesBaseAt(si: number, table: BindingTable): boolean;
|
|
152
|
+
}
|
|
153
|
+
export declare function textMovesOf(defined: ReadonlySet<string>, argvs: readonly (readonly LexedWord[])[], inFunctionBody: (si: number) => boolean, inherited?: {
|
|
154
|
+
readonly functions: ReadonlySet<string>;
|
|
155
|
+
readonly unread: ReadonlySet<string>;
|
|
156
|
+
}): TextMoves;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { argvOf, lexShell } from "../../shell/lex.js";
|
|
2
|
+
import { shellWordsOf } from "../../shell/shell-words.js";
|
|
3
|
+
import { resolveOperandLexically } from "./bash-readonly-classifier.js";
|
|
4
|
+
import { readProgramPosition } from "./bash-program-position.js";
|
|
5
|
+
import { valueOf } from "./shell-read-bindings.js";
|
|
6
|
+
import { evalDefinedFunctions, functionCallOf } from "./shell-read-functions.js";
|
|
7
|
+
import { codeBodies } from "./shell-reentry-text.js";
|
|
8
|
+
import { isAbsoluteForFamily } from "./safety.js";
|
|
9
|
+
const TEXT_RUNNING_PROGRAMS = new Set(["eval", "source", "."]);
|
|
10
|
+
const BASE_CANDIDATE_MAX = 256;
|
|
11
|
+
const tableSays = (name, at, ctx) => ctx.textBinds(name) || at.table.commandSets.has(name);
|
|
12
|
+
export class WorkdirThread {
|
|
13
|
+
readable;
|
|
14
|
+
candidates;
|
|
15
|
+
lostAt = false;
|
|
16
|
+
floors;
|
|
17
|
+
physical;
|
|
18
|
+
movedAt = -1;
|
|
19
|
+
constructor(start, readable, startsPhysical = false) {
|
|
20
|
+
this.readable = readable;
|
|
21
|
+
this.physical = startsPhysical;
|
|
22
|
+
this.lostAt = start === "unmodelled";
|
|
23
|
+
this.candidates = start === "unmodelled" ? [] : start.map((c) => (typeof c === "string" ? { base: c, skippedCd: -1, ...(readable && isAbsoluteForFamily("own", c) ? { pwd: c } : {}), dirStack: [] } : { ...c, skippedCd: -1 }));
|
|
24
|
+
}
|
|
25
|
+
get lost() {
|
|
26
|
+
return this.lostAt;
|
|
27
|
+
}
|
|
28
|
+
get floorBases() {
|
|
29
|
+
return this.floors;
|
|
30
|
+
}
|
|
31
|
+
reachable(andRunStart) {
|
|
32
|
+
return this.candidates.filter((c) => c.skippedCd < andRunStart);
|
|
33
|
+
}
|
|
34
|
+
end() {
|
|
35
|
+
return this.lostAt ? "unmodelled" : this.candidates;
|
|
36
|
+
}
|
|
37
|
+
where(andRunStart) {
|
|
38
|
+
if (this.lostAt || !this.readable)
|
|
39
|
+
return { PWD: "unknown", OLDPWD: "unknown", movedAt: this.movedAt };
|
|
40
|
+
const reachable = this.reachable(andRunStart);
|
|
41
|
+
const values = (pick) => {
|
|
42
|
+
const out = new Set();
|
|
43
|
+
for (const c of reachable) {
|
|
44
|
+
const v = pick(c);
|
|
45
|
+
if (v === undefined)
|
|
46
|
+
return "unknown";
|
|
47
|
+
out.add(v);
|
|
48
|
+
}
|
|
49
|
+
return [...out].map((spelled) => ({ spelled }));
|
|
50
|
+
};
|
|
51
|
+
return { PWD: values((c) => c.pwd), OLDPWD: values((c) => c.old), movedAt: this.movedAt };
|
|
52
|
+
}
|
|
53
|
+
setPhysical() {
|
|
54
|
+
this.physical = true;
|
|
55
|
+
}
|
|
56
|
+
get mayBePhysical() {
|
|
57
|
+
return this.physical;
|
|
58
|
+
}
|
|
59
|
+
lose(at, reachable, floorTargets = []) {
|
|
60
|
+
this.movedAt = Math.max(this.movedAt, at);
|
|
61
|
+
this.lostAt = true;
|
|
62
|
+
this.floors ??= [...new Set([...reachable.map((c) => c.base), ...floorTargets])];
|
|
63
|
+
}
|
|
64
|
+
shift(si, at, ctx) {
|
|
65
|
+
if (this.lostAt) {
|
|
66
|
+
if (this.floors !== undefined && this.floors.length > 0)
|
|
67
|
+
this.floors = [...new Set([...this.floors, ...floorOf(at.shift, this.reachable(at.andRunStart), ctx)])];
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.movedAt = si;
|
|
71
|
+
const reachable = this.reachable(at.andRunStart);
|
|
72
|
+
const s = at.shift;
|
|
73
|
+
if (s.kind === "lost")
|
|
74
|
+
return this.lose(si, reachable);
|
|
75
|
+
if (s.kind === "stack") {
|
|
76
|
+
this.candidates = this.candidates.map(({ dirStack: _, ...c }) => c);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const branched = [];
|
|
80
|
+
for (const c of this.candidates) {
|
|
81
|
+
if (c.skippedCd < at.andRunStart) {
|
|
82
|
+
const moved = this.movedFrom(c, at, ctx);
|
|
83
|
+
if (moved === "lost")
|
|
84
|
+
return this.lose(si, reachable, floorOf(s, reachable, ctx));
|
|
85
|
+
for (const m of moved)
|
|
86
|
+
branched.push({ ...m, skippedCd: c.skippedCd });
|
|
87
|
+
}
|
|
88
|
+
branched.push({ ...c, skippedCd: at.provable ? si : c.skippedCd });
|
|
89
|
+
}
|
|
90
|
+
const byKey = new Map();
|
|
91
|
+
for (const c of branched) {
|
|
92
|
+
const key = JSON.stringify([c.base, c.pwd, c.old, c.dirStack]);
|
|
93
|
+
const prior = byKey.get(key);
|
|
94
|
+
if (prior === undefined || c.skippedCd < prior.skippedCd)
|
|
95
|
+
byKey.set(key, c);
|
|
96
|
+
}
|
|
97
|
+
const next = [...byKey.values()];
|
|
98
|
+
if (next.length > BASE_CANDIDATE_MAX)
|
|
99
|
+
this.lose(si, reachable);
|
|
100
|
+
else
|
|
101
|
+
this.candidates = next;
|
|
102
|
+
}
|
|
103
|
+
movedFrom(c, at, ctx) {
|
|
104
|
+
const s = at.shift;
|
|
105
|
+
if (s.kind === "eval") {
|
|
106
|
+
const ran = ctx.evalEnd(s.text, c, at.table);
|
|
107
|
+
if (ran.physical)
|
|
108
|
+
this.physical = true;
|
|
109
|
+
return ran.end === "unmodelled" ? "lost" : ran.end.map((e) => (this.physical ? withoutPwd(e) : e));
|
|
110
|
+
}
|
|
111
|
+
const old = tableSays("PWD", at, ctx) ? {} : c.pwd !== undefined ? { old: c.pwd } : {};
|
|
112
|
+
const land = (base, physical, stack) => ({ base, skippedCd: c.skippedCd, ...(physical || this.physical || !this.readable ? {} : { pwd: base }), ...old, ...(stack !== undefined ? { dirStack: stack } : {}) });
|
|
113
|
+
if (s.kind === "swap" || s.kind === "pop") {
|
|
114
|
+
const top = c.dirStack?.[0];
|
|
115
|
+
if (top === undefined)
|
|
116
|
+
return "lost";
|
|
117
|
+
return [land(top, false, s.kind === "swap" ? [c.base, ...c.dirStack.slice(1)] : c.dirStack.slice(1))];
|
|
118
|
+
}
|
|
119
|
+
if (s.kind !== "cd")
|
|
120
|
+
return "lost";
|
|
121
|
+
const stack = s.push === true ? (c.dirStack === undefined ? undefined : [c.base, ...c.dirStack]) : c.dirStack;
|
|
122
|
+
const targets = targetsOf(s.target, c, at, ctx);
|
|
123
|
+
if (targets === "lost")
|
|
124
|
+
return "lost";
|
|
125
|
+
const out = [];
|
|
126
|
+
for (const t of targets) {
|
|
127
|
+
if ((s.physical || this.physical || c.pwd === undefined) && t.split("/").includes(".."))
|
|
128
|
+
return "lost";
|
|
129
|
+
const moved = resolveOperandLexically(c.base, t, ctx.homeDir);
|
|
130
|
+
if (moved === undefined)
|
|
131
|
+
return "lost";
|
|
132
|
+
out.push(land(moved, s.physical, stack));
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const withoutPwd = ({ pwd: _, ...c }) => c;
|
|
138
|
+
function floorOf(s, reachable, ctx) {
|
|
139
|
+
if (s.kind !== "cd")
|
|
140
|
+
return [];
|
|
141
|
+
if (s.target === "home")
|
|
142
|
+
return ctx.homeDir !== undefined ? [ctx.homeDir] : [];
|
|
143
|
+
if (typeof s.target === "string")
|
|
144
|
+
return [];
|
|
145
|
+
const target = s.target;
|
|
146
|
+
return [...(target.floorSpelled !== undefined ? [target.floorSpelled] : []), ...(target.expands === false ? reachable.flatMap((c) => resolveOperandLexically(c.base, target.spelled, ctx.homeDir) ?? []) : [])];
|
|
147
|
+
}
|
|
148
|
+
function targetsOf(target, c, at, ctx) {
|
|
149
|
+
if (target === "home")
|
|
150
|
+
return settledValues(at.table.stateOf("HOME"));
|
|
151
|
+
if (target === "previous")
|
|
152
|
+
return tableSays("OLDPWD", at, ctx) ? settledValues(at.table.stateOf("OLDPWD")) : c.old === undefined ? "lost" : [c.old];
|
|
153
|
+
if (target.expands !== false)
|
|
154
|
+
return "lost";
|
|
155
|
+
const dir = target.spelled;
|
|
156
|
+
if (!searchesCdPath(dir))
|
|
157
|
+
return [dir];
|
|
158
|
+
if (at.cdable && /^[A-Za-z_][A-Za-z0-9_]*$/.test(dir))
|
|
159
|
+
return "lost";
|
|
160
|
+
const cdPath = settledValues(at.table.stateOf("CDPATH"));
|
|
161
|
+
if (cdPath === "lost")
|
|
162
|
+
return "lost";
|
|
163
|
+
const entries = cdPath.flatMap((v) => (v === "" ? [] : v.split(":")));
|
|
164
|
+
return [...new Set([...entries.map((e) => (e === "" ? dir : `${e.replace(/\/+$/, "")}/${dir}`)), dir])];
|
|
165
|
+
}
|
|
166
|
+
export function searchesCdPath(dir) {
|
|
167
|
+
return dir !== "" && !isAbsoluteForFamily("own", dir) && !/^\.\.?(?:\/|$)/.test(dir) && !dir.startsWith("~");
|
|
168
|
+
}
|
|
169
|
+
function settledValues(state) {
|
|
170
|
+
if (state === "unknown" || state.some((v) => v.glob === true))
|
|
171
|
+
return "lost";
|
|
172
|
+
return state.map((v) => v.spelled);
|
|
173
|
+
}
|
|
174
|
+
export function baseShiftOf(program, args, targetOf) {
|
|
175
|
+
if (program === "cd") {
|
|
176
|
+
let physical = false;
|
|
177
|
+
let at = 0;
|
|
178
|
+
for (; at < args.length; at++) {
|
|
179
|
+
const w = args[at].folded;
|
|
180
|
+
if (w === "--") {
|
|
181
|
+
at++;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
if (!w.startsWith("-") || w === "-")
|
|
185
|
+
break;
|
|
186
|
+
for (const letter of w.slice(1)) {
|
|
187
|
+
if (letter === "P")
|
|
188
|
+
physical = true;
|
|
189
|
+
else if (letter === "L")
|
|
190
|
+
physical = false;
|
|
191
|
+
else if (letter !== "e" && letter !== "@")
|
|
192
|
+
return { shift: { kind: "lost" }, reported: "unmodelled" };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (at >= args.length)
|
|
196
|
+
return { shift: { kind: "cd", target: "home", physical }, reported: "home" };
|
|
197
|
+
if (args[at].folded === "-")
|
|
198
|
+
return { shift: { kind: "cd", target: "previous", physical }, reported: "previous" };
|
|
199
|
+
const target = targetOf(args[at]);
|
|
200
|
+
return { shift: { kind: "cd", target, physical }, reported: target };
|
|
201
|
+
}
|
|
202
|
+
if (shellWordsOf("stack_mover").has(program)) {
|
|
203
|
+
const words = args.map((a) => a.folded);
|
|
204
|
+
if (words.some((w) => w === "-n"))
|
|
205
|
+
return { shift: { kind: "stack" }, reported: "unmodelled" };
|
|
206
|
+
if (words.some((w) => /^[+-]\d+$/.test(w)) || words.some((w) => w.startsWith("-") && w !== "--"))
|
|
207
|
+
return { shift: { kind: "lost" }, reported: "unmodelled" };
|
|
208
|
+
const operand = args.find((a) => a.folded !== "--");
|
|
209
|
+
if (program === "popd")
|
|
210
|
+
return { shift: operand === undefined ? { kind: "pop" } : { kind: "lost" }, reported: "unmodelled" };
|
|
211
|
+
if (operand === undefined)
|
|
212
|
+
return { shift: { kind: "swap" }, reported: "unmodelled" };
|
|
213
|
+
return { shift: { kind: "cd", target: targetOf(operand), physical: false, push: true }, reported: targetOf(operand) };
|
|
214
|
+
}
|
|
215
|
+
if (shellWordsOf("stack_editor").has(program) && args.some((a) => a.folded.startsWith("-") || a.folded.startsWith("+")))
|
|
216
|
+
return { shift: { kind: "stack" } };
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
export function textMayMove(text, functions) {
|
|
220
|
+
const lexed = lexShell(text);
|
|
221
|
+
if ("parseFailure" in lexed)
|
|
222
|
+
return true;
|
|
223
|
+
return lexed.segments.some((s) => segmentMayMove(argvOf(s), functions));
|
|
224
|
+
}
|
|
225
|
+
const programOf = (argv) => {
|
|
226
|
+
const pos = readProgramPosition(argv);
|
|
227
|
+
return pos.program === undefined ? undefined : { name: pos.program.basename, args: argv.slice(pos.program.index + 1).map((w) => w.folded) };
|
|
228
|
+
};
|
|
229
|
+
const setsPhysical = (p) => p?.name === "set" && p.args.some((a, k) => /^-[A-Za-z]*P/.test(a) || (a === "-o" && p.args[k + 1] === "physical"));
|
|
230
|
+
export function textSetsPhysical(text) {
|
|
231
|
+
const lexed = lexShell(text);
|
|
232
|
+
return !("parseFailure" in lexed) && lexed.segments.some((s) => setsPhysical(programOf(argvOf(s))));
|
|
233
|
+
}
|
|
234
|
+
export function withCommandEnvironment(table, argv) {
|
|
235
|
+
const overlay = new Map();
|
|
236
|
+
const overlaid = () => ({ ...table, stateOf: (name) => overlay.get(name) ?? table.stateOf(name) });
|
|
237
|
+
for (const k of readProgramPosition(argv).assignments) {
|
|
238
|
+
const a = argv[k].assignment;
|
|
239
|
+
if (a === undefined)
|
|
240
|
+
continue;
|
|
241
|
+
if (a.op !== "=" || a.subscript !== undefined || a.form !== "scalar" || a.value.unresolved !== undefined) {
|
|
242
|
+
overlay.set(a.name, "unknown");
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const before = valueOf({ pieces: a.value.pieces }, table, true);
|
|
246
|
+
const ordered = valueOf({ pieces: a.value.pieces }, overlaid(), true);
|
|
247
|
+
overlay.set(a.name, before === "unknown" || ordered === "unknown" ? "unknown" : [...before, ...ordered.filter((v) => !before.some((b) => b.spelled === v.spelled))]);
|
|
248
|
+
}
|
|
249
|
+
return { ...overlaid(), commandSets: new Set(overlay.keys()) };
|
|
250
|
+
}
|
|
251
|
+
export function textCalls(text, functions) {
|
|
252
|
+
const lexed = lexShell(text);
|
|
253
|
+
if ("parseFailure" in lexed)
|
|
254
|
+
return true;
|
|
255
|
+
return lexed.segments.some((s) => functionCallOf(argvOf(s), functions) !== undefined);
|
|
256
|
+
}
|
|
257
|
+
export function segmentMayMove(argv, functions) {
|
|
258
|
+
const pos = readProgramPosition(argv);
|
|
259
|
+
if (pos.program === undefined)
|
|
260
|
+
return false;
|
|
261
|
+
const word = argv[pos.program.index];
|
|
262
|
+
return shellWordsOf("base_mover").has(pos.program.basename) || TEXT_RUNNING_PROGRAMS.has(pos.program.basename) || functionCallOf(argv, functions) !== undefined || word.pieces.some((p) => p.expansion !== undefined || p.literal === undefined);
|
|
263
|
+
}
|
|
264
|
+
export function textMovesOf(defined, argvs, inFunctionBody, inherited) {
|
|
265
|
+
const unread = new Set([...evalDefinedFunctions(argvs), ...(inherited?.unread ?? [])]);
|
|
266
|
+
const functions = new Set([...defined, ...unread, ...(inherited?.functions ?? [])]);
|
|
267
|
+
const programs = argvs.map(programOf);
|
|
268
|
+
const mayMove = argvs.map((argv) => segmentMayMove(argv, functions));
|
|
269
|
+
const bodyMoves = mayMove.some((m, si) => m && inFunctionBody(si));
|
|
270
|
+
const bodiesAt = programs.map((p) => (p === undefined ? [] : codeBodies(p.name, p.args)));
|
|
271
|
+
const mayCallAt = argvs.map((argv) => functionCallOf(argv, functions) !== undefined);
|
|
272
|
+
const runsTextAt = argvs.map((argv) => { const pos = readProgramPosition(argv); const word = pos.program === undefined ? undefined : argv[pos.program.index]; return word !== undefined && (["eval", "source", "."].includes(pos.program.basename) || word.pieces.some((p) => p.expansion !== undefined || p.literal === undefined)); });
|
|
273
|
+
const callAt = (si, table) => functionCallOf(argvs[si], functions, (name) => table.functionState(name));
|
|
274
|
+
const callsUnread = (si, table) => functionCallOf(argvs[si], unread, (name) => table.functionState(name)) !== undefined;
|
|
275
|
+
return {
|
|
276
|
+
functions, unread, mayMove, mayCallAt, runsBodyAt: mayCallAt.map((c, si) => c || runsTextAt[si] || bodiesAt[si].length > 0), bodiesAt, bodyMoves, callAt, callsUnread,
|
|
277
|
+
physicalAt: programs.map(setsPhysical),
|
|
278
|
+
cdableFrom: programs.findIndex((p) => p?.name === "shopt" && p.args.includes("-s") && p.args.includes("cdable_vars")),
|
|
279
|
+
losesBaseAt: (si, table) => (callAt(si, table) !== undefined && bodyMoves) || callsUnread(si, table) || bodiesAt[si].some((b) => bodyMoves || textMayMove(b, functions)),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* writes, the operands an option made edited in place, a copy's destination and its children, an in-place edit's
|
|
6
6
|
* backups), at the seats the role and the selected options name ({@link WriteSeat}: the directory entry, or where the
|
|
7
7
|
* path lands). The establisher (`shell-read-establish.ts`) places what this module names; the judge reads the result
|
|
8
|
-
* on the deny
|
|
8
|
+
* on the write face (the write deny rows, then the write roots). No table of programs or options lives here: what a
|
|
9
|
+
* program writes is its row's.
|
|
9
10
|
*/
|
|
10
11
|
import type { ReadCandidate, ShellReadSegment } from "./shell-read-plan.js";
|
|
11
12
|
import { type WriteSeat } from "./program-read-model.js";
|
|
@@ -47,5 +48,7 @@ export interface JudgedCandidate {
|
|
|
47
48
|
}
|
|
48
49
|
/** Every candidate a segment hands the judge: its reads minus the write targets, plus its `cd` target (the plan already
|
|
49
50
|
* lists it among the reads), plus its WRITE targets for the write face ({@link writeTargetsOf}), plus the deny-floor
|
|
50
|
-
* shadow of every unplaced home word ({@link denyFloorShadowsOf}) on both faces.
|
|
51
|
+
* shadow of every unplaced home word ({@link denyFloorShadowsOf}) on both faces. A word the program only writes keeps
|
|
52
|
+
* its read copy for the read deny rows alone (`denyOnly`): whether it lands inside the write roots — or cannot be placed
|
|
53
|
+
* at all — is the write face's question, never a read's. */
|
|
51
54
|
export declare function judgedCandidatesOf(segment: ShellReadSegment): readonly JudgedCandidate[];
|
|
@@ -29,6 +29,7 @@ function writeOwnersOf(segment) {
|
|
|
29
29
|
}
|
|
30
30
|
function writeTargetsOf(segment) {
|
|
31
31
|
const out = [];
|
|
32
|
+
const writeOnly = new Set();
|
|
32
33
|
const write = (c, seats, written, child, onlyIfExists) => void out.push({ candidate: { ...c, face: "write" }, ...(seats !== undefined ? { seats } : {}), ...(written !== undefined ? { written } : {}), ...(child === true ? { child } : {}), ...(onlyIfExists !== undefined ? { onlyIfExists } : {}) });
|
|
33
34
|
for (const r of segment.redirects)
|
|
34
35
|
if (r.direction === "out" && r.target !== undefined)
|
|
@@ -48,9 +49,12 @@ function writeTargetsOf(segment) {
|
|
|
48
49
|
continue;
|
|
49
50
|
const placed = owners.filter((o) => o.words.some((w) => w.raw === c.raw));
|
|
50
51
|
const fallback = placed.length === 0 ? owners.filter((o) => o.program) : [];
|
|
52
|
+
let only = placed.length > 0;
|
|
51
53
|
for (const o of [...placed, ...fallback]) {
|
|
52
|
-
if (o.readings.length === 0)
|
|
54
|
+
if (o.readings.length === 0) {
|
|
55
|
+
only = false;
|
|
53
56
|
continue;
|
|
57
|
+
}
|
|
54
58
|
const at = new Set(o.words.flatMap((w, i) => (w.raw === c.raw ? [i] : [])));
|
|
55
59
|
const roles = [];
|
|
56
60
|
let readings = 0;
|
|
@@ -64,8 +68,11 @@ function writeTargetsOf(segment) {
|
|
|
64
68
|
readings += 1;
|
|
65
69
|
unexplained ||= unplaced;
|
|
66
70
|
}
|
|
67
|
-
if (roles.length === 0)
|
|
71
|
+
if (roles.length === 0) {
|
|
72
|
+
only = false;
|
|
68
73
|
continue;
|
|
74
|
+
}
|
|
75
|
+
only &&= !unexplained && readings === o.all.length && roles.every((role) => role.effect !== "edit-in-place" && role.writtenAt === undefined);
|
|
69
76
|
const stated = roles.some((role) => (o.override ?? role.seats) !== undefined);
|
|
70
77
|
const seats = stated ? [...new Set(roles.flatMap((role) => o.override ?? role.seats ?? ["landing"]))] : undefined;
|
|
71
78
|
const written = unexplained || roles.some((role) => role.writtenAt === undefined) ? undefined : [...new Set(roles.flatMap((role) => role.writtenAt ?? []))];
|
|
@@ -74,13 +81,15 @@ function writeTargetsOf(segment) {
|
|
|
74
81
|
if (roles.some((role) => role.effect === "edit-in-place") && c.expands === false)
|
|
75
82
|
edited.set(o, [...(edited.get(o) ?? []), { candidate: c, ...(speculative !== undefined ? { onlyIfExists: speculative } : {}) }]);
|
|
76
83
|
}
|
|
84
|
+
if (only)
|
|
85
|
+
writeOnly.add(c);
|
|
77
86
|
}
|
|
78
87
|
for (const o of owners) {
|
|
79
88
|
for (const c of copyDestinationChildren(segment, o.words, o.readings))
|
|
80
89
|
write(c, ["landing", "entry"], undefined, true);
|
|
81
90
|
out.push(...backupTargetsOf(edited.get(o) ?? [], o.readings, o.override));
|
|
82
91
|
}
|
|
83
|
-
return out;
|
|
92
|
+
return { targets: out, writeOnly };
|
|
84
93
|
}
|
|
85
94
|
function backupTargetsOf(edited, readings, override) {
|
|
86
95
|
const suffixes = [...new Set(readings.flatMap((r) => r.suffixes))];
|
|
@@ -114,8 +123,8 @@ function copyDestinationChildren(segment, words, readings) {
|
|
|
114
123
|
return [...children.values()];
|
|
115
124
|
}
|
|
116
125
|
export function judgedCandidatesOf(segment) {
|
|
117
|
-
const
|
|
118
|
-
const
|
|
126
|
+
const { targets: writes, writeOnly } = writeTargetsOf(segment);
|
|
127
|
+
const reads = segment.reads.flatMap((c) => (c.provenance === "write_redirect_target" ? [] : [writeOnly.has(c) ? { ...c, denyOnly: true } : c]));
|
|
119
128
|
const plain = (cs) => cs.map((candidate) => ({ candidate }));
|
|
120
129
|
return [...plain(reads), ...plain(denyFloorShadowsOf(reads)), ...writes, ...plain(denyFloorShadowsOf(writes.map((w) => w.candidate)))];
|
|
121
130
|
}
|