gentle-pi 4.0.0 → 4.2.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/README.md +157 -18
- package/assets/agents/gentle-ai-explore.md +11 -4
- package/assets/agents/gentle-ai-verify.md +23 -4
- package/assets/agents/gentle-ai-worker.md +6 -6
- package/assets/install-wizard/index.html +47 -0
- package/assets/install-wizard/wizard.css +1279 -0
- package/assets/install-wizard/wizard.js +951 -0
- package/assets/orchestrator-delegation.md +70 -163
- package/assets/orchestrator-memory.md +11 -4
- package/assets/orchestrator-prompts.md +48 -0
- package/assets/orchestrator-tracking.md +53 -0
- package/assets/orchestrator-verification.md +28 -0
- package/assets/orchestrator-writer.md +38 -0
- package/assets/orchestrator.md +29 -25
- package/assets/sounds/LICENSE.md +52 -0
- package/assets/sounds/attention.wav +0 -0
- package/assets/sounds/error.wav +0 -0
- package/assets/sounds/success.wav +0 -0
- package/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +708 -22
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +399 -0
- package/docs/delegated-verification.md +3 -5
- package/docs/gentle-agents-activity.md +513 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +90 -33
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +1 -1
- package/docs/permission-modes.md +127 -0
- package/docs/prompt-history.md +1 -1
- package/docs/readme-reference.md +310 -47
- package/docs/render-cache-performance.md +28 -0
- package/docs/session-profile-format.md +39 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/yolo-mode.md +11 -10
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/child-context.ts +11 -2
- package/extensions/child-safety.ts +4 -2
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +718 -87
- package/extensions/gentle-ai.ts +991 -263
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +271 -80
- package/extensions/gentle-todo.ts +123 -18
- package/extensions/history/index.ts +11 -6
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +91 -21
- package/extensions/skill-registry.ts +175 -46
- package/extensions/startup-banner.ts +11 -2
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +40 -1
- package/lib/agents-runner.ts +90 -15
- package/lib/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +459 -0
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +484 -0
- package/lib/auto-permission-tool-context.ts +61 -0
- package/lib/background-jobs-tools.ts +202 -0
- package/lib/background-jobs.ts +253 -0
- package/lib/background-monitor.ts +154 -0
- package/lib/bounded-writer-admission.ts +83 -10
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +14 -4
- package/lib/command-palette-catalog.ts +3 -2
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +104 -28
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-shell-bundled.ts +535 -0
- package/lib/gentle-shell-launcher.ts +75 -24
- package/lib/gentle-shell-startup-flow.ts +54 -0
- package/lib/gentle-shell-startup-update.ts +92 -0
- package/lib/gentle-shell-uninstall.ts +840 -0
- package/lib/gentle-shell-update-panel.ts +231 -0
- package/lib/herdr-activity.ts +183 -0
- package/lib/inprocess-reviewer.ts +3 -0
- package/lib/jobs-sidebar-state.ts +20 -0
- package/lib/jobs-view.ts +161 -0
- package/lib/model-price-ratio.ts +83 -0
- package/lib/model-routing-authority.ts +6 -1
- package/lib/nan-provider.ts +33 -11
- package/lib/native-review-cli.ts +194 -73
- package/lib/notification-audio-native.ts +162 -0
- package/lib/notification-audio-windows.ts +399 -0
- package/lib/notification-audio.ts +332 -0
- package/lib/notification-customize.ts +241 -0
- package/lib/notification-events.ts +113 -0
- package/lib/notification-policy.ts +148 -0
- package/lib/notification-pulse-client.ts +367 -0
- package/lib/notification-pulse-protocol.ts +305 -0
- package/lib/notification-pulse-stream.ts +213 -0
- package/lib/notification-pulse-worker.ts +66 -0
- package/lib/notification-scheduler.ts +173 -0
- package/lib/notification-service.ts +202 -0
- package/lib/notification-ui.ts +87 -0
- package/lib/odd-phase.ts +1 -1
- package/lib/orchestrator-alias.ts +20 -0
- package/lib/orchestrator-catalog.ts +79 -0
- package/lib/orchestrator-consultation.ts +61 -0
- package/lib/orchestrator-discovery.ts +49 -0
- package/lib/orchestrator-helper-consent.ts +122 -0
- package/lib/orchestrator-helper.ts +167 -0
- package/lib/orchestrator-message-card.ts +105 -0
- package/lib/orchestrator-presence.ts +115 -14
- package/lib/orchestrator-scope.ts +80 -0
- package/lib/orchestrator-state.ts +107 -0
- package/lib/orchestrator-tool-card.ts +280 -0
- package/lib/orchestrator-work-search.ts +218 -0
- package/lib/orchestrator-work.ts +79 -0
- package/lib/permission-mode-policy.ts +59 -0
- package/lib/review-candidate-view-owner.ts +8 -6
- package/lib/review-candidate-view.ts +3 -2
- package/lib/review-compact-contract.ts +2 -2
- package/lib/review-host-relay.ts +7 -2
- package/lib/review-integration-v2.ts +100 -22
- package/lib/review-publication-gate.ts +1 -1
- package/lib/review-risk-assessment.ts +77 -51
- package/lib/review-sidebar-state.ts +2 -1
- package/lib/review-transaction.ts +72 -3
- package/lib/runtime-metrics-children.ts +1 -1
- package/lib/runtime-metrics-native.ts +3 -2
- package/lib/selection-engine.ts +22 -7
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +261 -0
- package/lib/session-profile-persistence.ts +187 -0
- package/lib/session-worktree-registry.ts +74 -8
- package/lib/shell-bar.ts +41 -11
- package/lib/shell-changes-view.ts +40 -13
- package/lib/shell-changes.ts +88 -71
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-sidebar-layout.ts +5 -1
- package/lib/shell-todo.ts +81 -26
- package/lib/shell-usage.ts +4 -3
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/vim-editor-adapter.ts +27 -5
- package/lib/visual-customization-policy.ts +20 -9
- package/lib/visual-customize-view.ts +171 -20
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +198 -121
- package/package.json +5 -5
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +536 -0
- package/runtime/gentle-shell-launcher.mjs +74 -23
- package/runtime/gentle-shell-startup-flow.mjs +55 -0
- package/runtime/gentle-shell-startup-update.mjs +93 -0
- package/runtime/gentle-shell-uninstall.mjs +841 -0
- package/runtime/gentle-shell-update-panel.mjs +232 -0
- package/runtime/native-review-cli.mjs +191 -70
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +77 -51
- package/runtime/status-timing-diagnostics.mjs +202 -0
- package/runtime/telemetry-trigger.mjs +3 -1
- package/scripts/bootstrap.cmd +255 -0
- package/scripts/bootstrap.sh +168 -0
- package/scripts/build-distribution.mjs +157 -0
- package/scripts/build-installer-bundles.mjs +136 -0
- package/scripts/build-runtime-modules.mjs +7 -0
- package/scripts/bundled-install.mjs +1308 -0
- package/scripts/bundled-wizard.mjs +399 -0
- package/scripts/check-types.mjs +4 -6
- package/scripts/gentle-ai-installer.mjs +79 -32
- package/scripts/install-gentle-ai.mjs +48 -1
- package/scripts/install-tui-mode-setting.mjs +10 -1
- package/scripts/install-wizard-preview.mjs +154 -0
- package/scripts/installer-downloads.mjs +648 -0
- package/scripts/installer-preflight.mjs +387 -0
- package/scripts/installer-probes.mjs +564 -0
- package/scripts/installer-runner.mjs +1102 -0
- package/scripts/installer-server.mjs +881 -0
- package/scripts/installer-windows-artifacts.json +15 -0
- package/scripts/installer-windows.mjs +752 -0
- package/scripts/main-channel.mjs +452 -0
- package/scripts/npm-pack-result.mjs +31 -0
- package/scripts/pi-update.mjs +223 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +22 -8
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +23 -5
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +82 -18
- package/skills/gentle-ai/SKILL.md +12 -11
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +12 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-protocol.test.ts +57 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +89 -4
- package/tests/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +276 -0
- package/tests/auto-mode-sdk.test.ts +84 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +634 -0
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +454 -0
- package/tests/autonomous-guard.test.ts +86 -16
- package/tests/background-cache-warming.test.ts +2 -1
- package/tests/background-jobs.test.ts +227 -0
- package/tests/background-monitor.test.ts +195 -0
- package/tests/background-subagents-default-mode.test.ts +28 -5
- package/tests/bounded-stop-contract.test.ts +85 -0
- package/tests/bounded-writer-admission.test.ts +52 -1
- package/tests/bridge-wake-lifecycle.test.ts +129 -0
- package/tests/bridge-wake-rendering.test.ts +56 -0
- package/tests/build-distribution.test.ts +269 -0
- package/tests/builtin-codemode-optout.test.ts +242 -0
- package/tests/bundled-install.test.ts +1319 -0
- package/tests/bundled-wizard.test.ts +505 -0
- package/tests/card-content-policy.test.ts +68 -0
- package/tests/card-style-policy.test.ts +34 -2
- package/tests/child-codegraph-bootstrap.test.ts +74 -0
- package/tests/child-package-entrypoints.test.ts +65 -0
- package/tests/child-package-injection.test.ts +74 -0
- package/tests/child-safety.test.ts +12 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +107 -2
- package/tests/command-palette.test.ts +5 -5
- package/tests/context-threshold.test.ts +31 -0
- package/tests/delegate-for-reason-contract.test.ts +327 -0
- package/tests/delegated-key-learnings-contract.test.ts +3 -2
- package/tests/devbinary/native-review-parity.devtest.ts +1 -1
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +316 -0
- package/tests/generic-agent-tools.test.ts +16 -1
- package/tests/gentle-agents.test.ts +1895 -76
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +19 -2
- package/tests/gentle-ai-child-guards.test.ts +202 -0
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
- package/tests/gentle-ai-installer.test.ts +548 -58
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +6 -6
- package/tests/gentle-ai.test.ts +999 -31
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +263 -19
- package/tests/gentle-shell-bundled.test.ts +1036 -0
- package/tests/gentle-shell-launcher.test.ts +251 -25
- package/tests/gentle-shell-startup-flow.test.ts +75 -0
- package/tests/gentle-shell-startup-update.test.ts +85 -0
- package/tests/gentle-shell-uninstall.test.ts +978 -0
- package/tests/gentle-shell-update-panel.test.ts +158 -0
- package/tests/gentle-shell.test.ts +611 -100
- package/tests/gentle-todo.test.ts +317 -14
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/inprocess-reviewer.test.ts +20 -0
- package/tests/install-gentle-ai-failure.test.ts +69 -0
- package/tests/install-tui-mode-setting.test.ts +15 -0
- package/tests/install-wizard.test.ts +1223 -0
- package/tests/installer-bundles.test.ts +122 -0
- package/tests/installer-posix-bootstrap.test.ts +1201 -0
- package/tests/installer-preflight.test.ts +750 -0
- package/tests/installer-probes.test.ts +1106 -0
- package/tests/installer-runner.test.ts +2159 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +395 -0
- package/tests/main-channel.test.ts +344 -0
- package/tests/maintainer/provider-relay.maintest.ts +13 -12
- package/tests/model-price-ratio.test.ts +116 -0
- package/tests/model-profiles-preservation.test.ts +153 -0
- package/tests/model-routing-package.test.ts +10 -0
- package/tests/nan-provider-runtime.test.ts +156 -0
- package/tests/nan-provider.test.ts +28 -18
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-cli.test.ts +236 -1
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +1 -1
- package/tests/native-review-parity.test.ts +12 -48
- package/tests/native-risk-evidence-parity.test.ts +21 -0
- package/tests/no-self-pause-contract.test.ts +87 -0
- package/tests/notification-audio-native.test.ts +281 -0
- package/tests/notification-audio-windows.test.ts +581 -0
- package/tests/notification-audio.test.ts +548 -0
- package/tests/notification-customize.test.ts +655 -0
- package/tests/notification-events.test.ts +115 -0
- package/tests/notification-policy.test.ts +201 -0
- package/tests/notification-pulse-client.test.ts +356 -0
- package/tests/notification-pulse-protocol.test.ts +213 -0
- package/tests/notification-pulse-stream.test.ts +320 -0
- package/tests/notification-scheduler.test.ts +258 -0
- package/tests/notification-ui.test.ts +157 -0
- package/tests/npm-pack-result.test.ts +41 -0
- package/tests/odd-integration.test.ts +14 -5
- package/tests/odd-routing-canonical-ratchet.test.ts +53 -12
- package/tests/odd-routing-contract.test.ts +80 -23
- package/tests/odd-runtime-delegation-gate.test.ts +4 -1
- package/tests/orchestrator-budget.test.ts +29 -6
- package/tests/orchestrator-catalog.test.ts +146 -0
- package/tests/orchestrator-consultation-sdk.test.ts +614 -0
- package/tests/orchestrator-consultation.test.ts +111 -0
- package/tests/orchestrator-discovery.test.ts +241 -0
- package/tests/orchestrator-helper-consent.test.ts +171 -0
- package/tests/orchestrator-helper.test.ts +297 -0
- package/tests/orchestrator-message-card.test.ts +190 -0
- package/tests/orchestrator-presence.test.ts +23 -0
- package/tests/orchestrator-rdd-ownership.test.ts +6 -5
- package/tests/orchestrator-scope.test.ts +107 -0
- package/tests/orchestrator-state.test.ts +114 -0
- package/tests/orchestrator-status-publication-contract.test.ts +34 -0
- package/tests/orchestrator-tool-card.test.ts +398 -0
- package/tests/orchestrator-work-search.test.ts +318 -0
- package/tests/orchestrator-work.test.ts +147 -0
- package/tests/package-manifest.test.ts +105 -21
- package/tests/packed-runner-owned-path.test.ts +35 -1
- package/tests/parallel-reason-contract.test.ts +21 -0
- package/tests/parallel-writer-admission.test.ts +210 -0
- package/tests/permission-mode-policy.test.ts +194 -0
- package/tests/persona-single-channel.test.ts +4 -12
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/pi-update-launcher.test.ts +87 -0
- package/tests/pi-update.test.ts +186 -0
- package/tests/provider-defect-handoff.test.ts +4 -3
- package/tests/quiet-tool-rendering.test.ts +160 -3
- package/tests/rdd-aware-verification-contract.test.ts +29 -41
- package/tests/rdd-status-line.test.ts +52 -0
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/review-agent-end-preflight.test.ts +62 -10
- package/tests/review-candidate-view.test.ts +101 -12
- package/tests/review-contract-prompt.test.ts +33 -4
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +255 -1
- package/tests/review-controller-workspace-root.test.ts +43 -2
- package/tests/review-controller.test.ts +2 -2
- package/tests/review-correction-lifecycle.test.ts +2 -1
- package/tests/review-gate.test.ts +7 -4
- package/tests/review-graph-schema.test.ts +2 -2
- package/tests/review-host-relay-routing.test.ts +5 -3
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +21 -1
- package/tests/review-json-arguments.test.ts +167 -0
- package/tests/review-last-event-closure.test.ts +157 -8
- package/tests/review-ledger-contract.test.ts +5 -4
- package/tests/review-policy-judgment-day.test.ts +1 -1
- package/tests/review-policy-ordinary.test.ts +2 -2
- package/tests/review-relay-transport-agent.test.ts +66 -1
- package/tests/review-risk-assessment.test.ts +109 -33
- package/tests/review-session-standing-permission-controller.test.ts +16 -17
- package/tests/review-session-standing-permission-ipc.test.ts +6 -5
- package/tests/review-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +83 -5
- package/tests/runtime-harness.mjs +42 -15
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +65 -5
- package/tests/session-change-capture.test.ts +10 -10
- package/tests/session-changes-shell.test.ts +242 -48
- package/tests/session-changes.test.ts +17 -24
- package/tests/session-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +394 -0
- package/tests/session-profile-persistence.test.ts +334 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +57 -2
- package/tests/shell-bar.test.ts +40 -0
- package/tests/shell-changes-view.test.ts +85 -34
- package/tests/shell-changes.test.ts +79 -62
- package/tests/shell-render-cache-runtime.test.ts +69 -0
- package/tests/shell-render-cache.test.ts +129 -0
- package/tests/shell-sidebar-layout.test.ts +27 -2
- package/tests/shell-todo.test.ts +132 -11
- package/tests/shell-usage-view.test.ts +1 -1
- package/tests/shell-usage.test.ts +35 -0
- package/tests/short-check-routing-contract.test.ts +49 -0
- package/tests/skill-registry-watcher.test.ts +119 -0
- package/tests/skill-registry.test.ts +311 -5
- package/tests/startup-banner.test.ts +117 -3
- package/tests/startup-home-repo.test.ts +170 -0
- package/tests/status-timing-diagnostics.test.ts +283 -0
- package/tests/support/orchestrator-modules.ts +19 -0
- package/tests/task-size-routing-contract.test.ts +241 -0
- package/tests/telemetry-trigger.test.ts +27 -1
- package/tests/test-strength-contract.test.ts +124 -0
- package/tests/verify-agent-contract.test.ts +90 -0
- package/tests/verify-first-launch-contract.test.ts +139 -0
- package/tests/verify-package-files.test.ts +56 -0
- package/tests/vim-editor-adapter.test.ts +47 -21
- package/tests/visual-customization-policy.test.ts +32 -3
- package/tests/visual-customize-view.test.ts +288 -10
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +18 -3
- package/tests/yolo-mode-runtime.test.ts +37 -10
- package/tests/yolo-mode.test.ts +38 -5
- package/tests/yolo-session-policy.test.ts +1 -1
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { posix, win32 } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
INSTALLER_VERSION,
|
|
5
|
+
GENTLE_AI_WINDOWS_MINIMUM_GO_VERSION,
|
|
6
|
+
} from "./gentle-ai-installer.mjs";
|
|
7
|
+
import { artifactFor, goPinVersion } from "./installer-downloads.mjs";
|
|
8
|
+
|
|
9
|
+
// Read package metadata only: never import the launcher or execute postinstall.
|
|
10
|
+
const metadata = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
11
|
+
function minimum(range) {
|
|
12
|
+
if (!/^>=\d+\.\d+\.\d+$/.test(range)) throw new Error("Unsupported preflight requirement range");
|
|
13
|
+
return range.slice(2);
|
|
14
|
+
}
|
|
15
|
+
export const requirements = Object.freeze({
|
|
16
|
+
node: minimum(metadata.engines.node),
|
|
17
|
+
pi: minimum(metadata.peerDependencies["@earendil-works/pi-coding-agent"]),
|
|
18
|
+
// pnpm pack can omit packageManager; acquisition still has a verified pin.
|
|
19
|
+
pnpm: metadata.packageManager?.replace(/^pnpm@/, "") ?? artifactFor("pnpm").version,
|
|
20
|
+
shell: metadata.version,
|
|
21
|
+
gentleAi: INSTALLER_VERSION,
|
|
22
|
+
go: GENTLE_AI_WINDOWS_MINIMUM_GO_VERSION,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/** Pi version the installer installs next to gentle-pi, and the version an older
|
|
26
|
+
* Pi is updated to (never below requirements.pi; the runner asserts it).
|
|
27
|
+
*/
|
|
28
|
+
export const PI_INSTALL_VERSION = "1.2.0";
|
|
29
|
+
|
|
30
|
+
/** Runtime persisted under PNPM_HOME. Bootstrap-only Node: `pnpm runtime set
|
|
31
|
+
* node <node> -g`, then `pnpm add -g npm@<npm> pnpm@<pnpm>`, or only npm when
|
|
32
|
+
* pnpm is already persistent (never downgraded). Persistent Node: one
|
|
33
|
+
* `pnpm add -g` of only the missing npm and/or bootstrap-only pnpm.
|
|
34
|
+
* npm 11.19.0 is the npm bundled with Node 24.21.0.
|
|
35
|
+
*/
|
|
36
|
+
export const persistencePins = Object.freeze({ node: "24.21.0", npm: "11.19.0", pnpm: requirements.pnpm });
|
|
37
|
+
|
|
38
|
+
/** Go the installer downloads only to build Gentle AI, when a build needs Go and
|
|
39
|
+
* the user's Go is missing, older than requirements.go or unchecked (installer-downloads.mjs).
|
|
40
|
+
*/
|
|
41
|
+
export const goAcquisition = Object.freeze({ version: goPinVersion });
|
|
42
|
+
|
|
43
|
+
/** pnpm 11 global bin directory: `$PNPM_HOME/bin`, not `$PNPM_HOME` itself.
|
|
44
|
+
* PNPM_HOME is the user's existing absolute value, otherwise pnpm's documented
|
|
45
|
+
* platform default. onPath compares PATH entries with that bin directory, so a
|
|
46
|
+
* globalBin probe must report this path. Returns null when it is unknowable.
|
|
47
|
+
*/
|
|
48
|
+
export function pnpmGlobalBin({ platform, env }) {
|
|
49
|
+
const path = platform === "win32" ? win32 : posix;
|
|
50
|
+
const absolute = (value) => typeof value === "string" && value.length > 0 && path.isAbsolute(value);
|
|
51
|
+
let pnpmHome = env.PNPM_HOME;
|
|
52
|
+
if (pnpmHome !== undefined) {
|
|
53
|
+
if (!absolute(pnpmHome)) return null;
|
|
54
|
+
} else if (platform === "win32") {
|
|
55
|
+
if (!absolute(env.LOCALAPPDATA)) return null;
|
|
56
|
+
pnpmHome = path.join(env.LOCALAPPDATA, "pnpm");
|
|
57
|
+
} else {
|
|
58
|
+
if (!absolute(env.HOME)) return null;
|
|
59
|
+
if (platform === "darwin") pnpmHome = path.join(env.HOME, "Library", "pnpm");
|
|
60
|
+
else pnpmHome = path.join(absolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : path.join(env.HOME, ".local", "share"), "pnpm");
|
|
61
|
+
}
|
|
62
|
+
pnpmHome = path.normalize(pnpmHome).replace(/(.)[\\/]+$/, "$1");
|
|
63
|
+
const bin = path.join(pnpmHome, "bin");
|
|
64
|
+
const comparable = (value) => {
|
|
65
|
+
const normal = path.normalize(value).replace(/(.)[\\/]+$/, "$1");
|
|
66
|
+
return platform === "win32" ? normal.toLowerCase() : normal;
|
|
67
|
+
};
|
|
68
|
+
const pathKey = platform === "win32" ? Object.keys(env).find((key) => key.toUpperCase() === "PATH") : "PATH";
|
|
69
|
+
const entries = String((pathKey && env[pathKey]) ?? "").split(path.delimiter).filter(absolute);
|
|
70
|
+
return { pnpmHome, path: bin, onPath: entries.some((entry) => comparable(entry) === comparable(bin)) };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const probeNames = ["node", "pnpm", "pi", "shell", "gentleAi", "go", "globalBin", "setup"];
|
|
74
|
+
|
|
75
|
+
/** Collect with caller-owned read-only probes; no default process/home adapters.
|
|
76
|
+
* A tool probe returns { available, version, usable }; missing is available:false.
|
|
77
|
+
* pnpm also needs compatible:true (runtime/capability evidence), shell global:true,
|
|
78
|
+
* and Gentle AI compatible:true (normal binary resolver/integrity evidence).
|
|
79
|
+
* Node may add persistent (resolvable from the user's PATH without bootstrap
|
|
80
|
+
* tool directories) and npm (a usable npm resolves there) booleans; pnpm may
|
|
81
|
+
* add persistent (resolvable from the user's PATH). A bootstrap-only Node or
|
|
82
|
+
* pnpm may add found: the stable version of the older or incompatible one on the
|
|
83
|
+
* user's PATH that the bootstrap left in place; a pnpm probe for the user's own
|
|
84
|
+
* pnpm in `$PNPM_HOME/bin` (where persisting pnpm writes) adds inGlobalBin:true
|
|
85
|
+
* and reports that pnpm itself, never as found, or, when that pnpm is never run
|
|
86
|
+
* (untrusted:true) or reports no stable version (unchecked:true), the bootstrap's
|
|
87
|
+
* pnpm in its place with persistent:true. globalBin returns { available, path, writable, onPath } for
|
|
88
|
+
* pnpmGlobalBin's `$PNPM_HOME/bin` directory; setup returns boolean, or
|
|
89
|
+
* { available: true, recoverable: true } for the pinned stack this pnpm installed
|
|
90
|
+
* whose setup did not finish (only its setup is then planned).
|
|
91
|
+
* Version values are exact stable versions, not raw arbitrary command output.
|
|
92
|
+
* On Windows the wizard passes pnpmHome, its PNPM_HOME decision (windowsPnpmHome),
|
|
93
|
+
* which the inventory keeps. A blocked decision runs no probe at all: the user's
|
|
94
|
+
* tools would otherwise run with that PNPM_HOME's bin first on PATH.
|
|
95
|
+
* On Windows a `folders` probe runs last: { node?, npm?, go?, pi?, shell? }, what
|
|
96
|
+
* the walk found on the folders those tools run from (S6 notice). It is kept as
|
|
97
|
+
* `folders` only when it names something; a failed walk is simply no record.
|
|
98
|
+
*/
|
|
99
|
+
export async function collectInventory({ platform, arch, probes = {}, pnpmHome }) {
|
|
100
|
+
const inventory = { platform, arch, ...(pnpmHome === undefined || pnpmHome === null ? {} : { pnpmHome }) };
|
|
101
|
+
if (pnpmHomeBlocker(inventory)) return inventory;
|
|
102
|
+
for (const name of probeNames) {
|
|
103
|
+
try {
|
|
104
|
+
inventory[name] = probes[name] ? await probes[name]() : { available: null };
|
|
105
|
+
} catch {
|
|
106
|
+
// Do not retain probe errors: they can contain private paths or credentials.
|
|
107
|
+
inventory[name] = { available: null };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Optional, advisory only (the plan notes a missing Git): kept only from a probe that answered.
|
|
111
|
+
if (probes.git) {
|
|
112
|
+
const git = await Promise.resolve().then(() => probes.git()).catch(() => null);
|
|
113
|
+
if (plainRecord(git) && typeof git.available === "boolean") inventory.git = { available: git.available };
|
|
114
|
+
}
|
|
115
|
+
if (platform === "win32" && probes.folders) {
|
|
116
|
+
const folders = await Promise.resolve().then(() => probes.folders()).catch(() => null);
|
|
117
|
+
if (plainRecord(folders) && !("available" in folders) && Object.keys(folders).length > 0) inventory.folders = folders;
|
|
118
|
+
}
|
|
119
|
+
return inventory;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const MAIN_BUILD = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-main\.[0-9a-f]{12}$/;
|
|
123
|
+
function plainRecord(value) {
|
|
124
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
125
|
+
}
|
|
126
|
+
/** Windows only: the blocker of a PNPM_HOME decision that is untrusted, or whose
|
|
127
|
+
* walk could not finish (S6), with the plan's record of it; null otherwise.
|
|
128
|
+
*/
|
|
129
|
+
function pnpmHomeBlocker({ platform, pnpmHome: home }) {
|
|
130
|
+
if (platform !== "win32" || !plainRecord(home)) return null;
|
|
131
|
+
if (home.failed === true) return { blocker: { code: "unknown-tool", tool: "pnpmHome" } };
|
|
132
|
+
if (home.available !== true || !plainRecord(home.untrusted) || typeof home.path !== "string") return null;
|
|
133
|
+
return { blocker: { code: "untrusted-pnpm-home", tool: "pnpmHome" },
|
|
134
|
+
record: { status: "untrusted", path: home.path, source: home.source === "user" ? "user" : "default" } };
|
|
135
|
+
}
|
|
136
|
+
function versionParts(version) {
|
|
137
|
+
if (typeof version !== "string" || !/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(version)) return null;
|
|
138
|
+
const parts = version.replace(/^v/, "").split(".").map(Number);
|
|
139
|
+
return parts.every(Number.isSafeInteger) ? parts : null;
|
|
140
|
+
}
|
|
141
|
+
function compareVersions(left, right) {
|
|
142
|
+
for (let i = 0; i < 3; i += 1) {
|
|
143
|
+
if (left[i] !== right[i]) return left[i] > right[i] ? 1 : -1;
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
if (compareVersions(versionParts(persistencePins.node), versionParts(requirements.node)) < 0) {
|
|
148
|
+
throw new Error("Persistent Node pin is below the repository minimum");
|
|
149
|
+
}
|
|
150
|
+
if (compareVersions(versionParts(goAcquisition.version), versionParts(requirements.go)) < 0) {
|
|
151
|
+
throw new Error("Go acquisition pin is below the Go requirement");
|
|
152
|
+
}
|
|
153
|
+
function classify(observation, required, extraCheck = () => true, exact = false) {
|
|
154
|
+
if (observation?.available === false) return "unavailable";
|
|
155
|
+
if (observation?.available !== true) return "unknown";
|
|
156
|
+
const version = versionParts(observation.version);
|
|
157
|
+
if (!version) return "unknown";
|
|
158
|
+
const order = compareVersions(version, versionParts(required));
|
|
159
|
+
// A known version below the minimum is incompatible whether or not it runs.
|
|
160
|
+
if (order < 0) return "incompatible";
|
|
161
|
+
if (observation.usable !== true) return "unknown";
|
|
162
|
+
if (exact && order !== 0) return "incompatible";
|
|
163
|
+
const extra = extraCheck(observation);
|
|
164
|
+
if (extra === false) return "incompatible";
|
|
165
|
+
return extra === true ? "reusable" : "unknown";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Pure ordered intent plan, not executable commands or a readiness certificate.
|
|
169
|
+
* Blocked plans contain no actions: never overwrite an incompatible/unknown tool.
|
|
170
|
+
* Compatible newer Node/Pi/Shell versions remain owned by their existing install.
|
|
171
|
+
*/
|
|
172
|
+
export function planPreflight(inventory, { channel = "release" } = {}) {
|
|
173
|
+
if (channel !== "release" && channel !== "main") throw new TypeError(`Unsupported installation channel: ${channel}`);
|
|
174
|
+
const main = channel === "main";
|
|
175
|
+
const tools = {};
|
|
176
|
+
const blockers = [];
|
|
177
|
+
const actions = [];
|
|
178
|
+
const { platform, arch } = inventory;
|
|
179
|
+
const supportedTarget = ["linux", "darwin", "win32"].includes(platform) && ["x64", "arm64"].includes(arch);
|
|
180
|
+
if (!supportedTarget) {
|
|
181
|
+
return { tools, blockers: [{ code: "unsupported-target", tool: "target" }], actions, ready: false };
|
|
182
|
+
}
|
|
183
|
+
// A blocked Windows PNPM_HOME decision: no probe ran, so it is the only blocker.
|
|
184
|
+
const homeBlocked = pnpmHomeBlocker(inventory);
|
|
185
|
+
if (homeBlocked) return { tools: homeBlocked.record ? { pnpmHome: homeBlocked.record } : {}, blockers: [homeBlocked.blocker], actions, ready: false };
|
|
186
|
+
// An installed Gentle Shell whose owner (pnpm or npm) is known is updated with
|
|
187
|
+
// that package manager instead of blocking: older, unusable or main-channel
|
|
188
|
+
// versions on release, and any version on main. A current pnpm-owned Shell
|
|
189
|
+
// keeps the pinned-stack checks below; a current npm-owned one needs nothing.
|
|
190
|
+
const shellSeen = inventory.shell;
|
|
191
|
+
const ownedShell = shellSeen?.available === true && (shellSeen.owner === "pnpm" || shellSeen.owner === "npm") &&
|
|
192
|
+
(versionParts(shellSeen.version) !== null || MAIN_BUILD.test(String(shellSeen.version)));
|
|
193
|
+
const current = ownedShell && shellSeen.usable === true && versionParts(shellSeen.version) !== null &&
|
|
194
|
+
compareVersions(versionParts(shellSeen.version), versionParts(requirements.shell)) >= 0;
|
|
195
|
+
const update = ownedShell && (main || !current) ? channel : null;
|
|
196
|
+
const npmCurrent = ownedShell && !update && shellSeen.owner === "npm";
|
|
197
|
+
function record(name, status, required) {
|
|
198
|
+
tools[name] = { status, ...(required ? { required } : {}) };
|
|
199
|
+
// Gentle AI and setup are only checked for the pinned pnpm Shell, so their
|
|
200
|
+
// unknown status adds nothing once that Shell blocks or is not the pinned one.
|
|
201
|
+
const shellBlocks = tools.shell?.status === "incompatible" || tools.shell?.status === "unknown" || update !== null || npmCurrent;
|
|
202
|
+
const derived = status === "unknown" && (name === "gentleAi" || name === "setup") && shellBlocks;
|
|
203
|
+
if (!derived && (status === "unknown" || status === "incompatible")) {
|
|
204
|
+
blockers.push({ code: `${status}-tool`, tool: name });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
record("node", classify(inventory.node, requirements.node), requirements.node);
|
|
208
|
+
// An older Pi whose owner (pnpm or npm) is known is updated with that package
|
|
209
|
+
// manager to PI_INSTALL_VERSION instead of blocking; the plan records the
|
|
210
|
+
// found version and owner the update must still match. An older Pi neither
|
|
211
|
+
// owns (mise, Homebrew, a binary) is left as it is: the installer's Pi is
|
|
212
|
+
// installed alongside, exactly as when Pi is absent.
|
|
213
|
+
const piSeen = inventory.pi;
|
|
214
|
+
const piStatus = classify(piSeen, requirements.pi);
|
|
215
|
+
if (piStatus === "incompatible" && piSeen.usable === true && (piSeen.owner === "pnpm" || piSeen.owner === "npm")) {
|
|
216
|
+
tools.pi = { status: "needs-update", required: requirements.pi, version: piSeen.version, owner: piSeen.owner };
|
|
217
|
+
} else if (piStatus === "incompatible" && piSeen.usable === true) {
|
|
218
|
+
tools.pi = { status: "needs-install", required: requirements.pi, version: piSeen.version };
|
|
219
|
+
} else {
|
|
220
|
+
record("pi", piStatus, requirements.pi);
|
|
221
|
+
}
|
|
222
|
+
const updatePi = tools.pi.status === "needs-update";
|
|
223
|
+
const installPi = tools.pi.status === "unavailable" || tools.pi.status === "needs-install";
|
|
224
|
+
if (update) record("shell", "needs-update", requirements.shell);
|
|
225
|
+
else if (npmCurrent) record("shell", "reusable", requirements.shell);
|
|
226
|
+
else record("shell", classify(inventory.shell, requirements.shell, (o) => o.global), requirements.shell);
|
|
227
|
+
// packageManager pins the acquisition choice, not a minimum supported pnpm.
|
|
228
|
+
// Existing versions require explicit engine/capability evidence from the probe.
|
|
229
|
+
record("pnpm", classify(inventory.pnpm, "0.0.0", (o) => o.compatible), requirements.pnpm);
|
|
230
|
+
// A user's pnpm in $PNPM_HOME/bin that could not be checked is left as it is while
|
|
231
|
+
// the bootstrap's pnpm runs the installer; the plan copy says so before consent.
|
|
232
|
+
if (tools.pnpm.status === "reusable" && inventory.pnpm.inGlobalBin === true && inventory.pnpm.unchecked === true) {
|
|
233
|
+
tools.pnpm = { ...tools.pnpm, unchecked: true };
|
|
234
|
+
}
|
|
235
|
+
// An older Node or an incompatible pnpm on the user's PATH is left as it is: the
|
|
236
|
+
// bootstrap acquired its pinned copy, which the probe reports (bootstrap-only)
|
|
237
|
+
// with the user's stable version as `found`. That copy runs the installer and
|
|
238
|
+
// is persisted below; the plan records both versions.
|
|
239
|
+
const replacedRuntimes = [];
|
|
240
|
+
for (const name of ["node", "pnpm"]) {
|
|
241
|
+
const seen = inventory[name];
|
|
242
|
+
const found = versionParts(seen?.found);
|
|
243
|
+
const replaced = name === "node" ? found !== null && compareVersions(found, versionParts(requirements.node)) < 0 : found !== null;
|
|
244
|
+
if (tools[name].status !== "reusable" || seen.persistent !== false || !replaced) continue;
|
|
245
|
+
tools[name] = { ...tools[name], found: found.join("."), version: persistencePins[name] };
|
|
246
|
+
replacedRuntimes.push(name);
|
|
247
|
+
}
|
|
248
|
+
record("gentleAi", classify(inventory.gentleAi, requirements.gentleAi, (o) => o.compatible, true), requirements.gentleAi);
|
|
249
|
+
const needsNative = tools.gentleAi.status === "unavailable";
|
|
250
|
+
// The main channel builds Gentle AI from source on every platform.
|
|
251
|
+
// gentle-pi's postinstall may build Gentle AI from source on Windows, so a
|
|
252
|
+
// Windows release update needs Go like an installation; main always does.
|
|
253
|
+
const windowsBuild = platform === "win32" && (update !== null || (needsNative && !npmCurrent));
|
|
254
|
+
const goStatus = windowsBuild || main ? classify(inventory.go, requirements.go) : "not-required";
|
|
255
|
+
if (goStatus === "unavailable" || goStatus === "incompatible" || goStatus === "unknown") {
|
|
256
|
+
// A missing, older or unreadable Go is left as it is: the build gets the
|
|
257
|
+
// installer's pinned Go, first on PATH, downloaded after consent only when
|
|
258
|
+
// a build actually runs.
|
|
259
|
+
const found = goStatus === "incompatible" ? versionParts(inventory.go.version).join(".") : null;
|
|
260
|
+
tools.go = { status: "needs-acquire", required: requirements.go, version: goAcquisition.version, ...(found ? { found } : {}),
|
|
261
|
+
...(goStatus === "unknown" ? { unchecked: true } : {}) };
|
|
262
|
+
} else {
|
|
263
|
+
record("go", goStatus, requirements.go);
|
|
264
|
+
}
|
|
265
|
+
const bin = inventory.globalBin;
|
|
266
|
+
const binKnown = bin?.available === true && typeof bin.path === "string" && bin.path.trim().length > 0 &&
|
|
267
|
+
bin.writable === true && typeof bin.onPath === "boolean";
|
|
268
|
+
record("globalBin", bin?.available === false ? "unavailable" : binKnown ? (bin.onPath ? "reusable" : "needs-setup") : "unknown");
|
|
269
|
+
// Windows only: the PNPM_HOME decision the wizard made before probing (S6,
|
|
270
|
+
// windowsPnpmHome). A passing user or default folder changes nothing here; a
|
|
271
|
+
// private one is recorded for the runner and the plan copy (a blocked one
|
|
272
|
+
// returned above).
|
|
273
|
+
const home = platform === "win32" && plainRecord(inventory.pnpmHome) ? inventory.pnpmHome : null;
|
|
274
|
+
if (home?.available === true && home.source === "private" && typeof home.path === "string" && typeof home.rejected?.path === "string") {
|
|
275
|
+
const finding = Object.fromEntries(["check", "at", "sid", "account", "rights"]
|
|
276
|
+
.filter((key) => typeof home.rejected[key] === "string").map((key) => [key, home.rejected[key]]));
|
|
277
|
+
tools.pnpmHome = { status: "private", path: home.path, default: home.rejected.path, finding };
|
|
278
|
+
}
|
|
279
|
+
// Windows only (S6 notice, never a blocker): reused tools whose folders another
|
|
280
|
+
// account can change, from the inventory's one `folders` walk. Only tools this
|
|
281
|
+
// plan uses as they are: the user's Node and its npm, a Go a build reuses, a Pi
|
|
282
|
+
// kept as it is, and an npm-owned Gentle Shell that is kept or updated by npm.
|
|
283
|
+
const folders = platform === "win32" && plainRecord(inventory.folders) ? inventory.folders : null;
|
|
284
|
+
if (folders) {
|
|
285
|
+
const userNode = tools.node.status === "reusable" && tools.node.found === undefined && inventory.node?.persistent === true;
|
|
286
|
+
const used = { node: userNode, npm: userNode && inventory.node?.npm === true, go: tools.go.status === "reusable", pi: tools.pi.status === "reusable",
|
|
287
|
+
shell: inventory.shell?.owner === "npm" && ["reusable", "needs-update"].includes(tools.shell.status) };
|
|
288
|
+
// A walk rejection, or a path the walk could not check; nothing else.
|
|
289
|
+
const known = /^(?:(?:target|parent|ancestor)-(?:reparse|owner|acl-mask)|unchecked)$/;
|
|
290
|
+
const reused = Object.keys(used).filter((tool) => used[tool] && plainRecord(folders[tool]) && known.test(String(folders[tool].check)))
|
|
291
|
+
.map((tool) => ({ tool, ...Object.fromEntries(["check", "at", "sid", "account", "rights"]
|
|
292
|
+
.filter((key) => typeof folders[tool][key] === "string").map((key) => [key, folders[tool][key]])) }));
|
|
293
|
+
if (reused.length > 0) tools.folders = { status: "notice", reused };
|
|
294
|
+
}
|
|
295
|
+
const missingShell = tools.shell.status === "unavailable";
|
|
296
|
+
// Setup recovery: the setup probe proved the pinned stack this pnpm installed
|
|
297
|
+
// (an earlier run stopped in setup), so only the public setup is rerun.
|
|
298
|
+
const recovering = inventory.setup?.available === true && inventory.setup.recoverable === true &&
|
|
299
|
+
["pi", "shell", "gentleAi"].every((name) => tools[name].status === "reusable");
|
|
300
|
+
let setupStatus = "unknown";
|
|
301
|
+
if (missingShell || needsNative || inventory.setup === false || recovering) setupStatus = "needs-setup";
|
|
302
|
+
else if (inventory.setup === true) setupStatus = "reusable";
|
|
303
|
+
record("setup", setupStatus);
|
|
304
|
+
// Runtimes are persisted only while installing Gentle Shell (the runner's
|
|
305
|
+
// persistence variants): with an existing Shell, updated or current, a replaced
|
|
306
|
+
// Node or pnpm keeps blocking as before. A setup recovery persists nothing and
|
|
307
|
+
// runs with the bootstrap's copy.
|
|
308
|
+
if (["needs-update", "reusable"].includes(tools.shell.status) && !recovering) {
|
|
309
|
+
for (const name of replacedRuntimes) blockers.push({ code: "incompatible-tool", tool: name });
|
|
310
|
+
}
|
|
311
|
+
if (blockers.length) return { tools, blockers, actions, ready: false };
|
|
312
|
+
|
|
313
|
+
function action(id, kind, target, version) {
|
|
314
|
+
actions.push({ id, kind, target, ...(version ? { version } : {}) });
|
|
315
|
+
}
|
|
316
|
+
// The pinned Go pair, at `at`, before the first step that builds with it.
|
|
317
|
+
function acquireGo(at = actions.length) {
|
|
318
|
+
if (tools.go.status !== "needs-acquire") return;
|
|
319
|
+
actions.splice(at, 0, { id: "acquire-go", kind: "acquire", target: "go", version: goAcquisition.version },
|
|
320
|
+
{ id: "verify-go", kind: "verify", target: "go" });
|
|
321
|
+
}
|
|
322
|
+
// Existing Gentle Shell: update it (installing a missing Pi or updating an older
|
|
323
|
+
// one first), then set it up. A current npm Shell only gets its Pi updated or
|
|
324
|
+
// the installer's Pi added.
|
|
325
|
+
if (update || npmCurrent) {
|
|
326
|
+
// Only an update builds: main, or a Windows release postinstall.
|
|
327
|
+
if (update) acquireGo();
|
|
328
|
+
if (updatePi) action("update-pi", "upgrade", "pi", PI_INSTALL_VERSION);
|
|
329
|
+
// A current npm Shell next to an older Pi neither manager owns gets the installer's Pi.
|
|
330
|
+
if (!update && tools.pi.status === "needs-install") action("install-pi", "install-global", "pi", requirements.pi);
|
|
331
|
+
if (update) {
|
|
332
|
+
if (installPi) action("install-pi", "install-global", "pi", requirements.pi);
|
|
333
|
+
action(`update-shell-${update}`, "upgrade", "shell");
|
|
334
|
+
action("setup-shell", "normal-setup", "shell");
|
|
335
|
+
}
|
|
336
|
+
action("verify-readiness", "verify", "stack");
|
|
337
|
+
return { tools, blockers, actions, ready: actions.length === 1 };
|
|
338
|
+
}
|
|
339
|
+
function acquire(name) {
|
|
340
|
+
if (tools[name].status !== "unavailable") return;
|
|
341
|
+
action(`acquire-${name}`, "acquire", name, requirements[name]);
|
|
342
|
+
action(`verify-${name}`, "verify", name);
|
|
343
|
+
}
|
|
344
|
+
acquire("node");
|
|
345
|
+
acquire("pnpm");
|
|
346
|
+
if (tools.globalBin.status !== "reusable") action("setup-global-bin", "setup", "globalBin");
|
|
347
|
+
// The pinned Go comes before anything is persisted or installed, so a failed
|
|
348
|
+
// download leaves this computer as it was.
|
|
349
|
+
const goAt = actions.length;
|
|
350
|
+
// A bootstrap Node lives in a temporary tools directory, and Gentle AI's Engram
|
|
351
|
+
// step needs a working npm: persist what is missing under PNPM_HOME through
|
|
352
|
+
// pnpm itself. A persistent Node is never replaced or shadowed. A recovery
|
|
353
|
+
// never persists: the earlier run did that before installing the stack.
|
|
354
|
+
const node = inventory.node;
|
|
355
|
+
const persistable = tools.node.status === "reusable" && !recovering;
|
|
356
|
+
// A persistent pnpm, such as a newer pnpm 11 in $PNPM_HOME/bin, is never replaced.
|
|
357
|
+
const pnpm = inventory.pnpm?.persistent === false;
|
|
358
|
+
if (persistable && node.persistent === false) {
|
|
359
|
+
action("persist-node", "persist-runtime", "node", persistencePins.node);
|
|
360
|
+
if (pnpm) action("persist-package-managers", "install-global", "package-managers");
|
|
361
|
+
else action("persist-npm", "install-global", "npm", persistencePins.npm);
|
|
362
|
+
action("configure-npm-prefix", "configure", "npm-prefix");
|
|
363
|
+
} else if (persistable) {
|
|
364
|
+
const npm = node.npm === false;
|
|
365
|
+
if (npm && pnpm) action("persist-package-managers", "install-global", "package-managers");
|
|
366
|
+
else if (npm) action("persist-npm", "install-global", "npm", persistencePins.npm);
|
|
367
|
+
else if (pnpm) action("persist-pnpm", "install-global", "pnpm", persistencePins.pnpm);
|
|
368
|
+
}
|
|
369
|
+
if (installPi) action("install-pi", "install-global", "pi", requirements.pi);
|
|
370
|
+
if (updatePi) action("update-pi", "upgrade", "pi", PI_INSTALL_VERSION);
|
|
371
|
+
if (missingShell) action("install-shell", "install-global", "shell", requirements.shell);
|
|
372
|
+
// Global gentle-pi postinstall owns native provisioning when Shell is missing.
|
|
373
|
+
// Otherwise the later runner must reuse that existing installer, not duplicate it.
|
|
374
|
+
if (needsNative && !missingShell) action("provision-native", "existing-installer", "gentleAi", requirements.gentleAi);
|
|
375
|
+
if (tools.setup.status !== "reusable") action("setup-shell", "normal-setup", "shell");
|
|
376
|
+
action("verify-readiness", "verify", "stack");
|
|
377
|
+
// Main overlays the verified release installation with both latest main commits;
|
|
378
|
+
// a stack that is already set up installs nothing (`gentle-shell upgrade --channel main` switches it).
|
|
379
|
+
const mainBuild = main && actions.length > 1;
|
|
380
|
+
if (mainBuild) {
|
|
381
|
+
action("build-gentle-ai-main", "build-native-main", "gentleAi");
|
|
382
|
+
action("install-shell-main", "install-global", "shell");
|
|
383
|
+
action("record-channel", "configure", "channel");
|
|
384
|
+
}
|
|
385
|
+
if (mainBuild || windowsBuild) acquireGo(goAt);
|
|
386
|
+
return { tools, blockers, actions, ready: actions.length === 1 };
|
|
387
|
+
}
|