gentle-pi 4.0.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.
Files changed (398) hide show
  1. package/README.md +122 -8
  2. package/assets/agents/gentle-ai-explore.md +11 -4
  3. package/assets/agents/gentle-ai-verify.md +23 -4
  4. package/assets/agents/gentle-ai-worker.md +6 -6
  5. package/assets/install-wizard/index.html +47 -0
  6. package/assets/install-wizard/wizard.css +1279 -0
  7. package/assets/install-wizard/wizard.js +951 -0
  8. package/assets/orchestrator-delegation.md +70 -163
  9. package/assets/orchestrator-memory.md +11 -4
  10. package/assets/orchestrator-prompts.md +48 -0
  11. package/assets/orchestrator-tracking.md +53 -0
  12. package/assets/orchestrator-verification.md +28 -0
  13. package/assets/orchestrator-writer.md +38 -0
  14. package/assets/orchestrator.md +29 -25
  15. package/assets/sounds/LICENSE.md +52 -0
  16. package/assets/sounds/attention.wav +0 -0
  17. package/assets/sounds/error.wav +0 -0
  18. package/assets/sounds/success.wav +0 -0
  19. package/bin/engram-guard +9 -0
  20. package/bin/gentle-shell-install.mjs +302 -0
  21. package/bin/gentle-shell.mjs +703 -22
  22. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
  23. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
  24. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
  25. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
  26. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
  27. package/docs/bundled-install.md +397 -0
  28. package/docs/delegated-verification.md +3 -5
  29. package/docs/gentle-agents-activity.md +513 -0
  30. package/docs/gentle-shell-uninstall.md +136 -0
  31. package/docs/gentle-shell.md +81 -31
  32. package/docs/install-wizard.md +2040 -0
  33. package/docs/native-authority-architecture.md +1 -1
  34. package/docs/permission-modes.md +82 -0
  35. package/docs/prompt-history.md +1 -1
  36. package/docs/readme-reference.md +267 -40
  37. package/docs/render-cache-performance.md +28 -0
  38. package/docs/sound-notifications-proposal.md +191 -0
  39. package/docs/sound-notifications.md +114 -0
  40. package/docs/yolo-mode.md +9 -9
  41. package/extensions/child-context.ts +11 -2
  42. package/extensions/child-safety.ts +4 -2
  43. package/extensions/codegraph-tools.ts +18 -5
  44. package/extensions/gentle-agents.ts +688 -88
  45. package/extensions/gentle-ai.ts +886 -238
  46. package/extensions/gentle-herdr-activity.ts +81 -0
  47. package/extensions/gentle-notifications.ts +104 -0
  48. package/extensions/gentle-shell.ts +265 -69
  49. package/extensions/gentle-todo.ts +108 -13
  50. package/extensions/history/index.ts +11 -6
  51. package/extensions/pi-pretty.ts +39 -3
  52. package/extensions/quiet-tools.ts +91 -21
  53. package/extensions/skill-registry.ts +175 -46
  54. package/extensions/startup-banner.ts +11 -2
  55. package/lib/agent-home.ts +10 -1
  56. package/lib/agent-model-resolution.ts +80 -0
  57. package/lib/agent-profiles.ts +13 -3
  58. package/lib/agents-completion-delivery.ts +11 -3
  59. package/lib/agents-messaging.ts +1 -1
  60. package/lib/agents-protocol.ts +40 -1
  61. package/lib/agents-runner.ts +90 -15
  62. package/lib/auto-permission-classifier.ts +280 -0
  63. package/lib/auto-permission-runtime.ts +268 -0
  64. package/lib/background-jobs-tools.ts +202 -0
  65. package/lib/background-jobs.ts +253 -0
  66. package/lib/background-monitor.ts +154 -0
  67. package/lib/bounded-writer-admission.ts +83 -10
  68. package/lib/builtin-codemode-optout.ts +166 -0
  69. package/lib/card-content-policy.ts +85 -0
  70. package/lib/child-package-injection.ts +62 -0
  71. package/lib/codemode-renderer.ts +14 -4
  72. package/lib/command-palette-catalog.ts +3 -2
  73. package/lib/context-threshold.ts +26 -0
  74. package/lib/gentle-ai-binary.ts +109 -4
  75. package/lib/gentle-shell-bundled.ts +517 -0
  76. package/lib/gentle-shell-launcher.ts +74 -23
  77. package/lib/gentle-shell-startup-flow.ts +54 -0
  78. package/lib/gentle-shell-startup-update.ts +92 -0
  79. package/lib/gentle-shell-uninstall.ts +840 -0
  80. package/lib/gentle-shell-update-panel.ts +231 -0
  81. package/lib/herdr-activity.ts +183 -0
  82. package/lib/inprocess-reviewer.ts +3 -0
  83. package/lib/jobs-sidebar-state.ts +20 -0
  84. package/lib/jobs-view.ts +161 -0
  85. package/lib/model-price-ratio.ts +83 -0
  86. package/lib/nan-provider.ts +33 -11
  87. package/lib/native-review-cli.ts +194 -73
  88. package/lib/notification-audio-native.ts +162 -0
  89. package/lib/notification-audio-windows.ts +399 -0
  90. package/lib/notification-audio.ts +332 -0
  91. package/lib/notification-customize.ts +241 -0
  92. package/lib/notification-events.ts +113 -0
  93. package/lib/notification-policy.ts +148 -0
  94. package/lib/notification-pulse-client.ts +367 -0
  95. package/lib/notification-pulse-protocol.ts +305 -0
  96. package/lib/notification-pulse-stream.ts +213 -0
  97. package/lib/notification-pulse-worker.ts +66 -0
  98. package/lib/notification-scheduler.ts +173 -0
  99. package/lib/notification-service.ts +202 -0
  100. package/lib/notification-ui.ts +87 -0
  101. package/lib/odd-phase.ts +1 -1
  102. package/lib/orchestrator-alias.ts +20 -0
  103. package/lib/orchestrator-catalog.ts +79 -0
  104. package/lib/orchestrator-consultation.ts +61 -0
  105. package/lib/orchestrator-discovery.ts +49 -0
  106. package/lib/orchestrator-helper-consent.ts +122 -0
  107. package/lib/orchestrator-helper.ts +167 -0
  108. package/lib/orchestrator-message-card.ts +105 -0
  109. package/lib/orchestrator-presence.ts +115 -14
  110. package/lib/orchestrator-scope.ts +80 -0
  111. package/lib/orchestrator-state.ts +107 -0
  112. package/lib/orchestrator-tool-card.ts +280 -0
  113. package/lib/orchestrator-work-search.ts +218 -0
  114. package/lib/orchestrator-work.ts +79 -0
  115. package/lib/permission-mode-policy.ts +59 -0
  116. package/lib/review-candidate-view-owner.ts +8 -6
  117. package/lib/review-candidate-view.ts +3 -2
  118. package/lib/review-compact-contract.ts +2 -2
  119. package/lib/review-host-relay.ts +7 -2
  120. package/lib/review-integration-v2.ts +100 -22
  121. package/lib/review-publication-gate.ts +1 -1
  122. package/lib/review-risk-assessment.ts +77 -51
  123. package/lib/review-sidebar-state.ts +2 -1
  124. package/lib/review-transaction.ts +72 -3
  125. package/lib/runtime-metrics-children.ts +1 -1
  126. package/lib/runtime-metrics-native.ts +3 -2
  127. package/lib/selection-engine.ts +18 -6
  128. package/lib/session-change-capture.ts +5 -4
  129. package/lib/session-changes.ts +27 -22
  130. package/lib/session-profile-binding.ts +83 -0
  131. package/lib/session-profile-freeze.ts +266 -0
  132. package/lib/session-worktree-registry.ts +74 -8
  133. package/lib/shell-bar.ts +41 -11
  134. package/lib/shell-changes-view.ts +40 -13
  135. package/lib/shell-changes.ts +86 -71
  136. package/lib/shell-sidebar-layout.ts +5 -1
  137. package/lib/shell-todo.ts +62 -19
  138. package/lib/shell-usage.ts +4 -3
  139. package/lib/status-timing-diagnostics.ts +201 -0
  140. package/lib/telemetry-trigger.ts +3 -1
  141. package/lib/vim-editor-adapter.ts +7 -3
  142. package/lib/visual-customization-policy.ts +20 -9
  143. package/lib/visual-customize-view.ts +171 -20
  144. package/lib/writer-surfaces.ts +162 -0
  145. package/lib/yolo-session-policy.ts +198 -121
  146. package/package.json +1 -1
  147. package/runtime/child-package-injection.mjs +63 -0
  148. package/runtime/gentle-ai-binary.mjs +109 -4
  149. package/runtime/gentle-shell-bundled.mjs +518 -0
  150. package/runtime/gentle-shell-launcher.mjs +73 -22
  151. package/runtime/gentle-shell-startup-flow.mjs +55 -0
  152. package/runtime/gentle-shell-startup-update.mjs +93 -0
  153. package/runtime/gentle-shell-uninstall.mjs +841 -0
  154. package/runtime/gentle-shell-update-panel.mjs +232 -0
  155. package/runtime/native-review-cli.mjs +191 -70
  156. package/runtime/review-integration-v2.mjs +100 -22
  157. package/runtime/review-risk-assessment.mjs +77 -51
  158. package/runtime/status-timing-diagnostics.mjs +202 -0
  159. package/runtime/telemetry-trigger.mjs +3 -1
  160. package/scripts/bootstrap.cmd +255 -0
  161. package/scripts/bootstrap.sh +168 -0
  162. package/scripts/build-distribution.mjs +157 -0
  163. package/scripts/build-installer-bundles.mjs +136 -0
  164. package/scripts/build-runtime-modules.mjs +7 -0
  165. package/scripts/bundled-install.mjs +1308 -0
  166. package/scripts/bundled-wizard.mjs +399 -0
  167. package/scripts/check-types.mjs +4 -6
  168. package/scripts/gentle-ai-installer.mjs +79 -32
  169. package/scripts/install-gentle-ai.mjs +48 -1
  170. package/scripts/install-tui-mode-setting.mjs +10 -1
  171. package/scripts/install-wizard-preview.mjs +154 -0
  172. package/scripts/installer-downloads.mjs +648 -0
  173. package/scripts/installer-preflight.mjs +387 -0
  174. package/scripts/installer-probes.mjs +564 -0
  175. package/scripts/installer-runner.mjs +1102 -0
  176. package/scripts/installer-server.mjs +881 -0
  177. package/scripts/installer-windows-artifacts.json +15 -0
  178. package/scripts/installer-windows.mjs +752 -0
  179. package/scripts/main-channel.mjs +452 -0
  180. package/scripts/npm-pack-result.mjs +31 -0
  181. package/scripts/render-cache-performance-worker.mjs +205 -0
  182. package/scripts/render-cache-performance.mjs +115 -0
  183. package/scripts/run-test-suite.mjs +22 -8
  184. package/scripts/test-installer-acceptance.sh +161 -0
  185. package/scripts/test-packed-runner.mjs +23 -5
  186. package/scripts/types-baseline.json +2 -92
  187. package/scripts/verify-package-files.mjs +82 -18
  188. package/skills/gentle-ai/SKILL.md +12 -11
  189. package/tests/agent-home.test.ts +56 -3
  190. package/tests/agent-model-resolution.test.ts +113 -0
  191. package/tests/agent-profiles.test.ts +12 -0
  192. package/tests/agent-risk-escalation.test.ts +123 -0
  193. package/tests/agents-completion-delivery.test.ts +17 -0
  194. package/tests/agents-protocol.test.ts +57 -0
  195. package/tests/agents-queries.test.ts +9 -9
  196. package/tests/agents-runner-process.test.ts +31 -0
  197. package/tests/agents-runner.test.ts +89 -4
  198. package/tests/auto-mode-invariants.test.ts +258 -0
  199. package/tests/auto-mode-sdk.test.ts +57 -0
  200. package/tests/auto-mode.test.ts +49 -0
  201. package/tests/auto-permission-classifier.test.ts +283 -0
  202. package/tests/auto-permission-runtime.test.ts +196 -0
  203. package/tests/background-cache-warming.test.ts +2 -1
  204. package/tests/background-jobs.test.ts +227 -0
  205. package/tests/background-monitor.test.ts +195 -0
  206. package/tests/background-subagents-default-mode.test.ts +28 -5
  207. package/tests/bounded-stop-contract.test.ts +85 -0
  208. package/tests/bounded-writer-admission.test.ts +52 -1
  209. package/tests/bridge-wake-lifecycle.test.ts +129 -0
  210. package/tests/bridge-wake-rendering.test.ts +56 -0
  211. package/tests/build-distribution.test.ts +269 -0
  212. package/tests/builtin-codemode-optout.test.ts +242 -0
  213. package/tests/bundled-install.test.ts +1319 -0
  214. package/tests/bundled-wizard.test.ts +505 -0
  215. package/tests/card-content-policy.test.ts +68 -0
  216. package/tests/card-style-policy.test.ts +34 -2
  217. package/tests/child-codegraph-bootstrap.test.ts +74 -0
  218. package/tests/child-package-entrypoints.test.ts +65 -0
  219. package/tests/child-package-injection.test.ts +74 -0
  220. package/tests/child-safety.test.ts +12 -0
  221. package/tests/child-web-package.test.ts +224 -0
  222. package/tests/codegraph-tools.test.ts +35 -0
  223. package/tests/codemode-rendering.test.ts +107 -2
  224. package/tests/command-palette.test.ts +5 -5
  225. package/tests/context-threshold.test.ts +31 -0
  226. package/tests/delegate-for-reason-contract.test.ts +327 -0
  227. package/tests/delegated-key-learnings-contract.test.ts +3 -2
  228. package/tests/devbinary/native-review-parity.devtest.ts +1 -1
  229. package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
  230. package/tests/engram-safety.test.ts +288 -0
  231. package/tests/generic-agent-tools.test.ts +16 -1
  232. package/tests/gentle-agents.test.ts +1722 -59
  233. package/tests/gentle-ai-binary-isolation.test.ts +135 -0
  234. package/tests/gentle-ai-binary.test.ts +19 -2
  235. package/tests/gentle-ai-child-guards.test.ts +202 -0
  236. package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
  237. package/tests/gentle-ai-installer.test.ts +548 -58
  238. package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
  239. package/tests/gentle-ai-renderer.test.ts +6 -6
  240. package/tests/gentle-ai.test.ts +999 -31
  241. package/tests/gentle-herdr-activity.test.ts +100 -0
  242. package/tests/gentle-notifications.test.ts +427 -0
  243. package/tests/gentle-shell-bin.test.ts +249 -5
  244. package/tests/gentle-shell-bundled.test.ts +1002 -0
  245. package/tests/gentle-shell-launcher.test.ts +234 -13
  246. package/tests/gentle-shell-startup-flow.test.ts +75 -0
  247. package/tests/gentle-shell-startup-update.test.ts +85 -0
  248. package/tests/gentle-shell-uninstall.test.ts +978 -0
  249. package/tests/gentle-shell-update-panel.test.ts +158 -0
  250. package/tests/gentle-shell.test.ts +541 -96
  251. package/tests/gentle-todo.test.ts +253 -2
  252. package/tests/herdr-activity.test.ts +243 -0
  253. package/tests/history-agent-home.test.ts +81 -0
  254. package/tests/history-child-guard.test.ts +70 -0
  255. package/tests/inprocess-reviewer.test.ts +20 -0
  256. package/tests/install-gentle-ai-failure.test.ts +69 -0
  257. package/tests/install-tui-mode-setting.test.ts +15 -0
  258. package/tests/install-wizard.test.ts +1223 -0
  259. package/tests/installer-bundles.test.ts +122 -0
  260. package/tests/installer-posix-bootstrap.test.ts +1201 -0
  261. package/tests/installer-preflight.test.ts +750 -0
  262. package/tests/installer-probes.test.ts +1106 -0
  263. package/tests/installer-runner.test.ts +2158 -0
  264. package/tests/installer-server.test.ts +1567 -0
  265. package/tests/installer-windows-bootstrap.test.ts +2410 -0
  266. package/tests/jobs-view.test.ts +160 -0
  267. package/tests/main-channel-upgrade.test.ts +389 -0
  268. package/tests/main-channel.test.ts +344 -0
  269. package/tests/maintainer/provider-relay.maintest.ts +13 -12
  270. package/tests/model-price-ratio.test.ts +116 -0
  271. package/tests/model-profiles-preservation.test.ts +153 -0
  272. package/tests/model-routing-package.test.ts +10 -0
  273. package/tests/nan-provider-runtime.test.ts +156 -0
  274. package/tests/nan-provider.test.ts +28 -18
  275. package/tests/native-binary-gate.test.ts +2 -0
  276. package/tests/native-review-capability-contract.test.ts +7 -1
  277. package/tests/native-review-cli.test.ts +236 -1
  278. package/tests/native-review-consent.test.ts +2 -2
  279. package/tests/native-review-parity-runtime.test.ts +1 -1
  280. package/tests/native-review-parity.test.ts +12 -48
  281. package/tests/native-risk-evidence-parity.test.ts +21 -0
  282. package/tests/no-self-pause-contract.test.ts +87 -0
  283. package/tests/notification-audio-native.test.ts +281 -0
  284. package/tests/notification-audio-windows.test.ts +581 -0
  285. package/tests/notification-audio.test.ts +548 -0
  286. package/tests/notification-customize.test.ts +655 -0
  287. package/tests/notification-events.test.ts +115 -0
  288. package/tests/notification-policy.test.ts +201 -0
  289. package/tests/notification-pulse-client.test.ts +356 -0
  290. package/tests/notification-pulse-protocol.test.ts +213 -0
  291. package/tests/notification-pulse-stream.test.ts +320 -0
  292. package/tests/notification-scheduler.test.ts +258 -0
  293. package/tests/notification-ui.test.ts +157 -0
  294. package/tests/npm-pack-result.test.ts +41 -0
  295. package/tests/odd-integration.test.ts +14 -5
  296. package/tests/odd-routing-canonical-ratchet.test.ts +53 -12
  297. package/tests/odd-routing-contract.test.ts +80 -23
  298. package/tests/orchestrator-budget.test.ts +29 -6
  299. package/tests/orchestrator-catalog.test.ts +146 -0
  300. package/tests/orchestrator-consultation-sdk.test.ts +614 -0
  301. package/tests/orchestrator-consultation.test.ts +111 -0
  302. package/tests/orchestrator-discovery.test.ts +241 -0
  303. package/tests/orchestrator-helper-consent.test.ts +171 -0
  304. package/tests/orchestrator-helper.test.ts +297 -0
  305. package/tests/orchestrator-message-card.test.ts +190 -0
  306. package/tests/orchestrator-presence.test.ts +23 -0
  307. package/tests/orchestrator-rdd-ownership.test.ts +6 -5
  308. package/tests/orchestrator-scope.test.ts +107 -0
  309. package/tests/orchestrator-state.test.ts +114 -0
  310. package/tests/orchestrator-status-publication-contract.test.ts +34 -0
  311. package/tests/orchestrator-tool-card.test.ts +398 -0
  312. package/tests/orchestrator-work-search.test.ts +318 -0
  313. package/tests/orchestrator-work.test.ts +147 -0
  314. package/tests/package-manifest.test.ts +101 -17
  315. package/tests/packed-runner-owned-path.test.ts +35 -1
  316. package/tests/parallel-reason-contract.test.ts +21 -0
  317. package/tests/parallel-writer-admission.test.ts +210 -0
  318. package/tests/permission-mode-policy.test.ts +194 -0
  319. package/tests/persona-single-channel.test.ts +4 -12
  320. package/tests/pi-pretty.test.ts +56 -0
  321. package/tests/provider-defect-handoff.test.ts +4 -3
  322. package/tests/quiet-tool-rendering.test.ts +160 -3
  323. package/tests/rdd-aware-verification-contract.test.ts +29 -41
  324. package/tests/render-cache-performance.test.ts +48 -0
  325. package/tests/review-agent-end-preflight.test.ts +62 -10
  326. package/tests/review-candidate-view.test.ts +101 -12
  327. package/tests/review-contract-prompt.test.ts +33 -4
  328. package/tests/review-controller-native-recovery.test.ts +327 -17
  329. package/tests/review-controller-native-routing.test.ts +255 -1
  330. package/tests/review-controller-workspace-root.test.ts +43 -2
  331. package/tests/review-controller.test.ts +2 -2
  332. package/tests/review-correction-lifecycle.test.ts +2 -1
  333. package/tests/review-gate.test.ts +7 -4
  334. package/tests/review-graph-schema.test.ts +2 -2
  335. package/tests/review-host-relay-routing.test.ts +5 -3
  336. package/tests/review-host-relay.test.ts +49 -2
  337. package/tests/review-integration-v2-forward.test.ts +21 -1
  338. package/tests/review-json-arguments.test.ts +167 -0
  339. package/tests/review-last-event-closure.test.ts +157 -8
  340. package/tests/review-ledger-contract.test.ts +5 -4
  341. package/tests/review-policy-judgment-day.test.ts +1 -1
  342. package/tests/review-policy-ordinary.test.ts +2 -2
  343. package/tests/review-relay-transport-agent.test.ts +66 -1
  344. package/tests/review-risk-assessment.test.ts +109 -33
  345. package/tests/review-session-standing-permission-controller.test.ts +16 -17
  346. package/tests/review-session-standing-permission-ipc.test.ts +6 -5
  347. package/tests/review-test-fixtures.ts +7 -1
  348. package/tests/review-transaction.test.ts +18 -1
  349. package/tests/run-test-suite.test.ts +83 -5
  350. package/tests/runtime-harness.mjs +42 -15
  351. package/tests/runtime-metrics-extension.test.ts +1 -1
  352. package/tests/runtime-metrics-native.test.ts +31 -1
  353. package/tests/selection-engine.test.ts +30 -0
  354. package/tests/session-change-capture.test.ts +10 -10
  355. package/tests/session-changes-shell.test.ts +242 -48
  356. package/tests/session-changes.test.ts +17 -24
  357. package/tests/session-profile-binding.test.ts +163 -0
  358. package/tests/session-profile-freeze.test.ts +381 -0
  359. package/tests/session-subject-routing-contract.test.ts +34 -0
  360. package/tests/session-worktree-registry.test.ts +57 -2
  361. package/tests/shell-bar.test.ts +40 -0
  362. package/tests/shell-changes-view.test.ts +85 -34
  363. package/tests/shell-changes.test.ts +61 -62
  364. package/tests/shell-render-cache-runtime.test.ts +69 -0
  365. package/tests/shell-render-cache.test.ts +129 -0
  366. package/tests/shell-sidebar-layout.test.ts +27 -2
  367. package/tests/shell-todo.test.ts +120 -2
  368. package/tests/shell-usage-view.test.ts +1 -1
  369. package/tests/shell-usage.test.ts +35 -0
  370. package/tests/short-check-routing-contract.test.ts +49 -0
  371. package/tests/skill-registry-watcher.test.ts +119 -0
  372. package/tests/skill-registry.test.ts +311 -5
  373. package/tests/startup-banner.test.ts +117 -3
  374. package/tests/startup-home-repo.test.ts +170 -0
  375. package/tests/status-timing-diagnostics.test.ts +283 -0
  376. package/tests/support/orchestrator-modules.ts +19 -0
  377. package/tests/task-size-routing-contract.test.ts +241 -0
  378. package/tests/telemetry-trigger.test.ts +27 -1
  379. package/tests/test-strength-contract.test.ts +124 -0
  380. package/tests/verify-agent-contract.test.ts +90 -0
  381. package/tests/verify-first-launch-contract.test.ts +139 -0
  382. package/tests/verify-package-files.test.ts +56 -0
  383. package/tests/vim-editor-adapter.test.ts +7 -7
  384. package/tests/visual-customization-policy.test.ts +32 -3
  385. package/tests/visual-customize-view.test.ts +288 -10
  386. package/tests/writer-edit-surface-scope.test.ts +59 -2
  387. package/tests/writer-surfaces.test.ts +199 -0
  388. package/tests/yolo-customize.test.ts +18 -3
  389. package/tests/yolo-mode-runtime.test.ts +37 -10
  390. package/tests/yolo-mode.test.ts +32 -4
  391. package/tests/yolo-session-policy.test.ts +1 -1
  392. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
  393. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
  394. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
  395. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  396. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
  397. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
  398. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
