gentle-pi 3.7.0 → 4.1.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 +155 -10
- package/assets/agents/gentle-ai-explore.md +13 -6
- package/assets/agents/gentle-ai-verify.md +27 -6
- package/assets/agents/gentle-ai-worker.md +13 -15
- 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 +75 -169
- package/assets/orchestrator-memory.md +12 -26
- package/assets/orchestrator-prompts.md +48 -0
- package/assets/orchestrator-skills.md +1 -1
- 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 +30 -38
- 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/assets/support/strict-tdd-verify.md +4 -266
- package/assets/support/strict-tdd.md +8 -360
- package/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +956 -50
- 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/bundled-install.md +397 -0
- package/docs/delegated-verification.md +27 -4
- package/docs/gentle-agents-activity.md +537 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +165 -51
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +3 -3
- package/docs/permission-modes.md +82 -0
- package/docs/prompt-history.md +280 -0
- package/docs/readme-reference.md +364 -231
- package/docs/render-cache-performance.md +28 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/telemetry.md +1 -1
- package/docs/yolo-mode.md +86 -0
- package/extensions/child-context.ts +35 -0
- package/extensions/child-safety.ts +25 -0
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +1093 -326
- package/extensions/gentle-ai.ts +1636 -890
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +1619 -136
- package/extensions/gentle-stats.ts +101 -0
- package/extensions/gentle-todo.ts +126 -20
- package/extensions/history/atomic-write.ts +38 -0
- package/extensions/history/hide-prompts.ts +183 -0
- package/extensions/history/index.ts +1424 -0
- package/extensions/history/load-shared-history.ts +39 -0
- package/extensions/history/selector-helpers.ts +538 -0
- package/extensions/history/session-scan.ts +233 -0
- package/extensions/history/store.ts +1119 -0
- package/extensions/nan-provider.ts +6 -0
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +254 -92
- package/extensions/resume-hint.ts +60 -0
- package/extensions/skill-registry.ts +191 -58
- package/extensions/startup-banner.ts +71 -33
- package/lib/agent-assets.ts +604 -0
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profile-pin.ts +12 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-message-delivery.ts +181 -0
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +99 -0
- package/lib/agents-runner.ts +180 -107
- package/lib/agents-view.ts +26 -3
- package/lib/agents-widget.ts +16 -9
- package/lib/append-system-prompt.ts +21 -0
- package/lib/auto-permission-classifier.ts +280 -0
- package/lib/auto-permission-runtime.ts +268 -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 +220 -0
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/card-style-policy.ts +60 -0
- package/lib/child-context-files.ts +166 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +195 -0
- package/lib/command-palette-catalog.ts +5 -10
- package/lib/command-palette.ts +25 -14
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +144 -0
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-ai-elapsed-store.ts +87 -0
- package/lib/gentle-ai-renderer.ts +196 -39
- package/lib/gentle-shell-bundled.ts +517 -0
- package/lib/gentle-shell-launcher.ts +89 -27
- package/lib/gentle-shell-resume-hint.ts +176 -0
- 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/history-capture-policy.ts +95 -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 +5 -1
- package/lib/nan-provider.ts +249 -0
- package/lib/native-review-cli.ts +242 -180
- 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-inference.ts +231 -0
- package/lib/odd-phase.ts +141 -0
- 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/overlay-repaint.ts +26 -0
- package/lib/permission-mode-policy.ts +59 -0
- package/lib/pi-tui-keys.ts +53 -0
- package/lib/review-candidate-view-owner.ts +73 -21
- package/lib/review-candidate-view.ts +115 -27
- 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-reminder-receipt.ts +48 -8
- package/lib/review-risk-assessment.ts +227 -56
- package/lib/review-sidebar-state.ts +224 -0
- 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 +527 -0
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-messaging-grants.ts +135 -0
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +266 -0
- package/lib/session-worktree-registry.ts +87 -9
- package/lib/shell-bar.ts +215 -30
- package/lib/shell-card.ts +287 -18
- package/lib/shell-changes-view.ts +42 -14
- package/lib/shell-changes.ts +86 -71
- package/lib/shell-prompt.ts +102 -6
- package/lib/shell-sidebar-layout.ts +83 -15
- package/lib/shell-sidebar.ts +15 -1
- package/lib/shell-todo.ts +84 -31
- package/lib/shell-usage-view.ts +9 -4
- package/lib/shell-usage.ts +70 -13
- package/lib/stats-collector.ts +381 -0
- package/lib/stats-view.ts +431 -0
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/theme-customization.ts +52 -0
- package/lib/vim-editor-adapter.ts +383 -0
- package/lib/vim-normal-engine.ts +154 -0
- package/lib/vim-operator-engine.ts +416 -0
- package/lib/vim-policy.ts +49 -0
- package/lib/vim-visual-engine.ts +107 -0
- package/lib/visual-customization-policy.ts +119 -0
- package/lib/visual-customize-view.ts +481 -0
- package/lib/visual-profiles.ts +228 -0
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +317 -0
- package/package.json +20 -8
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +518 -0
- package/runtime/gentle-shell-launcher.mjs +87 -25
- package/runtime/gentle-shell-resume-hint.mjs +177 -0
- 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 +239 -177
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +225 -54
- 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 +8 -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 +83 -35
- 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/mirror-odd-routing.mjs +2 -2
- package/scripts/npm-pack-result.mjs +31 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +90 -0
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +54 -19
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +91 -38
- package/skills/branch-pr/SKILL.md +24 -52
- package/skills/chained-pr/SKILL.md +31 -15
- package/skills/chained-pr/references/chaining-details.md +31 -20
- package/skills/gentle-ai/SKILL.md +16 -21
- package/skills/issue-creation/SKILL.md +8 -2
- package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
- package/skills/work-unit-commits/SKILL.md +4 -3
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +30 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-fake-child.ts +2 -2
- package/tests/agents-message-delivery.test.ts +106 -0
- package/tests/agents-protocol.test.ts +97 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +467 -93
- package/tests/agents-view-thread-identity.test.ts +169 -0
- package/tests/agents-view.test.ts +8 -2
- package/tests/agents-widget.test.ts +154 -15
- package/tests/append-system-prompt-route.test.ts +160 -0
- package/tests/append-system-prompt.test.ts +46 -0
- package/tests/artifact-language.test.ts +19 -213
- package/tests/ask-user-question.test.ts +44 -1
- package/tests/asset-installation-runtime.test.ts +5 -16
- package/tests/auto-mode-invariants.test.ts +258 -0
- package/tests/auto-mode-sdk.test.ts +57 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-classifier.test.ts +283 -0
- package/tests/auto-permission-runtime.test.ts +196 -0
- package/tests/autonomous-guard.test.ts +69 -1
- 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 +146 -0
- package/tests/branch-pr-skill.test.ts +43 -0
- 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 +87 -0
- package/tests/chained-pr-skill.test.ts +124 -0
- package/tests/child-codegraph-bootstrap.test.ts +74 -0
- package/tests/child-context-files.test.ts +255 -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 +94 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +596 -0
- package/tests/command-palette.test.ts +43 -7
- 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 -78
- package/tests/destructive-command-guard.test.ts +84 -0
- package/tests/devbinary/native-review-parity.devtest.ts +171 -3
- package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +288 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
- package/tests/generic-agent-tools.test.ts +69 -0
- package/tests/gentle-agents.test.ts +2804 -346
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +21 -4
- 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-elapsed-store.test.ts +68 -0
- package/tests/gentle-ai-installer.test.ts +566 -62
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +493 -14
- package/tests/gentle-ai.test.ts +1427 -236
- package/tests/gentle-card-text.ts +2 -1
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +896 -115
- package/tests/gentle-shell-bundled.test.ts +1002 -0
- package/tests/gentle-shell-launcher.test.ts +333 -65
- package/tests/gentle-shell-resume-hint.test.ts +270 -0
- 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 +4216 -119
- package/tests/gentle-stats.test.ts +152 -0
- package/tests/gentle-theme.test.ts +4 -1
- package/tests/gentle-todo.test.ts +333 -10
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-atomic-write.test.ts +57 -0
- package/tests/history-capture-policy.test.ts +102 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/history-command-registration.test.ts +164 -0
- package/tests/history-dedupe-entries.test.ts +123 -0
- package/tests/history-delete-backfill.test.ts +190 -0
- package/tests/history-delete-confirm.test.ts +460 -0
- package/tests/history-dispatch.test.ts +180 -0
- package/tests/history-drain-hidden.test.ts +110 -0
- package/tests/history-drain-order.test.ts +98 -0
- package/tests/history-expanded-globals.test.ts +62 -0
- package/tests/history-gc.test.ts +832 -0
- package/tests/history-header-layout.test.ts +265 -0
- package/tests/history-hide-prompts.test.ts +275 -0
- package/tests/history-lazy-windowing.test.ts +508 -0
- package/tests/history-legacy-migrate-v2.test.ts +297 -0
- package/tests/history-load-shared-history.test.ts +53 -0
- package/tests/history-max-results-cap.test.ts +76 -0
- package/tests/history-multi-reader.test.ts +203 -0
- package/tests/history-off-path.test.ts +170 -0
- package/tests/history-openflow-integration.test.ts +173 -0
- package/tests/history-overlay-margin.test.ts +326 -0
- package/tests/history-preview-layout.test.ts +93 -0
- package/tests/history-registry.test.ts +143 -0
- package/tests/history-scope-delete.test.ts +411 -0
- package/tests/history-search-caret-keys.test.ts +142 -0
- package/tests/history-seed-bootstrap.test.ts +170 -0
- package/tests/history-seed-regen.test.ts +129 -0
- package/tests/history-selector-windowing.test.ts +94 -0
- package/tests/history-session-scan-directory.test.ts +87 -0
- package/tests/history-session-scan-extract.test.ts +583 -0
- package/tests/history-session-writer.test.ts +351 -0
- package/tests/history-store-paths.test.ts +79 -0
- package/tests/history-tombstone-exact.test.ts +139 -0
- package/tests/history-wheel-mouse.test.ts +242 -0
- package/tests/inprocess-reviewer.test.ts +49 -19
- 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 +2158 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/issue-creation-skill.test.ts +61 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +389 -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-authority.test.ts +16 -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 +481 -0
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +13 -1
- package/tests/native-review-cli.test.ts +239 -118
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +101 -4
- 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 +76 -0
- package/tests/odd-phase-inference.test.ts +213 -0
- package/tests/odd-phase-loader.test.ts +253 -0
- package/tests/odd-phase.test.ts +307 -0
- package/tests/odd-routing-canonical-ratchet.test.ts +60 -14
- package/tests/odd-routing-contract.test.ts +158 -50
- package/tests/orchestrator-budget.test.ts +43 -45
- 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 +8 -7
- 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/overlay-repaint.test.ts +74 -0
- package/tests/package-manifest.test.ts +344 -123
- package/tests/packed-runner-owned-path.test.ts +80 -0
- 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 +10 -18
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/provider-defect-handoff.test.ts +7 -14
- package/tests/quiet-bash-runtime.test.ts +76 -0
- package/tests/quiet-tool-rendering.test.ts +569 -187
- package/tests/rdd-aware-verification-contract.test.ts +103 -40
- package/tests/rdd-status-line.test.ts +9 -4
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/resume-hint-extension.test.ts +122 -0
- package/tests/review-agent-end-preflight.test.ts +237 -21
- package/tests/review-candidate-owner-retry.test.ts +22 -1
- package/tests/review-candidate-view.test.ts +399 -12
- package/tests/review-contract-prompt.test.ts +138 -44
- package/tests/review-controller-lock-status.test.ts +0 -1
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +866 -6
- package/tests/review-controller-workspace-root.test.ts +206 -6
- 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 +342 -4
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +221 -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 +14 -37
- 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-reminder-receipt.test.ts +47 -1
- package/tests/review-risk-assessment.test.ts +594 -26
- 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-sidebar-state.test.ts +402 -0
- package/tests/review-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +202 -0
- package/tests/runtime-harness.mjs +186 -800
- package/tests/runtime-metrics-children.test.ts +16 -23
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +451 -0
- 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-messaging-grants.test.ts +255 -0
- package/tests/session-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +381 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +134 -2
- package/tests/shell-bar.test.ts +422 -1
- package/tests/shell-card.test.ts +353 -1
- package/tests/shell-changes-view.test.ts +135 -32
- package/tests/shell-changes.test.ts +61 -62
- package/tests/shell-prompt.test.ts +94 -2
- 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 +351 -22
- package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
- package/tests/shell-todo.test.ts +207 -3
- package/tests/shell-usage-view.test.ts +28 -1
- package/tests/shell-usage.test.ts +108 -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 +357 -2
- package/tests/startup-banner.test.ts +246 -4
- package/tests/startup-home-repo.test.ts +170 -0
- package/tests/stats-collector.test.ts +195 -0
- package/tests/stats-view.test.ts +202 -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 +107 -20
- package/tests/test-strength-contract.test.ts +124 -0
- package/tests/theme-customization.test.ts +72 -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-host-resolution.test.ts +37 -0
- package/tests/vim-editor-adapter.test.ts +804 -0
- package/tests/vim-normal-engine.test.ts +101 -0
- package/tests/vim-operator-engine.test.ts +215 -0
- package/tests/vim-policy.test.ts +19 -0
- package/tests/vim-visual-engine.test.ts +52 -0
- package/tests/visual-customization-policy.test.ts +139 -0
- package/tests/visual-customize-view.test.ts +696 -0
- package/tests/visual-profiles.test.ts +87 -0
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +271 -0
- package/tests/yolo-mode-runtime.test.ts +188 -0
- package/tests/yolo-mode.test.ts +289 -0
- package/tests/yolo-session-policy.test.ts +59 -0
- package/themes/Gentle.json +2 -1
- package/themes/Gentleman-Cute.json +2 -1
- package/themes/Gentleman-Sexy.json +2 -1
- package/assets/agents/sdd-apply.md +0 -159
- package/assets/agents/sdd-archive.md +0 -228
- package/assets/agents/sdd-design.md +0 -49
- package/assets/agents/sdd-explore.md +0 -48
- package/assets/agents/sdd-init.md +0 -56
- package/assets/agents/sdd-onboard.md +0 -52
- package/assets/agents/sdd-proposal.md +0 -64
- package/assets/agents/sdd-remediate.md +0 -37
- package/assets/agents/sdd-research.md +0 -49
- package/assets/agents/sdd-spec.md +0 -192
- package/assets/agents/sdd-status.md +0 -54
- package/assets/agents/sdd-tasks.md +0 -108
- package/assets/agents/sdd-verify.md +0 -124
- package/assets/chains/sdd-full.chain.md +0 -83
- package/assets/chains/sdd-plan.chain.md +0 -56
- package/assets/chains/sdd-verify.chain.md +0 -43
- package/assets/sdd-orchestrator-workflow.md +0 -319
- package/assets/support/sdd-status-contract.md +0 -77
- package/docs/assets/diagrams/sdd-cycle.svg +0 -14
- package/extensions/sdd-init.ts +0 -816
- package/lib/openspec-deltas.ts +0 -156
- package/lib/sdd-preflight.ts +0 -1066
- package/lib/sdd-research-capabilities.ts +0 -94
- package/lib/sdd-status.ts +0 -26
- package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
- package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
- package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
- package/tests/openspec-deltas.test.ts +0 -209
- package/tests/sdd-agent-tools.test.ts +0 -156
- package/tests/sdd-archive-replay.test.ts +0 -82
- package/tests/sdd-classical-continuation.test.ts +0 -74
- package/tests/sdd-execution-routing-contract.test.ts +0 -44
- package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
- package/tests/sdd-native-managed-uptake.test.ts +0 -243
- package/tests/sdd-no-attempts-contract.test.ts +0 -15
- package/tests/sdd-odd-integration.test.ts +0 -33
- package/tests/sdd-optional-research.test.ts +0 -124
- package/tests/sdd-planning-routing-contract.test.ts +0 -45
- package/tests/sdd-preflight-rpc-input.test.ts +0 -125
- package/tests/sdd-preflight.test.ts +0 -541
- package/tests/sdd-research-capabilities.test.ts +0 -114
- package/tests/sdd-research-live.test.ts +0 -241
- package/tests/sdd-selection-transport.test.ts +0 -653
- package/tests/sdd-status.test.ts +0 -9
- package/tests/sdd-task-truth.test.ts +0 -43
- /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,144 @@
|
|
|
1
|
+
// Recognizable executable forms only. This is deliberately not a shell/SQL
|
|
2
|
+
// interpreter or a sandbox: no expansion, script loading or remote inspection.
|
|
3
|
+
const DESTRUCTION_KIND = { DATABASE: "database", FILESYSTEM: "filesystem", GIT: "git" } as const;
|
|
4
|
+
type DestructionKind = (typeof DESTRUCTION_KIND)[keyof typeof DESTRUCTION_KIND];
|
|
5
|
+
export interface DestructiveCommandMatch {
|
|
6
|
+
kind: DestructionKind;
|
|
7
|
+
triggerIndex: number;
|
|
8
|
+
hardDeny: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface Token {
|
|
11
|
+
value: string;
|
|
12
|
+
raw: string;
|
|
13
|
+
operator: boolean;
|
|
14
|
+
index: number;
|
|
15
|
+
}
|
|
16
|
+
const separators = new Set([";", "&&", "||", "|", "&", "\n", "(", ")"]);
|
|
17
|
+
|
|
18
|
+
/** Keep quoted arguments intact so harmless prose is never treated as a command. */
|
|
19
|
+
function tokenize(command: string): Token[] {
|
|
20
|
+
const tokens: Token[] = [];
|
|
21
|
+
// Literal, named heredocs are data, not executable shell segments.
|
|
22
|
+
command = command.replace(/<<-?\s*(['"]?)([A-Za-z_]\w*)\1[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g, (raw) => {
|
|
23
|
+
const headerLength = raw.indexOf("\n") + 1;
|
|
24
|
+
return raw.slice(0, headerLength) + " ".repeat(raw.length - headerLength);
|
|
25
|
+
});
|
|
26
|
+
const pattern = /(?:"(?:\\.|[^"\\])*"|'[^']*'|\\.|[^\s;&|()'"\\])+|&&|\|\||[;&|()\n]/g;
|
|
27
|
+
for (const match of command.matchAll(pattern)) {
|
|
28
|
+
const raw = match[0];
|
|
29
|
+
const value = raw.replace(/"((?:\\.|[^"\\])*)"|'([^']*)'|\\(.)/g,
|
|
30
|
+
(_raw, double: string | undefined, single: string | undefined, escaped: string | undefined) => {
|
|
31
|
+
if (double !== undefined) return double.replace(/\\(["\\$`\n])/g, (_escape, char: string) => char === "\n" ? "" : char);
|
|
32
|
+
return single ?? escaped ?? "";
|
|
33
|
+
});
|
|
34
|
+
// Only raw operators are boundaries; quoted/escaped operators are words.
|
|
35
|
+
tokens.push({ value, raw, operator: separators.has(raw), index: match.index });
|
|
36
|
+
}
|
|
37
|
+
return tokens;
|
|
38
|
+
}
|
|
39
|
+
const executable = (value: string) => value.split("/").at(-1) ?? value;
|
|
40
|
+
|
|
41
|
+
function sqlDestroys(sql: string): boolean {
|
|
42
|
+
// Mask quoted identifiers as non-keyword atoms, and remove comments and
|
|
43
|
+
// strings. A table named "where" is not a predicate or a statement boundary.
|
|
44
|
+
const code = sql.replace(/--[^\n]*|\/\*[\s\S]*?\*\/|'(?:''|[^'])*'|"(?:""|[^"])*"|`(?:``|[^`])*`|\[(?:\]\]|[^\]])*\]/g,
|
|
45
|
+
(raw) => /^["`\[]/.test(raw) ? "identifier" : " ");
|
|
46
|
+
return code.split(";").some((statement) => {
|
|
47
|
+
if (/^\s*(?:DROP\s+(?:TABLE|DATABASE|SCHEMA|INDEX|VIEW)\b|TRUNCATE\s+(?:TABLE\s+)?\S)/i.test(statement)) return true;
|
|
48
|
+
if (!/^\s*DELETE\s+FROM\s+\S/i.test(statement)) return false;
|
|
49
|
+
return !/\bWHERE\b/i.test(statement) || /\bWHERE\s+(?:1\s*=\s*1|TRUE)\s*$/i.test(statement.trim());
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function recognizeDestructiveCommands(command: string, depth = 0): DestructiveCommandMatch[] {
|
|
54
|
+
if (depth > 4) return [];
|
|
55
|
+
const tokens = tokenize(command);
|
|
56
|
+
const matches: DestructiveCommandMatch[] = [];
|
|
57
|
+
let previous: Token[] = [];
|
|
58
|
+
let previousSeparator = "";
|
|
59
|
+
let start = 0;
|
|
60
|
+
for (let end = 0; end <= tokens.length; end++) {
|
|
61
|
+
if (end < tokens.length && !tokens[end].operator) continue;
|
|
62
|
+
const segment = tokens.slice(start, end);
|
|
63
|
+
let cursor = 0;
|
|
64
|
+
// Assignments and familiar executable wrappers preserve command position.
|
|
65
|
+
while (cursor < segment.length) {
|
|
66
|
+
const value = segment[cursor].value;
|
|
67
|
+
if (/^[A-Za-z_][A-Za-z_0-9]*=/.test(value)) {
|
|
68
|
+
cursor++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const name = executable(value);
|
|
72
|
+
if (!["env", "sudo", "command", "exec", "nohup", "timeout", "xargs"].includes(name)) break;
|
|
73
|
+
const valueOptions: Record<string, readonly string[]> = {
|
|
74
|
+
env: ["-u", "--unset", "-C", "--chdir"],
|
|
75
|
+
sudo: ["-u", "-g", "-h", "-p", "-C", "--user", "--group", "--host", "--prompt", "--chdir"],
|
|
76
|
+
timeout: ["-s", "--signal", "-k", "--kill-after"],
|
|
77
|
+
xargs: ["-n", "-I", "-P", "--max-args", "--replace", "--max-procs"],
|
|
78
|
+
};
|
|
79
|
+
cursor++;
|
|
80
|
+
while (cursor < segment.length && segment[cursor].value.startsWith("-")) {
|
|
81
|
+
const flag = segment[cursor++].value;
|
|
82
|
+
if (flag === "--") break;
|
|
83
|
+
if (valueOptions[name]?.includes(flag)) cursor++;
|
|
84
|
+
}
|
|
85
|
+
if (name === "timeout") cursor++;
|
|
86
|
+
}
|
|
87
|
+
const head = segment[cursor];
|
|
88
|
+
const name = executable(head?.value ?? "");
|
|
89
|
+
const args = segment.slice(cursor + 1).map((token) => token.value);
|
|
90
|
+
const add = (kind: DestructionKind, hardDeny = false) => matches.push({ kind, hardDeny, triggerIndex: head.index });
|
|
91
|
+
if (["sh", "bash", "zsh", "dash"].includes(name)) {
|
|
92
|
+
const flag = args.findIndex((arg) => /^-[a-z]*c[a-z]*$/.test(arg));
|
|
93
|
+
if (flag >= 0 && args[flag + 1]) {
|
|
94
|
+
// Nested offsets use the enclosing executable, which is stable even
|
|
95
|
+
// when quote removal changes the payload's byte positions.
|
|
96
|
+
matches.push(...recognizeDestructiveCommands(args[flag + 1], depth + 1).map((match) => ({ ...match, triggerIndex: head.index })));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (["psql", "mysql", "mariadb", "sqlite3"].includes(name)) {
|
|
100
|
+
const payloads = args.map((arg) => arg.replace(/^(?:--command|--execute)=/, ""));
|
|
101
|
+
const heredoc = command.slice(head.index).match(/^[^\n]*<<-?\s*(['"]?)([A-Za-z_]\w*)\1[^\n]*\n([\s\S]*?)\n\2(?=\n|$)/);
|
|
102
|
+
if (heredoc) payloads.push(heredoc[3]);
|
|
103
|
+
if (previousSeparator === "|" && ["echo", "printf"].includes(executable(previous[0]?.value ?? ""))) {
|
|
104
|
+
payloads.push(previous.slice(1).map((token) => token.value).join(" ").replace(/^%s\s*/, ""));
|
|
105
|
+
}
|
|
106
|
+
if (payloads.some(sqlDestroys)) add(DESTRUCTION_KIND.DATABASE);
|
|
107
|
+
}
|
|
108
|
+
if (name === "rm" && args.some((arg) => /^-[^-]*[rR]/.test(arg) || arg === "--recursive")) {
|
|
109
|
+
const rootTarget = args.some((arg) => /^(?:\/(?:\*+)?$|\.{1,2}\/?$|~(?:\/|$)|\$\{?HOME\}?(?:\/|$))/.test(arg));
|
|
110
|
+
add(DESTRUCTION_KIND.FILESYSTEM, rootTarget);
|
|
111
|
+
}
|
|
112
|
+
if (name === "find") {
|
|
113
|
+
if (args.includes("-delete")) add(DESTRUCTION_KIND.FILESYSTEM);
|
|
114
|
+
for (let execIndex = 0; execIndex < args.length; execIndex++) {
|
|
115
|
+
if (!["-exec", "-execdir"].includes(args[execIndex])) continue;
|
|
116
|
+
const invocation = segment.slice(cursor + execIndex + 2);
|
|
117
|
+
const terminator = invocation.findIndex((arg) => arg.value === ";" || arg.value === "+");
|
|
118
|
+
const nested = (terminator < 0 ? invocation : invocation.slice(0, terminator)).map((arg) => arg.raw).join(" ");
|
|
119
|
+
matches.push(...recognizeDestructiveCommands(nested, depth + 1).map((match) => ({ ...match, triggerIndex: head.index })));
|
|
120
|
+
if (terminator < 0) break;
|
|
121
|
+
execIndex += terminator + 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (name === "git") {
|
|
125
|
+
let index = 0;
|
|
126
|
+
while (args[index]?.startsWith("-")) {
|
|
127
|
+
const flag = args[index++];
|
|
128
|
+
if (["-C", "-c", "--git-dir", "--work-tree", "--namespace"].includes(flag)) index++;
|
|
129
|
+
}
|
|
130
|
+
const action = args[index];
|
|
131
|
+
const flags = args.slice(index + 1);
|
|
132
|
+
const force = flags.some((arg) => /^--force(?:-with-lease|=|$)/.test(arg) || /^-[^-]*f/.test(arg));
|
|
133
|
+
const hard = (action === "reset" && flags.includes("--hard")) || (action === "clean" && force) || (action === "push" && force);
|
|
134
|
+
const branchDelete = action === "branch" && (flags.some((arg) => /^-[^-]*D/.test(arg)) || (force && flags.some((arg) => arg === "--delete" || /^-[^-]*d/.test(arg))));
|
|
135
|
+
if (hard || branchDelete || ["reset", "clean", "restore", "rebase"].includes(action) ||
|
|
136
|
+
(action === "checkout" && (force || flags.includes("--"))) ||
|
|
137
|
+
(action === "stash" && ["drop", "clear"].includes(flags[0]))) add(DESTRUCTION_KIND.GIT, hard);
|
|
138
|
+
}
|
|
139
|
+
previous = segment;
|
|
140
|
+
previousSeparator = tokens[end]?.value ?? "";
|
|
141
|
+
start = end + 1;
|
|
142
|
+
}
|
|
143
|
+
return matches;
|
|
144
|
+
}
|
package/lib/gentle-ai-binary.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { dirname, isAbsolute, join, posix, relative, resolve, win32 } from "node:path";
|
|
5
5
|
import {
|
|
6
6
|
GENTLE_AI_INSTALL_METHOD,
|
|
7
7
|
GENTLE_AI_WINDOWS_SOURCE_MODULE,
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
isWindowsGoSumdbSourceTarget,
|
|
14
14
|
resolveGentleAiReleaseAsset,
|
|
15
15
|
} from "../scripts/gentle-ai-installer.mjs";
|
|
16
|
+
import { engramPinVersion } from "../scripts/installer-downloads.mjs";
|
|
17
|
+
import { bundledGentlePiConfigHome, bundledPiConfigDir, bundledPiConfigParent, runningBundledPrefix } from "./gentle-shell-bundled.ts";
|
|
16
18
|
import { fileURLToPath } from "node:url";
|
|
17
19
|
|
|
18
20
|
export const GENTLE_AI_BINARY_MISSING_CODE = "package-local-binary-missing";
|
|
@@ -45,6 +47,84 @@ export function gentleAiBinaryPath(
|
|
|
45
47
|
);
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Bundled isolation (S9). The bundled launcher (bin/gentle-shell.mjs) gives Pi
|
|
52
|
+
// and setup GENTLE_SHELL_PREFIX, its prefix. Every gentle-ai run a bundled
|
|
53
|
+
// gentle-pi starts gets Gentle AI's own variables (gentle-ai #5462): its state,
|
|
54
|
+
// backups and telemetry under <prefix>/gentle-ai, the gentle-pi config folder
|
|
55
|
+
// <prefix>/config/gentle-ai for its persona and background subagent files
|
|
56
|
+
// (and, for Gentle AI 4.0.0, GENTLE_PI_CONFIG_HOME=<prefix>/config), the pinned
|
|
57
|
+
// Engram in runtime/ with no Engram acquisition or dependency probe, and no
|
|
58
|
+
// self-update. They are never put in Pi's own environment: the agent's shell
|
|
59
|
+
// inherits it, and a gentle-ai the user runs there stays the user's. A
|
|
60
|
+
// gentle-ai release without #5462 ignores them.
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export interface BundledGentleAiPaths {
|
|
64
|
+
/** GENTLE_AI_HOME. */
|
|
65
|
+
home: string;
|
|
66
|
+
/** GENTLE_PI_CONFIG_HOME for Gentle AI only: the parent Gentle AI 4.0.0 appends gentle-ai to. */
|
|
67
|
+
piConfigParent: string;
|
|
68
|
+
/** GENTLE_AI_PI_CONFIG_DIR: gentle-pi's own bundled config home, <piConfigParent>/gentle-ai. */
|
|
69
|
+
piConfig: string;
|
|
70
|
+
/** The Engram scripts/bundled-install.mjs publishes (prefixLayout's `engram`). */
|
|
71
|
+
engram: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function bundledGentleAiPaths(prefix: string, platform: string = process.platform): BundledGentleAiPaths {
|
|
75
|
+
const path = platform === "win32" ? win32 : posix;
|
|
76
|
+
return {
|
|
77
|
+
home: path.join(prefix, "gentle-ai"),
|
|
78
|
+
piConfigParent: bundledPiConfigParent(prefix, platform),
|
|
79
|
+
piConfig: bundledPiConfigDir(prefix, platform),
|
|
80
|
+
engram: path.join(prefix, "runtime", `engram-${engramPinVersion}`, platform === "win32" ? "engram.exe" : "engram"),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* `env` plus GENTLE_AI_HOME, GENTLE_AI_PI_CONFIG_DIR, GENTLE_AI_ENGRAM_BIN
|
|
86
|
+
* (only when the pinned Engram is published), GENTLE_AI_NO_SELF_UPDATE=1 and
|
|
87
|
+
* GENTLE_PI_CONFIG_HOME (the parent of GENTLE_AI_PI_CONFIG_DIR)
|
|
88
|
+
* when this gentle-pi runs from the valid bundled install GENTLE_SHELL_PREFIX
|
|
89
|
+
* names (runningBundledPrefix). Otherwise `env` itself, unchanged: a gentle-pi
|
|
90
|
+
* installed elsewhere (the user's own, started from the agent's shell, which
|
|
91
|
+
* inherits the variable) keeps its own Gentle AI.
|
|
92
|
+
*/
|
|
93
|
+
export function gentleAiIsolationEnv(
|
|
94
|
+
env: NodeJS.ProcessEnv,
|
|
95
|
+
options: { packageRoot?: string; platform?: string } = {},
|
|
96
|
+
): NodeJS.ProcessEnv {
|
|
97
|
+
const platform = options.platform ?? process.platform;
|
|
98
|
+
const prefix = runningBundledPrefix(env, { packageRoot: options.packageRoot, platform });
|
|
99
|
+
if (prefix === undefined) return env;
|
|
100
|
+
const paths = bundledGentleAiPaths(prefix, platform);
|
|
101
|
+
let engram = false;
|
|
102
|
+
try {
|
|
103
|
+
engram = lstatSync(paths.engram).isFile();
|
|
104
|
+
} catch {
|
|
105
|
+
// Not published (a prefix from before Engram shipped): Gentle AI gets none.
|
|
106
|
+
}
|
|
107
|
+
// Gentle AI 4.0.0 ignores GENTLE_AI_ENGRAM_BIN and looks `engram` up on PATH
|
|
108
|
+
// first, then falls back to Homebrew (`brew tap`, #2105): the pinned one goes first.
|
|
109
|
+
const pathKey = Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? (platform === "win32" ? "Path" : "PATH");
|
|
110
|
+
const pathModule = platform === "win32" ? win32 : posix;
|
|
111
|
+
const path = engram
|
|
112
|
+
? { [pathKey]: [pathModule.dirname(paths.engram), ...(env[pathKey] ?? "").split(pathModule.delimiter).filter((entry) => entry.length > 0)].join(pathModule.delimiter) }
|
|
113
|
+
: {};
|
|
114
|
+
return {
|
|
115
|
+
...env,
|
|
116
|
+
...path,
|
|
117
|
+
GENTLE_AI_HOME: paths.home,
|
|
118
|
+
GENTLE_AI_PI_CONFIG_DIR: paths.piConfig,
|
|
119
|
+
...(engram ? { GENTLE_AI_ENGRAM_BIN: paths.engram } : {}),
|
|
120
|
+
GENTLE_AI_NO_SELF_UPDATE: "1",
|
|
121
|
+
// Gentle AI 4.0.0 knows no GENTLE_AI_PI_CONFIG_DIR: it writes gentle-pi's
|
|
122
|
+
// background-subagents policy to <GENTLE_PI_CONFIG_HOME>/gentle-ai, the same
|
|
123
|
+
// folder. Only these Gentle AI processes get it, never Pi or its shell.
|
|
124
|
+
GENTLE_PI_CONFIG_HOME: paths.piConfigParent,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
48
128
|
function sha256(value: Buffer): string {
|
|
49
129
|
return createHash("sha256").update(value).digest("hex");
|
|
50
130
|
}
|
|
@@ -55,7 +135,8 @@ function sha256(value: Buffer): string {
|
|
|
55
135
|
// Two explicit activation paths, in precedence order:
|
|
56
136
|
// 1. GENTLE_PI_GENTLE_AI_DEV_BINARY (session override, absolute path), then
|
|
57
137
|
// 2. the persistent registration file at
|
|
58
|
-
// <
|
|
138
|
+
// <config home>/dev-binary.json (a bundled prefix's gentle-pi, else
|
|
139
|
+
// GENTLE_PI_CONFIG_HOME, else ~/.pi/gentle-ai) with the strict
|
|
59
140
|
// shape {"schema":"gentle-pi.dev-binary/v1","path":"<absolute path>"}.
|
|
60
141
|
//
|
|
61
142
|
// The registration deliberately pins no digest: it is the unpinned field-test
|
|
@@ -113,7 +194,7 @@ function ambientDevBinaryEnvironment(): GentleAiDevBinaryEnvironment {
|
|
|
113
194
|
}
|
|
114
195
|
|
|
115
196
|
export function gentleAiDevBinaryRegistrationPath(environment: GentleAiDevBinaryEnvironment = ambientDevBinaryEnvironment()): string {
|
|
116
|
-
const configHome = environment.env.GENTLE_PI_CONFIG_HOME ?? join(environment.home, ".pi", "gentle-ai");
|
|
197
|
+
const configHome = bundledGentlePiConfigHome(environment.env) ?? environment.env.GENTLE_PI_CONFIG_HOME ?? join(environment.home, ".pi", "gentle-ai");
|
|
117
198
|
return join(configHome, "dev-binary.json");
|
|
118
199
|
}
|
|
119
200
|
|
|
@@ -268,6 +349,30 @@ function sameFile(before: ReturnType<typeof lstatSync>, after: ReturnType<typeof
|
|
|
268
349
|
return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs;
|
|
269
350
|
}
|
|
270
351
|
|
|
352
|
+
// Reading and hashing the whole binary on every native call is expensive, and
|
|
353
|
+
// on Windows antivirus scans each read. The digest is reused only while the
|
|
354
|
+
// file keeps its full identity; any rewrite changes ctime, which callers cannot
|
|
355
|
+
// set, so a replaced binary is always hashed again. Keyed per reader so an
|
|
356
|
+
// injected reader never sees another reader's digest.
|
|
357
|
+
const binaryDigestCache = new WeakMap<(path: string) => Buffer, Map<string, { readonly identity: string; readonly sha256: string }>>();
|
|
358
|
+
|
|
359
|
+
function binaryIdentity(path: string): string {
|
|
360
|
+
const details = lstatSync(path, { bigint: true });
|
|
361
|
+
return [details.dev, details.ino, details.size, details.mtimeNs, details.ctimeNs].join(":");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function binaryDigest(path: string, readBinary: (path: string) => Buffer): string {
|
|
365
|
+
let entries = binaryDigestCache.get(readBinary);
|
|
366
|
+
if (entries === undefined) binaryDigestCache.set(readBinary, entries = new Map());
|
|
367
|
+
const identity = binaryIdentity(path);
|
|
368
|
+
const cached = entries.get(path);
|
|
369
|
+
if (cached?.identity === identity) return cached.sha256;
|
|
370
|
+
const digest = sha256(readBinary(path));
|
|
371
|
+
if (binaryIdentity(path) === identity) entries.set(path, { identity, sha256: digest });
|
|
372
|
+
else entries.delete(path);
|
|
373
|
+
return digest;
|
|
374
|
+
}
|
|
375
|
+
|
|
271
376
|
export function resolveGentleAiBinary(
|
|
272
377
|
packageRoot = dirname(dirname(fileURLToPath(import.meta.url))),
|
|
273
378
|
platform = process.platform,
|
|
@@ -296,7 +401,7 @@ export function resolveGentleAiBinary(
|
|
|
296
401
|
const beforeManifest = lstatSync(manifestPath);
|
|
297
402
|
const manifestContents = readFileSync(manifestPath, "utf8");
|
|
298
403
|
const manifest = JSON.parse(manifestContents) as Record<string, unknown>;
|
|
299
|
-
const binarySha256 =
|
|
404
|
+
const binarySha256 = binaryDigest(binaryPath, readBinary);
|
|
300
405
|
const expected = expectedRuntimeManifest(platform, binarySha256, manifest);
|
|
301
406
|
if (!isCanonicalManifest(manifestContents, manifest, expected)) throw new Error("invalid runtime integrity manifest");
|
|
302
407
|
const afterBinary = lstatSync(binaryPath);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export const GENTLE_AI_TIMING_ENTRY = "gentle-ai-elapsed-timing/v1";
|
|
2
|
+
|
|
3
|
+
export interface GentleAiTimingRecord {
|
|
4
|
+
toolCallId: string;
|
|
5
|
+
startedAt: number;
|
|
6
|
+
endedAt?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** What the renderer consumes: a read-only lookup keyed by pi's stable toolCallId. */
|
|
10
|
+
export interface GentleAiTimingLookup {
|
|
11
|
+
lookup(toolCallId: string): GentleAiTimingRecord | undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface GentleAiTimingEntry {
|
|
15
|
+
type: string;
|
|
16
|
+
customType?: string;
|
|
17
|
+
data?: unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface GentleAiTimingSessionReader {
|
|
21
|
+
getEntries(): readonly GentleAiTimingEntry[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface GentleAiTimingAppendHost {
|
|
25
|
+
appendEntry(type: string, data?: unknown): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function parseGentleAiTimingData(data: unknown): GentleAiTimingRecord | undefined {
|
|
29
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return undefined;
|
|
30
|
+
const record = data as { toolCallId?: unknown; startedAt?: unknown; endedAt?: unknown };
|
|
31
|
+
if (typeof record.toolCallId !== "string" || record.toolCallId.length === 0) return undefined;
|
|
32
|
+
if (typeof record.startedAt !== "number" || !Number.isFinite(record.startedAt)) return undefined;
|
|
33
|
+
// Local capture + a plain typeof guard: under non-strict null checks a
|
|
34
|
+
// `x === undefined` condition leaves `unknown` unnarrowed, so the finite-
|
|
35
|
+
// number branch must be decided by typeof before the value is used.
|
|
36
|
+
const endedAt = record.endedAt;
|
|
37
|
+
if (endedAt !== undefined) {
|
|
38
|
+
if (typeof endedAt !== "number" || !Number.isFinite(endedAt)) return undefined;
|
|
39
|
+
return { toolCallId: record.toolCallId, startedAt: record.startedAt, endedAt };
|
|
40
|
+
}
|
|
41
|
+
return { toolCallId: record.toolCallId, startedAt: record.startedAt };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Replay: entries are append-only, so the last timing entry per toolCallId wins. */
|
|
45
|
+
export function readGentleAiTimings(entries: readonly GentleAiTimingEntry[]): Map<string, GentleAiTimingRecord> {
|
|
46
|
+
const timings = new Map<string, GentleAiTimingRecord>();
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
if (entry.type !== "custom" || entry.customType !== GENTLE_AI_TIMING_ENTRY) continue;
|
|
49
|
+
const record = parseGentleAiTimingData(entry.data);
|
|
50
|
+
if (record) timings.set(record.toolCallId, record);
|
|
51
|
+
}
|
|
52
|
+
return timings;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// No global store: the owning extension binds its own Pi API, and reading all
|
|
56
|
+
// entries also catches a launch that happened while the shell was absent.
|
|
57
|
+
export class GentleAiElapsedTimingLedger implements GentleAiTimingLookup {
|
|
58
|
+
private readonly recorded = new Set<string>();
|
|
59
|
+
private readonly session: GentleAiTimingSessionReader;
|
|
60
|
+
private readonly host?: GentleAiTimingAppendHost;
|
|
61
|
+
|
|
62
|
+
constructor(session: GentleAiTimingSessionReader, host?: GentleAiTimingAppendHost) {
|
|
63
|
+
this.session = session;
|
|
64
|
+
this.host = host;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
lookup(toolCallId: string): GentleAiTimingRecord | undefined {
|
|
68
|
+
return readGentleAiTimings(this.session.getEntries()).get(toolCallId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
recordStart(toolCallId: string, startedAt: number): void {
|
|
72
|
+
this.append(`${toolCallId}:start`, { toolCallId, startedAt });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
recordEnd(toolCallId: string, endedAt: number): void {
|
|
76
|
+
const startedAt = this.lookup(toolCallId)?.startedAt;
|
|
77
|
+
// A terminal without a durable start has no honest duration to freeze.
|
|
78
|
+
if (startedAt === undefined) return;
|
|
79
|
+
this.append(`${toolCallId}:end`, { toolCallId, startedAt, endedAt });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private append(stage: string, data: GentleAiTimingRecord): void {
|
|
83
|
+
if (this.recorded.has(stage)) return;
|
|
84
|
+
this.host?.appendEntry(GENTLE_AI_TIMING_ENTRY, data);
|
|
85
|
+
this.recorded.add(stage);
|
|
86
|
+
}
|
|
87
|
+
}
|