@sema-agent/core 7.54.1 → 7.56.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 +950 -0
- package/dist/agents/agent-transcript-tool.js +2 -1
- package/dist/agents/builtin-agents.d.ts +12 -14
- package/dist/agents/cascade.js +1 -1
- package/dist/agents/cc-peer-directory.d.ts +36 -0
- package/dist/agents/cc-peer-directory.js +139 -0
- package/dist/agents/cc-peer-wire.d.ts +105 -0
- package/dist/agents/cc-peer-wire.js +98 -0
- package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
- package/dist/agents/cc-uds-peer-channel.js +457 -0
- package/dist/agents/child-model-seat.d.ts +0 -11
- package/dist/agents/child-model-seat.js +0 -3
- package/dist/agents/cross-session-envelope.d.ts +4 -1
- package/dist/agents/cross-session-envelope.js +8 -1
- package/dist/agents/cross-session-judge.d.ts +2 -2
- package/dist/agents/list-agents-tool.d.ts +3 -1
- package/dist/agents/list-agents-tool.js +9 -8
- package/dist/agents/observer.d.ts +4 -5
- package/dist/agents/observer.js +2 -1
- package/dist/agents/peer-admission.d.ts +6 -0
- package/dist/agents/peer-admission.js +4 -1
- package/dist/agents/peer-directory.d.ts +65 -13
- package/dist/agents/peer-directory.js +90 -25
- package/dist/agents/peer-notices.d.ts +1 -1
- package/dist/agents/peer-session-drain.d.ts +3 -1
- package/dist/agents/peer-session-drain.js +3 -2
- package/dist/agents/read-inbox-tool.js +2 -1
- package/dist/agents/repair-loop.js +2 -2
- package/dist/agents/retain-ledger.d.ts +1 -1
- package/dist/agents/roster-store.d.ts +1 -1
- package/dist/agents/send-message-tool.d.ts +10 -0
- package/dist/agents/send-message-tool.js +37 -8
- package/dist/agents/subagent.d.ts +39 -82
- package/dist/agents/subagent.js +109 -106
- package/dist/agents/tool-filter.d.ts +31 -46
- package/dist/agents/tool-filter.js +13 -33
- package/dist/agents/verify.d.ts +6 -5
- package/dist/agents/verify.js +3 -3
- package/dist/brain/anthropic.js +43 -81
- package/dist/brain/errors.d.ts +15 -0
- package/dist/brain/errors.js +10 -0
- package/dist/brain/media-degrade.d.ts +18 -7
- package/dist/brain/media-degrade.js +13 -2
- package/dist/brain/model-capability.d.ts +37 -0
- package/dist/brain/model-capability.js +34 -0
- package/dist/brain/model-presets.d.ts +6 -12
- package/dist/brain/model-presets.js +1 -6
- package/dist/brain/open-responses.js +27 -35
- package/dist/brain/openai.js +30 -98
- package/dist/brain/provider-dialect.d.ts +39 -3
- package/dist/brain/provider-dialect.js +28 -0
- package/dist/brain/reasoning-request.d.ts +96 -0
- package/dist/brain/reasoning-request.js +174 -0
- package/dist/brain/reasoning.d.ts +12 -12
- package/dist/brain/reasoning.js +4 -3
- package/dist/brain/request-params.d.ts +62 -9
- package/dist/brain/request-params.js +42 -7
- package/dist/brain/retry.d.ts +1 -1
- package/dist/brain/route-adjudicator.d.ts +15 -0
- package/dist/brain/route-adjudicator.js +15 -0
- package/dist/brain/status-sink.d.ts +20 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/brain/stream-engine.js +6 -5
- package/dist/brain/stream-shared.d.ts +1 -1
- package/dist/core/a2a.d.ts +2 -1
- package/dist/core/a2a.js +19 -13
- package/dist/core/advisor-verdict.d.ts +120 -0
- package/dist/core/advisor-verdict.js +92 -0
- package/dist/core/agent-definition.d.ts +17 -8
- package/dist/core/allow-entry.d.ts +69 -36
- package/dist/core/allow-entry.js +81 -52
- package/dist/core/allow-rule-shadow.d.ts +10 -3
- package/dist/core/approval-object.d.ts +2 -1
- package/dist/core/approval-object.js +6 -6
- package/dist/core/ask-origin.d.ts +127 -29
- package/dist/core/ask-origin.js +87 -25
- package/dist/core/ask-question.d.ts +51 -8
- package/dist/core/ask-question.js +12 -7
- package/dist/core/ask-unresolvable-notice.d.ts +8 -3
- package/dist/core/ask-unresolvable-notice.js +16 -2
- package/dist/core/auto-compaction.d.ts +11 -8
- package/dist/core/auto-compaction.js +4 -2
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/auto-mode-defaults.d.ts +8 -8
- package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
- package/dist/core/auto-mode-prompt.d.ts +1 -1
- package/dist/core/auto-mode.d.ts +17 -9
- package/dist/core/auto-mode.js +9 -6
- package/dist/core/background-agent-store.d.ts +3 -3
- package/dist/core/canonical-json.d.ts +1 -1
- package/dist/core/canonical-json.js +2 -2
- package/dist/core/checkpoint-generation.d.ts +53 -0
- package/dist/core/checkpoint-generation.js +35 -0
- package/dist/core/checkpoint-queries.d.ts +180 -0
- package/dist/core/checkpoint-queries.js +272 -0
- package/dist/core/checkpoint-requirements.d.ts +19 -4
- package/dist/core/checkpoint-requirements.js +8 -14
- package/dist/core/checkpoint-store.d.ts +165 -72
- package/dist/core/checkpoint-store.js +79 -82
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +3 -16
- package/dist/core/compliance.d.ts +2 -2
- package/dist/core/context-edit.d.ts +6 -7
- package/dist/core/cross-boundary-taint.d.ts +11 -4
- package/dist/core/cross-boundary-taint.js +3 -3
- package/dist/core/decision-receipt.d.ts +37 -3
- package/dist/core/decision-receipt.js +19 -19
- package/dist/core/delegation-not-started.d.ts +6 -0
- package/dist/core/delegation-not-started.js +17 -0
- package/dist/core/effective-path-target.d.ts +1 -1
- package/dist/core/engine-attestation.d.ts +93 -0
- package/dist/core/engine-attestation.js +70 -0
- package/dist/core/engine-capabilities.d.ts +4 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +45 -12
- package/dist/core/exec-output-tail.d.ts +1 -1
- package/dist/core/file-history-retention.d.ts +1 -1
- package/dist/core/file-history-store.d.ts +147 -21
- package/dist/core/file-history-store.js +58 -32
- package/dist/core/gate-fold.d.ts +1 -1
- package/dist/core/gate-fold.js +24 -11
- package/dist/core/gate-lanes.js +81 -68
- package/dist/core/gate-outcome.d.ts +40 -6
- package/dist/core/gate-outcome.js +56 -0
- package/dist/core/governance-codes.d.ts +17 -3
- package/dist/core/governance-codes.js +16 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +21 -20
- package/dist/core/hooks.js +3 -1
- package/dist/core/host-floor.d.ts +128 -0
- package/dist/core/host-floor.js +206 -0
- package/dist/core/host-target.d.ts +84 -0
- package/dist/core/host-target.js +92 -0
- package/dist/core/image-downsample.d.ts +1 -1
- package/dist/core/lsp-diagnostics.d.ts +4 -4
- package/dist/core/lsp.d.ts +3 -3
- package/dist/core/mailbox-store.d.ts +4 -0
- package/dist/core/mcp-connection-pool.d.ts +18 -2
- package/dist/core/mcp-connection-pool.js +53 -6
- package/dist/core/mcp-failure.d.ts +23 -4
- package/dist/core/mcp-failure.js +26 -11
- package/dist/core/mcp-listing-drift.d.ts +10 -0
- package/dist/core/mcp-listing-drift.js +6 -0
- package/dist/core/mcp-server-link.d.ts +173 -0
- package/dist/core/mcp-server-link.js +127 -0
- package/dist/core/mcp-server-spec.d.ts +4 -2
- package/dist/core/mcp-session-end.d.ts +26 -0
- package/dist/core/mcp-session-end.js +29 -0
- package/dist/core/mcp-startup-request.d.ts +17 -0
- package/dist/core/mcp-startup-request.js +17 -0
- package/dist/core/mcp.d.ts +17 -13
- package/dist/core/mcp.js +240 -189
- package/dist/core/media-byte-cap.d.ts +1 -1
- package/dist/core/memory-engine/delegation-settlement.js +15 -24
- package/dist/core/memory-engine/engine.d.ts +9 -9
- package/dist/core/memory-engine/engine.js +4 -4
- package/dist/core/memory-engine/layout.d.ts +60 -8
- package/dist/core/memory-engine/layout.js +65 -6
- package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
- package/dist/core/memory-engine/tools.js +4 -1
- package/dist/core/mode-policy.d.ts +1 -1
- package/dist/core/model-seat.d.ts +10 -3
- package/dist/core/mods/classic-events.d.ts +67 -0
- package/dist/core/mods/classic-events.js +1 -0
- package/dist/core/mods/dollar.d.ts +13 -0
- package/dist/core/mods/extension-call.d.ts +31 -0
- package/dist/core/mods/extension-call.js +43 -0
- package/dist/core/mods/kernel/dispatch.d.ts +20 -3
- package/dist/core/mods/kernel/dispatch.js +140 -69
- package/dist/core/mods/kernel/events.d.ts +47 -11
- package/dist/core/mods/kernel/events.js +66 -42
- package/dist/core/mods/kernel/index.d.ts +4 -4
- package/dist/core/mods/kernel/index.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +39 -5
- package/dist/core/mods/kernel/receive.js +290 -17
- package/dist/core/mods/kernel/registration.d.ts +33 -2
- package/dist/core/mods/kernel/registration.js +6 -7
- package/dist/core/mods/kernel/site-rules.d.ts +44 -16
- package/dist/core/mods/kernel/site-rules.js +24 -42
- package/dist/core/mods/spec-seats.d.ts +69 -0
- package/dist/core/mods/spec-seats.js +114 -0
- package/dist/core/mods/stations.d.ts +28 -8
- package/dist/core/mods/stations.js +29 -13
- package/dist/core/mods/tool-call.d.ts +79 -0
- package/dist/core/mods/tool-call.js +152 -0
- package/dist/core/namespaced-rule-name.d.ts +44 -24
- package/dist/core/namespaced-rule-name.js +50 -43
- package/dist/core/output-style.d.ts +1 -1
- package/dist/core/param-rule-reach.d.ts +15 -0
- package/dist/core/param-rule-reach.js +30 -0
- package/dist/core/parent-spec-seat.d.ts +13 -1
- package/dist/core/parent-spec-seat.js +6 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/pause-registry.d.ts +16 -5
- package/dist/core/pause-registry.js +6 -2
- package/dist/core/peer-channel.d.ts +70 -0
- package/dist/core/peer-channel.js +1 -0
- package/dist/core/permission-rule-consent.d.ts +30 -5
- package/dist/core/permission-rule-consent.js +147 -50
- package/dist/core/permission-rule-model.d.ts +245 -65
- package/dist/core/permission-rule-model.js +421 -107
- package/dist/core/permission-rule-org.d.ts +6 -2
- package/dist/core/permission-rule-org.js +7 -3
- package/dist/core/permission-rule-provider.d.ts +6 -2
- package/dist/core/permission-rule-provider.js +41 -11
- package/dist/core/permission-rule-store.d.ts +22 -26
- package/dist/core/permission-rule-store.js +12 -6
- package/dist/core/permission-rule-sync.d.ts +11 -2
- package/dist/core/permission-rule-sync.js +11 -3
- package/dist/core/permission-rule-syntax.d.ts +5 -4
- package/dist/core/permission-rule-syntax.js +2 -2
- package/dist/core/permission-rules.d.ts +53 -15
- package/dist/core/permission-rules.js +125 -100
- package/dist/core/persisted-rule-arms.d.ts +6 -4
- package/dist/core/persisted-rule-arms.js +10 -12
- package/dist/core/present-plan-tool.js +3 -1
- package/dist/core/principal-name.d.ts +20 -0
- package/dist/core/principal-name.js +22 -0
- package/dist/core/prompt-section-seat.js +4 -6
- package/dist/core/protocol-http-auth.d.ts +26 -5
- package/dist/core/protocol-http-auth.js +137 -4
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/read-only-shell-table.d.ts +1 -1
- package/dist/core/read-only-shell.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +16 -0
- package/dist/core/retired-keys.js +16 -4
- package/dist/core/rewrite-rejudge.d.ts +60 -0
- package/dist/core/rewrite-rejudge.js +36 -0
- package/dist/core/roles.d.ts +34 -3
- package/dist/core/roles.js +66 -53
- package/dist/core/rule-lane-reads.d.ts +20 -15
- package/dist/core/rule-lane-reads.js +45 -17
- package/dist/core/rule-offer-read.d.ts +53 -0
- package/dist/core/rule-offer-read.js +146 -0
- package/dist/core/rule-owner.d.ts +46 -0
- package/dist/core/rule-owner.js +29 -0
- package/dist/core/runner/advisor-instructions.d.ts +70 -0
- package/dist/core/runner/advisor-instructions.js +61 -0
- package/dist/core/runner/advisor-tool.d.ts +33 -0
- package/dist/core/runner/advisor-tool.js +112 -0
- package/dist/core/runner/announce-once-ledger.d.ts +18 -26
- package/dist/core/runner/announce-once-ledger.js +19 -46
- package/dist/core/runner/approval-feedback.js +6 -3
- package/dist/core/runner/assemble-result.d.ts +2 -1
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/call-hook-stations.d.ts +3 -2
- package/dist/core/runner/caller-roster.d.ts +21 -2
- package/dist/core/runner/caller-roster.js +38 -0
- package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
- package/dist/core/runner/capability-withheld-seat.js +24 -0
- package/dist/core/runner/classifier-ladder.d.ts +33 -13
- package/dist/core/runner/classifier-ladder.js +15 -14
- package/dist/core/runner/compaction-restate.d.ts +10 -6
- package/dist/core/runner/compaction-restate.js +9 -3
- package/dist/core/runner/construction-doors.js +3 -0
- package/dist/core/runner/contracts.d.ts +40 -33
- package/dist/core/runner/conversation-segment.d.ts +28 -0
- package/dist/core/runner/conversation-segment.js +42 -0
- package/dist/core/runner/cross-boundary-lane.d.ts +21 -16
- package/dist/core/runner/cross-boundary-lane.js +58 -34
- package/dist/core/runner/decide-continuation.d.ts +7 -4
- package/dist/core/runner/decide-continuation.js +2 -2
- package/dist/core/runner/denial-limit-arms.d.ts +4 -4
- package/dist/core/runner/denial-limit-arms.js +1 -1
- package/dist/core/runner/derived-route-fallback.d.ts +24 -24
- package/dist/core/runner/derived-route-fallback.js +9 -7
- package/dist/core/runner/file-history-request.d.ts +18 -0
- package/dist/core/runner/file-history-request.js +21 -13
- package/dist/core/runner/file-history-restore.d.ts +45 -0
- package/dist/core/runner/file-history-restore.js +46 -0
- package/dist/core/runner/gate-exit.d.ts +9 -3
- package/dist/core/runner/gate-exit.js +11 -5
- package/dist/core/runner/image.d.ts +7 -17
- package/dist/core/runner/image.js +9 -60
- package/dist/core/runner/initial-run-state.js +1 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +6 -6
- package/dist/core/runner/permission-rule-lanes.js +38 -15
- package/dist/core/runner/person-path-rows.d.ts +12 -9
- package/dist/core/runner/person-path-rows.js +19 -8
- package/dist/core/runner/prepare-advisor.d.ts +63 -0
- package/dist/core/runner/prepare-advisor.js +77 -0
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
- package/dist/core/runner/prepare-ask-lane.js +5 -2
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
- package/dist/core/runner/prepare-caps-and-workflow.js +5 -4
- package/dist/core/runner/prepare-config-doors.d.ts +31 -38
- package/dist/core/runner/prepare-config-doors.js +57 -34
- package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
- package/dist/core/runner/prepare-defer-classify.js +7 -8
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +1 -1
- package/dist/core/runner/prepare-file-history.d.ts +48 -8
- package/dist/core/runner/prepare-file-history.js +41 -73
- package/dist/core/runner/prepare-gate-stations.d.ts +2 -5
- package/dist/core/runner/prepare-gate-stations.js +12 -8
- package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
- package/dist/core/runner/prepare-hands-readface.js +15 -14
- package/dist/core/runner/prepare-inherited-gate.d.ts +11 -7
- package/dist/core/runner/prepare-inherited-gate.js +8 -6
- package/dist/core/runner/prepare-memory-engine-session.js +1 -1
- package/dist/core/runner/prepare-park-ask.js +12 -12
- package/dist/core/runner/prepare-policy-chain.d.ts +7 -4
- package/dist/core/runner/prepare-policy-chain.js +68 -81
- package/dist/core/runner/prepare-project-context.d.ts +5 -2
- package/dist/core/runner/prepare-project-context.js +6 -3
- package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
- package/dist/core/runner/prepare-prompt-assembly.js +10 -8
- package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
- package/dist/core/runner/prepare-prompt-inputs.js +4 -6
- package/dist/core/runner/prepare-protocol-tools.d.ts +4 -2
- package/dist/core/runner/prepare-protocol-tools.js +5 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
- package/dist/core/runner/prepare-safety-scan.js +8 -23
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
- package/dist/core/runner/prepare-suspend-saga.js +5 -6
- package/dist/core/runner/prepare-task.d.ts +1 -1
- package/dist/core/runner/prepare-task.js +41 -35
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
- package/dist/core/runner/prepare-tool-disclosure-mount.js +24 -18
- package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
- package/dist/core/runner/prepare-turn-wiring.js +2 -7
- package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
- package/dist/core/runner/prepare-wiring-manifest.js +3 -3
- package/dist/core/runner/resume-admission.js +1 -1
- package/dist/core/runner/resume-apply.js +11 -12
- package/dist/core/runner/resume-checkpoint-screen.js +3 -0
- package/dist/core/runner/resume-claim.d.ts +4 -2
- package/dist/core/runner/resume-claim.js +15 -8
- package/dist/core/runner/resume-policy-outcome.js +1 -1
- package/dist/core/runner/retired-runner-deps.js +2 -1
- package/dist/core/runner/rewind-files.d.ts +15 -0
- package/dist/core/runner/rewind-files.js +132 -0
- package/dist/core/runner/run-attachment-seats.js +2 -2
- package/dist/core/runner/run-brain-sinks.d.ts +4 -1
- package/dist/core/runner/run-brain-sinks.js +4 -2
- package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
- package/dist/core/runner/run-compaction-machinery.js +2 -12
- package/dist/core/runner/run-git-lane.d.ts +1 -0
- package/dist/core/runner/run-harness-handlers.js +14 -7
- package/dist/core/runner/run-leg.d.ts +1 -1
- package/dist/core/runner/run-leg.js +43 -18
- package/dist/core/runner/run-settle-and-teardown.js +3 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.d.ts +1 -0
- package/dist/core/runner/run-turn-boundary.js +15 -22
- package/dist/core/runner/runtask.d.ts +3 -0
- package/dist/core/runner/runtask.js +8 -9
- package/dist/core/runner/session-read-file-states.d.ts +15 -0
- package/dist/core/runner/session-read-file-states.js +28 -0
- package/dist/core/runner/session-rule-policy.d.ts +22 -21
- package/dist/core/runner/session-rule-policy.js +17 -31
- package/dist/core/runner/shell-env-withheld.d.ts +33 -0
- package/dist/core/runner/shell-env-withheld.js +51 -0
- package/dist/core/runner/side-call-account.d.ts +32 -8
- package/dist/core/runner/side-call-account.js +23 -1
- package/dist/core/runner/skills-seat.js +6 -2
- package/dist/core/runner/stream-settle-backstop.js +1 -1
- package/dist/core/runner/strict-output-schema.d.ts +1 -1
- package/dist/core/runner/sub-agent-deny-lists.js +4 -6
- package/dist/core/runner/synthetic-tools.d.ts +16 -13
- package/dist/core/runner/synthetic-tools.js +8 -3
- package/dist/core/runner/tool-disclosure.d.ts +10 -12
- package/dist/core/runner/tool-disclosure.js +3 -4
- package/dist/core/runner/tool-end-body.d.ts +1 -1
- package/dist/core/runner/tool-end-body.js +10 -9
- package/dist/core/runner/tool-face-overlay.d.ts +2 -1
- package/dist/core/runner/tool-face-overlay.js +17 -1
- package/dist/core/runner/tool-not-executed.d.ts +5 -0
- package/dist/core/runner/tool-not-executed.js +11 -0
- package/dist/core/runner/tool-occurrence.d.ts +8 -0
- package/dist/core/runner/tool-occurrence.js +7 -0
- package/dist/core/runner/tool-progress-lane.d.ts +2 -2
- package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
- package/dist/core/runner/turn-attachments.d.ts +91 -80
- package/dist/core/runner/turn-attachments.js +62 -37
- package/dist/core/runner-deps.d.ts +42 -42
- package/dist/core/runtime-caps.d.ts +4 -4
- package/dist/core/safety-axis-vocab.d.ts +6 -4
- package/dist/core/safety-axis-vocab.js +6 -5
- package/dist/core/safety-merge-corpus.js +26 -2
- package/dist/core/same-plain-value.d.ts +40 -0
- package/dist/core/same-plain-value.js +49 -0
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secret-env.d.ts +26 -3
- package/dist/core/secret-env.js +44 -8
- package/dist/core/select-model.js +3 -3
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +1 -1
- package/dist/core/session-policy-store.d.ts +102 -35
- package/dist/core/session-policy-store.js +210 -48
- package/dist/core/session-reconcile.d.ts +2 -2
- package/dist/core/session-store.d.ts +106 -37
- package/dist/core/session-store.js +165 -91
- package/dist/core/session.d.ts +101 -57
- package/dist/core/session.js +53 -0
- package/dist/core/shared-memory/tools.js +3 -1
- package/dist/core/shared-memory/types.d.ts +1 -1
- package/dist/core/shell-read-silence.d.ts +11 -12
- package/dist/core/shell-read-silence.js +9 -5
- package/dist/core/shell-run-candidates.js +3 -1
- package/dist/core/shell-wrapper-projection.js +1 -1
- package/dist/core/shell-wrapper-table.d.ts +14 -7
- package/dist/core/shell-wrapper-table.js +5 -4
- package/dist/core/side-query.d.ts +10 -0
- package/dist/core/side-query.js +15 -1
- package/dist/core/skill-grant.d.ts +7 -2
- package/dist/core/skill-grant.js +5 -0
- package/dist/core/skills-directory.js +6 -2
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +168 -3
- package/dist/core/store-contracts/file-history-store-contract.js +74 -4
- package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
- package/dist/core/store-contracts/peer-directory-contract.js +14 -2
- package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
- package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
- package/dist/core/store-contracts/session-repo-contract.js +176 -0
- package/dist/core/store-contracts/session-store-contract.d.ts +6 -8
- package/dist/core/store-contracts/session-store-contract.js +361 -51
- package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
- package/dist/core/stub-env.d.ts +2 -0
- package/dist/core/stub-env.js +1 -0
- package/dist/core/surrogate-safe-slice.d.ts +3 -3
- package/dist/core/synthetic-read-verdict.js +3 -3
- package/dist/core/task-event.d.ts +42 -8
- package/dist/core/task-notification.d.ts +19 -15
- package/dist/core/task-registry-agent.d.ts +4 -4
- package/dist/core/task-registry-shared.d.ts +4 -4
- package/dist/core/task-registry-shared.js +1 -0
- package/dist/core/task-registry.d.ts +13 -4
- package/dist/core/task-registry.js +14 -2
- package/dist/core/task-result.d.ts +21 -3
- package/dist/core/task-spec.d.ts +69 -64
- package/dist/core/task-stream.d.ts +6 -6
- package/dist/core/task-tool-shape.d.ts +4 -4
- package/dist/core/tighten-task-spec.d.ts +1 -1
- package/dist/core/tighten-task-spec.js +2 -1
- package/dist/core/tool-catalog-entries.d.ts +36 -2
- package/dist/core/tool-catalog-entries.js +69 -43
- package/dist/core/tool-check-reading.d.ts +11 -0
- package/dist/core/tool-check-reading.js +11 -6
- package/dist/core/tool-conformance.js +1 -0
- package/dist/core/tool-errors.d.ts +2 -2
- package/dist/core/tool-face-reader.js +5 -3
- package/dist/core/tool-face.d.ts +21 -2
- package/dist/core/tool-face.js +1 -1
- package/dist/core/tool-model-gate.d.ts +5 -5
- package/dist/core/tool-name-aliases.d.ts +29 -8
- package/dist/core/tool-name-aliases.js +22 -4
- package/dist/core/tool-name-readings.d.ts +10 -43
- package/dist/core/tool-name-readings.js +2 -11
- package/dist/core/tool-policy.d.ts +85 -196
- package/dist/core/tool-policy.js +150 -175
- package/dist/core/tool-registry.d.ts +14 -7
- package/dist/core/tool-registry.js +17 -7
- package/dist/core/tool-result-store.js +5 -1
- package/dist/core/tool-roster.d.ts +57 -33
- package/dist/core/tool-roster.js +56 -68
- package/dist/core/tool-spec-members.js +8 -3
- package/dist/core/tool-spec.d.ts +53 -31
- package/dist/core/tools.js +51 -10
- package/dist/core/trace.d.ts +6 -2
- package/dist/core/types.d.ts +1 -1
- package/dist/core/untrusted-egress.d.ts +1 -1
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/user-attachment.d.ts +8 -1
- package/dist/core/user-attachment.js +64 -4
- package/dist/core/warm-resume.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +58 -0
- package/dist/core/wiring-manifest.js +14 -5
- package/dist/core/workflow-journal-store.d.ts +62 -0
- package/dist/core/workflow-journal-store.js +78 -2
- package/dist/core/workflow-run-store.d.ts +6 -0
- package/dist/core/workflow-run-store.js +2 -0
- package/dist/engine/compaction/compaction.d.ts +9 -9
- package/dist/engine/compaction/compaction.js +3 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +26 -8
- package/dist/engine/execution-env/node-execution-env.js +54 -24
- package/dist/engine/harness/agent-harness.d.ts +39 -9
- package/dist/engine/harness/agent-harness.js +87 -33
- package/dist/engine/harness/types.d.ts +58 -9
- package/dist/engine/harness/types.js +12 -1
- package/dist/engine/llm/types.d.ts +36 -19
- package/dist/engine/llm/validation.d.ts +4 -1
- package/dist/engine/llm/validation.js +13 -1
- package/dist/engine/loop/agent-loop.d.ts +23 -1
- package/dist/engine/loop/agent-loop.js +26 -34
- package/dist/engine/loop/types.d.ts +31 -19
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/memory-repo.d.ts +18 -12
- package/dist/engine/session/memory-repo.js +24 -9
- package/dist/engine/session/parent-chain-guard.d.ts +3 -2
- package/dist/engine/session/parent-chain-guard.js +1 -1
- package/dist/engine/session/repo-utils.d.ts +32 -3
- package/dist/engine/session/repo-utils.js +50 -5
- package/dist/index.d.ts +47 -23
- package/dist/index.js +35 -17
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/harness.d.ts +3 -3
- package/dist/internal/harness.js +2 -2
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/goal.js +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +8 -6
- package/dist/orchestration/run-workflow-tool.js +10 -6
- package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
- package/dist/orchestration/workflow-types.d.ts +18 -0
- package/dist/orchestration/workflow-types.js +6 -0
- package/dist/orchestration/workflow.d.ts +5 -13
- package/dist/orchestration/workflow.js +57 -49
- package/dist/prompt-assembly/artifact.d.ts +2 -2
- package/dist/prompt-assembly/artifact.js +5 -3
- package/dist/prompt-assembly/assemble.d.ts +3 -2
- package/dist/prompt-assembly/assemble.js +1 -1
- package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
- package/dist/prompt-assembly/catalog/schema.js +34 -2
- package/dist/prompt-assembly/catalog/sections.js +0 -1
- package/dist/prompt-assembly/composer.d.ts +3 -1
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/event-registry.js +2 -0
- package/dist/prompt-assembly/overlay.js +3 -3
- package/dist/prompt-assembly/renderers.d.ts +1 -1
- package/dist/prompt-assembly/renderers.js +0 -3
- package/dist/prompt-assembly/role-base.d.ts +15 -0
- package/dist/prompt-assembly/role-base.js +9 -0
- package/dist/prompt-assembly/select.d.ts +39 -14
- package/dist/prompt-assembly/select.js +47 -16
- package/dist/prompt-assembly/sugar.d.ts +17 -11
- package/dist/prompt-assembly/sugar.js +8 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +39 -2
- package/dist/prompt-assembly/tool-catalog.js +117 -4
- package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
- package/dist/prompt-assembly/turn-snapshot.js +0 -3
- package/dist/prompt-assembly/types.d.ts +0 -7
- package/dist/prompts/default.d.ts +14 -14
- package/dist/scenarios/full-body.d.ts +15 -15
- package/dist/scenarios/full-body.js +4 -3
- package/dist/shell/lex.js +10 -1
- package/dist/stores/file/adoption/adopt.d.ts +4 -3
- package/dist/stores/file/adoption/adopt.js +27 -33
- package/dist/stores/file/adoption/marker.d.ts +6 -13
- package/dist/stores/file/adoption/marker.js +9 -17
- package/dist/stores/file/checkpoint-store.d.ts +24 -0
- package/dist/stores/file/checkpoint-store.js +45 -7
- package/dist/stores/file/file-history-store.d.ts +20 -19
- package/dist/stores/file/file-history-store.js +157 -141
- package/dist/stores/file/fs-atomic.d.ts +15 -0
- package/dist/stores/file/fs-atomic.js +89 -14
- package/dist/stores/file/index.d.ts +1 -1
- package/dist/stores/file/index.js +2 -1
- package/dist/stores/file/mailbox-store.d.ts +3 -0
- package/dist/stores/file/mailbox-store.js +3 -1
- package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
- package/dist/stores/file/permission-rule-adopt.js +18 -18
- package/dist/stores/file/permission-rule-store.d.ts +18 -2
- package/dist/stores/file/permission-rule-store.js +31 -18
- package/dist/stores/file/session-policy-store.d.ts +3 -3
- package/dist/stores/file/session-store.d.ts +54 -16
- package/dist/stores/file/session-store.js +114 -85
- package/dist/stores/file/workflow-journal-store.d.ts +10 -4
- package/dist/stores/file/workflow-journal-store.js +28 -13
- package/dist/tools/artifact/artifact-text.d.ts +1 -1
- package/dist/tools/artifact/artifact-tool.js +5 -3
- package/dist/tools/fs/bash-program-position.d.ts +19 -2
- package/dist/tools/fs/bash-program-position.js +56 -9
- package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
- package/dist/tools/fs/fs-bash.d.ts +2 -2
- package/dist/tools/fs/fs-bash.js +8 -6
- package/dist/tools/fs/fs-glob.d.ts +86 -0
- package/dist/tools/fs/fs-glob.js +226 -0
- package/dist/tools/fs/fs-read.js +14 -14
- package/dist/tools/fs/fs-search-tools.js +7 -7
- package/dist/tools/fs/fs-shared.d.ts +8 -8
- package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
- package/dist/tools/fs/notebook.d.ts +2 -2
- package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
- package/dist/tools/fs/pattern-kill-guard.js +397 -0
- package/dist/tools/fs/pdf.d.ts +9 -6
- package/dist/tools/fs/pdf.js +3 -3
- package/dist/tools/fs/program-read-model.d.ts +57 -7
- package/dist/tools/fs/program-read-model.js +11 -1
- package/dist/tools/fs/program-read-model.rows.js +78 -21
- package/dist/tools/fs/read-deny-home.js +3 -2
- package/dist/tools/fs/read-deny.d.ts +29 -7
- package/dist/tools/fs/read-deny.js +63 -22
- package/dist/tools/fs/removal-targets.d.ts +18 -8
- package/dist/tools/fs/removal-targets.js +47 -20
- package/dist/tools/fs/safety.d.ts +9 -9
- package/dist/tools/fs/search.d.ts +16 -6
- package/dist/tools/fs/search.js +20 -26
- package/dist/tools/fs/shell-read-argv.js +89 -12
- package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
- package/dist/tools/fs/shell-read-bindings.js +41 -0
- package/dist/tools/fs/shell-read-check.js +12 -20
- package/dist/tools/fs/shell-read-environment.d.ts +5 -1
- package/dist/tools/fs/shell-read-environment.js +3 -2
- package/dist/tools/fs/shell-read-establish.d.ts +15 -0
- package/dist/tools/fs/shell-read-establish.js +19 -20
- package/dist/tools/fs/shell-read-judge.d.ts +4 -0
- package/dist/tools/fs/shell-read-judge.js +5 -2
- package/dist/tools/fs/shell-read-plan.js +24 -16
- package/dist/tools/fs/shell-read-start.d.ts +6 -1
- package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
- package/dist/tools/fs/shell-read-write-targets.js +2 -3
- package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
- package/dist/tools/fs/shell-reentry-text.js +40 -2
- package/dist/tools/loop-tick.d.ts +2 -2
- package/dist/tools/scheduler-tools.js +5 -1
- package/dist/tools/task-list.d.ts +7 -7
- package/dist/tools/task-list.js +5 -1
- package/dist/tools/todo.js +65 -64
- package/dist/tools/web.d.ts +20 -15
- package/dist/tools/web.js +55 -69
- package/dist/tools/worktree.d.ts +3 -3
- package/dist/tools/worktree.js +3 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +5894 -5638
- package/wire-changes/core@7.55.0.json +3318 -0
- package/wire-changes/core@7.56.0.json +3891 -0
- package/wire-changes/mods-kernel@7.56.0.json +303 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
- package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { shellWordsOf } from "../shell/shell-words.js";
|
|
3
|
-
import {
|
|
3
|
+
import { lexShell } from "../shell/lex.js";
|
|
4
|
+
import { readProgramPosition } from "../tools/fs/bash-program-position.js";
|
|
5
|
+
import { parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
4
6
|
import { compileReadDeny } from "../tools/fs/read-deny.js";
|
|
5
7
|
import { carriesShellRedirection, parseLeadingCommandName, splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
|
|
6
8
|
import { INLINE_TRUNCATION_MARK, inlineUntrusted } from "./untrusted-text.js";
|
|
7
|
-
import { isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
|
|
9
|
+
import { catalogRuleFaceOf, hostTargetOf, isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
|
|
10
|
+
import { HOST_UNREADABLE_SENTENCE, hostPatternAdmits, readHostPattern, spelledHostPattern } from "./host-target.js";
|
|
8
11
|
import { heldValues } from "./tool-name-readings.js";
|
|
9
12
|
import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
|
|
10
13
|
import { bindPathRules, judgeRuleRows, rowsWhereTheyLead } from "./path-rule-binding.js";
|
|
11
14
|
import { readShellCommand } from "./shell-run-candidates.js";
|
|
12
15
|
import { lineDescriptorsOfText, readOnlyLineObjection, readOnlyShellVerdict } from "./read-only-shell.js";
|
|
13
16
|
import { ARTIFACT_ACTIONS } from "./artifact-host.js";
|
|
14
|
-
import { ARTIFACT_TOOL_NAME } from "./tool-catalog-entries.js";
|
|
17
|
+
import { ARTIFACT_TOOL_NAME, TOOL_CATALOG_ENTRIES } from "./tool-catalog-entries.js";
|
|
18
|
+
import { paramRuleReaches } from "./param-rule-reach.js";
|
|
15
19
|
export const RULE_BEHAVIORS = Object.freeze(["deny", "ask", "allow"]);
|
|
16
20
|
const RULE_BEHAVIOR_SET = new Set(RULE_BEHAVIORS);
|
|
17
21
|
export function isRuleBehavior(v) {
|
|
@@ -43,64 +47,121 @@ export function callPathFaceOf(call) {
|
|
|
43
47
|
export function faceRowsSpeakFor(face) {
|
|
44
48
|
return face !== undefined && Object.values(PATH_RULE_FACES).some((columns) => columns.deny?.includes(face) === true || columns.ask?.includes(face) === true);
|
|
45
49
|
}
|
|
50
|
+
function catalogVocabularyOf(tool) {
|
|
51
|
+
const params = catalogRuleFaceOf(tool)?.params;
|
|
52
|
+
if (params === undefined)
|
|
53
|
+
throw new Error(`permission rules: the catalog declares no rule vocabulary for ${JSON.stringify(tool)}`);
|
|
54
|
+
return params;
|
|
55
|
+
}
|
|
56
|
+
const DELEGATION_RULE_TOOL = (() => {
|
|
57
|
+
const row = TOOL_CATALOG_ENTRIES.find((e) => e.id === "agent");
|
|
58
|
+
if (row === undefined)
|
|
59
|
+
throw new Error("permission rules: the catalog carries no delegation tool row (`agent`)");
|
|
60
|
+
return row.name;
|
|
61
|
+
})();
|
|
46
62
|
export const NAME_RULE_FACES = Object.freeze({
|
|
47
|
-
Skill: Object.freeze({ param: "skill", params: Object.freeze(
|
|
63
|
+
Skill: Object.freeze({ param: "skill", params: Object.freeze(catalogVocabularyOf("Skill")) }),
|
|
48
64
|
[ARTIFACT_TOOL_NAME]: Object.freeze({ param: "action", params: Object.freeze(["action"]), words: ARTIFACT_ACTIONS }),
|
|
65
|
+
[DELEGATION_RULE_TOOL]: Object.freeze({ param: "subagent_type", params: Object.freeze(["subagent_type"]), values: "mounted" }),
|
|
49
66
|
});
|
|
67
|
+
function nameRuleFaceOf(tool) {
|
|
68
|
+
return Object.hasOwn(NAME_RULE_FACES, tool) ? NAME_RULE_FACES[tool] : undefined;
|
|
69
|
+
}
|
|
50
70
|
function keyedValueParamOf(tool) {
|
|
51
|
-
const face =
|
|
71
|
+
const face = nameRuleFaceOf(tool);
|
|
52
72
|
return face?.words !== undefined ? face.param : undefined;
|
|
53
73
|
}
|
|
54
|
-
export function
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
export function normalizeSubagentType(value) {
|
|
75
|
+
return value.normalize("NFKC").toLowerCase().replace(/[\p{White_Space}\p{Pd}_]+/gu, "");
|
|
76
|
+
}
|
|
77
|
+
export function canonicalNameValue(value, values) {
|
|
78
|
+
if (values.includes(value))
|
|
79
|
+
return { kind: "entry", entry: value };
|
|
80
|
+
const key = normalizeSubagentType(value);
|
|
81
|
+
const hits = key === "" ? [] : [...new Set(values.filter((v) => normalizeSubagentType(v) === key))];
|
|
82
|
+
if (hits.length === 1)
|
|
83
|
+
return { kind: "entry", entry: hits[0] };
|
|
84
|
+
return hits.length > 1 ? { kind: "ambiguous", entries: hits } : { kind: "unknown" };
|
|
85
|
+
}
|
|
86
|
+
export function nameValueAbsentReason(tool) {
|
|
87
|
+
const face = nameRuleFaceOf(tool);
|
|
88
|
+
if (face === undefined || (face.words === undefined && face.values === undefined))
|
|
89
|
+
return undefined;
|
|
90
|
+
const key = face.param;
|
|
91
|
+
return `the ${tool} call names no ${key} as the tool reads it, so the ${key} it runs is not one its arguments show — a rule by ${key} cannot be read against it`;
|
|
57
92
|
}
|
|
58
93
|
export function nameGrammarReads(tool, content) {
|
|
59
94
|
if (ruleToolGrammarOf(tool) !== "name")
|
|
60
95
|
return false;
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
96
|
+
const face = nameRuleFaceOf(tool);
|
|
97
|
+
const key = contentKeyOf(content);
|
|
98
|
+
if (face.words !== undefined)
|
|
99
|
+
return key === face.param;
|
|
100
|
+
return key === undefined || face.params.includes(key) || !(catalogRuleFaceOf(tool)?.params ?? []).includes(key);
|
|
101
|
+
}
|
|
102
|
+
export function mountedValueArmOf(text, tool, behavior) {
|
|
103
|
+
if (behavior === "allow" || nameRuleFaceOf(tool)?.values !== "mounted")
|
|
104
|
+
return undefined;
|
|
105
|
+
const parsed = parseRuleText(text, behavior);
|
|
106
|
+
return "rule" in parsed && parsed.rule.match === "name" ? parsed.rule : undefined;
|
|
107
|
+
}
|
|
108
|
+
export function mountedOnlyArgumentOf(tool, content, mountedParams) {
|
|
109
|
+
if (nameRuleFaceOf(tool)?.values !== "mounted" || mountedParams === undefined)
|
|
110
|
+
return undefined;
|
|
111
|
+
const key = contentKeyOf(content);
|
|
112
|
+
return key !== undefined && mountedParams.includes(key) && !(catalogRuleFaceOf(tool)?.params ?? []).includes(key) ? key : undefined;
|
|
113
|
+
}
|
|
114
|
+
export function mountedArgumentArmOf(rule) {
|
|
115
|
+
const face = nameRuleFaceOf(rule.tool);
|
|
116
|
+
if (rule.behavior === "allow" || rule.match !== "name" || face?.values !== "mounted")
|
|
117
|
+
return undefined;
|
|
118
|
+
const key = contentKeyOf(rule.command);
|
|
119
|
+
if (key === undefined || face.params.includes(key) || !(catalogRuleFaceOf(rule.tool)?.params ?? []).includes(key))
|
|
120
|
+
return undefined;
|
|
121
|
+
const pattern = rule.command.slice(rule.command.indexOf(":") + 1).trim();
|
|
122
|
+
return pattern === "" ? undefined : { param: key, pattern };
|
|
123
|
+
}
|
|
124
|
+
function contentKeyOf(content) {
|
|
64
125
|
const colon = content.indexOf(":");
|
|
65
|
-
return colon > 0
|
|
126
|
+
return colon > 0 ? content.slice(0, colon).trim() : undefined;
|
|
66
127
|
}
|
|
67
|
-
export const
|
|
68
|
-
WebFetch: Object.freeze({ param: "url" }),
|
|
69
|
-
});
|
|
128
|
+
export const DOMAIN_RULE_MARKER = "domain:";
|
|
70
129
|
export function ruleToolGrammarOf(toolName) {
|
|
71
130
|
if (toolName === COMMAND_RULE_TOOL)
|
|
72
131
|
return "command";
|
|
73
132
|
if (Object.hasOwn(NAME_RULE_FACES, toolName))
|
|
74
133
|
return "name";
|
|
75
|
-
if (
|
|
134
|
+
if (hostTargetOf(toolName) !== undefined)
|
|
76
135
|
return "domain";
|
|
77
136
|
return pathTargetOf(toolName) !== undefined ? "path" : undefined;
|
|
78
137
|
}
|
|
79
|
-
export function
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
82
|
-
return undefined;
|
|
83
|
-
const value = args[face.param];
|
|
84
|
-
if (typeof value !== "string")
|
|
85
|
-
return undefined;
|
|
86
|
-
let url;
|
|
87
|
-
try {
|
|
88
|
-
url = new URL(value);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
138
|
+
export function shellRuleParamOf(tool, content) {
|
|
139
|
+
const colon = content.indexOf(":");
|
|
140
|
+
if (colon <= 0)
|
|
91
141
|
return undefined;
|
|
142
|
+
const param = content.slice(0, colon).trim();
|
|
143
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(param) && (catalogRuleFaceOf(tool)?.params ?? []).includes(param) ? param : undefined;
|
|
144
|
+
}
|
|
145
|
+
export function ruleLaneOf(tool, content) {
|
|
146
|
+
if (isShellFaceToolName(tool))
|
|
147
|
+
return shellRuleParamOf(tool, content) !== undefined && !(tool === COMMAND_RULE_TOOL && content.endsWith(":*")) ? "param" : "command";
|
|
148
|
+
switch (ruleToolGrammarOf(tool)) {
|
|
149
|
+
case "name":
|
|
150
|
+
return nameGrammarReads(tool, content) ? "name" : "param";
|
|
151
|
+
case "path":
|
|
152
|
+
return content.indexOf(":") <= 0 ? "path" : "param";
|
|
153
|
+
case "domain":
|
|
154
|
+
return content.startsWith(DOMAIN_RULE_MARKER) ? "domain" : "param";
|
|
155
|
+
default:
|
|
156
|
+
return "param";
|
|
92
157
|
}
|
|
93
|
-
if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username !== "" || url.password !== "")
|
|
94
|
-
return undefined;
|
|
95
|
-
const host = url.hostname.replace(/\.+$/, "");
|
|
96
|
-
return host === "" ? undefined : host;
|
|
97
158
|
}
|
|
98
159
|
export function nameRuleSubjectOf(toolName, args) {
|
|
99
160
|
const face = Object.hasOwn(NAME_RULE_FACES, toolName) ? NAME_RULE_FACES[toolName] : undefined;
|
|
100
161
|
if (face === undefined || typeof args !== "object" || args === null || Array.isArray(args) || !Object.hasOwn(args, face.param))
|
|
101
162
|
return undefined;
|
|
102
163
|
const value = args[face.param];
|
|
103
|
-
return typeof value === "string" ? value : undefined;
|
|
164
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
104
165
|
}
|
|
105
166
|
export function laneToolNamesOf(req) {
|
|
106
167
|
const words = heldValues("policy.persistedGrammar", { get: ruleWordOf }, req);
|
|
@@ -111,9 +172,9 @@ function ruleWordOf(name) {
|
|
|
111
172
|
return COMMAND_RULE_TOOL;
|
|
112
173
|
return ruleToolGrammarOf(name) !== undefined ? name : undefined;
|
|
113
174
|
}
|
|
114
|
-
export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "subpath", "path", "name", "name_prefix", "host", "subdomain"]);
|
|
175
|
+
export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "pattern", "subpath", "path", "name", "name_prefix", "host", "subdomain"]);
|
|
115
176
|
function isCommandMatch(match) {
|
|
116
|
-
return match === "exact" || match === "prefix" || match === "wildcard";
|
|
177
|
+
return match === "exact" || match === "prefix" || match === "wildcard" || match === "pattern";
|
|
117
178
|
}
|
|
118
179
|
export const RULE_ADD_ORIGINS = Object.freeze(["user", "imported-cc", "starter"]);
|
|
119
180
|
export const MAX_RULE_TEXT_CHARS = 512;
|
|
@@ -282,10 +343,32 @@ const CONTROL_CHARS_GLOBAL_RE = new RegExp(CONTROL_CHARS_RE.source, "gu");
|
|
|
282
343
|
const RULE_SPELLING_REJECT_BYTES = "<>";
|
|
283
344
|
const RULE_SPELLING_REJECT_RE = new RegExp(`[${RULE_SPELLING_REJECT_BYTES}]`);
|
|
284
345
|
function valueRuleFormsSentence() {
|
|
285
|
-
return Object.
|
|
286
|
-
.map((
|
|
346
|
+
return Object.keys(NAME_RULE_FACES)
|
|
347
|
+
.map((tool) => `${nameRuleFormOf(tool)} rules`)
|
|
287
348
|
.join(", ");
|
|
288
349
|
}
|
|
350
|
+
function nameRuleFormOf(tool) {
|
|
351
|
+
return formatRuleText(`<${nameRuleFaceOf(tool).param}>`, "name", tool);
|
|
352
|
+
}
|
|
353
|
+
export function ruleShapeOf(call, behavior) {
|
|
354
|
+
if (isShellFaceToolName(call.toolName))
|
|
355
|
+
return formatRuleText("<command>", "exact", COMMAND_RULE_TOOL);
|
|
356
|
+
switch (ruleToolGrammarOf(call.toolName)) {
|
|
357
|
+
case "name":
|
|
358
|
+
return nameRuleFormOf(call.toolName);
|
|
359
|
+
case "domain":
|
|
360
|
+
return formatRuleText("<host>", "host", call.toolName);
|
|
361
|
+
case "path": {
|
|
362
|
+
if (behavior !== "allow")
|
|
363
|
+
return formatRuleText("<path>", "path", call.toolName);
|
|
364
|
+
const face = callPathFaceOf(call) ?? (pathTargetOf(call.toolName)?.access === "read" ? "read" : "write");
|
|
365
|
+
const word = Object.keys(PATH_RULE_FACES).find((tool) => PATH_RULE_FACES[tool].allow?.includes(face) === true) ?? call.toolName;
|
|
366
|
+
return formatRuleText("<path>", "path", word);
|
|
367
|
+
}
|
|
368
|
+
default:
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
289
372
|
const DISCLOSED_RULE_TEXT_MAX_CHARS = 120;
|
|
290
373
|
export function escapeForDisclosure(value) {
|
|
291
374
|
let text;
|
|
@@ -319,6 +402,19 @@ export function renderUntrustedCommandText(text, maxLen = DISCLOSED_RULE_TEXT_MA
|
|
|
319
402
|
export function hasUnrenderableCharacters(text) {
|
|
320
403
|
return CONTROL_CHARS_RE.test(text);
|
|
321
404
|
}
|
|
405
|
+
export function ruleContentOf(parsed) {
|
|
406
|
+
const content = parsed.ruleContent;
|
|
407
|
+
return content === undefined || (parsed.toolName === COMMAND_RULE_TOOL && foldSpacing(content) === "*") ? undefined : content;
|
|
408
|
+
}
|
|
409
|
+
export function respelledRuleTextOf(text, behavior) {
|
|
410
|
+
const outer = text.trim();
|
|
411
|
+
const open = outer.indexOf("(");
|
|
412
|
+
const candidate = open < 0 ? outer : outer.slice(0, open).replace(/\s+$/u, "") + outer.slice(open);
|
|
413
|
+
if (candidate === text || ruleSpellingOf(candidate) === undefined)
|
|
414
|
+
return undefined;
|
|
415
|
+
const read = parseRuleText(candidate, behavior);
|
|
416
|
+
return "rule" in read ? read.rule.rule : undefined;
|
|
417
|
+
}
|
|
322
418
|
export function parseRuleText(text, behavior) {
|
|
323
419
|
if (!isRuleBehavior(behavior)) {
|
|
324
420
|
return reject("invalid.grammar", `rule "${text}" was parsed under behavior ${escapeForDisclosure(behavior)}, which is not one of ${RULE_BEHAVIORS.join("/")}`);
|
|
@@ -330,8 +426,19 @@ export function parseRuleText(text, behavior) {
|
|
|
330
426
|
if (CONTROL_CHARS_RE.test(text)) {
|
|
331
427
|
return reject("invalid.control_chars", "rule text contains control characters (C0/C1, tab excepted), which cannot be part of a readable command");
|
|
332
428
|
}
|
|
333
|
-
const parsed =
|
|
334
|
-
if (parsed
|
|
429
|
+
const parsed = ruleSpellingOf(text);
|
|
430
|
+
if (parsed === undefined) {
|
|
431
|
+
const respelling = respelledRuleTextOf(text, behavior);
|
|
432
|
+
return {
|
|
433
|
+
reject: {
|
|
434
|
+
code: "invalid.grammar",
|
|
435
|
+
message: `"${text}" is not a rule spelling — a rule is the tool's name exactly as spelled, then its content in parentheses (no blank, tab or invisible character around the rule or before the "("), and the ")" ends it${respelling !== undefined ? `; spelled as a rule it is "${respelling}"` : ""}`,
|
|
436
|
+
...(respelling !== undefined ? { respelling } : {}),
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const ruleContent = ruleContentOf(parsed);
|
|
441
|
+
if (ruleContent === undefined) {
|
|
335
442
|
return reject("invalid.grammar", `"${text}" is not a Tool(content) rule — a bare tool name claims the whole tool and is not a command rule`);
|
|
336
443
|
}
|
|
337
444
|
if (parsed.toolName !== COMMAND_RULE_TOOL) {
|
|
@@ -339,16 +446,16 @@ export function parseRuleText(text, behavior) {
|
|
|
339
446
|
if (grammar === undefined) {
|
|
340
447
|
return reject("unsupported.tool", `only Bash command rules, rules on path-targeting tools (Read, Edit, Write, …), ${valueRuleFormsSentence()} and WebFetch domain rules have a rule form in this version (got "${parsed.toolName}")`);
|
|
341
448
|
}
|
|
342
|
-
if (!tighten && RULE_SPELLING_REJECT_RE.test(
|
|
449
|
+
if (!tighten && RULE_SPELLING_REJECT_RE.test(ruleContent)) {
|
|
343
450
|
return reject("invalid.grammar", `"${text}" carries a shell redirection byte (${[...RULE_SPELLING_REJECT_BYTES].map((b) => `\`${b}\``).join(" or ")}) — no path, name or host allow spells one, and the grant would not read as what it looks like`);
|
|
344
451
|
}
|
|
345
452
|
if (grammar === "name")
|
|
346
|
-
return parseNameRuleContent(text, parsed.toolName,
|
|
453
|
+
return parseNameRuleContent(text, parsed.toolName, ruleContent, behavior);
|
|
347
454
|
if (grammar === "domain")
|
|
348
|
-
return parseDomainRuleContent(text, parsed.toolName,
|
|
349
|
-
return parsePathRuleContent(text, parsed.toolName,
|
|
455
|
+
return parseDomainRuleContent(text, parsed.toolName, ruleContent, behavior);
|
|
456
|
+
return parsePathRuleContent(text, parsed.toolName, ruleContent, behavior);
|
|
350
457
|
}
|
|
351
|
-
const content =
|
|
458
|
+
const content = foldSpacing(ruleContent) ?? ruleContent;
|
|
352
459
|
const prefixBody = /^(.+):\*$/.exec(content)?.[1];
|
|
353
460
|
let match;
|
|
354
461
|
let body;
|
|
@@ -366,14 +473,18 @@ export function parseRuleText(text, behavior) {
|
|
|
366
473
|
body = wildcardBody;
|
|
367
474
|
}
|
|
368
475
|
else if (hasUnescapedStar(content)) {
|
|
369
|
-
|
|
476
|
+
match = "pattern";
|
|
477
|
+
body = content;
|
|
370
478
|
}
|
|
371
479
|
else {
|
|
372
480
|
match = "exact";
|
|
373
481
|
body = content;
|
|
374
482
|
}
|
|
375
483
|
}
|
|
376
|
-
|
|
484
|
+
if (match === "pattern" && !tighten && hasUnescapedStar(body.trim().split(/[ \t]+/)[0] ?? "")) {
|
|
485
|
+
return reject("unsupported.wildcard", `a \`*\` in the first word of "${text}" leaves open which program runs — an allow names the program it grants (a deny or an ask may leave it open)`);
|
|
486
|
+
}
|
|
487
|
+
const shape = ruleLaneShapeOf(match === "pattern" ? globFloorText(body) : body, MATCH_READING);
|
|
377
488
|
if ("reject" in shape) {
|
|
378
489
|
if (body.trim() === "" || shape.reject === "empty command")
|
|
379
490
|
return reject("invalid.empty_command", `rule "${text}" names no command`);
|
|
@@ -381,14 +492,16 @@ export function parseRuleText(text, behavior) {
|
|
|
381
492
|
return reject("invalid.unbalanced_quotes", `rule "${text}" has unbalanced quoting, so the command it names is not determinable`);
|
|
382
493
|
return reject("invalid.not_simple_command", `rule "${text}" is not a command this lane can name (${shape.reject})`);
|
|
383
494
|
}
|
|
384
|
-
if (match === "wildcard" && shape.segments.length > 1) {
|
|
385
|
-
return reject("unsupported.wildcard",
|
|
495
|
+
if ((match === "wildcard" || match === "pattern") && shape.segments.length > 1) {
|
|
496
|
+
return reject("unsupported.wildcard", match === "pattern"
|
|
497
|
+
? `a \`*\` pattern names one command, and "${text}" names more than one — a compound is covered segment by segment, and the compound-prefix form spells a chain with the \`:*\` marker`
|
|
498
|
+
: `a \`<chain> *\` pattern names more than one command, and the trailing-star form has no chain reading — the compound-prefix form spells one with the \`:*\` marker ("${text}")`);
|
|
386
499
|
}
|
|
387
500
|
const head = commandBasename(shape.names[shape.names.length - 1] ?? "");
|
|
388
|
-
if (
|
|
501
|
+
if (match !== "exact" && BARE_INTERPRETER_NAMES.has(head) && !tighten) {
|
|
389
502
|
return reject("invalid.bare_interpreter_prefix", shape.segments.length > 1
|
|
390
503
|
? `prefix rule "${text}" leaves its final segment open-ended under the interpreter "${head}" — such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`
|
|
391
|
-
:
|
|
504
|
+
: `${match === "pattern" ? "pattern" : "prefix"} rule "${text}" is headed by the interpreter "${head}" — such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`);
|
|
392
505
|
}
|
|
393
506
|
const command = foldSpacing(body);
|
|
394
507
|
if (command === undefined) {
|
|
@@ -404,13 +517,15 @@ export function formatRuleText(command, match, tool) {
|
|
|
404
517
|
if (match === "name")
|
|
405
518
|
return `${tool}(${keyedValueParamOf(tool) !== undefined ? `${keyedValueParamOf(tool)}:` : ""}${command})`;
|
|
406
519
|
if (match === "host" || match === "subdomain")
|
|
407
|
-
return `${tool}(domain:${match
|
|
520
|
+
return `${tool}(domain:${spelledHostPattern({ match, host: command })})`;
|
|
408
521
|
return `${tool}(${command}${match === "prefix" || match === "name_prefix" ? ":*" : match === "wildcard" ? " *" : ""})`;
|
|
409
522
|
}
|
|
410
523
|
function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = true) {
|
|
411
524
|
const face = NAME_RULE_FACES[tool];
|
|
412
525
|
if (face?.words !== undefined)
|
|
413
526
|
return parseKeyedValueRuleContent(text, tool, content, behavior, { param: face.param, words: face.words });
|
|
527
|
+
if (face?.values === "mounted")
|
|
528
|
+
return parseMountedValueRuleContent(text, tool, content, behavior, face);
|
|
414
529
|
const bare = content.startsWith("/") ? content.slice(1) : content;
|
|
415
530
|
const colon = bare.indexOf(":");
|
|
416
531
|
if (face !== undefined && colon > 0 && face.params.includes(bare.slice(0, colon).trim())) {
|
|
@@ -457,6 +572,28 @@ function parseKeyedValueRuleContent(text, tool, content, behavior, face) {
|
|
|
457
572
|
}
|
|
458
573
|
return { rule: { behavior, rule: formatRuleText(word, "name", tool), tool, match: "name", command: word } };
|
|
459
574
|
}
|
|
575
|
+
function parseMountedValueRuleContent(text, tool, content, behavior, face) {
|
|
576
|
+
const form = nameRuleFormOf(tool);
|
|
577
|
+
const key = contentKeyOf(content);
|
|
578
|
+
if (key !== undefined && face.params.includes(key)) {
|
|
579
|
+
return reject("invalid.grammar", `"${text}" is the parameter spelling, which a ${tool} rule does not have — a ${tool} rule names the ${face.param} itself: ${form}`);
|
|
580
|
+
}
|
|
581
|
+
if (behavior === "allow" && key !== undefined && (catalogRuleFaceOf(tool)?.params ?? []).includes(key)) {
|
|
582
|
+
return reject("invalid.grammar", `"${text}" is a rule on the ${key} argument, which an allow entry has no form for — an allow reads one way, so it does not grant the ${face.param} "${content}" either; a ${tool} allow names the ${face.param}: ${form}`);
|
|
583
|
+
}
|
|
584
|
+
if (content === "" || content.trim() !== content) {
|
|
585
|
+
return reject("invalid.grammar", `the ${face.param} in "${text}" is empty or carries surrounding whitespace — a ${tool} rule names the ${face.param} exactly as it is spelled, so the spelling must be the one meant`);
|
|
586
|
+
}
|
|
587
|
+
if (content.includes("*")) {
|
|
588
|
+
return reject("unsupported.wildcard", `a \`*\` in the ${face.param} of "${text}" has no rule form — a ${tool} rule names one ${face.param} (${form}); the whole tool is the bare ${tool} entry`);
|
|
589
|
+
}
|
|
590
|
+
const canonical = formatRuleText(content, "name", tool);
|
|
591
|
+
const back = parsePermissionRule(canonical);
|
|
592
|
+
if (back.toolName !== tool || back.ruleContent !== content) {
|
|
593
|
+
return reject("invalid.grammar", `the ${face.param} in "${text}" has no spelling the rule syntax reads back (a backslash or an unbalanced parenthesis) — no ${tool} rule can name it`);
|
|
594
|
+
}
|
|
595
|
+
return { rule: { behavior, rule: canonical, tool, match: "name", command: content } };
|
|
596
|
+
}
|
|
460
597
|
export function nameRuleAdmits(rule, name) {
|
|
461
598
|
if (rule.match === "name")
|
|
462
599
|
return name === rule.command;
|
|
@@ -464,55 +601,50 @@ export function nameRuleAdmits(rule, name) {
|
|
|
464
601
|
return name.startsWith(rule.command);
|
|
465
602
|
return false;
|
|
466
603
|
}
|
|
604
|
+
export function nameRuleReach(rule, name, values) {
|
|
605
|
+
const face = nameRuleFaceOf(rule.tool);
|
|
606
|
+
if (face?.values !== "mounted")
|
|
607
|
+
return nameRuleAdmits(rule, name) ? "reached" : "not_reached";
|
|
608
|
+
if (rule.match !== "name")
|
|
609
|
+
return "not_reached";
|
|
610
|
+
if (values === undefined)
|
|
611
|
+
return "unread";
|
|
612
|
+
const operand = canonicalNameValue(rule.command, values);
|
|
613
|
+
const called = canonicalNameValue(name, values);
|
|
614
|
+
if (operand.kind === "ambiguous")
|
|
615
|
+
return rule.behavior !== "allow" && called.kind === "entry" && operand.entries.includes(called.entry) ? "reached" : "not_reached";
|
|
616
|
+
if (operand.kind === "entry" && called.kind === "entry")
|
|
617
|
+
return operand.entry === called.entry ? "reached" : "not_reached";
|
|
618
|
+
return name === rule.command ? "reached" : "not_reached";
|
|
619
|
+
}
|
|
620
|
+
export function nameValuesUnreadReason(tool) {
|
|
621
|
+
const face = nameRuleFaceOf(tool);
|
|
622
|
+
return `the ${tool} tool this call reaches declares no ${face?.param ?? "value"} values it offers, so which ${face?.param ?? "value"} the call runs cannot be read against a ${tool} rule`;
|
|
623
|
+
}
|
|
467
624
|
function parseDomainRuleContent(text, tool, content, behavior) {
|
|
468
|
-
if (
|
|
469
|
-
return reject("unsupported.tool", `a ${tool} rule by host has an allow form only in this version ("${text}") — a ${behavior} by host is not read; write the ${behavior} on the whole ${tool} tool`);
|
|
470
|
-
}
|
|
471
|
-
if (!content.startsWith("domain:")) {
|
|
625
|
+
if (!content.startsWith(DOMAIN_RULE_MARKER)) {
|
|
472
626
|
return reject("invalid.grammar", `a ${tool} rule names a host: ${tool}(domain:<host>) for one, ${tool}(domain:*.<domain>) for every host below a domain ("${text}")`);
|
|
473
627
|
}
|
|
474
|
-
const
|
|
475
|
-
if (
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
628
|
+
const read = readHostPattern(content.slice(DOMAIN_RULE_MARKER.length));
|
|
629
|
+
if ("problem" in read) {
|
|
630
|
+
switch (read.problem) {
|
|
631
|
+
case "every_host":
|
|
632
|
+
return reject("unsupported.wildcard", `"${text}" names every host — the whole tool is the bare ${tool} entry`);
|
|
633
|
+
case "inner_wildcard":
|
|
634
|
+
return reject("unsupported.wildcard", `a \`*\` in "${text}" other than a leading \`*.\` has no rule form — a ${tool} rule names one host, or every host below one domain with \`*.<domain>\``);
|
|
635
|
+
case "empty_label":
|
|
636
|
+
return reject("invalid.grammar", `"${text}" names a host with an empty label (a leading or doubled dot) — write the host itself, and every host below a domain as \`*.<domain>\``);
|
|
637
|
+
case "not_a_host":
|
|
638
|
+
return reject("invalid.grammar", `"${text}" does not name a host alone — write the host as a URL carries it, with no scheme, path, port or credentials`);
|
|
639
|
+
case "below_address":
|
|
640
|
+
return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
|
|
641
|
+
}
|
|
485
642
|
}
|
|
486
|
-
|
|
487
|
-
return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
|
|
488
|
-
const match = below ? "subdomain" : "host";
|
|
643
|
+
const { match, host } = read.pattern;
|
|
489
644
|
return { rule: { behavior, rule: formatRuleText(host, match, tool), tool, match, command: host } };
|
|
490
645
|
}
|
|
491
|
-
function hostOfSpelling(spelled) {
|
|
492
|
-
if (spelled === "" || /[\s/\\@?#%]/.test(spelled) || (spelled.includes(":") && !/^\[[^\]]+\]$/.test(spelled)))
|
|
493
|
-
return undefined;
|
|
494
|
-
let url;
|
|
495
|
-
try {
|
|
496
|
-
url = new URL(`https://${spelled}/`);
|
|
497
|
-
}
|
|
498
|
-
catch {
|
|
499
|
-
return undefined;
|
|
500
|
-
}
|
|
501
|
-
if (url.port !== "" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "")
|
|
502
|
-
return undefined;
|
|
503
|
-
const host = url.hostname.replace(/\.+$/, "");
|
|
504
|
-
return host === "" ? undefined : host;
|
|
505
|
-
}
|
|
506
|
-
function isAddressHost(host) {
|
|
507
|
-
return host.startsWith("[") || /^\d+\.\d+\.\d+\.\d+$/.test(host);
|
|
508
|
-
}
|
|
509
646
|
export function domainRuleAdmits(rule, host) {
|
|
510
|
-
|
|
511
|
-
return host === rule.command;
|
|
512
|
-
if (rule.match !== "subdomain" || isAddressHost(host) || !host.endsWith(`.${rule.command}`))
|
|
513
|
-
return false;
|
|
514
|
-
const labels = host.slice(0, -(rule.command.length + 1));
|
|
515
|
-
return labels !== "" && !labels.split(".").includes("");
|
|
647
|
+
return (rule.match === "host" || rule.match === "subdomain") && hostPatternAdmits({ match: rule.match, host: rule.command }, host);
|
|
516
648
|
}
|
|
517
649
|
function parsePathRuleContent(text, tool, content, behavior) {
|
|
518
650
|
if (behavior === "allow" && PATH_RULE_FACES[tool]?.allow === undefined) {
|
|
@@ -712,6 +844,10 @@ const NOT_REACHED = { reach: "not_reached" };
|
|
|
712
844
|
export function programRunReachOf(rule, command) {
|
|
713
845
|
if (!isCommandMatch(rule.match))
|
|
714
846
|
return NOT_REACHED;
|
|
847
|
+
if (rule.match === "pattern") {
|
|
848
|
+
const pattern = commandGlobOf(rule.command, "words");
|
|
849
|
+
return pattern === undefined ? NOT_REACHED : anySegmentReach(readCommandMemo(command), (argv) => globArgvReachOf(pattern, argv));
|
|
850
|
+
}
|
|
715
851
|
const body = readRuleBody(rule.command);
|
|
716
852
|
if (body === undefined)
|
|
717
853
|
return NOT_REACHED;
|
|
@@ -727,7 +863,8 @@ export function programRunReachOf(rule, command) {
|
|
|
727
863
|
return hidden === undefined ? NOT_REACHED : { reach: "unreadable", reason: hidden.peelUnreadable };
|
|
728
864
|
let outcome = REACHED;
|
|
729
865
|
for (let i = 0; i < body.segments.length; i++) {
|
|
730
|
-
const
|
|
866
|
+
const bodyArgv = body.segments[i].argv;
|
|
867
|
+
const r = segmentReachOf(shape.segments[i], (argv) => argvReachOf(bodyArgv, argv, exact || i < body.segments.length - 1));
|
|
731
868
|
if (r.reach === "not_reached")
|
|
732
869
|
return hidden === undefined ? NOT_REACHED : { reach: "unreadable", reason: hidden.peelUnreadable };
|
|
733
870
|
if (r.reach === "unreadable")
|
|
@@ -736,9 +873,12 @@ export function programRunReachOf(rule, command) {
|
|
|
736
873
|
return outcome;
|
|
737
874
|
}
|
|
738
875
|
const bodyArgv = body.segments[0].argv;
|
|
876
|
+
return anySegmentReach(shape, (argv) => argvReachOf(bodyArgv, argv, exact));
|
|
877
|
+
}
|
|
878
|
+
function anySegmentReach(shape, reachOf) {
|
|
739
879
|
let unreadable;
|
|
740
880
|
for (const segment of shape.segments) {
|
|
741
|
-
const r = segmentReachOf(
|
|
881
|
+
const r = segmentReachOf(segment, reachOf);
|
|
742
882
|
if (r.reach === "reached")
|
|
743
883
|
return REACHED;
|
|
744
884
|
if (r.reach === "unreadable")
|
|
@@ -749,12 +889,12 @@ export function programRunReachOf(rule, command) {
|
|
|
749
889
|
export function ruleReachesProgramRun(rule, command) {
|
|
750
890
|
return programRunReachOf(rule, command).reach === "reached";
|
|
751
891
|
}
|
|
752
|
-
function segmentReachOf(
|
|
892
|
+
function segmentReachOf(segment, reachOf) {
|
|
753
893
|
if (segment.unreadable !== undefined)
|
|
754
894
|
return { reach: "unreadable", reason: segment.unreadable };
|
|
755
895
|
let unreadable;
|
|
756
896
|
for (const argv of [segment.argv, ...segment.peeled]) {
|
|
757
|
-
const r =
|
|
897
|
+
const r = reachOf(argv);
|
|
758
898
|
if (r.reach === "reached")
|
|
759
899
|
return REACHED;
|
|
760
900
|
if (r.reach === "unreadable")
|
|
@@ -783,6 +923,148 @@ function argvReachOf(bodyArgv, argv, exact) {
|
|
|
783
923
|
const loose = argv.slice(bodyArgv.length).find((w) => w.expands === "many");
|
|
784
924
|
return loose === undefined ? NOT_REACHED : { reach: "unreadable", reason: `an unquoted expansion (${loose.raw}) may expand to nothing` };
|
|
785
925
|
}
|
|
926
|
+
const GLOB_STAR = -1;
|
|
927
|
+
const GLOB_HOLE = -2;
|
|
928
|
+
const GLOB_GROUP = -3;
|
|
929
|
+
function globFloorText(body) {
|
|
930
|
+
let out = "";
|
|
931
|
+
for (let i = 0; i < body.length; i++) {
|
|
932
|
+
if (body[i] === "\\" && body[i + 1] === "*") {
|
|
933
|
+
out += "*";
|
|
934
|
+
i++;
|
|
935
|
+
}
|
|
936
|
+
else
|
|
937
|
+
out += body[i] === "*" ? "x" : body[i];
|
|
938
|
+
}
|
|
939
|
+
return out;
|
|
940
|
+
}
|
|
941
|
+
function commandGlobOf(body, reading) {
|
|
942
|
+
const shape = ruleLaneShapeOf(globFloorText(body), MATCH_READING);
|
|
943
|
+
return "reject" in shape || shape.segments.length !== 1 ? undefined : compileGlob(reading === "words" ? unquotedPattern(body) : body);
|
|
944
|
+
}
|
|
945
|
+
function unquotedPattern(body) {
|
|
946
|
+
let out = "";
|
|
947
|
+
let quote;
|
|
948
|
+
for (const ch of body) {
|
|
949
|
+
if (quote === undefined ? ch === "'" || ch === '"' : ch === quote)
|
|
950
|
+
quote = quote === undefined ? ch : undefined;
|
|
951
|
+
else
|
|
952
|
+
out += ch;
|
|
953
|
+
}
|
|
954
|
+
return out;
|
|
955
|
+
}
|
|
956
|
+
function compileGlob(pattern) {
|
|
957
|
+
const nodes = [];
|
|
958
|
+
const groupEnds = new Map();
|
|
959
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
960
|
+
const ch = pattern[i];
|
|
961
|
+
if (ch === "\\" && pattern[i + 1] === "*") {
|
|
962
|
+
nodes.push(0x2a);
|
|
963
|
+
i++;
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
if (ch === "/" && pattern.startsWith("**/", i + 1)) {
|
|
967
|
+
let j = i + 1;
|
|
968
|
+
while (pattern.startsWith("**/", j))
|
|
969
|
+
j += 3;
|
|
970
|
+
nodes.push(0x2f, GLOB_GROUP, GLOB_STAR, 0x2f);
|
|
971
|
+
groupEnds.set(nodes.length - 3, nodes.length);
|
|
972
|
+
i = j - 1;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
nodes.push(ch === "*" ? GLOB_STAR : ch.charCodeAt(0));
|
|
976
|
+
}
|
|
977
|
+
return { nodes, groupEnds };
|
|
978
|
+
}
|
|
979
|
+
function globMatches(glob, subject, holes) {
|
|
980
|
+
const { nodes, groupEnds } = glob;
|
|
981
|
+
const m = nodes.length;
|
|
982
|
+
let live = new Uint8Array(m + 1);
|
|
983
|
+
let next = new Uint8Array(m + 1);
|
|
984
|
+
const close = (set) => {
|
|
985
|
+
for (let p = 0; p < m; p++) {
|
|
986
|
+
if (set[p] !== 1)
|
|
987
|
+
continue;
|
|
988
|
+
if (nodes[p] === GLOB_STAR || nodes[p] === GLOB_GROUP)
|
|
989
|
+
set[p + 1] = 1;
|
|
990
|
+
if (nodes[p] === GLOB_GROUP)
|
|
991
|
+
set[groupEnds.get(p)] = 1;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
live[0] = 1;
|
|
995
|
+
close(live);
|
|
996
|
+
for (const unit of subject) {
|
|
997
|
+
next.fill(0);
|
|
998
|
+
if (unit === GLOB_HOLE && holes === "any")
|
|
999
|
+
next.fill(1, live.indexOf(1));
|
|
1000
|
+
else {
|
|
1001
|
+
for (let p = 0; p < m; p++) {
|
|
1002
|
+
if (live[p] !== 1)
|
|
1003
|
+
continue;
|
|
1004
|
+
if (nodes[p] === GLOB_STAR)
|
|
1005
|
+
next[p] = 1;
|
|
1006
|
+
else if (nodes[p] === unit)
|
|
1007
|
+
next[p + 1] = 1;
|
|
1008
|
+
}
|
|
1009
|
+
close(next);
|
|
1010
|
+
}
|
|
1011
|
+
if (next.indexOf(1) < 0)
|
|
1012
|
+
return false;
|
|
1013
|
+
[live, next] = [next, live];
|
|
1014
|
+
}
|
|
1015
|
+
return live[m] === 1;
|
|
1016
|
+
}
|
|
1017
|
+
function unitsOf(text) {
|
|
1018
|
+
const out = [];
|
|
1019
|
+
for (let i = 0; i < text.length; i++)
|
|
1020
|
+
out.push(text.charCodeAt(i));
|
|
1021
|
+
return out;
|
|
1022
|
+
}
|
|
1023
|
+
function globAdmitsSpelling(body, folded) {
|
|
1024
|
+
const glob = commandGlobOf(body, "spelling");
|
|
1025
|
+
return glob !== undefined && globMatches(glob, unitsOf(folded), "inside_star");
|
|
1026
|
+
}
|
|
1027
|
+
function globArgvReachOf(glob, argv) {
|
|
1028
|
+
if (argv.length === 0)
|
|
1029
|
+
return NOT_REACHED;
|
|
1030
|
+
const subject = [];
|
|
1031
|
+
let hole;
|
|
1032
|
+
let anchored = false;
|
|
1033
|
+
argv.forEach((w, k) => {
|
|
1034
|
+
if (w.expands !== "many") {
|
|
1035
|
+
if (anchored)
|
|
1036
|
+
subject.push(0x20);
|
|
1037
|
+
anchored = true;
|
|
1038
|
+
}
|
|
1039
|
+
if (w.expands === false)
|
|
1040
|
+
subject.push(...unitsOf(w.text));
|
|
1041
|
+
else {
|
|
1042
|
+
subject.push(GLOB_HOLE);
|
|
1043
|
+
hole ??= { at: k, raw: w.raw };
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
if (globMatches(glob, subject, "inside_star"))
|
|
1047
|
+
return REACHED;
|
|
1048
|
+
if (hole === undefined || !globMatches(glob, subject, "any"))
|
|
1049
|
+
return NOT_REACHED;
|
|
1050
|
+
return { reach: "unreadable", reason: hole.at === 0 ? "the program name is an expansion" : `an expansion (${hole.raw}) stands where the rule's pattern reads` };
|
|
1051
|
+
}
|
|
1052
|
+
export function commandPatternMayRun(body, programs) {
|
|
1053
|
+
const lexed = lexShell(body);
|
|
1054
|
+
const words = "parseFailure" in lexed ? undefined : lexed.segments[0]?.tokens.filter((t) => t.kind === "word");
|
|
1055
|
+
if (words === undefined || words.length === 0)
|
|
1056
|
+
return true;
|
|
1057
|
+
const position = readProgramPosition(words);
|
|
1058
|
+
const program = position.program;
|
|
1059
|
+
if (program === undefined || position.refusal !== undefined || hasUnescapedStar(words[program.index]?.raw ?? "*"))
|
|
1060
|
+
return true;
|
|
1061
|
+
const rest = words.slice(program.index + 1).map((w) => ` ${w.raw}`).join("");
|
|
1062
|
+
const glob = compileGlob(unquotedPattern(`${program.basename}${rest}`.toLowerCase()));
|
|
1063
|
+
return programs.some((p) => {
|
|
1064
|
+
const head = unitsOf(p.toLowerCase());
|
|
1065
|
+
return globMatches(glob, head, "inside_star") || globMatches(glob, [...head, 0x20, GLOB_HOLE], "any");
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
786
1068
|
function readRuleBody(body) {
|
|
787
1069
|
const shape = readShellCommand(body);
|
|
788
1070
|
if (shape.grouped || shape.segments.length === 0)
|
|
@@ -806,6 +1088,8 @@ function admitsUnder(rule, command, reading) {
|
|
|
806
1088
|
return false;
|
|
807
1089
|
if (rule.match === "exact")
|
|
808
1090
|
return folded === rule.command;
|
|
1091
|
+
if (rule.match === "pattern")
|
|
1092
|
+
return shape.segments.length === 1 && globAdmitsSpelling(rule.command, folded);
|
|
809
1093
|
const bodyShape = ruleLaneShapeOf(rule.command, reading);
|
|
810
1094
|
if ("reject" in bodyShape || foldSpacing(rule.command) === undefined)
|
|
811
1095
|
return false;
|
|
@@ -815,9 +1099,9 @@ function admitsUnder(rule, command, reading) {
|
|
|
815
1099
|
}
|
|
816
1100
|
export const EDITED_RULE_BREADTH_WARNING_CODES = Object.freeze(["broad_prefix", "compound_prefix"]);
|
|
817
1101
|
export function ruleBreadthWarningsOf(rule) {
|
|
818
|
-
if (rule.match !== "prefix" && rule.match !== "wildcard")
|
|
1102
|
+
if (rule.match !== "prefix" && rule.match !== "wildcard" && rule.match !== "pattern")
|
|
819
1103
|
return [];
|
|
820
|
-
const bodyShape = ruleLaneShapeOf(rule.command, MATCH_READING);
|
|
1104
|
+
const bodyShape = ruleLaneShapeOf(rule.match === "pattern" ? globFloorText(rule.command) : rule.command, MATCH_READING);
|
|
821
1105
|
if ("reject" in bodyShape)
|
|
822
1106
|
return [];
|
|
823
1107
|
if (bodyShape.segments.length > 1) {
|
|
@@ -1102,8 +1386,10 @@ export function adjudicatePersistedPathRules(rules, call, views, anchorsOf, lead
|
|
|
1102
1386
|
return undefined;
|
|
1103
1387
|
}
|
|
1104
1388
|
export function adjudicatePersistedNameRules(rules, call) {
|
|
1105
|
-
|
|
1106
|
-
|
|
1389
|
+
const name = nameRuleSubjectOf(call.tool, call.request.args);
|
|
1390
|
+
const values = call.request.face?.nameValues;
|
|
1391
|
+
if (name === undefined) {
|
|
1392
|
+
if (nameValueAbsentReason(call.tool) === undefined)
|
|
1107
1393
|
return undefined;
|
|
1108
1394
|
for (const behavior of ["deny", "ask"]) {
|
|
1109
1395
|
const row = rules.find((rule) => rule.match === "name" && eligiblePersisted(rule, call, behavior));
|
|
@@ -1114,21 +1400,48 @@ export function adjudicatePersistedNameRules(rules, call) {
|
|
|
1114
1400
|
}
|
|
1115
1401
|
for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
|
|
1116
1402
|
for (const rule of rules) {
|
|
1117
|
-
if (eligiblePersisted(rule, call, behavior)
|
|
1403
|
+
if (!eligiblePersisted(rule, call, behavior))
|
|
1404
|
+
continue;
|
|
1405
|
+
const arm = mountedArgumentArmOf(rule);
|
|
1406
|
+
if (arm !== undefined && paramRuleReaches(call.request, arm.param, arm.pattern))
|
|
1118
1407
|
return { behavior, rules: [rule] };
|
|
1408
|
+
const reach = nameRuleReach(rule, name, values);
|
|
1409
|
+
if (reach === "reached")
|
|
1410
|
+
return { behavior, rules: [rule] };
|
|
1411
|
+
if (reach === "unread" && behavior !== "allow")
|
|
1412
|
+
return behavior === "deny" ? { behavior, rules: [rule] } : { behavior, rules: [rule], unreadable: nameValuesUnreadReason(call.tool) };
|
|
1119
1413
|
}
|
|
1120
1414
|
}
|
|
1121
1415
|
return undefined;
|
|
1122
1416
|
}
|
|
1123
1417
|
export function adjudicatePersistedDomainRules(rules, call) {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1418
|
+
for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
|
|
1419
|
+
const reading = behavior === "allow" ? call.hosts.grant : call.hosts.tighten;
|
|
1420
|
+
if (reading === undefined)
|
|
1421
|
+
continue;
|
|
1422
|
+
const speaks = (rule) => (rule.match === "host" || rule.match === "subdomain") && eligiblePersisted(rule, call, behavior);
|
|
1423
|
+
if ("unreadable" in reading) {
|
|
1424
|
+
if (behavior === "allow")
|
|
1425
|
+
continue;
|
|
1426
|
+
const row = rules.find(speaks);
|
|
1427
|
+
if (row === undefined)
|
|
1428
|
+
continue;
|
|
1429
|
+
return behavior === "deny" ? { behavior: "deny", rules: [row] } : { behavior: "ask", rules: [row], unreadable: `the host the call reaches cannot be read (${HOST_UNREADABLE_SENTENCE[reading.unreadable]})` };
|
|
1430
|
+
}
|
|
1431
|
+
const row = rules.find((rule) => speaks(rule) && domainRuleAdmits(rule, reading.host));
|
|
1432
|
+
if (row !== undefined)
|
|
1433
|
+
return { behavior, rules: [row] };
|
|
1129
1434
|
}
|
|
1130
1435
|
return undefined;
|
|
1131
1436
|
}
|
|
1437
|
+
export function suggestRuleForHost(tool, host) {
|
|
1438
|
+
if (ruleToolGrammarOf(tool) !== "domain")
|
|
1439
|
+
return undefined;
|
|
1440
|
+
const parsed = parseRuleText(formatRuleText(host, "host", tool), "allow");
|
|
1441
|
+
if (!("rule" in parsed) || parsed.rule.match !== "host" || parsed.rule.command !== host)
|
|
1442
|
+
return undefined;
|
|
1443
|
+
return { kind: "single", rule: parsed.rule.rule, match: "host", command: host };
|
|
1444
|
+
}
|
|
1132
1445
|
export function suggestRuleForName(tool, name) {
|
|
1133
1446
|
if (ruleToolGrammarOf(tool) !== "name")
|
|
1134
1447
|
return undefined;
|
|
@@ -1163,6 +1476,7 @@ export function segmentCoverageOf(command, rules, call) {
|
|
|
1163
1476
|
});
|
|
1164
1477
|
}
|
|
1165
1478
|
export const RULE_OFFER_BATCH_MEMBER_KINDS = Object.freeze(["command", "directoryRead"]);
|
|
1479
|
+
export const RULE_OFFER_KINDS = Object.freeze(["single", "batch"]);
|
|
1166
1480
|
export const UNCOVERED_SEGMENT_REASONS = Object.freeze(["redirection", "no_rule_form", "cap_overflow"]);
|
|
1167
1481
|
export const UNCOVERED_SEGMENT_REASON_BASELINE = {
|
|
1168
1482
|
redirection: "This part carries a redirection, so it is approved per use — no rule can cover this spelling. If you trust its redirection-free form, a rule can be written for that form (the card's edit row, or settings).",
|