@@ -30,6 +30,23 @@ const binUrl = new URL("../bin/gentle-shell.mjs", import.meta.url);
30
30
  const binPath = fileURLToPath(binUrl);
31
31
  const packageRoot = dirname(dirname(binPath));
32
32
 
33
+ test("Herdr activity is discoverable through the isolated launcher package", async () => {
34
+ const { buildPiInvocation } = await import("../lib/gentle-shell-launcher.ts");
35
+ const manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
36
+ assert.ok(manifest.pi.extensions.includes("./extensions"));
37
+ assert.ok(existsSync(join(packageRoot, "extensions", "gentle-herdr-activity.ts")));
38
+ for (const takeOver of [false, true]) {
39
+ const invocation = buildPiInvocation({
40
+ runtime: { kind: "path", command: "fake-pi", args: [] },
41
+ home: { mode: "isolated", source: "default", dir: "/fake/home" },
42
+ packageRoot, declaration: undefined, takeOver, otherPackagePaths: [],
43
+ passthrough: [], baseEnv: {}, homedir: "/fake", cwd: "/fake/cwd",
44
+ });
45
+ assert.ok(invocation.args.includes(packageRoot));
46
+ assert.equal(invocation.env.PI_CODING_AGENT_DIR, "/fake/home");
47
+ }
48
+ });
49
+
33
50
  test("real adjacent Pi resolves through its public entry without PATH or a runtime override", (t) => {
34
51
  const f = fixture(t);
35
52
  const result = spawnSync(process.execPath, [binPath, "--version"], {
@@ -53,13 +70,17 @@ function standaloneLauncher(t: test.TestContext) {
53
70
  writeFileSync(join(root, "bin", "gentle-shell.mjs"), readFileSync(binPath));
54
71
  writeFileSync(join(root, "package.json"), readFileSync(join(packageRoot, "package.json")));
55
72
  for (const dir of ["runtime", "scripts"]) symlinkSync(join(packageRoot, dir), join(root, dir), "junction");
56
- const env = { HOME: f.home, USERPROFILE: f.home, PATH: `${root}${delimiter}${dirname(process.execPath)}`, GENTLE_SHELL_NO_AUTO_SETUP: "1" };
73
+ const env = {
74
+ HOME: f.home, USERPROFILE: f.home, PATH: `${root}${delimiter}${dirname(process.execPath)}`, GENTLE_SHELL_NO_AUTO_SETUP: "1",
75
+ // Keep executable discovery isolated without making cmd shims unlaunchable.
76
+ ...(process.platform === "win32" ? { ComSpec: process.env.ComSpec, SystemRoot: process.env.SystemRoot } : {}),
77
+ };
57
78
  return { ...f, root, env, launcher: join(root, "bin", "gentle-shell.mjs") };
58
79
  }
59
80
 
60
81
  test("a genuinely absent adjacent peer falls back to PATH", (t) => {
61
82
  const f = standaloneLauncher(t);
62
- writePiScript(join(f.root, "pi"), "0.99.2");
83
+ writePathPiScript(f.root, "0.99.2");
63
84
  const result = spawnSync(process.execPath, [f.launcher, "--version"], { env: f.env, encoding: "utf8" });
64
85
  assert.equal(result.status, 0, result.stderr);
65
86
  assert.match(result.stdout, /pi 0\.99\.2/);
@@ -71,8 +92,7 @@ test("malformed adjacent metadata never silently falls back to PATH; env overrid
71
92
  mkdirSync(join(peer, "dist", "bundle"), { recursive: true });
72
93
  writeFileSync(join(peer, "dist", "index.js"), "");
73
94
  writePiScript(join(peer, "dist", "bundle", "cli.js"), "0.99.1");
74
- const fallback = join(f.root, "pi");
75
- writePiScript(fallback, "0.99.2");
95
+ const fallback = writePathPiScript(f.root, "0.99.2");
76
96
  const base = { name: "@earendil-works/pi-coding-agent", type: "module", exports: { ".": { import: "./dist/index.js" } }, bin: { pi: "dist/bundle/cli.js" } };
77
97
  for (const metadata of [{ ...base, name: "impostor" }, { ...base, bin: { pi: "../outside.js" } }, { ...base, bin: {} }, "malformed"]) {
78
98
  writeFileSync(join(peer, "package.json"), metadata === "malformed" ? "{" : JSON.stringify(metadata));
@@ -117,6 +137,9 @@ function fixture(t: test.TestContext) {
117
137
  GENTLE_SHELL_PI: piScript,
118
138
  GENTLE_SHELL_NO_AUTO_SETUP: "1",
119
139
  };
140
+ // A suite launched from inside a Gentle Shell session inherits the outer
141
+ // session's recorded user Pi home, which --link now honours first (#2015).
142
+ delete env.GENTLE_SHELL_USER_PI_HOME;
120
143
  return { root, home, gentleShellHome, piScript, env };
121
144
  }
122
145
 
@@ -152,6 +175,7 @@ function writePiScript(path: string, version: string, removeExitCode = 0) {
152
175
  " PI_CODING_AGENT_DIR: process.env.PI_CODING_AGENT_DIR,",
153
176
  " GENTLE_PI_AGENT_HOME: process.env.GENTLE_PI_AGENT_HOME,",
154
177
  " GENTLE_SHELL_USER_PI_HOME: process.env.GENTLE_SHELL_USER_PI_HOME,",
178
+ " GENTLE_SHELL_CHILD_PACKAGE_INJECTION: process.env.GENTLE_SHELL_CHILD_PACKAGE_INJECTION,",
155
179
  "}));",
156
180
  "process.exit(0);",
157
181
  "",
@@ -160,6 +184,17 @@ function writePiScript(path: string, version: string, removeExitCode = 0) {
160
184
  chmodSync(path, 0o755);
161
185
  }
162
186
 
187
+ // Match the real platform lookup: POSIX accepts the shebang executable,
188
+ // Windows discovers a .cmd shim via PATHEXT. Bundled JS peers stay unchanged.
189
+ function writePathPiScript(root: string, version: string): string {
190
+ const script = join(root, process.platform === "win32" ? "pi-fixture.mjs" : "pi");
191
+ writePiScript(script, version);
192
+ if (process.platform !== "win32") return script;
193
+ const command = join(root, "pi.cmd");
194
+ writeFileSync(command, `@echo off\r\n"${process.execPath}" "${script}" %*\r\nexit /b %errorlevel%\r\n`);
195
+ return command;
196
+ }
197
+
163
198
  function run(env: NodeJS.ProcessEnv, args: string[], options: { cwd?: string } = {}) {
164
199
  return spawnSync(process.execPath, [binPath, ...args], { encoding: "utf8", env, ...options });
165
200
  }
@@ -642,6 +677,20 @@ test("forwarded args reach pi after the injected extension flags, in order", (t)
642
677
  assert.equal(payload.GENTLE_PI_AGENT_HOME, f.gentleShellHome);
643
678
  });
644
679
 
680
+ // #1690: the spawned pi must carry the launcher's own -e set so the subagent
681
+ // runner can forward it to delegated children; a stale inherited value is replaced.
682
+ test("an isolated launch without a gentle-pi declaration signals its package injection to pi", (t) => {
683
+ const f = fixture(t);
684
+ const stale = JSON.stringify({ version: 1, noExtensions: true, extensionPaths: [join(f.root, "outer")] });
685
+ for (const inherited of [undefined, stale]) {
686
+ const result = run({ ...f.env, GENTLE_SHELL_CHILD_PACKAGE_INJECTION: inherited }, ["--mode", "rpc"]);
687
+ assert.equal(result.status, 0, result.stderr);
688
+ const payload = JSON.parse(result.stdout);
689
+ assert.deepEqual(payload.args.slice(0, 2), ["-e", packageRoot]);
690
+ assert.deepEqual(JSON.parse(payload.GENTLE_SHELL_CHILD_PACKAGE_INJECTION), { version: 1, noExtensions: false, extensionPaths: [packageRoot] });
691
+ }
692
+ });
693
+
645
694
  test("an isolated launch keeps its own agent home and carries the user's original Pi home, even when nested", (t) => {
646
695
  const f = fixture(t);
647
696
  const base = { ...f.env };
@@ -667,6 +716,10 @@ test("an isolated launch keeps its own agent home and carries the user's origina
667
716
  const linked = launch({ PI_CODING_AGENT_DIR: customHome }, ["--link"]);
668
717
  assert.equal(linked.PI_CODING_AGENT_DIR, customHome);
669
718
  assert.equal(linked.GENTLE_SHELL_USER_PI_HOME, customHome);
719
+ // --link from inside an isolated session reuses the preserved original home (#2015).
720
+ const nestedLinked = launch({ PI_CODING_AGENT_DIR: f.gentleShellHome, GENTLE_SHELL_USER_PI_HOME: customHome }, ["--link"]);
721
+ assert.equal(nestedLinked.PI_CODING_AGENT_DIR, customHome);
722
+ assert.equal(nestedLinked.GENTLE_SHELL_USER_PI_HOME, customHome);
670
723
  assert.equal(existsSync(join(customHome, "sessions")), false);
671
724
  });
672
725
 
@@ -801,7 +854,10 @@ test("gentle-shell setup provisions the resolved home through the pinned gentle-
801
854
  assert.deepEqual(payload.args, ["install", "--agent", "pi", "--scope", "global"]);
802
855
  assert.equal(payload.PI_CODING_AGENT_DIR, f.gentleShellHome);
803
856
  assert.equal(payload.GENTLE_PI_AGENT_HOME, f.gentleShellHome);
804
- assert.ok(payload.PATH.startsWith(`${dirname(f.piScript)}${delimiter}`), payload.PATH);
857
+ // First the temporary `pi` that runs the resolved runtime, then the runtime's own folder.
858
+ const [launcher, runtimeDir] = payload.PATH.split(delimiter);
859
+ assert.match(launcher, /gentle-shell-pi-/, payload.PATH);
860
+ assert.equal(runtimeDir, dirname(f.piScript), payload.PATH);
805
861
 
806
862
  // Home resolution runs exactly as a normal run: the isolated home gets
807
863
  // created with its bootstrap TUI setting, same as a plain `gentle-shell`.
@@ -847,6 +903,119 @@ test("gentle-shell setup passes through the gentle-ai exit code", (t) => {
847
903
  assert.equal(result.status, 3);
848
904
  });
849
905
 
906
+ // Windows cannot execute a shebang fixture directly; use the launcher's
907
+ // existing .cmd support without changing the runtime under test.
908
+ function setupMCPExecutable(path: string): string {
909
+ chmodSync(path, 0o755);
910
+ if (process.platform !== "win32") return path;
911
+ const wrapper = `${path}.cmd`;
912
+ writeFileSync(wrapper, `@echo off\r\n"${process.execPath}" "${path}" %*\r\n`);
913
+ return wrapper;
914
+ }
915
+
916
+ test("setup MCP migration warns without re-enabling configured servers", async (t) => {
917
+ for (const mode of ["link", "path"] as const) {
918
+ await t.test(mode, (t) => {
919
+ const f = fixture(t);
920
+ const target = mode === "link" ? join(f.home, ".pi", "agent") : f.gentleShellHome;
921
+ mkdirSync(join(target, "npm"), { recursive: true });
922
+ const settingsPath = join(target, "settings.json");
923
+ const settings = { theme: "kanagawa", packages: ["npm:pi-mcp-adapter", "npm:other"], extensions: ["-builtin:mcp", "user-extension.ts"] };
924
+ writeFileSync(settingsPath, JSON.stringify(settings));
925
+ const npmPath = join(target, "npm", "package.json");
926
+ writeFileSync(npmPath, JSON.stringify({ dependencies: { "pi-mcp-adapter": "^3.0.0", other: "1.0.0" } }));
927
+ const mcpPath = join(target, "mcp.json");
928
+ const mcpText = '{"mcpServers":{"context7":{"command":"npx","args":["context7-mcp"]}}}';
929
+ writeFileSync(mcpPath, mcpText);
930
+ const installer = join(f.root, "retire-adapter.mjs");
931
+ writeFileSync(installer, [
932
+ "#!/usr/bin/env node",
933
+ 'import { readFileSync, writeFileSync } from "node:fs";',
934
+ 'import { join } from "node:path";',
935
+ 'const home = process.env.PI_CODING_AGENT_DIR;',
936
+ 'const settingsPath = join(home, "settings.json");',
937
+ 'const settings = JSON.parse(readFileSync(settingsPath, "utf8"));',
938
+ 'settings.packages = settings.packages.filter(p => p !== "npm:pi-mcp-adapter");',
939
+ 'writeFileSync(settingsPath, JSON.stringify(settings));',
940
+ 'const npmPath = join(home, "npm", "package.json");',
941
+ 'const manifest = JSON.parse(readFileSync(npmPath, "utf8"));',
942
+ 'delete manifest.dependencies["pi-mcp-adapter"];',
943
+ 'writeFileSync(npmPath, JSON.stringify(manifest));',
944
+ 'console.log("installed");',
945
+ ].join("\n"));
946
+ assert.equal(readFileSync(installer, "utf8").split("\n")[0], "#!/usr/bin/env node");
947
+ const args = mode === "link" ? ["--link", "setup"] : ["--home", target, "setup"];
948
+ const result = run({ ...f.env, PI_CODING_AGENT_DIR: target, GENTLE_SHELL_PI: setupMCPExecutable(f.piScript), GENTLE_SHELL_GENTLE_AI_BIN: setupMCPExecutable(installer) }, args);
949
+ assert.equal(result.status, 0, result.stderr);
950
+ assert.equal(result.stdout, "installed\n");
951
+ assert.equal(result.stderr, `gentle-shell: provisioning ${target} with the gentle-ai companion packages\n` +
952
+ `gentle-shell: WARNING: Pi built-in MCP is disabled in ${settingsPath}; pi-mcp-adapter is absent or inactive, so servers in ${mcpPath} will not load. If you want these servers enabled, remove -builtin:mcp from extensions in ${settingsPath}, then restart Pi. If MCP is intentionally disabled, keep the setting.\n`);
953
+ assert.deepEqual(JSON.parse(readFileSync(settingsPath, "utf8")), { ...settings, packages: ["npm:other"] });
954
+ assert.deepEqual(JSON.parse(readFileSync(npmPath, "utf8")), { dependencies: { other: "1.0.0" } });
955
+ assert.equal(readFileSync(mcpPath, "utf8"), mcpText);
956
+ });
957
+ }
958
+ });
959
+
960
+ test("setup MCP migration keeps enabled, unconfigured and dry-run homes unchanged", async (t) => {
961
+ for (const scenario of ["enabled", "unconfigured", "dry-run", "active-adapter"] as const) {
962
+ await t.test(scenario, (t) => {
963
+ const f = fixture(t);
964
+ const target = join(f.home, ".pi", "agent");
965
+ mkdirSync(target, { recursive: true });
966
+ const settingsPath = join(target, "settings.json");
967
+ const settingsText = JSON.stringify({ extensions: scenario === "enabled" ? [] : ["-builtin:mcp"], packages: scenario === "active-adapter" ? ["npm:pi-mcp-adapter@3.0.0"] : [] });
968
+ writeFileSync(settingsPath, settingsText);
969
+ const mcpPath = join(target, "mcp.json");
970
+ const mcpText = JSON.stringify({ mcpServers: scenario === "unconfigured" ? {} : { context7: { command: "npx" } } });
971
+ writeFileSync(mcpPath, mcpText);
972
+ const installer = join(f.root, "fake-gentle-ai.mjs");
973
+ writeGentleAiScript(installer);
974
+ const dryRun = scenario === "dry-run";
975
+ const result = run({ ...f.env, PI_CODING_AGENT_DIR: target, GENTLE_SHELL_PI: setupMCPExecutable(f.piScript), GENTLE_SHELL_GENTLE_AI_BIN: setupMCPExecutable(installer) }, ["--link", "setup", ...(dryRun ? ["--dry-run"] : [])]);
976
+ assert.equal(result.status, 0, result.stderr);
977
+ assert.deepEqual(JSON.parse(result.stdout).args, ["install", "--agent", "pi", "--scope", "global", ...(dryRun ? ["--dry-run"] : [])]);
978
+ assert.doesNotMatch(result.stderr, /WARNING:|built-in MCP/);
979
+ assert.equal(readFileSync(settingsPath, "utf8"), settingsText);
980
+ assert.equal(readFileSync(mcpPath, "utf8"), mcpText);
981
+ });
982
+ }
983
+ });
984
+
985
+ test("setup MCP migration warns when adapter resource loading is disabled", async (t) => {
986
+ for (const adapter of [
987
+ { source: "npm:pi-mcp-adapter", autoload: false },
988
+ { source: "npm:pi-mcp-adapter", autoload: false, extensions: ["!**"] },
989
+ { source: "npm:pi-mcp-adapter", extensions: ["!**"] },
990
+ { source: "npm:pi-mcp-adapter", extensions: ["index.ts"] },
991
+ ]) {
992
+ await t.test(JSON.stringify(adapter), (t) => {
993
+ const f = fixture(t);
994
+ const target = join(f.home, ".pi", "agent");
995
+ mkdirSync(target, { recursive: true });
996
+ const settingsPath = join(target, "settings.json");
997
+ const uncertain = "extensions" in adapter && adapter.extensions.includes("index.ts");
998
+ const settingsText = JSON.stringify({ extensions: ["-builtin:mcp"], packages: [adapter] });
999
+ writeFileSync(settingsPath, settingsText);
1000
+ const mcpPath = join(target, "mcp.json");
1001
+ const mcpText = '{"mcpServers":{"context7":{"command":"npx"}}}';
1002
+ writeFileSync(mcpPath, mcpText);
1003
+ const installer = join(f.root, "fake-gentle-ai.mjs");
1004
+ writeGentleAiScript(installer);
1005
+ const result = run({ ...f.env, PI_CODING_AGENT_DIR: target, GENTLE_SHELL_PI: setupMCPExecutable(f.piScript), GENTLE_SHELL_GENTLE_AI_BIN: setupMCPExecutable(installer) }, ["--link", "setup"]);
1006
+ assert.equal(result.status, 0, result.stderr);
1007
+ assert.deepEqual(JSON.parse(result.stdout).args, ["install", "--agent", "pi", "--scope", "global"]);
1008
+ const diagnosis = uncertain
1009
+ ? "pi-mcp-adapter activity cannot be confirmed from its extension filters, so servers in " + mcpPath + " may not load"
1010
+ : "pi-mcp-adapter is absent or inactive, so servers in " + mcpPath + " will not load";
1011
+ assert.equal(result.stderr, `gentle-shell: provisioning ${target} with the gentle-ai companion packages\n` +
1012
+ `gentle-shell: WARNING: Pi built-in MCP is disabled in ${settingsPath}; ${diagnosis}. If you want these servers enabled, remove -builtin:mcp from extensions in ${settingsPath}, then restart Pi. If MCP is intentionally disabled, keep the setting.\n`);
1013
+ assert.equal(readFileSync(settingsPath, "utf8"), settingsText);
1014
+ assert.equal(readFileSync(mcpPath, "utf8"), mcpText);
1015
+ });
1016
+ }
1017
+ });
1018
+
850
1019
  // --- setup subcommand's self-heal for a missing package-local binary -------
851
1020
  //
852
1021
  // `npm install -g <tarball>` on a machine whose npm config disables lifecycle
@@ -1676,6 +1845,81 @@ test("an empty or nonexistent --home is still auto-provisioned", (t) => {
1676
1845
  assert.equal(readFileSync(counterPath, "utf8").trim().split("\n").length, 1, "a fresh --home must still be auto-provisioned");
1677
1846
  });
1678
1847
 
1848
+ // #2106: both installers finish with `gentle-shell setup`; the first launch
1849
+ // after it must not install the companion packages again.
1850
+ // Gentle AI requires a `pi` on its PATH and runs `pi install npm:gentle-pi`.
1851
+ // When gentle-shell runs a Pi that is not itself on PATH (the adjacent
1852
+ // package, or GENTLE_SHELL_PI), setup must give Gentle AI that same Pi.
1853
+ test("setup gives Gentle AI a `pi` on its PATH that runs the same Pi gentle-shell resolved", { skip: process.platform === "win32" }, (t) => {
1854
+ const f = fixture(t);
1855
+ const probe = join(f.root, "pi-from-gentle-ai.txt");
1856
+ const gentleAiScript = join(f.root, "fake-gentle-ai.mjs");
1857
+ writeFileSync(gentleAiScript, ["#!/usr/bin/env node", "import { spawnSync } from 'node:child_process';", "import { writeFileSync } from 'node:fs';",
1858
+ "const pi = spawnSync('pi', ['--version'], { encoding: 'utf8' });",
1859
+ `writeFileSync(${JSON.stringify(probe)}, JSON.stringify({ status: pi.status, error: pi.error?.code ?? null, stdout: pi.stdout }));`,
1860
+ "process.exit(0);", ""].join("\n"), { mode: 0o755 });
1861
+ // No `pi` anywhere on the user's PATH: only node and the system tools.
1862
+ const temp = join(f.root, "tmp");
1863
+ mkdirSync(temp);
1864
+ const env = { ...f.env, TMPDIR: temp, PATH: [dirname(process.execPath), "/usr/bin", "/bin"].join(":"), GENTLE_SHELL_GENTLE_AI_BIN: gentleAiScript, GENTLE_SHELL_GENTLE_AI_PIN: "3.6.0" };
1865
+
1866
+ const result = run(env, ["setup"]);
1867
+ assert.equal(result.status, 0, result.stderr);
1868
+ assert.deepEqual(JSON.parse(readFileSync(probe, "utf8")), { status: 0, error: null, stdout: "0.99.1\n" });
1869
+ // The launcher is temporary: nothing is left behind in the temp folder it used.
1870
+ assert.deepEqual(readdirSync(temp), []);
1871
+ });
1872
+
1873
+ test("setup without a usable temp folder runs as before, without the temporary pi", { skip: process.platform === "win32" }, (t) => {
1874
+ const f = fixture(t);
1875
+ const gentleAiScript = join(f.root, "fake-gentle-ai.mjs");
1876
+ writeGentleAiScriptCountingRuns(gentleAiScript, join(f.root, "runs.log"));
1877
+ const env = { ...f.env, TMPDIR: join(f.root, "missing-tmp"), GENTLE_SHELL_GENTLE_AI_BIN: gentleAiScript, GENTLE_SHELL_GENTLE_AI_PIN: "3.6.0" };
1878
+ const result = run(env, ["setup"]);
1879
+ assert.equal(result.status, 0, result.stderr);
1880
+ assert.doesNotMatch(result.stderr, /mkdtemp|gentle-shell-pi-/);
1881
+ });
1882
+
1883
+ test("a successful manual setup records the isolated home, so the first launch does not set it up again", (t) => {
1884
+ const f = fixture(t);
1885
+ const gentleAiScript = join(f.root, "fake-gentle-ai.mjs");
1886
+ const counterPath = join(f.root, "gentle-ai-runs.log");
1887
+ writeGentleAiScriptCountingRuns(gentleAiScript, counterPath);
1888
+ const env = enableAutoProvision({ ...f.env, GENTLE_SHELL_GENTLE_AI_BIN: gentleAiScript, GENTLE_SHELL_GENTLE_AI_PIN: "3.6.0" });
1889
+
1890
+ const setup = run(env, ["setup"]);
1891
+ assert.equal(setup.status, 0, setup.stderr);
1892
+ const config = JSON.parse(readFileSync(join(f.home, ".gentle-shell", "config.json"), "utf8"));
1893
+ const homeKey = realpathSync(f.gentleShellHome);
1894
+ assert.equal(config.provisioned[homeKey].gentleAi, "3.6.0");
1895
+ assert.equal(config.provisioned[homeKey].gentlePi, ownGentlePiVersion());
1896
+
1897
+ const launch = run(env, ["--mode", "rpc"]);
1898
+ assert.equal(launch.status, 0, launch.stderr);
1899
+ assert.doesNotMatch(launch.stderr, /first run in/);
1900
+ assert.equal(readFileSync(counterPath, "utf8").trim().split("\n").length, 1, "gentle-ai ran only for the manual setup");
1901
+ });
1902
+
1903
+ test("manual setup records nothing for a dry run, a failed setup or pi's own default agent home", (t) => {
1904
+ const f = fixture(t);
1905
+ const configPath = join(f.home, ".gentle-shell", "config.json");
1906
+ const provisioned = () => (existsSync(configPath) ? JSON.parse(readFileSync(configPath, "utf8")).provisioned ?? {} : {});
1907
+ const gentleAiScript = join(f.root, "fake-gentle-ai.mjs");
1908
+ const failingScript = join(f.root, "failing-gentle-ai.mjs");
1909
+ writeGentleAiScriptCountingRuns(gentleAiScript, join(f.root, "runs.log"));
1910
+ writeGentleAiScriptCountingRuns(failingScript, join(f.root, "failed-runs.log"), 3);
1911
+ const env = { ...f.env, PI_CODING_AGENT_DIR: undefined, GENTLE_SHELL_GENTLE_AI_BIN: gentleAiScript, GENTLE_SHELL_GENTLE_AI_PIN: "3.6.0" };
1912
+
1913
+ assert.equal(run(env, ["setup", "--dry-run"]).status, 0);
1914
+ assert.deepEqual(provisioned(), {}, "dry run");
1915
+ assert.notEqual(run({ ...env, GENTLE_SHELL_GENTLE_AI_BIN: failingScript }, ["setup"]).status, 0);
1916
+ assert.deepEqual(provisioned(), {}, "failed setup");
1917
+ const defaultPiHome = join(f.home, ".pi", "agent");
1918
+ const own = run(env, ["--home", defaultPiHome, "setup"]);
1919
+ assert.equal(own.status, 0, own.stderr);
1920
+ assert.deepEqual(provisioned(), {}, "pi's own default agent home is never recorded, so it is never auto-provisioned later");
1921
+ });
1922
+
1679
1923
  test("a --home equal to pi's own default agent home is never auto-provisioned, even when empty", (t) => {
1680
1924
  const f = fixture(t);
1681
1925
  const defaultPiHome = join(f.home, ".pi", "agent");