@sema-agent/core 7.51.2 → 7.53.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 +698 -2
- package/README.md +3 -1
- package/dist/agents/agent-transcript-tool.d.ts +1 -1
- package/dist/agents/cascade.js +4 -0
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -0
- package/dist/agents/cumulative-stats.d.ts +3 -0
- package/dist/agents/cumulative-stats.js +3 -0
- package/dist/agents/list-agents-tool.d.ts +1 -1
- package/dist/agents/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -5
- package/dist/agents/peer-session-drain.js +271 -180
- package/dist/agents/read-inbox-tool.d.ts +8 -1
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/repair-loop.js +4 -0
- package/dist/agents/send-message-tool.d.ts +11 -16
- package/dist/agents/send-message-tool.js +11 -13
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +105 -76
- package/dist/agents/tool-filter.d.ts +58 -13
- package/dist/agents/tool-filter.js +34 -14
- package/dist/bench/metrics.d.ts +1 -0
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +17 -13
- package/dist/brain/model-presets.d.ts +4 -5
- package/dist/brain/model-presets.js +6 -5
- package/dist/brain/open-responses.js +16 -12
- package/dist/brain/openai.js +16 -12
- package/dist/brain/request-params.d.ts +12 -33
- package/dist/brain/request-params.js +5 -2
- package/dist/brain/request-route.d.ts +9 -8
- package/dist/brain/request-route.js +1 -0
- package/dist/brain/route-adjudicator.d.ts +123 -47
- package/dist/brain/route-adjudicator.js +171 -42
- package/dist/brain/route-conformance.js +2 -2
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/status-sink.d.ts +18 -0
- package/dist/brain/status-sink.js +7 -0
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +30 -13
- package/dist/core/allow-entry.d.ts +42 -9
- package/dist/core/allow-entry.js +25 -18
- package/dist/core/auto-compaction.d.ts +80 -20
- package/dist/core/auto-compaction.js +65 -18
- package/dist/core/auto-promote.d.ts +2 -1
- package/dist/core/auto-promote.js +2 -2
- package/dist/core/caller-tool-name.d.ts +9 -0
- package/dist/core/caller-tool-name.js +13 -0
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +57 -24
- package/dist/core/checkpoint-store.js +3 -3
- package/dist/core/child-clamp-seats.d.ts +6 -0
- package/dist/core/child-clamp-seats.js +2 -1
- package/dist/core/compliance.js +3 -1
- package/dist/core/credential-vocabulary.d.ts +33 -8
- package/dist/core/credential-vocabulary.js +32 -7
- package/dist/core/decision-receipt.d.ts +2 -2
- package/dist/core/delegation-frames.d.ts +9 -2
- package/dist/core/delegation-name.d.ts +18 -0
- package/dist/core/delegation-name.js +47 -0
- package/dist/core/effective-path-target.d.ts +25 -6
- package/dist/core/effective-path-target.js +24 -2
- package/dist/core/engine-capabilities.d.ts +8 -1
- package/dist/core/engine-capabilities.js +1 -1
- package/dist/core/engine-notice.d.ts +49 -21
- package/dist/core/frontmatter-fence.d.ts +10 -0
- package/dist/core/frontmatter-fence.js +6 -0
- package/dist/core/gate-fold.js +8 -25
- package/dist/core/gate-lanes.js +358 -274
- package/dist/core/gate-outcome.d.ts +11 -6
- package/dist/core/governance-codes.d.ts +11 -4
- package/dist/core/governance-codes.js +12 -3
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +283 -739
- package/dist/core/hooks.js +100 -193
- package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
- package/dist/{tools/fs → core}/image-admission.js +24 -6
- package/dist/core/locked-config.d.ts +84 -32
- package/dist/core/locked-config.js +128 -42
- package/dist/core/mcp-connection-pool.d.ts +4 -1
- package/dist/core/mcp-connection-pool.js +7 -2
- package/dist/core/mcp-probe.d.ts +12 -9
- package/dist/core/mcp-probe.js +3 -3
- package/dist/core/mcp-server-spec.d.ts +104 -10
- package/dist/core/mcp-server-spec.js +183 -33
- package/dist/core/mcp.d.ts +31 -20
- package/dist/core/mcp.js +66 -56
- package/dist/core/memory-engine/data-plane.js +5 -1
- package/dist/core/memory-engine/distiller.js +16 -2
- package/dist/core/memory-engine/engine.d.ts +22 -30
- package/dist/core/memory-engine/engine.js +16 -10
- package/dist/core/memory-engine/frontmatter.d.ts +6 -0
- package/dist/core/memory-engine/frontmatter.js +19 -16
- package/dist/core/memory.d.ts +2 -2
- package/dist/core/mode-policy.d.ts +5 -16
- package/dist/core/mode-policy.js +7 -19
- package/dist/core/model-seat.d.ts +4 -1
- package/dist/core/mods/dollar.d.ts +109 -0
- package/dist/core/mods/dollar.js +154 -0
- package/dist/core/mods/host.d.ts +23 -0
- package/dist/core/mods/host.js +21 -0
- package/dist/core/mods/kernel/budget.d.ts +43 -0
- package/dist/core/mods/kernel/budget.js +83 -0
- package/dist/core/mods/kernel/dispatch.d.ts +100 -0
- package/dist/core/mods/kernel/dispatch.js +699 -0
- package/dist/core/mods/kernel/events.d.ts +245 -0
- package/dist/core/mods/kernel/events.js +210 -0
- package/dist/core/mods/kernel/index.d.ts +6 -0
- package/dist/core/mods/kernel/index.js +5 -0
- package/dist/core/mods/kernel/receive.d.ts +94 -0
- package/dist/core/mods/kernel/receive.js +188 -0
- package/dist/core/mods/kernel/registration.d.ts +131 -0
- package/dist/core/mods/kernel/registration.js +102 -0
- package/dist/core/mods/kernel/site-rules.d.ts +149 -0
- package/dist/core/mods/kernel/site-rules.js +296 -0
- package/dist/core/mods/stations.d.ts +344 -0
- package/dist/core/mods/stations.js +437 -0
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +13 -23
- package/dist/core/parent-spec-seat.d.ts +18 -7
- package/dist/core/parent-spec-seat.js +6 -2
- package/dist/core/path-identity.js +2 -1
- package/dist/core/permission-rule-model.d.ts +58 -9
- package/dist/core/permission-rule-model.js +81 -7
- package/dist/core/permission-rule-org.d.ts +163 -33
- package/dist/core/permission-rule-org.js +205 -71
- package/dist/core/permission-rule-syntax.d.ts +15 -0
- package/dist/core/permission-rule-syntax.js +24 -16
- package/dist/core/permission-rules.d.ts +14 -2
- package/dist/core/permission-rules.js +63 -23
- package/dist/core/prompt-section-seat.d.ts +15 -27
- package/dist/core/prompt-section-seat.js +73 -40
- package/dist/core/protocol-http-auth.d.ts +3 -1
- package/dist/core/protocol-http-auth.js +2 -0
- package/dist/core/reminder-mint.d.ts +2 -2
- package/dist/core/remote-env.d.ts +24 -12
- package/dist/core/remote-env.js +1 -0
- package/dist/core/resume-startup-cause.d.ts +16 -1
- package/dist/core/resume-startup-cause.js +4 -1
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +12 -2
- package/dist/core/retired-keys.js +15 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/announce-once-ledger.d.ts +31 -1
- package/dist/core/runner/announce-once-ledger.js +22 -0
- package/dist/core/runner/assemble-result.d.ts +9 -0
- package/dist/core/runner/assemble-result.js +3 -1
- package/dist/core/runner/call-hook-stations.d.ts +23 -27
- package/dist/core/runner/call-hook-stations.js +58 -42
- package/dist/core/runner/caller-stop-gate.d.ts +25 -0
- package/dist/core/runner/caller-stop-gate.js +7 -0
- package/dist/core/runner/compaction-seams.d.ts +8 -22
- package/dist/core/runner/compaction-seams.js +28 -53
- package/dist/core/runner/construction-doors.js +11 -1
- package/dist/core/runner/contracts.d.ts +86 -83
- package/dist/core/runner/credential-origin-seat.d.ts +2 -0
- package/dist/core/runner/credential-origin-seat.js +19 -0
- package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
- package/dist/core/runner/gate-exit.d.ts +47 -36
- package/dist/core/runner/gate-exit.js +12 -5
- package/dist/core/runner/image.js +1 -2
- package/dist/core/runner/inline-image.js +1 -1
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- package/dist/core/runner/notice-seat-input.d.ts +11 -0
- package/dist/core/runner/notice-seat-input.js +1 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +3 -1
- package/dist/core/runner/person-path-rows.js +3 -2
- package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
- package/dist/core/runner/prepare-ask-lane.js +6 -15
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
- package/dist/core/runner/prepare-config-doors.d.ts +52 -1
- package/dist/core/runner/prepare-config-doors.js +168 -143
- package/dist/core/runner/prepare-defer-classify.js +7 -11
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.js +7 -5
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
- package/dist/core/runner/prepare-gate-stations.js +11 -10
- package/dist/core/runner/prepare-hands-readface.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -1
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
- package/dist/core/runner/prepare-inherited-gate.js +10 -23
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +20 -7
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-park-ask.js +10 -14
- package/dist/core/runner/prepare-policy-chain.d.ts +17 -15
- package/dist/core/runner/prepare-policy-chain.js +104 -109
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +11 -12
- package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
- package/dist/core/runner/prepare-prompt-assembly.js +138 -101
- package/dist/core/runner/prepare-prompt-inputs.d.ts +28 -23
- package/dist/core/runner/prepare-prompt-inputs.js +11 -10
- package/dist/core/runner/prepare-protocol-tools.d.ts +11 -7
- package/dist/core/runner/prepare-protocol-tools.js +20 -13
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- package/dist/core/runner/prepare-safety-scan.js +6 -10
- package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
- package/dist/core/runner/prepare-suspend-saga.js +7 -5
- package/dist/core/runner/prepare-task.js +41 -23
- package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
- package/dist/core/runner/prepare-turn-wiring.js +29 -27
- package/dist/core/runner/prepare-wiring-manifest.d.ts +13 -9
- package/dist/core/runner/prepare-wiring-manifest.js +14 -11
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +7 -5
- package/dist/core/runner/resume-apply.d.ts +5 -2
- package/dist/core/runner/resume-apply.js +24 -42
- package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
- package/dist/core/runner/resume-checkpoint-screen.js +9 -1
- package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/resume-preflight.js +38 -16
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +4 -5
- package/dist/core/runner/run-brain-sinks.d.ts +3 -2
- package/dist/core/runner/run-brain-sinks.js +5 -1
- package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
- package/dist/core/runner/run-compaction-machinery.js +7 -33
- package/dist/core/runner/run-harness-handlers.js +4 -2
- package/dist/core/runner/run-identity-wiring.d.ts +3 -1
- package/dist/core/runner/run-identity-wiring.js +3 -2
- package/dist/core/runner/run-leg.js +103 -95
- package/dist/core/runner/run-notification-lane.js +1 -0
- package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
- package/dist/core/runner/run-recovery-lanes.js +14 -11
- package/dist/core/runner/run-settle-and-teardown.js +9 -28
- package/dist/core/runner/run-stop-and-final-verify.js +18 -25
- package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
- package/dist/core/runner/run-terminal-adoption.js +34 -22
- package/dist/core/runner/run-tool-mount-facts.js +5 -3
- package/dist/core/runner/run-turn-boundary.js +32 -37
- package/dist/core/runner/runtask.d.ts +6 -7
- package/dist/core/runner/runtask.js +15 -73
- package/dist/core/runner/session-rule-policy.d.ts +5 -5
- package/dist/core/runner/session-rule-policy.js +13 -8
- package/dist/core/runner/side-call-account.d.ts +68 -0
- package/dist/core/runner/side-call-account.js +61 -0
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/stream-halt-verbs.js +12 -17
- package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
- package/dist/core/runner/stream-steer-verb.d.ts +1 -1
- package/dist/core/runner/stream-steer-verb.js +20 -38
- package/dist/core/runner/sub-agent-deny-lists.d.ts +124 -23
- package/dist/core/runner/sub-agent-deny-lists.js +230 -71
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner/tool-end-body.d.ts +5 -6
- package/dist/core/runner/tool-end-body.js +6 -4
- package/dist/core/runner/tool-not-executed.d.ts +15 -0
- package/dist/core/runner/tool-not-executed.js +30 -0
- package/dist/core/runner/tool-occurrence.d.ts +8 -7
- package/dist/core/runner/tool-occurrence.js +5 -5
- package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
- package/dist/core/runner-deps.d.ts +43 -36
- package/dist/core/safety-merge-corpus.js +18 -2
- package/dist/core/secret-env.d.ts +88 -20
- package/dist/core/secret-env.js +192 -38
- package/dist/core/session-policy-store.d.ts +71 -16
- package/dist/core/session-policy-store.js +138 -28
- package/dist/core/session-reconcile.d.ts +4 -0
- package/dist/core/session-reconcile.js +2 -1
- package/dist/core/shell-read-silence.js +3 -32
- package/dist/core/side-query.d.ts +5 -3
- package/dist/core/skill-grant.d.ts +3 -2
- package/dist/core/skill-grant.js +17 -9
- package/dist/core/skills-directory.d.ts +31 -11
- package/dist/core/skills-directory.js +655 -104
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/task-event.d.ts +49 -3
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-result.d.ts +8 -0
- package/dist/core/task-spec.d.ts +20 -19
- package/dist/core/task-stream.d.ts +14 -4
- package/dist/core/tighten-task-spec.d.ts +4 -3
- package/dist/core/tighten-task-spec.js +2 -4
- package/dist/core/tool-check-reading.d.ts +74 -0
- package/dist/core/tool-check-reading.js +80 -0
- package/dist/core/tool-policy.d.ts +62 -18
- package/dist/core/tool-policy.js +86 -15
- package/dist/core/tool-registry.d.ts +20 -0
- package/dist/core/tool-registry.js +4 -0
- package/dist/core/tool-spec.d.ts +10 -6
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +51 -32
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/unc-spelling.d.ts +10 -0
- package/dist/core/unc-spelling.js +3 -0
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-egress.js +4 -1
- package/dist/core/untrusted-text.d.ts +16 -0
- package/dist/core/untrusted-text.js +7 -2
- package/dist/core/user-attachment.d.ts +25 -4
- package/dist/core/user-attachment.js +63 -42
- package/dist/core/wiring-manifest.d.ts +18 -15
- package/dist/core/wiring-manifest.js +2 -2
- package/dist/engine/compaction/utils.js +1 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
- package/dist/engine/execution-env/symlink-chain.js +1 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/engine/harness/agent-harness.js +5 -4
- package/dist/engine/harness/types.d.ts +20 -16
- package/dist/engine/harness/types.js +7 -1
- package/dist/engine/llm/types.d.ts +12 -7
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +18 -6
- package/dist/engine/loop/types.d.ts +8 -2
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
- package/dist/engine/session/import-validate.js +3 -10
- package/dist/engine/session/session.d.ts +4 -4
- package/dist/engine/session/session.js +4 -4
- package/dist/index.d.ts +33 -23
- package/dist/index.js +23 -15
- package/dist/internal/harness.d.ts +2 -1
- package/dist/internal/harness.js +2 -1
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/orchestration/run-spec.d.ts +4 -6
- package/dist/orchestration/run-spec.js +39 -63
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow.d.ts +11 -3
- package/dist/orchestration/workflow.js +11 -2
- package/dist/prompt-assembly/artifact-store.d.ts +72 -56
- package/dist/prompt-assembly/artifact-store.js +60 -52
- package/dist/prompt-assembly/artifact.d.ts +90 -76
- package/dist/prompt-assembly/artifact.js +197 -145
- package/dist/prompt-assembly/assemble.d.ts +36 -29
- package/dist/prompt-assembly/assemble.js +34 -220
- package/dist/prompt-assembly/bands.d.ts +20 -0
- package/dist/prompt-assembly/bands.js +18 -0
- package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
- package/dist/prompt-assembly/catalog/constitution.js +69 -0
- package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
- package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
- package/dist/prompt-assembly/catalog/index.d.ts +11 -0
- package/dist/prompt-assembly/catalog/index.js +60 -0
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
- package/dist/prompt-assembly/catalog/modes.js +137 -0
- package/dist/prompt-assembly/catalog/role.d.ts +10 -0
- package/dist/prompt-assembly/catalog/role.js +157 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +138 -0
- package/dist/prompt-assembly/catalog/schema.js +25 -0
- package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
- package/dist/prompt-assembly/catalog/sections.js +98 -0
- package/dist/prompt-assembly/catalog/team.d.ts +4 -0
- package/dist/prompt-assembly/catalog/team.js +258 -0
- package/dist/prompt-assembly/composer.d.ts +26 -28
- package/dist/prompt-assembly/composer.js +124 -46
- package/dist/prompt-assembly/epoch.d.ts +89 -79
- package/dist/prompt-assembly/epoch.js +115 -142
- package/dist/prompt-assembly/explain.d.ts +58 -34
- package/dist/prompt-assembly/explain.js +21 -24
- package/dist/prompt-assembly/overlay.d.ts +137 -0
- package/dist/prompt-assembly/overlay.js +339 -0
- package/dist/prompt-assembly/renderers.d.ts +21 -0
- package/dist/prompt-assembly/renderers.js +39 -0
- package/dist/prompt-assembly/section-usage.d.ts +3 -3
- package/dist/prompt-assembly/section-usage.js +1 -1
- package/dist/prompt-assembly/select.d.ts +71 -0
- package/dist/prompt-assembly/select.js +106 -0
- package/dist/prompt-assembly/sugar.d.ts +31 -0
- package/dist/prompt-assembly/sugar.js +20 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
- package/dist/prompt-assembly/types.d.ts +45 -148
- package/dist/prompts/coordinator.d.ts +2 -21
- package/dist/prompts/coordinator.js +3 -255
- package/dist/prompts/cowork.d.ts +27 -16
- package/dist/prompts/cowork.js +2 -23
- package/dist/prompts/default.d.ts +38 -511
- package/dist/prompts/default.js +20 -288
- package/dist/prompts/supervisor.d.ts +6 -71
- package/dist/prompts/supervisor.js +6 -117
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +4 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
- package/dist/tools/fs/dir-entries.d.ts +4 -2
- package/dist/tools/fs/dir-entries.js +16 -4
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +22 -11
- package/dist/tools/fs/fs-pdf.js +17 -13
- package/dist/tools/fs/fs-read.js +4 -3
- package/dist/tools/fs/fs-shared.d.ts +1 -10
- package/dist/tools/fs/fs-shared.js +3 -4
- package/dist/tools/fs/fs-write.js +2 -1
- package/dist/tools/fs/notebook.js +1 -1
- package/dist/tools/fs/pdf.d.ts +20 -10
- package/dist/tools/fs/pdf.js +19 -10
- package/dist/tools/fs/program-read-model.d.ts +68 -8
- package/dist/tools/fs/program-read-model.js +112 -33
- package/dist/tools/fs/program-read-model.rows.js +30 -10
- package/dist/tools/fs/read-deny-home.d.ts +25 -9
- package/dist/tools/fs/read-deny-home.js +28 -27
- package/dist/tools/fs/read-deny.d.ts +41 -11
- package/dist/tools/fs/read-deny.js +51 -5
- package/dist/tools/fs/safety.d.ts +4 -26
- package/dist/tools/fs/safety.js +10 -32
- package/dist/tools/fs/search.js +15 -10
- package/dist/tools/fs/shell-read-argv.d.ts +3 -2
- package/dist/tools/fs/shell-read-argv.js +118 -38
- package/dist/tools/fs/shell-read-establish.d.ts +5 -5
- package/dist/tools/fs/shell-read-establish.js +11 -61
- package/dist/tools/fs/shell-read-identity.js +6 -3
- package/dist/tools/fs/shell-read-judge.d.ts +13 -15
- package/dist/tools/fs/shell-read-judge.js +18 -4
- package/dist/tools/fs/shell-read-plan.d.ts +2 -2
- package/dist/tools/web.js +1 -1
- package/package.json +13 -1
- package/test/export-surface.snapshot.json +306 -103
- package/wire-changes/core@7.52.0.json +5859 -0
- package/wire-changes/core@7.53.0.json +1946 -0
- package/dist/core/hook-seats.d.ts +0 -66
- package/dist/core/hook-seats.js +0 -37
- package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
- package/dist/prompt-assembly/packs/sema-default.js +0 -147
- package/dist/prompts/simple-sections.d.ts +0 -58
- package/dist/prompts/simple-sections.js +0 -43
|
@@ -34,8 +34,30 @@
|
|
|
34
34
|
* refused and disclosed — identity comparison alone cannot catch it, and an in-memory-only mark would
|
|
35
35
|
* accept revision N−1 after a restart. Signature / predecessor chaining belongs to the privilege-
|
|
36
36
|
* separation ticket; the high-water mark is the floor that needs no key distribution.
|
|
37
|
+
*
|
|
38
|
+
* ## One reading of a row
|
|
39
|
+
*
|
|
40
|
+
* A snapshot is installed and served on its ENVELOPE ({@link validateOrgSnapshot}: revision, observation
|
|
41
|
+
* time, a rules list) and the anti-rollback fence; what its rows say never decides which snapshot is in
|
|
42
|
+
* force. Whether the rules are a list is judged in ONE place ({@link listRefusalOf}) for the partition and for
|
|
43
|
+
* every store that hands a resolution over — rules that are not a list, or that carry more places than a rule set may
|
|
44
|
+
* (the same cap the rule compiler holds a set to), are governance nobody can read (the
|
|
45
|
+
* partition refuses the snapshot; the org lane answers unavailable), never a policy of zero rows — and ONE reader
|
|
46
|
+
* ({@link orgRuleListOf}) copies the whole list in one receipt: a member of a row that cannot be read is left out of
|
|
47
|
+
* that row's copy alone, and every row is judged on its copy. A snapshot the partition holds — fetched or read back
|
|
48
|
+
* from its persistence — is that same copy ({@link orgSnapshotCopyOf}). Every row is then read by ONE reader
|
|
49
|
+
* ({@link readOrgRules}), the same for every store that hands a resolution over, on its policy members
|
|
50
|
+
* ({@link ORG_ROW_POLICY_MEMBERS}): a row is READ when its behavior is `deny` or `ask` and its text is a shell command
|
|
51
|
+
* rule under that behavior. Any other row — another grammar's rule, a shell rule the validator refuses, a row
|
|
52
|
+
* with no text, a behavior this layer has no bucket for — is UNREADABLE, and stands at its strictest
|
|
53
|
+
* reading over the calls it may reach ({@link OrgRowReach}): an `ask` row asks a person, every other row
|
|
54
|
+
* refuses. Readable deny and ask rows hold as published beside it. One unreadable row therefore never
|
|
55
|
+
* takes the rest of the policy down with it, and never turns a published deny into a question; the
|
|
56
|
+
* snapshot is served DEGRADED, and every resolution and every adjudication over it names the unreadable
|
|
57
|
+
* rows ({@link describeUnreadableOrgRows}).
|
|
37
58
|
*/
|
|
38
|
-
import { type PersistedRule, type RuleRejectCode } from "./permission-rule-model.js";
|
|
59
|
+
import { type ParsedRule, type PersistedRule, type RuleRejectCode } from "./permission-rule-model.js";
|
|
60
|
+
import type { DenyFace } from "../tools/fs/read-deny.js";
|
|
39
61
|
import type { PermissionRuleStore } from "./permission-rule-provider.js";
|
|
40
62
|
/** One org rule. There is structurally no allow bucket (design/179 §9: the org layer only tightens). */
|
|
41
63
|
export interface OrgPermissionRule {
|
|
@@ -171,42 +193,146 @@ export declare const UNGOVERNED_ORG_RESOLUTION: Readonly<OrgRuleResolution>;
|
|
|
171
193
|
* bound is refused loudly, never coerced.
|
|
172
194
|
*/
|
|
173
195
|
export declare function createOrgRulePartition(cfg: OrgRulePartitionConfig): OrgRulePartition;
|
|
196
|
+
/** Whether two snapshots carry the same POLICY: row for row, the same policy members ({@link ORG_ROW_POLICY_MEMBERS}) — never
|
|
197
|
+
* observation metadata, never a member no judgment reads (which a copy may carry or leave out). The ONE test of "the same
|
|
198
|
+
* revision, the same content": the partition's equal-revision guard asks it of two snapshots it holds in one form
|
|
199
|
+
* ({@link orgSnapshotCopyOf}), and a persistence backend asks it of what it stores. */
|
|
200
|
+
export declare function sameOrgPolicyContent(a: OrgRuleSnapshot, b: OrgRuleSnapshot): boolean;
|
|
174
201
|
/**
|
|
175
|
-
* Which
|
|
176
|
-
* that can match nothing on this build. Empty ⟺ every rule is enforceable.
|
|
202
|
+
* Which calls an UNREADABLE row may reach — the calls its strictest reading stands over.
|
|
177
203
|
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
204
|
+
* `"every"` when the row names no tool this layer can place: no rule text, a head the rule syntax cannot read, the lone
|
|
205
|
+
* `*`, or a tool with no rule grammar (a misspelled tool name and a tool this build does not know look the same, and
|
|
206
|
+
* both may be the shell). Otherwise the rule WORD it names — a shell tool's head is the command word, as a call's is —
|
|
207
|
+
* and, for a tool whose path rules speak for a face ({@link PATH_RULE_FACES}), the faces of the column its strictest
|
|
208
|
+
* behavior reads: such a row reaches what a readable rule of its tool could reach, and nothing else.
|
|
209
|
+
*/
|
|
210
|
+
export type OrgRowReach = "every" | {
|
|
211
|
+
readonly words: readonly string[];
|
|
212
|
+
readonly faces: readonly DenyFace[];
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* A row the layer cannot read: its text (as published; the rendering of the scalar its `rule` holds when that is not a string;
|
|
216
|
+
* `<unreadable>` when it holds none — a structure is no value, {@link orgRowPolicyOf}), its 1-based place in the
|
|
217
|
+
* snapshot, why (`code`, and `detail` for a row whose shape — not its text — is at fault), the behavior it stands at, and
|
|
218
|
+
* the calls it reaches. `behavior` is the row's STRICTEST reading: `ask` only when the row says `ask`, `deny` for a row
|
|
219
|
+
* that says `deny` and for one whose behavior cannot be read.
|
|
220
|
+
*/
|
|
221
|
+
export interface UnreadableOrgRow {
|
|
222
|
+
readonly kind: "unreadable";
|
|
223
|
+
readonly index: number;
|
|
224
|
+
readonly rule: string;
|
|
225
|
+
readonly code: RuleRejectCode;
|
|
226
|
+
readonly detail?: string;
|
|
227
|
+
readonly behavior: "deny" | "ask";
|
|
228
|
+
readonly reach: OrgRowReach;
|
|
229
|
+
}
|
|
230
|
+
/** A row the layer reads: a shell command rule under the behavior it publishes. */
|
|
231
|
+
export interface ReadOrgRow {
|
|
232
|
+
readonly kind: "read";
|
|
233
|
+
readonly index: number;
|
|
234
|
+
readonly rule: string;
|
|
235
|
+
readonly behavior: "deny" | "ask";
|
|
236
|
+
readonly parsed: ParsedRule;
|
|
237
|
+
}
|
|
238
|
+
/** One call as the org layer judges it: the rule words it answers to (`laneToolNamesOf`; one word is enough), its shell
|
|
239
|
+
* command when it is the shell tool's, and the face of its path target when it has one (`callPathFaceOf`). */
|
|
240
|
+
export interface OrgRuleCall {
|
|
241
|
+
readonly tool: string | readonly string[];
|
|
242
|
+
readonly command?: string;
|
|
243
|
+
readonly face?: DenyFace;
|
|
244
|
+
}
|
|
245
|
+
/** Every row of a snapshot read once, and the one judgment of a call against them. */
|
|
246
|
+
export interface OrgRulesReading {
|
|
247
|
+
readonly rows: readonly (ReadOrgRow | UnreadableOrgRow)[];
|
|
248
|
+
readonly unreadable: readonly UnreadableOrgRow[];
|
|
249
|
+
verdictFor(call: OrgRuleCall): OrgRuleVerdict | undefined;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The members of an org row the layer reads to judge a call — the row's POLICY. A row IS these members: every reader holds and
|
|
253
|
+
* judges a row as its policy ({@link orgRowPolicyOf}), and two rows with the same policy members are the same policy
|
|
254
|
+
* ({@link sameOrgPolicyContent}). Any other member a row carries is never judged, held, served or persisted (the receipt reads
|
|
255
|
+
* each of a row's own members once — a getter on one runs once — and copies none of what it holds beyond a scalar).
|
|
256
|
+
*/
|
|
257
|
+
export declare const ORG_ROW_POLICY_MEMBERS: readonly ["rule", "behavior"];
|
|
258
|
+
/** A JSON scalar — the only value a member the partition keeps holds as data. */
|
|
259
|
+
type Scalar = string | number | boolean | null;
|
|
260
|
+
type ScalarMembers<K extends string> = {
|
|
261
|
+
[P in K]?: Scalar;
|
|
262
|
+
};
|
|
263
|
+
type OrgRowPolicy = Readonly<ScalarMembers<(typeof ORG_ROW_POLICY_MEMBERS)[number]>>;
|
|
264
|
+
/**
|
|
265
|
+
* The envelope's rules LIST, read once: `refused` when it is not a list of at most the cap's places ({@link listRefusalOf} —
|
|
266
|
+
* judged before the list is received, so an oversized or sparse list builds no row) or cannot be received at all;
|
|
267
|
+
* otherwise its rows, each as its policy ({@link orgRowPolicyOf}) — the list received whole in one receipt
|
|
268
|
+
* ({@link ORG_RULES_RECEIPT}), so a row's member that cannot be read is that row's alone, a row that cannot be read at all (and
|
|
269
|
+
* a hole) is a row with nothing in it (`{}`: no rule text, read at its strictest reading) and the rows beside it are untouched.
|
|
270
|
+
* The ONE reader of the list — the partition holds a snapshot's rows as these, fetched and read back alike
|
|
271
|
+
* ({@link orgSnapshotCopyOf}), and every reading of rows ({@link readOrgRules}) reads them through it — so no store's rows can be
|
|
272
|
+
* read two ways, and no row can take the reading of another down with it.
|
|
273
|
+
*/
|
|
274
|
+
export declare function orgRuleListOf(rules: unknown): {
|
|
275
|
+
readonly rows: readonly OrgRowPolicy[];
|
|
276
|
+
} | {
|
|
277
|
+
readonly refused: string;
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* The ONE reading of an org snapshot's rows: each row read once ({@link readOrgRow}), and the one judgment of a call
|
|
281
|
+
* against them. Every reader of org rows goes through it — the partition's degraded line, the lane's adjudication,
|
|
282
|
+
* {@link orgRuleVerdictFor}, {@link unenforceableOrgRules} and the shadow view — so no two of them can read one row two ways.
|
|
283
|
+
*
|
|
284
|
+
* A call meets the rows in three answers. DENY: a readable deny reaches one of its program runs, or an unreadable row
|
|
285
|
+
* standing at deny reaches the call — the first such row in snapshot order speaks. ASK: a readable rule the call cannot be
|
|
286
|
+
* READ against (the lexer's reason — a rule that cannot be judged may be a deny, so it outranks a reached ask), else a
|
|
287
|
+
* readable ask reaching it or an unreadable row standing at ask reaching it, the first in snapshot order. Otherwise none.
|
|
288
|
+
* An unreadable row's verdict carries its reason ({@link unreadableOrgRowReason}) beside its text.
|
|
289
|
+
*
|
|
290
|
+
* The rows are read off the one list reader's copy ({@link orgRuleListOf}): rules that are not a list come back `refused` —
|
|
291
|
+
* the org's rules cannot be read, which is never a policy of zero rows — and a row is judged on what its copy holds: a policy
|
|
292
|
+
* member that could not be read is one the row does not have (no `rule` ⇒ no rule text, every call; no `behavior` ⇒ its
|
|
293
|
+
* strictest reading over the calls of the tool it names), read like any other unreadable row while the rows beside it keep
|
|
294
|
+
* their effect.
|
|
295
|
+
*/
|
|
296
|
+
export declare function readOrgRules(rules: unknown): OrgRulesReading | {
|
|
297
|
+
readonly refused: string;
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Which rows of an org snapshot this layer cannot read, each with its code — the rows that stand at their strictest
|
|
301
|
+
* reading instead of as published. Empty ⟺ every row is read. Every unreadable row is reported, a row with no rule text
|
|
302
|
+
* or a behavior other than deny / ask included (its text rendered when it is not a string). Rules that are not a list, or a
|
|
303
|
+
* list past the cap, throw the one list judgment's sentence ({@link readListedOrgRules}) — never a reading of fewer rows.
|
|
182
304
|
*/
|
|
183
305
|
export declare function unenforceableOrgRules(rules: readonly OrgPermissionRule[]): Array<{
|
|
184
306
|
rule: string;
|
|
185
307
|
code: RuleRejectCode;
|
|
186
308
|
}>;
|
|
309
|
+
/** The reason an unreadable row's verdict carries — its place in the snapshot, its code, and the repair. */
|
|
310
|
+
export declare function unreadableOrgRowReason(row: Pick<UnreadableOrgRow, "index" | "code" | "detail">): string;
|
|
311
|
+
/** The ONE clause for an org verdict that carries an `unreadable` reason — a rule the call could not be read against, or
|
|
312
|
+
* a row this layer cannot read — on a refusal and on a question alike. */
|
|
313
|
+
export declare function orgUnreadableClause(reason: string): string;
|
|
187
314
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* against a rule is an ASK with the reason — fail closed, never silence).
|
|
194
|
-
*
|
|
195
|
-
* The skip below is a defensive floor, and what stands behind it is worth stating precisely: rules that
|
|
196
|
-
* arrive from the org partition's `resolve` ({@link createOrgRulePartition}) have passed {@link validateOrgSnapshot} on BOTH the install
|
|
197
|
-
* and the serving path (backlog #177), so an unenforceable entry cannot reach here through the overlay
|
|
198
|
-
* — the whole snapshot is refused, or the resolution reports `unavailable`, and either way it is
|
|
199
|
-
* disclosed. A caller that hands this function a snapshot it obtained ELSEWHERE gets no such guarantee:
|
|
200
|
-
* the skip is silent, and naming the dead entries is that caller's job — {@link unenforceableOrgRules}
|
|
201
|
-
* is the shared way to do it.
|
|
315
|
+
* The ONE line naming a snapshot's unreadable rows (count, the first {@link DISCLOSED_UNREADABLE_ROWS} by place, text and
|
|
316
|
+
* code, the rest summarized), or `undefined` when every row is read. The partition puts it on every resolution that
|
|
317
|
+
* serves such a snapshot, and the org lane hands it to its operator sink on every adjudication over one, so the two seats
|
|
318
|
+
* describe one snapshot in one sentence. Each text is escaped — the characters that most often make a row unreadable are
|
|
319
|
+
* exactly the ones that do not render.
|
|
202
320
|
*/
|
|
203
|
-
export declare function
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
*
|
|
209
|
-
*
|
|
321
|
+
export declare function describeUnreadableOrgRows(unreadable: readonly UnreadableOrgRow[], revision?: unknown): string | undefined;
|
|
322
|
+
/**
|
|
323
|
+
* design/182 §7.2 — which org rule speaks for this call, if any: {@link readOrgRules}'s judgment, over a snapshot read for
|
|
324
|
+
* this call alone. Matching uses the SAME parser and the SAME tightening reach the personal deny/ask rows use
|
|
325
|
+
* ({@link programRunReachOf}: an interpreter-headed prefix deny like `Bash(node:*)` is a legitimately wide tightening and
|
|
326
|
+
* matches; a segment hit is a hit; a call the lexer cannot read against a rule is an ASK with the reason — fail closed,
|
|
327
|
+
* never silence), and a row this layer cannot read stands at its strictest reading over the calls it may reach. A caller
|
|
328
|
+
* judging many calls against one snapshot reads it once with {@link readOrgRules} instead. Rules that are not a list, or a
|
|
329
|
+
* list past the cap, throw the one list judgment's sentence ({@link readListedOrgRules}) — never a verdict over fewer rows.
|
|
330
|
+
*/
|
|
331
|
+
export declare function orgRuleVerdictFor(rules: readonly OrgPermissionRule[], call: OrgRuleCall): OrgRuleVerdict | undefined;
|
|
332
|
+
/** The org layer's answer for one call: which rule spoke, with what behavior — and, when the rule could not be judged
|
|
333
|
+
* against the call, why (`unreadable`): either the call could not be READ against it (the lexer's reason; `behavior` is
|
|
334
|
+
* then `ask` — the org published a rule that may reach this call, and nobody can say), or the row is not one this layer
|
|
335
|
+
* reads ({@link unreadableOrgRowReason}; `behavior` is the row's strictest reading). */
|
|
210
336
|
export interface OrgRuleVerdict {
|
|
211
337
|
readonly behavior: "deny" | "ask";
|
|
212
338
|
readonly rule: string;
|
|
@@ -234,14 +360,18 @@ export interface OrgRuleVerdict {
|
|
|
234
360
|
* either way. org ASK rules do not shadow: the personal lane never consumes a `requiresRealApproval`
|
|
235
361
|
* ask, so the two never actually meet on one call.
|
|
236
362
|
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
363
|
+
* An UNREADABLE row standing at deny refuses every call it reaches ({@link readOrgRules}), so it shadows every personal
|
|
364
|
+
* rule of a tool it reaches — by the rule word, or by the face that tool's calls are on — exactly as the gate refuses
|
|
365
|
+
* those calls; one standing at ask does not shadow, as a readable ask does not.
|
|
366
|
+
*
|
|
367
|
+
* design/382 §2.6 — no READABLE org row reaches a Read directory rule in this version: the org layer has no Read
|
|
368
|
+
* grammar, and the rule's `command` is a PATH that must not be fed to the shell lane's segment reader as though it
|
|
369
|
+
* were a command line.
|
|
241
370
|
*/
|
|
242
371
|
export declare function orgRuleShadows(orgRules: readonly OrgPermissionRule[], rule: Pick<PersistedRule, "tool" | "command">): boolean;
|
|
243
|
-
/** The shadow predicate with the org
|
|
372
|
+
/** The shadow predicate with the org rows read ONCE — what a read over many rows uses. */
|
|
244
373
|
export declare function compileOrgShadowPredicate(orgRules: readonly OrgPermissionRule[]): (rule: Pick<PersistedRule, "tool" | "command">) => boolean;
|
|
245
374
|
/** Does this store carry a write face? Re-exported convenience for org-ticket integrations that need to
|
|
246
375
|
* distinguish a read-only projection from a writable personal bucket without importing the writer seam. */
|
|
247
376
|
export declare function isWritablePermissionRuleStore(store: PermissionRuleStore): boolean;
|
|
377
|
+
export {};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { escapeForDisclosure, parseRuleText, programRunReachOf, ruleReachesProgramRun } from "./permission-rule-model.js";
|
|
1
|
+
import { COMMAND_RULE_TOOL, PATH_RULE_FACES, escapeForDisclosure, parseRuleText, programRunReachOf, ruleReachesProgramRun, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
2
|
+
import { isNameableToolName, ruleSpellingOf } from "./permission-rule-syntax.js";
|
|
3
|
+
import { isShellFaceToolName, pathTargetOf } from "./tool-registry.js";
|
|
2
4
|
import { writerOf } from "./permission-rule-store.js";
|
|
5
|
+
import { canonicalize } from "./canonical-json.js";
|
|
6
|
+
import { types } from "node:util";
|
|
7
|
+
import { describeFault, receive } from "./mods/kernel/receive.js";
|
|
8
|
+
import { DEFAULT_CAPS } from "./permission-rules.js";
|
|
3
9
|
export const ORG_UNAVAILABLE_DECISION_REASON = "org_unavailable";
|
|
4
10
|
export const ORG_ADJUDICATION_TIMEOUT_MS = 15_000;
|
|
5
11
|
export function settleOrgVerdictWithin(p, fallback, opts) {
|
|
@@ -48,7 +54,7 @@ export function createOrgRulePartition(cfg) {
|
|
|
48
54
|
let memoryState;
|
|
49
55
|
const readState = async () => {
|
|
50
56
|
const state = cfg.persistence !== undefined ? await cfg.persistence.readOrgState() : memoryState;
|
|
51
|
-
return state === undefined ? undefined :
|
|
57
|
+
return state === undefined ? undefined : orgStateCopyOf(state);
|
|
52
58
|
};
|
|
53
59
|
const writeState = async (state) => {
|
|
54
60
|
if (cfg.persistence !== undefined)
|
|
@@ -75,7 +81,7 @@ export function createOrgRulePartition(cfg) {
|
|
|
75
81
|
throw new Error(`the org snapshot source did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms`);
|
|
76
82
|
if ("e" in settled)
|
|
77
83
|
throw settled.e;
|
|
78
|
-
fetched =
|
|
84
|
+
fetched = orgSnapshotCopyOf(settled.v);
|
|
79
85
|
}
|
|
80
86
|
catch (err) {
|
|
81
87
|
fetchFailed = true;
|
|
@@ -152,6 +158,9 @@ export function createOrgRulePartition(cfg) {
|
|
|
152
158
|
if (!freshlyInstalled && !fetchFailed && fetched === undefined) {
|
|
153
159
|
disclosures.push("org snapshot source returned no snapshot — continuing on the last-known-good");
|
|
154
160
|
}
|
|
161
|
+
const degraded = describeUnreadableOrgRows(readListedOrgRules(installed.snapshot.rules).unreadable, installed.snapshot.revision);
|
|
162
|
+
if (degraded !== undefined)
|
|
163
|
+
disclosures.push(degraded);
|
|
155
164
|
return {
|
|
156
165
|
source: "org",
|
|
157
166
|
status: freshlyInstalled ? "fresh" : "last-known-good",
|
|
@@ -188,34 +197,187 @@ function persistedMarkIsCoherent(state) {
|
|
|
188
197
|
Number.isFinite(state.snapshot.revision) &&
|
|
189
198
|
state.revisionHighWater >= state.snapshot.revision);
|
|
190
199
|
}
|
|
191
|
-
function sameOrgPolicyContent(a, b) {
|
|
200
|
+
export function sameOrgPolicyContent(a, b) {
|
|
192
201
|
if (a.rules.length !== b.rules.length)
|
|
193
202
|
return false;
|
|
194
|
-
|
|
203
|
+
const policy = (r) => canonicalize(orgRowPolicyOf(r));
|
|
204
|
+
return a.rules.every((r, i) => policy(b.rules[i]) === policy(r));
|
|
195
205
|
}
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
const DISCLOSED_UNREADABLE_ROWS = 20;
|
|
207
|
+
function reachOfRowHead(text, behavior) {
|
|
208
|
+
const head = typeof text === "string" ? ruleSpellingOf(text)?.toolName : undefined;
|
|
209
|
+
if (head === undefined || !isNameableToolName(head))
|
|
210
|
+
return "every";
|
|
211
|
+
if (head === COMMAND_RULE_TOOL || isShellFaceToolName(head))
|
|
212
|
+
return { words: [COMMAND_RULE_TOOL], faces: [] };
|
|
213
|
+
if (ruleToolGrammarOf(head) === undefined)
|
|
214
|
+
return "every";
|
|
215
|
+
const column = Object.hasOwn(PATH_RULE_FACES, head) ? PATH_RULE_FACES[head]?.[behavior] : undefined;
|
|
216
|
+
return { words: [head], faces: column ?? [] };
|
|
217
|
+
}
|
|
218
|
+
function rowReaches(reach, words, face) {
|
|
219
|
+
if (reach === "every")
|
|
220
|
+
return true;
|
|
221
|
+
return reach.words.some((w) => words.includes(w)) || (face !== undefined && reach.faces.includes(face));
|
|
222
|
+
}
|
|
223
|
+
function listRefusalOf(rules) {
|
|
224
|
+
if (typeof rules === "object" && rules !== null) {
|
|
225
|
+
if (types.isProxy(rules))
|
|
226
|
+
return "rules is a Proxy, not an array";
|
|
227
|
+
const held = HELD_LIST_REFUSALS.get(rules);
|
|
228
|
+
if (held !== undefined)
|
|
229
|
+
return held;
|
|
209
230
|
}
|
|
210
|
-
|
|
231
|
+
if (!Array.isArray(rules))
|
|
232
|
+
return "rules is not an array";
|
|
233
|
+
const cap = DEFAULT_CAPS.maxRules;
|
|
234
|
+
return rules.length > cap ? `rules list has ${rules.length} entries; the cap is ${cap} — refused whole (not truncated)` : undefined;
|
|
235
|
+
}
|
|
236
|
+
export const ORG_ROW_POLICY_MEMBERS = Object.freeze(["rule", "behavior"]);
|
|
237
|
+
const ORG_SNAPSHOT_ENVELOPE_MEMBERS = Object.freeze(["revision", "fetchedAtMs", "signature"]);
|
|
238
|
+
const ORG_STATE_MEMBERS = Object.freeze(["revisionHighWater", "installedAtMs"]);
|
|
239
|
+
function scalarMembersOf(value, members) {
|
|
240
|
+
const held = {};
|
|
241
|
+
if (typeof value !== "object" || value === null)
|
|
242
|
+
return held;
|
|
243
|
+
for (const k of members) {
|
|
244
|
+
const v = Object.hasOwn(value, k) ? value[k] : undefined;
|
|
245
|
+
if (typeof v === "string" || typeof v === "boolean" || v === null || (typeof v === "number" && Number.isFinite(v)))
|
|
246
|
+
held[k] = v;
|
|
247
|
+
}
|
|
248
|
+
return held;
|
|
249
|
+
}
|
|
250
|
+
function orgRowPolicyOf(row) {
|
|
251
|
+
return scalarMembersOf(row, ORG_ROW_POLICY_MEMBERS);
|
|
252
|
+
}
|
|
253
|
+
const ORG_RULES_DEPTH = 2;
|
|
254
|
+
const ORG_RULES_RECEIPT = Object.freeze({ accessors: "call-once", depth: ORG_RULES_DEPTH, others: "copy", contain: true, isProxy: (v) => types.isProxy(v) });
|
|
255
|
+
export function orgRuleListOf(rules) {
|
|
256
|
+
const refused = listRefusalOf(rules);
|
|
257
|
+
if (refused !== undefined)
|
|
258
|
+
return { refused };
|
|
259
|
+
const received = receive(rules, ORG_RULES_RECEIPT);
|
|
260
|
+
if (!received.ok)
|
|
261
|
+
return { refused: `rules could not be received (${describeFault(received.fault)})` };
|
|
262
|
+
const copy = received.value;
|
|
263
|
+
return { rows: Array.from({ length: copy.length }, (_, i) => orgRowPolicyOf(Object.hasOwn(copy, i) ? copy[i] : undefined)) };
|
|
264
|
+
}
|
|
265
|
+
const HELD_LIST_REFUSALS = new WeakMap();
|
|
266
|
+
function heldRefusalOf(words) {
|
|
267
|
+
const held = Object.freeze({});
|
|
268
|
+
HELD_LIST_REFUSALS.set(held, words);
|
|
269
|
+
return held;
|
|
270
|
+
}
|
|
271
|
+
function orgSnapshotCopyOf(v) {
|
|
272
|
+
if (v === undefined)
|
|
273
|
+
return undefined;
|
|
274
|
+
if (typeof v === "object" && v !== null && types.isProxy(v))
|
|
275
|
+
throw new Error("the snapshot is a Proxy, not data");
|
|
276
|
+
const envelope = scalarMembersOf(v, ORG_SNAPSHOT_ENVELOPE_MEMBERS);
|
|
277
|
+
const rules = typeof v === "object" && v !== null ? v.rules : undefined;
|
|
278
|
+
const refusal = listRefusalOf(rules);
|
|
279
|
+
if (refusal !== undefined)
|
|
280
|
+
return { ...envelope, rules: heldRefusalOf(refusal) };
|
|
281
|
+
const list = orgRuleListOf(rules);
|
|
282
|
+
if ("refused" in list)
|
|
283
|
+
throw new Error(`the snapshot's ${list.refused}`);
|
|
284
|
+
return { ...envelope, rules: list.rows };
|
|
285
|
+
}
|
|
286
|
+
function orgStateCopyOf(state) {
|
|
287
|
+
if (typeof state === "object" && state !== null && types.isProxy(state))
|
|
288
|
+
throw new Error("the persisted org state is a Proxy, not data");
|
|
289
|
+
const snapshot = typeof state === "object" && state !== null ? state.snapshot : undefined;
|
|
290
|
+
return { ...scalarMembersOf(state, ORG_STATE_MEMBERS), snapshot: orgSnapshotCopyOf(snapshot ?? null) };
|
|
291
|
+
}
|
|
292
|
+
function readOrgRow(row, index) {
|
|
293
|
+
const { rule: text, behavior: said } = orgRowPolicyOf(row);
|
|
294
|
+
const behavior = said === "ask" ? "ask" : "deny";
|
|
295
|
+
const unreadable = (code, detail) => ({
|
|
296
|
+
kind: "unreadable",
|
|
297
|
+
index,
|
|
298
|
+
rule: typeof text === "string" ? text : text === undefined ? "<unreadable>" : escapeForDisclosure(text),
|
|
299
|
+
code,
|
|
300
|
+
...(detail !== undefined ? { detail } : {}),
|
|
301
|
+
behavior,
|
|
302
|
+
reach: reachOfRowHead(text, behavior),
|
|
303
|
+
});
|
|
304
|
+
if (typeof text !== "string" || text === "")
|
|
305
|
+
return unreadable("invalid.grammar", "no rule text");
|
|
306
|
+
if (said !== "deny" && said !== "ask")
|
|
307
|
+
return unreadable("invalid.grammar", said === undefined ? "no behavior" : `behavior "${escapeForDisclosure(said)}" is not deny or ask`);
|
|
308
|
+
const parsed = parseRuleText(text, said);
|
|
309
|
+
if ("reject" in parsed)
|
|
310
|
+
return unreadable(parsed.reject.code);
|
|
311
|
+
if (parsed.rule.tool !== COMMAND_RULE_TOOL)
|
|
312
|
+
return unreadable("unsupported.tool");
|
|
313
|
+
return { kind: "read", index, rule: text, behavior: said, parsed: parsed.rule };
|
|
314
|
+
}
|
|
315
|
+
export function readOrgRules(rules) {
|
|
316
|
+
const list = orgRuleListOf(rules);
|
|
317
|
+
if ("refused" in list)
|
|
318
|
+
return list;
|
|
319
|
+
const rows = list.rows.map((row, i) => readOrgRow(row, i + 1));
|
|
320
|
+
const unreadable = rows.filter((r) => r.kind === "unreadable");
|
|
321
|
+
return {
|
|
322
|
+
rows,
|
|
323
|
+
unreadable,
|
|
324
|
+
verdictFor(call) {
|
|
325
|
+
const words = typeof call.tool === "string" ? [call.tool] : call.tool;
|
|
326
|
+
const command = words.includes(COMMAND_RULE_TOOL) && typeof call.command === "string" ? call.command : undefined;
|
|
327
|
+
let ask;
|
|
328
|
+
let unjudged;
|
|
329
|
+
for (const row of rows) {
|
|
330
|
+
if (row.kind === "unreadable") {
|
|
331
|
+
if (!rowReaches(row.reach, words, call.face))
|
|
332
|
+
continue;
|
|
333
|
+
const verdict = { behavior: row.behavior, rule: row.rule, unreadable: unreadableOrgRowReason(row) };
|
|
334
|
+
if (row.behavior === "deny")
|
|
335
|
+
return verdict;
|
|
336
|
+
ask ??= verdict;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (command === undefined)
|
|
340
|
+
continue;
|
|
341
|
+
const outcome = programRunReachOf(row.parsed, command);
|
|
342
|
+
if (outcome.reach === "not_reached")
|
|
343
|
+
continue;
|
|
344
|
+
if (outcome.reach === "unreadable") {
|
|
345
|
+
unjudged ??= { behavior: "ask", rule: row.rule, unreadable: outcome.reason };
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (row.behavior === "deny")
|
|
349
|
+
return { behavior: "deny", rule: row.rule };
|
|
350
|
+
ask ??= { behavior: "ask", rule: row.rule };
|
|
351
|
+
}
|
|
352
|
+
return unjudged ?? ask;
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
export function unenforceableOrgRules(rules) {
|
|
357
|
+
return readListedOrgRules(rules).unreadable.map((u) => ({ rule: u.rule, code: u.code }));
|
|
358
|
+
}
|
|
359
|
+
function readListedOrgRules(rules) {
|
|
360
|
+
const reading = readOrgRules(rules);
|
|
361
|
+
if ("refused" in reading)
|
|
362
|
+
throw new TypeError(`the org rules cannot be read: ${reading.refused}`);
|
|
363
|
+
return reading;
|
|
364
|
+
}
|
|
365
|
+
export function unreadableOrgRowReason(row) {
|
|
366
|
+
return `row ${row.index} of the organization snapshot is not a rule this layer reads (${row.code}${row.detail !== undefined ? `: ${row.detail}` : ""}); republish it in the shell tool's command grammar`;
|
|
367
|
+
}
|
|
368
|
+
export function orgUnreadableClause(reason) {
|
|
369
|
+
return `which could not be read against it (${reason})`;
|
|
211
370
|
}
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
371
|
+
export function describeUnreadableOrgRows(unreadable, revision) {
|
|
372
|
+
if (unreadable.length === 0)
|
|
373
|
+
return undefined;
|
|
374
|
+
const named = unreadable
|
|
375
|
+
.slice(0, DISCLOSED_UNREADABLE_ROWS)
|
|
376
|
+
.map((u) => `row ${u.index} "${escapeForDisclosure(u.rule)}" (${u.code}${u.detail !== undefined ? `: ${u.detail}` : ""})`)
|
|
216
377
|
.join(", ");
|
|
217
|
-
const more =
|
|
218
|
-
return
|
|
378
|
+
const more = unreadable.length - DISCLOSED_UNREADABLE_ROWS;
|
|
379
|
+
return (`the org snapshot${revision !== undefined ? ` (revision ${escapeForDisclosure(revision)})` : ""} is served DEGRADED: ${unreadable.length} row(s) this layer cannot read — ${named}${more > 0 ? ` and ${more} more` : ""}; ` +
|
|
380
|
+
`each stands at its strictest reading over the calls it may reach (a deny row, or one whose behavior cannot be read, refuses them; an ask row asks a person), and every readable row stands as published — republish the named rows in the shell tool's command grammar`);
|
|
219
381
|
}
|
|
220
382
|
function validateOrgSnapshot(s, nowMs) {
|
|
221
383
|
if (typeof s.revision !== "number" || !Number.isFinite(s.revision))
|
|
@@ -225,61 +387,33 @@ function validateOrgSnapshot(s, nowMs) {
|
|
|
225
387
|
if (s.fetchedAtMs > nowMs + ORG_FETCHED_AT_SKEW_ALLOWANCE_MS) {
|
|
226
388
|
return `fetchedAtMs is ${s.fetchedAtMs - nowMs}ms in the future (allowance ${ORG_FETCHED_AT_SKEW_ALLOWANCE_MS}ms) — a future-dated observation would satisfy the staleness bound indefinitely`;
|
|
227
389
|
}
|
|
228
|
-
|
|
229
|
-
return "rules is not an array";
|
|
230
|
-
for (const r of s.rules) {
|
|
231
|
-
if (typeof r?.rule !== "string" || r.rule === "")
|
|
232
|
-
return "a rule entry carries no rule text";
|
|
233
|
-
if (r.behavior !== "deny" && r.behavior !== "ask") {
|
|
234
|
-
return `rule "${escapeForDisclosure(r.rule)}" carries behavior "${escapeForDisclosure(r.behavior)}" — the org layer has no allow bucket`;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
const unenforceable = unenforceableOrgRules(s.rules);
|
|
238
|
-
const first = unenforceable[0];
|
|
239
|
-
if (first !== undefined) {
|
|
240
|
-
const rest = unenforceable.length > 1 ? ` (${unenforceable.length} in all — ${describeUnenforceable(unenforceable)})` : "";
|
|
241
|
-
return `rule "${escapeForDisclosure(first.rule)}" does not parse (${first.code}) — an unenforceable deny/ask must not stand as policy${rest}`;
|
|
242
|
-
}
|
|
243
|
-
return undefined;
|
|
390
|
+
return listRefusalOf(s.rules);
|
|
244
391
|
}
|
|
245
392
|
export function orgRuleVerdictFor(rules, call) {
|
|
246
|
-
|
|
247
|
-
let unreadable;
|
|
248
|
-
for (const r of rules) {
|
|
249
|
-
const parsed = parseRuleText(r.rule, r.behavior);
|
|
250
|
-
if ("reject" in parsed)
|
|
251
|
-
continue;
|
|
252
|
-
if (parsed.rule.tool !== call.tool)
|
|
253
|
-
continue;
|
|
254
|
-
const outcome = programRunReachOf(parsed.rule, call.command);
|
|
255
|
-
if (outcome.reach === "not_reached")
|
|
256
|
-
continue;
|
|
257
|
-
if (outcome.reach === "unreadable") {
|
|
258
|
-
unreadable ??= { behavior: "ask", rule: r.rule, unreadable: outcome.reason };
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (r.behavior === "deny")
|
|
262
|
-
return { behavior: "deny", rule: r.rule };
|
|
263
|
-
ask ??= { behavior: "ask", rule: r.rule };
|
|
264
|
-
}
|
|
265
|
-
return unreadable ?? ask;
|
|
393
|
+
return readListedOrgRules(rules).verdictFor(call);
|
|
266
394
|
}
|
|
267
395
|
export function orgRuleShadows(orgRules, rule) {
|
|
268
396
|
return compileOrgShadowPredicate(orgRules)(rule);
|
|
269
397
|
}
|
|
270
398
|
export function compileOrgShadowPredicate(orgRules) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
continue;
|
|
275
|
-
const parsed = parseRuleText(d.rule, "deny");
|
|
276
|
-
if (!("reject" in parsed))
|
|
277
|
-
denies.push(parsed.rule);
|
|
278
|
-
}
|
|
399
|
+
const { rows } = readListedOrgRules(orgRules);
|
|
400
|
+
const denies = rows.flatMap((r) => (r.kind === "read" && r.behavior === "deny" ? [r.parsed] : []));
|
|
401
|
+
const standing = rows.flatMap((r) => (r.kind === "unreadable" && r.behavior === "deny" ? [r.reach] : []));
|
|
279
402
|
return (rule) => {
|
|
280
|
-
if (
|
|
403
|
+
if (standing.length > 0) {
|
|
404
|
+
const word = isShellFaceToolName(rule.tool) ? COMMAND_RULE_TOOL : rule.tool;
|
|
405
|
+
const target = pathTargetOf(rule.tool);
|
|
406
|
+
const faces = Object.hasOwn(PATH_RULE_FACES, rule.tool)
|
|
407
|
+
? (PATH_RULE_FACES[rule.tool]?.allow ?? [])
|
|
408
|
+
: target !== undefined
|
|
409
|
+
? [target.access === "read" ? "read" : "write"]
|
|
410
|
+
: [undefined];
|
|
411
|
+
if (standing.some((reach) => faces.some((face) => rowReaches(reach, [word], face))))
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
if (rule.tool !== COMMAND_RULE_TOOL || denies.length === 0)
|
|
281
415
|
return false;
|
|
282
|
-
return denies.some((d) =>
|
|
416
|
+
return denies.some((d) => ruleReachesProgramRun(d, rule.command));
|
|
283
417
|
};
|
|
284
418
|
}
|
|
285
419
|
export function isWritablePermissionRuleStore(store) {
|
|
@@ -28,3 +28,18 @@ export declare function lastIndexOfUnescaped(s: string, ch: string): number;
|
|
|
28
28
|
* factory) to refuse the downgrades.
|
|
29
29
|
*/
|
|
30
30
|
export declare function parsePermissionRule(rule: string): ParsedPermissionRule;
|
|
31
|
+
/**
|
|
32
|
+
* Can a rule name this tool name — is it the rule syntax's name head ({@link RULE_TOOL_NAME}) and not the lone `*`? The
|
|
33
|
+
* lone `*` is the wildcard (every tool), not a name: a tool called `*` could never be granted or refused on its own. The
|
|
34
|
+
* mount contract for every name a caller tool answers to (its name and each alias), so each one is a name every list can
|
|
35
|
+
* write; a protocol-minted name (`<ns>__<peer>__<tool>`, segments in `[A-Za-z0-9_-]`) is always one.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isNameableToolName(name: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* One rule entry read by the rule syntax, or `undefined` when the syntax cannot read it: blanks around it, a tool name (the
|
|
40
|
+
* text before the first unescaped `(`, as written) outside {@link RULE_TOOL_NAME}, or a `(` the parse cannot close at the
|
|
41
|
+
* entry's end (the lenient parser's downgrade: the whole string read as a name); otherwise {@link parsePermissionRule}'s
|
|
42
|
+
* reading. THE test of "this is a rule spelling" — every entry list's reader asks it before any rule grammar reads the
|
|
43
|
+
* content (what the parentheses hold, a parenthesis or a control character included, is that grammar's to judge).
|
|
44
|
+
*/
|
|
45
|
+
export declare function ruleSpellingOf(rule: string): ParsedPermissionRule | undefined;
|
|
@@ -1,25 +1,19 @@
|
|
|
1
|
+
function escapedAt(s, i) {
|
|
2
|
+
let backslashes = 0;
|
|
3
|
+
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--)
|
|
4
|
+
backslashes++;
|
|
5
|
+
return backslashes % 2 === 1;
|
|
6
|
+
}
|
|
1
7
|
export function indexOfUnescaped(s, ch) {
|
|
2
|
-
for (let i = 0; i < s.length; i++)
|
|
3
|
-
if (s[i]
|
|
4
|
-
continue;
|
|
5
|
-
let backslashes = 0;
|
|
6
|
-
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--)
|
|
7
|
-
backslashes++;
|
|
8
|
-
if (backslashes % 2 === 0)
|
|
8
|
+
for (let i = 0; i < s.length; i++)
|
|
9
|
+
if (s[i] === ch && !escapedAt(s, i))
|
|
9
10
|
return i;
|
|
10
|
-
}
|
|
11
11
|
return -1;
|
|
12
12
|
}
|
|
13
13
|
export function lastIndexOfUnescaped(s, ch) {
|
|
14
|
-
for (let i = s.length - 1; i >= 0; i--)
|
|
15
|
-
if (s[i]
|
|
16
|
-
continue;
|
|
17
|
-
let backslashes = 0;
|
|
18
|
-
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--)
|
|
19
|
-
backslashes++;
|
|
20
|
-
if (backslashes % 2 === 0)
|
|
14
|
+
for (let i = s.length - 1; i >= 0; i--)
|
|
15
|
+
if (s[i] === ch && !escapedAt(s, i))
|
|
21
16
|
return i;
|
|
22
|
-
}
|
|
23
17
|
return -1;
|
|
24
18
|
}
|
|
25
19
|
function unescapeRuleContent(s) {
|
|
@@ -42,3 +36,17 @@ export function parsePermissionRule(rule) {
|
|
|
42
36
|
return { toolName: name.trim() };
|
|
43
37
|
return { toolName: name.trim(), ruleContent: unescapeRuleContent(content) };
|
|
44
38
|
}
|
|
39
|
+
const RULE_TOOL_NAME = /^(?:\*|[A-Za-z0-9_](?:[A-Za-z0-9_.:*-]*[A-Za-z0-9_*-])?)$/;
|
|
40
|
+
export function isNameableToolName(name) {
|
|
41
|
+
return typeof name === "string" && name !== "*" && RULE_TOOL_NAME.test(name);
|
|
42
|
+
}
|
|
43
|
+
export function ruleSpellingOf(rule) {
|
|
44
|
+
if (rule !== rule.trim())
|
|
45
|
+
return undefined;
|
|
46
|
+
const open = indexOfUnescaped(rule, "(");
|
|
47
|
+
const name = open < 0 ? rule : rule.slice(0, open);
|
|
48
|
+
if (!RULE_TOOL_NAME.test(name))
|
|
49
|
+
return undefined;
|
|
50
|
+
const parsed = parsePermissionRule(rule);
|
|
51
|
+
return parsed.toolName === name ? parsed : undefined;
|
|
52
|
+
}
|