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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import test from "node:test";
|
|
6
|
+
import { CHILD_PACKAGE_INJECTION_ENV, encodeChildPackageInjection, parseChildPackageInjection } from "../lib/child-package-injection.ts";
|
|
6
7
|
import {
|
|
7
8
|
MIN_PI_VERSION,
|
|
8
9
|
MIN_SETUP_GENTLE_AI_VERSION,
|
|
@@ -32,6 +33,8 @@ import {
|
|
|
32
33
|
quoteForCmdExe,
|
|
33
34
|
recordProvisioned,
|
|
34
35
|
resolveHome,
|
|
36
|
+
USER_PI_HOME_ENV,
|
|
37
|
+
userPiHome,
|
|
35
38
|
resolvePiRuntime,
|
|
36
39
|
restoreJsonField,
|
|
37
40
|
settingsDeclareGentlePi,
|
|
@@ -152,6 +155,50 @@ test("parseLauncherArgs recognises the home subcommand as argv[0] and captures t
|
|
|
152
155
|
assert.deepEqual(parsed.passthrough, []);
|
|
153
156
|
});
|
|
154
157
|
|
|
158
|
+
test("parseLauncherArgs recognises the upgrade subcommand as argv[0] and captures its arguments", () => {
|
|
159
|
+
const parsed = parseLauncherArgs(["upgrade", "--channel", "main"]);
|
|
160
|
+
assert.equal(parsed.command, "upgrade");
|
|
161
|
+
assert.deepEqual(parsed.commandArgs, ["--channel", "main"]);
|
|
162
|
+
assert.deepEqual(parsed.passthrough, []);
|
|
163
|
+
assert.equal(parsed.piSubcommand, undefined);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("parseLauncherArgs recognises the self-uninstall subcommand as argv[0] and captures its arguments", () => {
|
|
167
|
+
const parsed = parseLauncherArgs(["self-uninstall", "--dry-run", "--include-shared"]);
|
|
168
|
+
assert.equal(parsed.command, "self-uninstall");
|
|
169
|
+
assert.deepEqual(parsed.commandArgs, ["--dry-run", "--include-shared"]);
|
|
170
|
+
assert.deepEqual(parsed.passthrough, []);
|
|
171
|
+
assert.equal(parsed.piSubcommand, undefined);
|
|
172
|
+
assert.equal(parsed.error, undefined);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("parseLauncherArgs keeps uninstall as pi's own alias for remove", () => {
|
|
176
|
+
const parsed = parseLauncherArgs(["uninstall", "npm:x"]);
|
|
177
|
+
assert.equal(parsed.command, undefined);
|
|
178
|
+
assert.equal(parsed.piSubcommand, "uninstall");
|
|
179
|
+
assert.deepEqual(parsed.passthrough, ["uninstall", "npm:x"]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("help text documents gentle-shell self-uninstall and its flags", () => {
|
|
183
|
+
const text = helpText();
|
|
184
|
+
assert.ok(text.includes(" gentle-shell self-uninstall [--dry-run] [--yes] [--include-shared]"));
|
|
185
|
+
assert.ok(text.includes(" self-uninstall Remove Gentle Shell: its own data, the isolated home and the gentle-pi"));
|
|
186
|
+
assert.match(text, /'gentle-shell uninstall <source>' is pi's own alias for 'remove'/);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("help text documents gentle-shell upgrade and its channels", () => {
|
|
190
|
+
const text = helpText();
|
|
191
|
+
assert.ok(text.includes(" gentle-shell upgrade [--channel release|main]"));
|
|
192
|
+
assert.ok(text.includes(" upgrade Update Gentle Shell along its channel: the latest release, or the latest"));
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("help text documents the bundled upgrade, its rollback and the pinned Pi update", () => {
|
|
196
|
+
const text = helpText();
|
|
197
|
+
assert.ok(text.includes(" gentle-shell upgrade --rollback"));
|
|
198
|
+
assert.match(text, /bundled install.*release channel only.*--rollback switches back to the previous kept version/s);
|
|
199
|
+
assert.match(text, /In a bundled install Pi ships pinned: 'update' without a package\n\s+runs 'gentle-shell upgrade'/);
|
|
200
|
+
});
|
|
201
|
+
|
|
155
202
|
test("parseLauncherArgs treats home as a plain passthrough token when it is not argv[0]", () => {
|
|
156
203
|
const parsed = parseLauncherArgs(["--isolated", "home"]);
|
|
157
204
|
assert.equal(parsed.command, undefined);
|
|
@@ -308,6 +355,22 @@ test("resolveHome falls back to <homedir>/.pi/agent for --link with no override"
|
|
|
308
355
|
assert.deepEqual(resolved, { mode: "link", dir: join("/home/alan", ".pi", "agent"), source: "flag" });
|
|
309
356
|
});
|
|
310
357
|
|
|
358
|
+
test("--link inside an isolated session resolves the preserved original Pi home (#2015)", () => {
|
|
359
|
+
const nestedEnv = { PI_CODING_AGENT_DIR: "/home/alan/.gentle-shell/agent", GENTLE_SHELL_USER_PI_HOME: "/custom/pi" };
|
|
360
|
+
// Nested: the inherited isolated home must not be re-selected.
|
|
361
|
+
assert.deepEqual(
|
|
362
|
+
resolveHome({ args: args({ link: true }), env: nestedEnv, homedir: "/home/alan", config: undefined }),
|
|
363
|
+
{ mode: "link", dir: "/custom/pi", source: "flag" },
|
|
364
|
+
);
|
|
365
|
+
assert.deepEqual(
|
|
366
|
+
resolveHome({ args: args(), env: nestedEnv, homedir: "/home/alan", config: { mode: "link" } }),
|
|
367
|
+
{ mode: "link", dir: "/custom/pi", source: "config" },
|
|
368
|
+
);
|
|
369
|
+
// Clean terminal: a custom override and the conventional home are unchanged.
|
|
370
|
+
assert.equal(resolveHome({ args: args({ link: true }), env: { PI_CODING_AGENT_DIR: "/custom/pi" }, homedir: "/home/alan", config: undefined }).dir, "/custom/pi");
|
|
371
|
+
assert.equal(resolveHome({ args: args({ link: true }), env: {}, homedir: "/home/alan", config: undefined }).dir, join("/home/alan", ".pi", "agent"));
|
|
372
|
+
});
|
|
373
|
+
|
|
311
374
|
test("resolveHome honours --isolated and reads GENTLE_SHELL_HOME", () => {
|
|
312
375
|
const resolved = resolveHome({
|
|
313
376
|
args: args({ isolated: true }),
|
|
@@ -620,20 +683,37 @@ test("checkPeerVersionPin passes for a matching pin", () => {
|
|
|
620
683
|
assert.deepEqual(result, { ok: true, pinned: ">=0.85.1" });
|
|
621
684
|
});
|
|
622
685
|
|
|
686
|
+
test("Pi 0.99.1 baseline and host peers follow the open development range policy", () => {
|
|
687
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
688
|
+
assert.equal(MIN_PI_VERSION, "0.99.1");
|
|
689
|
+
assert.equal(pkg.engines.node, ">=22.19.0");
|
|
690
|
+
for (const name of ["@earendil-works/pi-ai", "@earendil-works/pi-tui"]) {
|
|
691
|
+
assert.equal(pkg.peerDependencies[name], "*");
|
|
692
|
+
assert.equal(pkg.peerDependenciesMeta[name].optional, true);
|
|
693
|
+
assert.equal(pkg.dependencies[name], undefined);
|
|
694
|
+
}
|
|
695
|
+
// Development ranges are policy specifiers; their floor never drops below
|
|
696
|
+
// the runtime minimum the launcher enforces.
|
|
697
|
+
for (const name of ["@earendil-works/pi-coding-agent", "@earendil-works/pi-ai", "@earendil-works/pi-tui"]) {
|
|
698
|
+
assert.equal(pkg.devDependencies[name], ">=1.0.0", name);
|
|
699
|
+
assert.equal(checkPiVersion(pkg.devDependencies[name].slice(2)).ok, true, name);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
623
703
|
test("checkPiVersion accepts a version equal to the minimum", () => {
|
|
624
|
-
assert.deepEqual(checkPiVersion("0.
|
|
704
|
+
assert.deepEqual(checkPiVersion("0.99.1"), { ok: true, version: "0.99.1" });
|
|
625
705
|
});
|
|
626
706
|
|
|
627
707
|
test("checkPiVersion accepts a version above the minimum", () => {
|
|
628
|
-
assert.deepEqual(checkPiVersion("0.
|
|
708
|
+
assert.deepEqual(checkPiVersion("0.100.0"), { ok: true, version: "0.100.0" });
|
|
629
709
|
});
|
|
630
710
|
|
|
631
711
|
test("checkPiVersion accepts a v-prefixed version", () => {
|
|
632
|
-
assert.deepEqual(checkPiVersion("v0.
|
|
712
|
+
assert.deepEqual(checkPiVersion("v0.99.1"), { ok: true, version: "0.99.1" });
|
|
633
713
|
});
|
|
634
714
|
|
|
635
715
|
test("checkPiVersion accepts a prerelease suffix at the minimum", () => {
|
|
636
|
-
assert.deepEqual(checkPiVersion("pi version 0.
|
|
716
|
+
assert.deepEqual(checkPiVersion("pi version 0.99.1-rc.2"), { ok: true, version: "0.99.1" });
|
|
637
717
|
});
|
|
638
718
|
|
|
639
719
|
test("checkPiVersion rejects a version below the minimum and names both versions", () => {
|
|
@@ -642,7 +722,7 @@ test("checkPiVersion rejects a version below the minimum and names both versions
|
|
|
642
722
|
if (result.ok) throw new Error("expected a failing result");
|
|
643
723
|
assert.equal(result.version, "0.85.0");
|
|
644
724
|
assert.match(result.message, /0\.85\.0/);
|
|
645
|
-
assert.match(result.message, /0\.
|
|
725
|
+
assert.match(result.message, /0\.99\.1/);
|
|
646
726
|
});
|
|
647
727
|
|
|
648
728
|
test("checkPiVersion rejects a prerelease below the minimum", () => {
|
|
@@ -656,7 +736,7 @@ test("checkPiVersion reports unparsable output with the raw text and the minimum
|
|
|
656
736
|
if (result.ok) throw new Error("expected a failing result");
|
|
657
737
|
assert.equal(result.version, undefined);
|
|
658
738
|
assert.match(result.message, /not a version/);
|
|
659
|
-
assert.match(result.message, /0\.
|
|
739
|
+
assert.match(result.message, /0\.99\.1/);
|
|
660
740
|
});
|
|
661
741
|
|
|
662
742
|
test("checkPiVersion accepts a custom minimum", () => {
|
|
@@ -833,7 +913,7 @@ test("findGentlePiDeclaration detects a bare npm:gentle-pi object source entry",
|
|
|
833
913
|
});
|
|
834
914
|
|
|
835
915
|
test("findGentlePiDeclaration recognises a relative path entry whose package.json name is gentle-pi", () => {
|
|
836
|
-
const resolvedDir =
|
|
916
|
+
const resolvedDir = resolve("/agent", "..", "..", "work", "gentle-pi");
|
|
837
917
|
const result = findGentlePiDeclaration('{"packages":["../../work/gentle-pi"]}', {
|
|
838
918
|
agentDir: "/agent",
|
|
839
919
|
readPackageName: readPackageNameStub({ [resolvedDir]: "gentle-pi" }),
|
|
@@ -844,17 +924,17 @@ test("findGentlePiDeclaration recognises a relative path entry whose package.jso
|
|
|
844
924
|
test("findGentlePiDeclaration recognises an absolute path entry whose package.json name is gentle-pi", () => {
|
|
845
925
|
const result = findGentlePiDeclaration('{"packages":["/checkouts/gentle-pi"]}', {
|
|
846
926
|
agentDir: "/agent",
|
|
847
|
-
readPackageName: readPackageNameStub({ "/checkouts/gentle-pi": "gentle-pi" }),
|
|
927
|
+
readPackageName: readPackageNameStub({ [resolve("/checkouts/gentle-pi")]: "gentle-pi" }),
|
|
848
928
|
});
|
|
849
|
-
assert.deepEqual(result, { kind: "path", dir: "/checkouts/gentle-pi" });
|
|
929
|
+
assert.deepEqual(result, { kind: "path", dir: resolve("/checkouts/gentle-pi") });
|
|
850
930
|
});
|
|
851
931
|
|
|
852
932
|
test("findGentlePiDeclaration recognises a path object source entry whose package.json name is gentle-pi", () => {
|
|
853
933
|
const result = findGentlePiDeclaration('{"packages":[{"source":"../gentle-pi","extensions":[]}]}', {
|
|
854
934
|
agentDir: "/agent",
|
|
855
|
-
readPackageName: readPackageNameStub({ [
|
|
935
|
+
readPackageName: readPackageNameStub({ [resolve("/agent", "..", "gentle-pi")]: "gentle-pi" }),
|
|
856
936
|
});
|
|
857
|
-
assert.deepEqual(result, { kind: "path", dir:
|
|
937
|
+
assert.deepEqual(result, { kind: "path", dir: resolve("/agent", "..", "gentle-pi") });
|
|
858
938
|
});
|
|
859
939
|
|
|
860
940
|
test("findGentlePiDeclaration is undefined for a path entry whose package.json name is not gentle-pi", () => {
|
|
@@ -887,9 +967,9 @@ test("findGentlePiDeclaration is undefined when packages lists unrelated entries
|
|
|
887
967
|
test("findGentlePiDeclaration returns the first matching declaration, path or npm, in list order", () => {
|
|
888
968
|
const result = findGentlePiDeclaration('{"packages":["../gentle-pi","npm:gentle-pi"]}', {
|
|
889
969
|
agentDir: "/agent",
|
|
890
|
-
readPackageName: readPackageNameStub({ [
|
|
970
|
+
readPackageName: readPackageNameStub({ [resolve("/agent", "..", "gentle-pi")]: "gentle-pi" }),
|
|
891
971
|
});
|
|
892
|
-
assert.deepEqual(result, { kind: "path", dir:
|
|
972
|
+
assert.deepEqual(result, { kind: "path", dir: resolve("/agent", "..", "gentle-pi") });
|
|
893
973
|
});
|
|
894
974
|
|
|
895
975
|
// --- decideTakeOver -----------------------------------------------------------
|
|
@@ -973,7 +1053,7 @@ test("otherPackageInjections resolves a path entry relative to agentDir", () =>
|
|
|
973
1053
|
agentDir: "/agent",
|
|
974
1054
|
skip: { kind: "npm" },
|
|
975
1055
|
});
|
|
976
|
-
assert.deepEqual(result.paths, [
|
|
1056
|
+
assert.deepEqual(result.paths, [resolve("/agent", "..", "..", "work", "pi-qwen-ambassador")]);
|
|
977
1057
|
});
|
|
978
1058
|
|
|
979
1059
|
test("otherPackageInjections skips a git entry and warns", () => {
|
|
@@ -999,7 +1079,7 @@ test("otherPackageInjections warns for an object entry with extensions or autolo
|
|
|
999
1079
|
});
|
|
1000
1080
|
|
|
1001
1081
|
test("otherPackageInjections skips the entry matching a path declaration being taken over", () => {
|
|
1002
|
-
const declaredDir =
|
|
1082
|
+
const declaredDir = resolve("/agent", "..", "..", "work", "gentle-pi");
|
|
1003
1083
|
const result = otherPackageInjections({
|
|
1004
1084
|
settingsText: '{"packages":["npm:some-other","../../work/gentle-pi"]}',
|
|
1005
1085
|
agentDir: "/agent",
|
|
@@ -1014,7 +1094,7 @@ test("otherPackageInjections tolerates undefined or malformed settings text", ()
|
|
|
1014
1094
|
});
|
|
1015
1095
|
|
|
1016
1096
|
test("otherPackageInjections skips every gentle-pi entry, not only the one matching the declaration kind", () => {
|
|
1017
|
-
const declaredDir =
|
|
1097
|
+
const declaredDir = resolve("/agent", "..", "..", "work", "gentle-pi");
|
|
1018
1098
|
const result = otherPackageInjections({
|
|
1019
1099
|
// The declaration being taken over is the path entry, but settings
|
|
1020
1100
|
// also carries a second, unrelated npm:gentle-pi entry: both must be
|
|
@@ -1069,7 +1149,7 @@ test("otherPackageInjections keeps a declared package directory that isDirectory
|
|
|
1069
1149
|
});
|
|
1070
1150
|
|
|
1071
1151
|
test("otherPackageInjections excludes a settings path entry that resolves via realpath to the same physical directory as a realpath'd skip (R4-forced-root-symlink-double-injection)", () => {
|
|
1072
|
-
const linkOtherDir =
|
|
1152
|
+
const linkOtherDir = resolve("/agent", "link-other");
|
|
1073
1153
|
const result = otherPackageInjections({
|
|
1074
1154
|
settingsText: '{"packages":["link-other","npm:some-other"]}',
|
|
1075
1155
|
agentDir: "/agent",
|
|
@@ -1088,7 +1168,7 @@ test("otherPackageInjections keeps comparing raw strings when no realpath resolv
|
|
|
1088
1168
|
agentDir: "/agent",
|
|
1089
1169
|
skip: { kind: "path", dir: "/real/other" },
|
|
1090
1170
|
});
|
|
1091
|
-
assert.deepEqual(result.paths, [
|
|
1171
|
+
assert.deepEqual(result.paths, [resolve("/agent", "link-other"), join("/agent", "npm", "node_modules", "some-other")]);
|
|
1092
1172
|
});
|
|
1093
1173
|
|
|
1094
1174
|
test("otherPackageInjections defaults to including every declared package when isDirectory is not provided (existing callers keep pure string resolution)", () => {
|
|
@@ -1103,6 +1183,24 @@ test("otherPackageInjections defaults to including every declared package when i
|
|
|
1103
1183
|
|
|
1104
1184
|
// --- buildPiInvocation -------------------------------------------------------
|
|
1105
1185
|
|
|
1186
|
+
test("package injection relies on Pi's -e resource discovery in isolated and takeover modes", () => {
|
|
1187
|
+
for (const takeOver of [false, true]) {
|
|
1188
|
+
const built = buildPiInvocation({
|
|
1189
|
+
runtime: { kind: "path", command: "/usr/bin/pi", args: [] },
|
|
1190
|
+
home: { mode: "isolated", dir: "/gentle-shell/agent", source: "default" },
|
|
1191
|
+
packageRoot: "/pkg",
|
|
1192
|
+
declaration: undefined,
|
|
1193
|
+
takeOver,
|
|
1194
|
+
otherPackagePaths: [],
|
|
1195
|
+
passthrough: [],
|
|
1196
|
+
baseEnv: {},
|
|
1197
|
+
homedir: "/home/u",
|
|
1198
|
+
cwd: "/work",
|
|
1199
|
+
});
|
|
1200
|
+
assert.deepEqual(built.args, takeOver ? ["--no-extensions", "-e", "/pkg"] : ["-e", "/pkg"]);
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1106
1204
|
const linkHome: ResolvedHome = { mode: "link", dir: "/pi/agent", source: "flag" };
|
|
1107
1205
|
const isolatedHomeResolved: ResolvedHome = { mode: "isolated", dir: "/gentle-shell/agent", source: "default" };
|
|
1108
1206
|
|
|
@@ -1116,8 +1214,10 @@ test("buildPiInvocation injects the launcher env into baseEnv", () => {
|
|
|
1116
1214
|
otherPackagePaths: [],
|
|
1117
1215
|
passthrough: [],
|
|
1118
1216
|
baseEnv: { PATH: "/usr/bin" },
|
|
1217
|
+
homedir: "/home/u",
|
|
1218
|
+
cwd: "/work",
|
|
1119
1219
|
});
|
|
1120
|
-
assert.deepEqual(built.env, { PATH: "/usr/bin", PI_CODING_AGENT_DIR: "/pi/agent", GENTLE_PI_AGENT_HOME: "/pi/agent" });
|
|
1220
|
+
assert.deepEqual(built.env, { PATH: "/usr/bin", PI_CODING_AGENT_DIR: "/pi/agent", GENTLE_PI_AGENT_HOME: "/pi/agent", GENTLE_SHELL_USER_PI_HOME: join("/home/u", ".pi", "agent") });
|
|
1121
1221
|
});
|
|
1122
1222
|
|
|
1123
1223
|
test("buildPiInvocation skips injection when there is a declaration and no takeover (npm matches the launcher's own install)", () => {
|
|
@@ -1130,6 +1230,8 @@ test("buildPiInvocation skips injection when there is a declaration and no takeo
|
|
|
1130
1230
|
otherPackagePaths: [],
|
|
1131
1231
|
passthrough: ["--mode", "rpc"],
|
|
1132
1232
|
baseEnv: {},
|
|
1233
|
+
homedir: "/home/u",
|
|
1234
|
+
cwd: "/work",
|
|
1133
1235
|
});
|
|
1134
1236
|
assert.deepEqual(built.command, "/usr/bin/pi");
|
|
1135
1237
|
assert.deepEqual(built.args, ["--mode", "rpc"]);
|
|
@@ -1145,16 +1247,12 @@ test("buildPiInvocation adds the gentle-pi injection flags when there is no decl
|
|
|
1145
1247
|
otherPackagePaths: [],
|
|
1146
1248
|
passthrough: ["--mode", "rpc"],
|
|
1147
1249
|
baseEnv: {},
|
|
1250
|
+
homedir: "/home/u",
|
|
1251
|
+
cwd: "/work",
|
|
1148
1252
|
});
|
|
1149
1253
|
assert.deepEqual(built.args, [
|
|
1150
1254
|
"-e",
|
|
1151
1255
|
"/pkg",
|
|
1152
|
-
"--theme",
|
|
1153
|
-
join("/pkg", "themes"),
|
|
1154
|
-
"--skill",
|
|
1155
|
-
join("/pkg", "skills"),
|
|
1156
|
-
"--prompt-template",
|
|
1157
|
-
join("/pkg", "prompts"),
|
|
1158
1256
|
"--mode",
|
|
1159
1257
|
"rpc",
|
|
1160
1258
|
]);
|
|
@@ -1171,6 +1269,8 @@ test("buildPiInvocation emits only runtime args and passthrough when a pi subcom
|
|
|
1171
1269
|
passthrough: ["install", "npm:x"],
|
|
1172
1270
|
piSubcommand: "install",
|
|
1173
1271
|
baseEnv: {},
|
|
1272
|
+
homedir: "/home/u",
|
|
1273
|
+
cwd: "/work",
|
|
1174
1274
|
});
|
|
1175
1275
|
assert.deepEqual(built.args, ["install", "npm:x"]);
|
|
1176
1276
|
assert.equal(built.command, "/usr/bin/pi");
|
|
@@ -1187,11 +1287,14 @@ test("buildPiInvocation still injects the launcher env for a pi subcommand", ()
|
|
|
1187
1287
|
passthrough: ["list"],
|
|
1188
1288
|
piSubcommand: "list",
|
|
1189
1289
|
baseEnv: { PATH: "/usr/bin" },
|
|
1290
|
+
homedir: "/home/u",
|
|
1291
|
+
cwd: "/work",
|
|
1190
1292
|
});
|
|
1191
1293
|
assert.deepEqual(built.env, {
|
|
1192
1294
|
PATH: "/usr/bin",
|
|
1193
1295
|
PI_CODING_AGENT_DIR: "/gentle-shell/agent",
|
|
1194
1296
|
GENTLE_PI_AGENT_HOME: "/gentle-shell/agent",
|
|
1297
|
+
GENTLE_SHELL_USER_PI_HOME: join("/home/u", ".pi", "agent"),
|
|
1195
1298
|
});
|
|
1196
1299
|
});
|
|
1197
1300
|
|
|
@@ -1205,11 +1308,51 @@ test("buildPiInvocation in link mode with a pi subcommand is exactly pi <subcomm
|
|
|
1205
1308
|
otherPackagePaths: [join("/agent", "npm", "node_modules", "some-other")],
|
|
1206
1309
|
passthrough: ["auth", "status"],
|
|
1207
1310
|
piSubcommand: "auth",
|
|
1208
|
-
baseEnv: {},
|
|
1311
|
+
baseEnv: { PI_CODING_AGENT_DIR: "/pi/agent" },
|
|
1312
|
+
homedir: "/home/u",
|
|
1313
|
+
cwd: "/work",
|
|
1209
1314
|
});
|
|
1210
1315
|
assert.equal(built.command, "/usr/bin/pi");
|
|
1211
1316
|
assert.deepEqual(built.args, ["auth", "status"]);
|
|
1212
|
-
assert.deepEqual(built.env, { PI_CODING_AGENT_DIR: "/pi/agent", GENTLE_PI_AGENT_HOME: "/pi/agent" });
|
|
1317
|
+
assert.deepEqual(built.env, { PI_CODING_AGENT_DIR: "/pi/agent", GENTLE_PI_AGENT_HOME: "/pi/agent", GENTLE_SHELL_USER_PI_HOME: "/pi/agent" });
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
// The isolated home overrides PI_CODING_AGENT_DIR for the session, so the
|
|
1321
|
+
// user's own Pi home travels separately for read-only features such as
|
|
1322
|
+
// /gentle:stats. Isolation itself is unchanged.
|
|
1323
|
+
test("buildPiInvocation carries the user's original Pi home without weakening isolation", () => {
|
|
1324
|
+
const build = (baseEnv: Record<string, string | undefined>, home: ResolvedHome = isolatedHomeResolved) => buildPiInvocation({
|
|
1325
|
+
runtime: { kind: "path", command: "/usr/bin/pi", args: [] },
|
|
1326
|
+
home,
|
|
1327
|
+
packageRoot: "/pkg",
|
|
1328
|
+
declaration: undefined,
|
|
1329
|
+
takeOver: false,
|
|
1330
|
+
otherPackagePaths: [],
|
|
1331
|
+
passthrough: [],
|
|
1332
|
+
baseEnv,
|
|
1333
|
+
homedir: "/home/u",
|
|
1334
|
+
cwd: "/work",
|
|
1335
|
+
}).env;
|
|
1336
|
+
const conventional = build({});
|
|
1337
|
+
assert.equal(conventional.PI_CODING_AGENT_DIR, "/gentle-shell/agent");
|
|
1338
|
+
assert.equal(conventional.GENTLE_PI_AGENT_HOME, "/gentle-shell/agent");
|
|
1339
|
+
assert.equal(conventional.GENTLE_SHELL_USER_PI_HOME, join("/home/u", ".pi", "agent"));
|
|
1340
|
+
// A custom Pi home is preserved; an empty value falls through like Pi itself.
|
|
1341
|
+
const custom = build({ PI_CODING_AGENT_DIR: "/custom/pi" });
|
|
1342
|
+
assert.equal(custom.PI_CODING_AGENT_DIR, "/gentle-shell/agent");
|
|
1343
|
+
assert.equal(custom.GENTLE_SHELL_USER_PI_HOME, "/custom/pi");
|
|
1344
|
+
assert.equal(build({ PI_CODING_AGENT_DIR: "" }).GENTLE_SHELL_USER_PI_HOME, join("/home/u", ".pi", "agent"));
|
|
1345
|
+
// A nested launch inherits the outer isolated PI_CODING_AGENT_DIR; the
|
|
1346
|
+
// original home the outer launcher recorded must win over it.
|
|
1347
|
+
const nested = build({ PI_CODING_AGENT_DIR: "/gentle-shell/agent", GENTLE_SHELL_USER_PI_HOME: "/custom/pi" });
|
|
1348
|
+
assert.equal(nested.PI_CODING_AGENT_DIR, "/gentle-shell/agent");
|
|
1349
|
+
assert.equal(nested.GENTLE_SHELL_USER_PI_HOME, "/custom/pi");
|
|
1350
|
+
// --link: the active and original homes are the same directory.
|
|
1351
|
+
const linked = build({ PI_CODING_AGENT_DIR: "/pi/agent" }, linkHome);
|
|
1352
|
+
assert.equal(linked.PI_CODING_AGENT_DIR, "/pi/agent");
|
|
1353
|
+
assert.equal(linked.GENTLE_SHELL_USER_PI_HOME, "/pi/agent");
|
|
1354
|
+
assert.equal(USER_PI_HOME_ENV, "GENTLE_SHELL_USER_PI_HOME");
|
|
1355
|
+
assert.equal(userPiHome({ GENTLE_SHELL_USER_PI_HOME: "/kept" }, "/home/u"), "/kept");
|
|
1213
1356
|
});
|
|
1214
1357
|
|
|
1215
1358
|
test("buildPiInvocation keeps the runtime's own args ahead of the injection and passthrough", () => {
|
|
@@ -1222,6 +1365,8 @@ test("buildPiInvocation keeps the runtime's own args ahead of the injection and
|
|
|
1222
1365
|
otherPackagePaths: [],
|
|
1223
1366
|
passthrough: [],
|
|
1224
1367
|
baseEnv: {},
|
|
1368
|
+
homedir: "/home/u",
|
|
1369
|
+
cwd: "/work",
|
|
1225
1370
|
});
|
|
1226
1371
|
assert.equal(built.args[0], "/bundled/cli.js");
|
|
1227
1372
|
assert.equal(built.command, "/usr/bin/node");
|
|
@@ -1237,6 +1382,8 @@ test("buildPiInvocation takes over a conflicting path declaration: --no-extensio
|
|
|
1237
1382
|
otherPackagePaths: [join("/agent", "npm", "node_modules", "some-other")],
|
|
1238
1383
|
passthrough: ["--mode", "rpc"],
|
|
1239
1384
|
baseEnv: {},
|
|
1385
|
+
homedir: "/home/u",
|
|
1386
|
+
cwd: "/work",
|
|
1240
1387
|
});
|
|
1241
1388
|
assert.deepEqual(built.args, [
|
|
1242
1389
|
"--no-extensions",
|
|
@@ -1244,12 +1391,6 @@ test("buildPiInvocation takes over a conflicting path declaration: --no-extensio
|
|
|
1244
1391
|
join("/agent", "npm", "node_modules", "some-other"),
|
|
1245
1392
|
"-e",
|
|
1246
1393
|
"/pkg",
|
|
1247
|
-
"--theme",
|
|
1248
|
-
join("/pkg", "themes"),
|
|
1249
|
-
"--skill",
|
|
1250
|
-
join("/pkg", "skills"),
|
|
1251
|
-
"--prompt-template",
|
|
1252
|
-
join("/pkg", "prompts"),
|
|
1253
1394
|
"--mode",
|
|
1254
1395
|
"rpc",
|
|
1255
1396
|
]);
|
|
@@ -1265,17 +1406,13 @@ test("buildPiInvocation takes over with --package-root even for a matching npm d
|
|
|
1265
1406
|
otherPackagePaths: [],
|
|
1266
1407
|
passthrough: [],
|
|
1267
1408
|
baseEnv: {},
|
|
1409
|
+
homedir: "/home/u",
|
|
1410
|
+
cwd: "/work",
|
|
1268
1411
|
});
|
|
1269
1412
|
assert.deepEqual(built.args, [
|
|
1270
1413
|
"--no-extensions",
|
|
1271
1414
|
"-e",
|
|
1272
1415
|
"/forced/root",
|
|
1273
|
-
"--theme",
|
|
1274
|
-
join("/forced/root", "themes"),
|
|
1275
|
-
"--skill",
|
|
1276
|
-
join("/forced/root", "skills"),
|
|
1277
|
-
"--prompt-template",
|
|
1278
|
-
join("/forced/root", "prompts"),
|
|
1279
1416
|
]);
|
|
1280
1417
|
});
|
|
1281
1418
|
|
|
@@ -1289,6 +1426,8 @@ test("buildPiInvocation takes over with --package-root even when there is no dec
|
|
|
1289
1426
|
otherPackagePaths: [join("/agent", "npm", "node_modules", "some-other")],
|
|
1290
1427
|
passthrough: [],
|
|
1291
1428
|
baseEnv: {},
|
|
1429
|
+
homedir: "/home/u",
|
|
1430
|
+
cwd: "/work",
|
|
1292
1431
|
});
|
|
1293
1432
|
assert.deepEqual(built.args, [
|
|
1294
1433
|
"--no-extensions",
|
|
@@ -1296,12 +1435,6 @@ test("buildPiInvocation takes over with --package-root even when there is no dec
|
|
|
1296
1435
|
join("/agent", "npm", "node_modules", "some-other"),
|
|
1297
1436
|
"-e",
|
|
1298
1437
|
"/forced/root",
|
|
1299
|
-
"--theme",
|
|
1300
|
-
join("/forced/root", "themes"),
|
|
1301
|
-
"--skill",
|
|
1302
|
-
join("/forced/root", "skills"),
|
|
1303
|
-
"--prompt-template",
|
|
1304
|
-
join("/forced/root", "prompts"),
|
|
1305
1438
|
]);
|
|
1306
1439
|
});
|
|
1307
1440
|
|
|
@@ -1316,6 +1449,8 @@ test("buildPiInvocation injects loose extension entries during a takeover, after
|
|
|
1316
1449
|
looseExtensionEntries: [join("/agent", "extensions", "a.ts"), join("/project", ".pi", "extensions", "b.js")],
|
|
1317
1450
|
passthrough: ["--mode", "rpc"],
|
|
1318
1451
|
baseEnv: {},
|
|
1452
|
+
homedir: "/home/u",
|
|
1453
|
+
cwd: "/work",
|
|
1319
1454
|
});
|
|
1320
1455
|
assert.deepEqual(built.args, [
|
|
1321
1456
|
"--no-extensions",
|
|
@@ -1327,12 +1462,6 @@ test("buildPiInvocation injects loose extension entries during a takeover, after
|
|
|
1327
1462
|
join("/project", ".pi", "extensions", "b.js"),
|
|
1328
1463
|
"-e",
|
|
1329
1464
|
"/pkg",
|
|
1330
|
-
"--theme",
|
|
1331
|
-
join("/pkg", "themes"),
|
|
1332
|
-
"--skill",
|
|
1333
|
-
join("/pkg", "skills"),
|
|
1334
|
-
"--prompt-template",
|
|
1335
|
-
join("/pkg", "prompts"),
|
|
1336
1465
|
"--mode",
|
|
1337
1466
|
"rpc",
|
|
1338
1467
|
]);
|
|
@@ -1348,8 +1477,10 @@ test("buildPiInvocation omits loose extension entry flags when the list is empty
|
|
|
1348
1477
|
otherPackagePaths: [],
|
|
1349
1478
|
passthrough: [],
|
|
1350
1479
|
baseEnv: {},
|
|
1480
|
+
homedir: "/home/u",
|
|
1481
|
+
cwd: "/work",
|
|
1351
1482
|
});
|
|
1352
|
-
assert.deepEqual(withoutField.args, ["--no-extensions", "-e", "/pkg"
|
|
1483
|
+
assert.deepEqual(withoutField.args, ["--no-extensions", "-e", "/pkg"]);
|
|
1353
1484
|
|
|
1354
1485
|
const withEmptyField = buildPiInvocation({
|
|
1355
1486
|
runtime: { kind: "path", command: "/usr/bin/pi", args: [] },
|
|
@@ -1361,6 +1492,8 @@ test("buildPiInvocation omits loose extension entry flags when the list is empty
|
|
|
1361
1492
|
looseExtensionEntries: [],
|
|
1362
1493
|
passthrough: [],
|
|
1363
1494
|
baseEnv: {},
|
|
1495
|
+
homedir: "/home/u",
|
|
1496
|
+
cwd: "/work",
|
|
1364
1497
|
});
|
|
1365
1498
|
assert.deepEqual(withEmptyField.args, withoutField.args);
|
|
1366
1499
|
});
|
|
@@ -1381,6 +1514,8 @@ test("buildPiInvocation dedupes loose extension entries against other-package pa
|
|
|
1381
1514
|
looseExtensionEntries: ["/shared/dup.ts", "/agent/extensions/a.ts", "/agent/extensions/a.ts"],
|
|
1382
1515
|
passthrough: [],
|
|
1383
1516
|
baseEnv: {},
|
|
1517
|
+
homedir: "/home/u",
|
|
1518
|
+
cwd: "/work",
|
|
1384
1519
|
});
|
|
1385
1520
|
assert.deepEqual(built.args, [
|
|
1386
1521
|
"--no-extensions",
|
|
@@ -1392,12 +1527,6 @@ test("buildPiInvocation dedupes loose extension entries against other-package pa
|
|
|
1392
1527
|
"/agent/extensions/a.ts",
|
|
1393
1528
|
"-e",
|
|
1394
1529
|
"/pkg",
|
|
1395
|
-
"--theme",
|
|
1396
|
-
join("/pkg", "themes"),
|
|
1397
|
-
"--skill",
|
|
1398
|
-
join("/pkg", "skills"),
|
|
1399
|
-
"--prompt-template",
|
|
1400
|
-
join("/pkg", "prompts"),
|
|
1401
1530
|
]);
|
|
1402
1531
|
});
|
|
1403
1532
|
|
|
@@ -1415,6 +1544,8 @@ test("buildPiInvocation dedupes the launcher's own package root against an other
|
|
|
1415
1544
|
otherPackagePaths: [join("/agent", "npm", "node_modules", "some-other"), "/pkg"],
|
|
1416
1545
|
passthrough: [],
|
|
1417
1546
|
baseEnv: {},
|
|
1547
|
+
homedir: "/home/u",
|
|
1548
|
+
cwd: "/work",
|
|
1418
1549
|
});
|
|
1419
1550
|
const eFlags = built.args.filter((arg, index) => built.args[index - 1] === "-e");
|
|
1420
1551
|
assert.deepEqual(eFlags, [join("/agent", "npm", "node_modules", "some-other"), "/pkg"]);
|
|
@@ -1424,15 +1555,130 @@ test("buildPiInvocation dedupes the launcher's own package root against an other
|
|
|
1424
1555
|
join("/agent", "npm", "node_modules", "some-other"),
|
|
1425
1556
|
"-e",
|
|
1426
1557
|
"/pkg",
|
|
1427
|
-
"--theme",
|
|
1428
|
-
join("/pkg", "themes"),
|
|
1429
|
-
"--skill",
|
|
1430
|
-
join("/pkg", "skills"),
|
|
1431
|
-
"--prompt-template",
|
|
1432
|
-
join("/pkg", "prompts"),
|
|
1433
1558
|
]);
|
|
1434
1559
|
});
|
|
1435
1560
|
|
|
1561
|
+
// --- child package injection signal (#1690) ----------------------------------
|
|
1562
|
+
//
|
|
1563
|
+
// The launcher exports exactly the -e set it computed itself, so the subagent
|
|
1564
|
+
// runner can forward it to delegated children. Passthrough -e flags are not
|
|
1565
|
+
// part of the signal.
|
|
1566
|
+
|
|
1567
|
+
const injectionInput = {
|
|
1568
|
+
runtime: { kind: "path" as const, command: "/usr/bin/pi", args: [] },
|
|
1569
|
+
home: isolatedHomeResolved,
|
|
1570
|
+
packageRoot: "/pkg",
|
|
1571
|
+
otherPackagePaths: [],
|
|
1572
|
+
passthrough: ["-e", "/user/ext.ts", "--mode", "rpc"],
|
|
1573
|
+
homedir: "/home/u",
|
|
1574
|
+
cwd: "/work",
|
|
1575
|
+
};
|
|
1576
|
+
|
|
1577
|
+
test("buildPiInvocation signals the packageRoot injection when there is no declaration", () => {
|
|
1578
|
+
const built = buildPiInvocation({ ...injectionInput, declaration: undefined, takeOver: false, baseEnv: {} });
|
|
1579
|
+
assert.deepEqual(parseChildPackageInjection(built.env), { noExtensions: false, extensionPaths: ["/pkg"] });
|
|
1580
|
+
});
|
|
1581
|
+
|
|
1582
|
+
test("buildPiInvocation signals the exact deduped takeover -e set in order, without passthrough -e flags", () => {
|
|
1583
|
+
const built = buildPiInvocation({
|
|
1584
|
+
...injectionInput,
|
|
1585
|
+
home: linkHome,
|
|
1586
|
+
declaration: { kind: "path", dir: "/other/checkout" },
|
|
1587
|
+
takeOver: true,
|
|
1588
|
+
otherPackagePaths: ["/agent/npm/node_modules/some-other", "/shared/dup.ts", "/pkg"],
|
|
1589
|
+
looseExtensionEntries: ["/shared/dup.ts", "/agent/extensions/a.ts", "/agent/extensions/a.ts"],
|
|
1590
|
+
baseEnv: {},
|
|
1591
|
+
});
|
|
1592
|
+
const launcherPaths = built.args.slice(0, built.args.indexOf("--mode") - 2).filter((_, index, all) => all[index - 1] === "-e");
|
|
1593
|
+
const signal = parseChildPackageInjection(built.env);
|
|
1594
|
+
assert.deepEqual(signal, {
|
|
1595
|
+
noExtensions: true,
|
|
1596
|
+
// /pkg keeps its first (other-package) position, exactly as in argv.
|
|
1597
|
+
extensionPaths: ["/agent/npm/node_modules/some-other", "/shared/dup.ts", "/pkg", "/agent/extensions/a.ts"],
|
|
1598
|
+
});
|
|
1599
|
+
assert.deepEqual(signal?.extensionPaths, launcherPaths);
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
test("buildPiInvocation signals absolute paths resolved against the given cwd when a takeover receives a relative loose entry", () => {
|
|
1603
|
+
const cwd = resolve("/elsewhere", "launch-dir");
|
|
1604
|
+
assert.notEqual(cwd, process.cwd());
|
|
1605
|
+
const built = buildPiInvocation({
|
|
1606
|
+
...injectionInput,
|
|
1607
|
+
cwd,
|
|
1608
|
+
home: linkHome,
|
|
1609
|
+
declaration: { kind: "npm" },
|
|
1610
|
+
takeOver: true,
|
|
1611
|
+
looseExtensionEntries: [join("relative-home", "extensions", "a.ts")],
|
|
1612
|
+
baseEnv: {},
|
|
1613
|
+
});
|
|
1614
|
+
assert.ok(built.args.includes(join("relative-home", "extensions", "a.ts")), "the -e argv itself is unchanged");
|
|
1615
|
+
assert.deepEqual(parseChildPackageInjection(built.env)?.extensionPaths, [resolve(cwd, "relative-home", "extensions", "a.ts"), "/pkg"]);
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
test("buildPiInvocation signals one entry for a file spelled both relative and absolute, while argv keeps both spellings", () => {
|
|
1619
|
+
const cwd = resolve("/elsewhere", "launch-dir");
|
|
1620
|
+
const relative = join("relative-home", "extensions", "a.ts");
|
|
1621
|
+
const absolute = resolve(cwd, relative);
|
|
1622
|
+
const built = buildPiInvocation({
|
|
1623
|
+
...injectionInput,
|
|
1624
|
+
cwd,
|
|
1625
|
+
home: linkHome,
|
|
1626
|
+
declaration: { kind: "npm" },
|
|
1627
|
+
takeOver: true,
|
|
1628
|
+
otherPackagePaths: ["/agent/npm/node_modules/some-other", absolute],
|
|
1629
|
+
looseExtensionEntries: [relative, "/agent/extensions/b.ts"],
|
|
1630
|
+
baseEnv: {},
|
|
1631
|
+
});
|
|
1632
|
+
assert.deepEqual(built.args.slice(0, built.args.indexOf("--mode") - 2), [
|
|
1633
|
+
"--no-extensions",
|
|
1634
|
+
"-e",
|
|
1635
|
+
"/agent/npm/node_modules/some-other",
|
|
1636
|
+
"-e",
|
|
1637
|
+
absolute,
|
|
1638
|
+
"-e",
|
|
1639
|
+
relative,
|
|
1640
|
+
"-e",
|
|
1641
|
+
"/agent/extensions/b.ts",
|
|
1642
|
+
"-e",
|
|
1643
|
+
"/pkg",
|
|
1644
|
+
]);
|
|
1645
|
+
assert.deepEqual(parseChildPackageInjection(built.env)?.extensionPaths, [
|
|
1646
|
+
"/agent/npm/node_modules/some-other",
|
|
1647
|
+
absolute,
|
|
1648
|
+
"/agent/extensions/b.ts",
|
|
1649
|
+
"/pkg",
|
|
1650
|
+
]);
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
test("buildPiInvocation does not signal when settings already declare gentle-pi, and drops an inherited signal", () => {
|
|
1654
|
+
const stale = encodeChildPackageInjection({ noExtensions: false, extensionPaths: ["/outer/pkg"] });
|
|
1655
|
+
for (const baseEnv of [{}, { [CHILD_PACKAGE_INJECTION_ENV]: stale }]) {
|
|
1656
|
+
const built = buildPiInvocation({ ...injectionInput, home: linkHome, declaration: { kind: "npm" }, takeOver: false, baseEnv });
|
|
1657
|
+
assert.equal(Object.hasOwn(built.env, CHILD_PACKAGE_INJECTION_ENV), false);
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
test("buildPiInvocation does not signal for a pi subcommand, and drops an inherited signal", () => {
|
|
1662
|
+
const stale = encodeChildPackageInjection({ noExtensions: false, extensionPaths: ["/outer/pkg"] });
|
|
1663
|
+
for (const takeOver of [false, true]) {
|
|
1664
|
+
const built = buildPiInvocation({
|
|
1665
|
+
...injectionInput,
|
|
1666
|
+
declaration: undefined,
|
|
1667
|
+
takeOver,
|
|
1668
|
+
passthrough: ["list"],
|
|
1669
|
+
piSubcommand: "list",
|
|
1670
|
+
baseEnv: { [CHILD_PACKAGE_INJECTION_ENV]: stale },
|
|
1671
|
+
});
|
|
1672
|
+
assert.equal(Object.hasOwn(built.env, CHILD_PACKAGE_INJECTION_ENV), false);
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
test("buildPiInvocation replaces an inherited signal with its own injection", () => {
|
|
1677
|
+
const stale = encodeChildPackageInjection({ noExtensions: true, extensionPaths: ["/outer/pkg"] });
|
|
1678
|
+
const built = buildPiInvocation({ ...injectionInput, declaration: undefined, takeOver: false, baseEnv: { [CHILD_PACKAGE_INJECTION_ENV]: stale } });
|
|
1679
|
+
assert.deepEqual(parseChildPackageInjection(built.env), { noExtensions: false, extensionPaths: ["/pkg"] });
|
|
1680
|
+
});
|
|
1681
|
+
|
|
1436
1682
|
// --- discoverLooseExtensionEntries -------------------------------------------
|
|
1437
1683
|
//
|
|
1438
1684
|
// Pure mirror of pi's own discoverExtensionsInDir (packages/coding-agent/src/
|
|
@@ -1759,3 +2005,25 @@ test("helpText documents pi's own package-management subcommands", () => {
|
|
|
1759
2005
|
assert.match(text, /\bconfig\b/);
|
|
1760
2006
|
assert.match(text, /\bauth\b/);
|
|
1761
2007
|
});
|
|
2008
|
+
|
|
2009
|
+
// T3 B3: the isolated-home bootstrap creates ~/.gentle-shell private (0700) even under
|
|
2010
|
+
// umask 002, so the bundled prefix claim (which refuses group/other-writable folders) accepts it.
|
|
2011
|
+
test("the isolated-home bootstrap creates ~/.gentle-shell as 0700 under umask 002", { skip: process.platform === "win32" ? "POSIX modes" : false }, async () => {
|
|
2012
|
+
const { chmodSync, mkdtempSync, rmSync, statSync, writeFileSync } = await import("node:fs");
|
|
2013
|
+
const { spawnSync } = await import("node:child_process");
|
|
2014
|
+
const { tmpdir } = await import("node:os");
|
|
2015
|
+
const root = mkdtempSync(join(tmpdir(), "gentle-shell-umask-"));
|
|
2016
|
+
try {
|
|
2017
|
+
const piScript = join(root, "fake-pi.mjs");
|
|
2018
|
+
writeFileSync(piScript, "#!/usr/bin/env node\nif (process.argv.includes('--version')) console.log('0.99.1');\n");
|
|
2019
|
+
chmodSync(piScript, 0o755);
|
|
2020
|
+
const binPath = fileURLToPath(new URL("../bin/gentle-shell.mjs", import.meta.url));
|
|
2021
|
+
const env = { HOME: root, USERPROFILE: root, PATH: process.env.PATH ?? "/usr/bin:/bin", GENTLE_SHELL_PI: piScript, GENTLE_SHELL_NO_AUTO_SETUP: "1" };
|
|
2022
|
+
const result = spawnSync("/bin/sh", ["-c", 'umask 002; exec "$0" "$@"', process.execPath, binPath, "hello"], { env, encoding: "utf8", input: "" });
|
|
2023
|
+
assert.equal(result.status, 0, result.stderr);
|
|
2024
|
+
assert.equal(statSync(join(root, ".gentle-shell")).mode & 0o777, 0o700);
|
|
2025
|
+
assert.equal(statSync(join(root, ".gentle-shell", "agent")).mode & 0o777, 0o700);
|
|
2026
|
+
} finally {
|
|
2027
|
+
rmSync(root, { recursive: true, force: true });
|
|
2028
|
+
}
|
|
2029
|
+
});
|