gentle-pi 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/README.md +157 -18
  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 +708 -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/auto-permission-answers.md +25 -0
  28. package/docs/auto-permission-evidence.md +39 -0
  29. package/docs/bundled-install.md +399 -0
  30. package/docs/delegated-verification.md +3 -5
  31. package/docs/gentle-agents-activity.md +513 -0
  32. package/docs/gentle-shell-uninstall.md +136 -0
  33. package/docs/gentle-shell.md +90 -33
  34. package/docs/install-wizard.md +2040 -0
  35. package/docs/native-authority-architecture.md +1 -1
  36. package/docs/permission-modes.md +127 -0
  37. package/docs/prompt-history.md +1 -1
  38. package/docs/readme-reference.md +310 -47
  39. package/docs/render-cache-performance.md +28 -0
  40. package/docs/session-profile-format.md +39 -0
  41. package/docs/sound-notifications-proposal.md +191 -0
  42. package/docs/sound-notifications.md +114 -0
  43. package/docs/yolo-mode.md +11 -10
  44. package/extensions/ask-user-choice.ts +4 -0
  45. package/extensions/ask-user-question.ts +6 -1
  46. package/extensions/child-context.ts +11 -2
  47. package/extensions/child-safety.ts +4 -2
  48. package/extensions/codegraph-tools.ts +18 -5
  49. package/extensions/gentle-agents.ts +718 -87
  50. package/extensions/gentle-ai.ts +991 -263
  51. package/extensions/gentle-herdr-activity.ts +81 -0
  52. package/extensions/gentle-notifications.ts +104 -0
  53. package/extensions/gentle-shell.ts +271 -80
  54. package/extensions/gentle-todo.ts +123 -18
  55. package/extensions/history/index.ts +11 -6
  56. package/extensions/pi-pretty.ts +39 -3
  57. package/extensions/quiet-tools.ts +91 -21
  58. package/extensions/skill-registry.ts +175 -46
  59. package/extensions/startup-banner.ts +11 -2
  60. package/lib/agent-home.ts +10 -1
  61. package/lib/agent-model-resolution.ts +80 -0
  62. package/lib/agent-profiles.ts +13 -3
  63. package/lib/agents-completion-delivery.ts +11 -3
  64. package/lib/agents-messaging.ts +1 -1
  65. package/lib/agents-protocol.ts +40 -1
  66. package/lib/agents-runner.ts +90 -15
  67. package/lib/auto-permission-answers.ts +133 -0
  68. package/lib/auto-permission-classifier.ts +459 -0
  69. package/lib/auto-permission-evidence.ts +88 -0
  70. package/lib/auto-permission-memory.ts +69 -0
  71. package/lib/auto-permission-runtime.ts +484 -0
  72. package/lib/auto-permission-tool-context.ts +61 -0
  73. package/lib/background-jobs-tools.ts +202 -0
  74. package/lib/background-jobs.ts +253 -0
  75. package/lib/background-monitor.ts +154 -0
  76. package/lib/bounded-writer-admission.ts +83 -10
  77. package/lib/builtin-codemode-optout.ts +166 -0
  78. package/lib/card-content-policy.ts +85 -0
  79. package/lib/child-package-injection.ts +62 -0
  80. package/lib/codemode-renderer.ts +14 -4
  81. package/lib/command-palette-catalog.ts +3 -2
  82. package/lib/context-threshold.ts +26 -0
  83. package/lib/destructive-command-guard.ts +104 -28
  84. package/lib/gentle-ai-binary.ts +109 -4
  85. package/lib/gentle-shell-bundled.ts +535 -0
  86. package/lib/gentle-shell-launcher.ts +75 -24
  87. package/lib/gentle-shell-startup-flow.ts +54 -0
  88. package/lib/gentle-shell-startup-update.ts +92 -0
  89. package/lib/gentle-shell-uninstall.ts +840 -0
  90. package/lib/gentle-shell-update-panel.ts +231 -0
  91. package/lib/herdr-activity.ts +183 -0
  92. package/lib/inprocess-reviewer.ts +3 -0
  93. package/lib/jobs-sidebar-state.ts +20 -0
  94. package/lib/jobs-view.ts +161 -0
  95. package/lib/model-price-ratio.ts +83 -0
  96. package/lib/model-routing-authority.ts +6 -1
  97. package/lib/nan-provider.ts +33 -11
  98. package/lib/native-review-cli.ts +194 -73
  99. package/lib/notification-audio-native.ts +162 -0
  100. package/lib/notification-audio-windows.ts +399 -0
  101. package/lib/notification-audio.ts +332 -0
  102. package/lib/notification-customize.ts +241 -0
  103. package/lib/notification-events.ts +113 -0
  104. package/lib/notification-policy.ts +148 -0
  105. package/lib/notification-pulse-client.ts +367 -0
  106. package/lib/notification-pulse-protocol.ts +305 -0
  107. package/lib/notification-pulse-stream.ts +213 -0
  108. package/lib/notification-pulse-worker.ts +66 -0
  109. package/lib/notification-scheduler.ts +173 -0
  110. package/lib/notification-service.ts +202 -0
  111. package/lib/notification-ui.ts +87 -0
  112. package/lib/odd-phase.ts +1 -1
  113. package/lib/orchestrator-alias.ts +20 -0
  114. package/lib/orchestrator-catalog.ts +79 -0
  115. package/lib/orchestrator-consultation.ts +61 -0
  116. package/lib/orchestrator-discovery.ts +49 -0
  117. package/lib/orchestrator-helper-consent.ts +122 -0
  118. package/lib/orchestrator-helper.ts +167 -0
  119. package/lib/orchestrator-message-card.ts +105 -0
  120. package/lib/orchestrator-presence.ts +115 -14
  121. package/lib/orchestrator-scope.ts +80 -0
  122. package/lib/orchestrator-state.ts +107 -0
  123. package/lib/orchestrator-tool-card.ts +280 -0
  124. package/lib/orchestrator-work-search.ts +218 -0
  125. package/lib/orchestrator-work.ts +79 -0
  126. package/lib/permission-mode-policy.ts +59 -0
  127. package/lib/review-candidate-view-owner.ts +8 -6
  128. package/lib/review-candidate-view.ts +3 -2
  129. package/lib/review-compact-contract.ts +2 -2
  130. package/lib/review-host-relay.ts +7 -2
  131. package/lib/review-integration-v2.ts +100 -22
  132. package/lib/review-publication-gate.ts +1 -1
  133. package/lib/review-risk-assessment.ts +77 -51
  134. package/lib/review-sidebar-state.ts +2 -1
  135. package/lib/review-transaction.ts +72 -3
  136. package/lib/runtime-metrics-children.ts +1 -1
  137. package/lib/runtime-metrics-native.ts +3 -2
  138. package/lib/selection-engine.ts +22 -7
  139. package/lib/session-change-capture.ts +5 -4
  140. package/lib/session-changes.ts +27 -22
  141. package/lib/session-profile-binding.ts +83 -0
  142. package/lib/session-profile-freeze.ts +261 -0
  143. package/lib/session-profile-persistence.ts +187 -0
  144. package/lib/session-worktree-registry.ts +74 -8
  145. package/lib/shell-bar.ts +41 -11
  146. package/lib/shell-changes-view.ts +40 -13
  147. package/lib/shell-changes.ts +88 -71
  148. package/lib/shell-prompt.ts +6 -4
  149. package/lib/shell-sidebar-layout.ts +5 -1
  150. package/lib/shell-todo.ts +81 -26
  151. package/lib/shell-usage.ts +4 -3
  152. package/lib/status-timing-diagnostics.ts +201 -0
  153. package/lib/telemetry-trigger.ts +3 -1
  154. package/lib/vim-editor-adapter.ts +27 -5
  155. package/lib/visual-customization-policy.ts +20 -9
  156. package/lib/visual-customize-view.ts +171 -20
  157. package/lib/writer-surfaces.ts +162 -0
  158. package/lib/yolo-session-policy.ts +198 -121
  159. package/package.json +5 -5
  160. package/runtime/child-package-injection.mjs +63 -0
  161. package/runtime/gentle-ai-binary.mjs +109 -4
  162. package/runtime/gentle-shell-bundled.mjs +536 -0
  163. package/runtime/gentle-shell-launcher.mjs +74 -23
  164. package/runtime/gentle-shell-startup-flow.mjs +55 -0
  165. package/runtime/gentle-shell-startup-update.mjs +93 -0
  166. package/runtime/gentle-shell-uninstall.mjs +841 -0
  167. package/runtime/gentle-shell-update-panel.mjs +232 -0
  168. package/runtime/native-review-cli.mjs +191 -70
  169. package/runtime/review-integration-v2.mjs +100 -22
  170. package/runtime/review-risk-assessment.mjs +77 -51
  171. package/runtime/status-timing-diagnostics.mjs +202 -0
  172. package/runtime/telemetry-trigger.mjs +3 -1
  173. package/scripts/bootstrap.cmd +255 -0
  174. package/scripts/bootstrap.sh +168 -0
  175. package/scripts/build-distribution.mjs +157 -0
  176. package/scripts/build-installer-bundles.mjs +136 -0
  177. package/scripts/build-runtime-modules.mjs +7 -0
  178. package/scripts/bundled-install.mjs +1308 -0
  179. package/scripts/bundled-wizard.mjs +399 -0
  180. package/scripts/check-types.mjs +4 -6
  181. package/scripts/gentle-ai-installer.mjs +79 -32
  182. package/scripts/install-gentle-ai.mjs +48 -1
  183. package/scripts/install-tui-mode-setting.mjs +10 -1
  184. package/scripts/install-wizard-preview.mjs +154 -0
  185. package/scripts/installer-downloads.mjs +648 -0
  186. package/scripts/installer-preflight.mjs +387 -0
  187. package/scripts/installer-probes.mjs +564 -0
  188. package/scripts/installer-runner.mjs +1102 -0
  189. package/scripts/installer-server.mjs +881 -0
  190. package/scripts/installer-windows-artifacts.json +15 -0
  191. package/scripts/installer-windows.mjs +752 -0
  192. package/scripts/main-channel.mjs +452 -0
  193. package/scripts/npm-pack-result.mjs +31 -0
  194. package/scripts/pi-update.mjs +223 -0
  195. package/scripts/render-cache-performance-worker.mjs +205 -0
  196. package/scripts/render-cache-performance.mjs +115 -0
  197. package/scripts/run-test-suite.mjs +22 -8
  198. package/scripts/test-installer-acceptance.sh +161 -0
  199. package/scripts/test-packed-runner.mjs +23 -5
  200. package/scripts/types-baseline.json +2 -92
  201. package/scripts/verify-package-files.mjs +82 -18
  202. package/skills/gentle-ai/SKILL.md +12 -11
  203. package/tests/agent-home.test.ts +56 -3
  204. package/tests/agent-model-resolution.test.ts +113 -0
  205. package/tests/agent-profiles.test.ts +12 -0
  206. package/tests/agent-risk-escalation.test.ts +123 -0
  207. package/tests/agents-completion-delivery.test.ts +17 -0
  208. package/tests/agents-protocol.test.ts +57 -0
  209. package/tests/agents-queries.test.ts +9 -9
  210. package/tests/agents-runner-process.test.ts +31 -0
  211. package/tests/agents-runner.test.ts +89 -4
  212. package/tests/append-system-prompt-route.test.ts +18 -1
  213. package/tests/auto-mode-human-answers.test.ts +68 -0
  214. package/tests/auto-mode-invariants.test.ts +276 -0
  215. package/tests/auto-mode-sdk.test.ts +84 -0
  216. package/tests/auto-mode.test.ts +49 -0
  217. package/tests/auto-permission-answers.test.ts +170 -0
  218. package/tests/auto-permission-classifier.test.ts +634 -0
  219. package/tests/auto-permission-compound.test.ts +24 -0
  220. package/tests/auto-permission-evidence.test.ts +88 -0
  221. package/tests/auto-permission-memory.test.ts +46 -0
  222. package/tests/auto-permission-public-regressions.test.ts +111 -0
  223. package/tests/auto-permission-runtime.test.ts +454 -0
  224. package/tests/autonomous-guard.test.ts +86 -16
  225. package/tests/background-cache-warming.test.ts +2 -1
  226. package/tests/background-jobs.test.ts +227 -0
  227. package/tests/background-monitor.test.ts +195 -0
  228. package/tests/background-subagents-default-mode.test.ts +28 -5
  229. package/tests/bounded-stop-contract.test.ts +85 -0
  230. package/tests/bounded-writer-admission.test.ts +52 -1
  231. package/tests/bridge-wake-lifecycle.test.ts +129 -0
  232. package/tests/bridge-wake-rendering.test.ts +56 -0
  233. package/tests/build-distribution.test.ts +269 -0
  234. package/tests/builtin-codemode-optout.test.ts +242 -0
  235. package/tests/bundled-install.test.ts +1319 -0
  236. package/tests/bundled-wizard.test.ts +505 -0
  237. package/tests/card-content-policy.test.ts +68 -0
  238. package/tests/card-style-policy.test.ts +34 -2
  239. package/tests/child-codegraph-bootstrap.test.ts +74 -0
  240. package/tests/child-package-entrypoints.test.ts +65 -0
  241. package/tests/child-package-injection.test.ts +74 -0
  242. package/tests/child-safety.test.ts +12 -0
  243. package/tests/child-web-package.test.ts +224 -0
  244. package/tests/codegraph-tools.test.ts +35 -0
  245. package/tests/codemode-rendering.test.ts +107 -2
  246. package/tests/command-palette.test.ts +5 -5
  247. package/tests/context-threshold.test.ts +31 -0
  248. package/tests/delegate-for-reason-contract.test.ts +327 -0
  249. package/tests/delegated-key-learnings-contract.test.ts +3 -2
  250. package/tests/devbinary/native-review-parity.devtest.ts +1 -1
  251. package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
  252. package/tests/engram-safety.test.ts +316 -0
  253. package/tests/generic-agent-tools.test.ts +16 -1
  254. package/tests/gentle-agents.test.ts +1895 -76
  255. package/tests/gentle-ai-binary-isolation.test.ts +135 -0
  256. package/tests/gentle-ai-binary.test.ts +19 -2
  257. package/tests/gentle-ai-child-guards.test.ts +202 -0
  258. package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
  259. package/tests/gentle-ai-installer.test.ts +548 -58
  260. package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
  261. package/tests/gentle-ai-renderer.test.ts +6 -6
  262. package/tests/gentle-ai.test.ts +999 -31
  263. package/tests/gentle-herdr-activity.test.ts +100 -0
  264. package/tests/gentle-notifications.test.ts +427 -0
  265. package/tests/gentle-shell-bin.test.ts +263 -19
  266. package/tests/gentle-shell-bundled.test.ts +1036 -0
  267. package/tests/gentle-shell-launcher.test.ts +251 -25
  268. package/tests/gentle-shell-startup-flow.test.ts +75 -0
  269. package/tests/gentle-shell-startup-update.test.ts +85 -0
  270. package/tests/gentle-shell-uninstall.test.ts +978 -0
  271. package/tests/gentle-shell-update-panel.test.ts +158 -0
  272. package/tests/gentle-shell.test.ts +611 -100
  273. package/tests/gentle-todo.test.ts +317 -14
  274. package/tests/herdr-activity.test.ts +243 -0
  275. package/tests/history-agent-home.test.ts +81 -0
  276. package/tests/history-child-guard.test.ts +70 -0
  277. package/tests/inprocess-reviewer.test.ts +20 -0
  278. package/tests/install-gentle-ai-failure.test.ts +69 -0
  279. package/tests/install-tui-mode-setting.test.ts +15 -0
  280. package/tests/install-wizard.test.ts +1223 -0
  281. package/tests/installer-bundles.test.ts +122 -0
  282. package/tests/installer-posix-bootstrap.test.ts +1201 -0
  283. package/tests/installer-preflight.test.ts +750 -0
  284. package/tests/installer-probes.test.ts +1106 -0
  285. package/tests/installer-runner.test.ts +2159 -0
  286. package/tests/installer-server.test.ts +1567 -0
  287. package/tests/installer-windows-bootstrap.test.ts +2410 -0
  288. package/tests/jobs-view.test.ts +160 -0
  289. package/tests/main-channel-upgrade.test.ts +395 -0
  290. package/tests/main-channel.test.ts +344 -0
  291. package/tests/maintainer/provider-relay.maintest.ts +13 -12
  292. package/tests/model-price-ratio.test.ts +116 -0
  293. package/tests/model-profiles-preservation.test.ts +153 -0
  294. package/tests/model-routing-package.test.ts +10 -0
  295. package/tests/nan-provider-runtime.test.ts +156 -0
  296. package/tests/nan-provider.test.ts +28 -18
  297. package/tests/native-binary-gate.test.ts +2 -0
  298. package/tests/native-review-capability-contract.test.ts +7 -1
  299. package/tests/native-review-cli.test.ts +236 -1
  300. package/tests/native-review-consent.test.ts +2 -2
  301. package/tests/native-review-parity-runtime.test.ts +1 -1
  302. package/tests/native-review-parity.test.ts +12 -48
  303. package/tests/native-risk-evidence-parity.test.ts +21 -0
  304. package/tests/no-self-pause-contract.test.ts +87 -0
  305. package/tests/notification-audio-native.test.ts +281 -0
  306. package/tests/notification-audio-windows.test.ts +581 -0
  307. package/tests/notification-audio.test.ts +548 -0
  308. package/tests/notification-customize.test.ts +655 -0
  309. package/tests/notification-events.test.ts +115 -0
  310. package/tests/notification-policy.test.ts +201 -0
  311. package/tests/notification-pulse-client.test.ts +356 -0
  312. package/tests/notification-pulse-protocol.test.ts +213 -0
  313. package/tests/notification-pulse-stream.test.ts +320 -0
  314. package/tests/notification-scheduler.test.ts +258 -0
  315. package/tests/notification-ui.test.ts +157 -0
  316. package/tests/npm-pack-result.test.ts +41 -0
  317. package/tests/odd-integration.test.ts +14 -5
  318. package/tests/odd-routing-canonical-ratchet.test.ts +53 -12
  319. package/tests/odd-routing-contract.test.ts +80 -23
  320. package/tests/odd-runtime-delegation-gate.test.ts +4 -1
  321. package/tests/orchestrator-budget.test.ts +29 -6
  322. package/tests/orchestrator-catalog.test.ts +146 -0
  323. package/tests/orchestrator-consultation-sdk.test.ts +614 -0
  324. package/tests/orchestrator-consultation.test.ts +111 -0
  325. package/tests/orchestrator-discovery.test.ts +241 -0
  326. package/tests/orchestrator-helper-consent.test.ts +171 -0
  327. package/tests/orchestrator-helper.test.ts +297 -0
  328. package/tests/orchestrator-message-card.test.ts +190 -0
  329. package/tests/orchestrator-presence.test.ts +23 -0
  330. package/tests/orchestrator-rdd-ownership.test.ts +6 -5
  331. package/tests/orchestrator-scope.test.ts +107 -0
  332. package/tests/orchestrator-state.test.ts +114 -0
  333. package/tests/orchestrator-status-publication-contract.test.ts +34 -0
  334. package/tests/orchestrator-tool-card.test.ts +398 -0
  335. package/tests/orchestrator-work-search.test.ts +318 -0
  336. package/tests/orchestrator-work.test.ts +147 -0
  337. package/tests/package-manifest.test.ts +105 -21
  338. package/tests/packed-runner-owned-path.test.ts +35 -1
  339. package/tests/parallel-reason-contract.test.ts +21 -0
  340. package/tests/parallel-writer-admission.test.ts +210 -0
  341. package/tests/permission-mode-policy.test.ts +194 -0
  342. package/tests/persona-single-channel.test.ts +4 -12
  343. package/tests/pi-pretty.test.ts +56 -0
  344. package/tests/pi-update-launcher.test.ts +87 -0
  345. package/tests/pi-update.test.ts +186 -0
  346. package/tests/provider-defect-handoff.test.ts +4 -3
  347. package/tests/quiet-tool-rendering.test.ts +160 -3
  348. package/tests/rdd-aware-verification-contract.test.ts +29 -41
  349. package/tests/rdd-status-line.test.ts +52 -0
  350. package/tests/render-cache-performance.test.ts +48 -0
  351. package/tests/review-agent-end-preflight.test.ts +62 -10
  352. package/tests/review-candidate-view.test.ts +101 -12
  353. package/tests/review-contract-prompt.test.ts +33 -4
  354. package/tests/review-controller-native-recovery.test.ts +327 -17
  355. package/tests/review-controller-native-routing.test.ts +255 -1
  356. package/tests/review-controller-workspace-root.test.ts +43 -2
  357. package/tests/review-controller.test.ts +2 -2
  358. package/tests/review-correction-lifecycle.test.ts +2 -1
  359. package/tests/review-gate.test.ts +7 -4
  360. package/tests/review-graph-schema.test.ts +2 -2
  361. package/tests/review-host-relay-routing.test.ts +5 -3
  362. package/tests/review-host-relay.test.ts +49 -2
  363. package/tests/review-integration-v2-forward.test.ts +21 -1
  364. package/tests/review-json-arguments.test.ts +167 -0
  365. package/tests/review-last-event-closure.test.ts +157 -8
  366. package/tests/review-ledger-contract.test.ts +5 -4
  367. package/tests/review-policy-judgment-day.test.ts +1 -1
  368. package/tests/review-policy-ordinary.test.ts +2 -2
  369. package/tests/review-relay-transport-agent.test.ts +66 -1
  370. package/tests/review-risk-assessment.test.ts +109 -33
  371. package/tests/review-session-standing-permission-controller.test.ts +16 -17
  372. package/tests/review-session-standing-permission-ipc.test.ts +6 -5
  373. package/tests/review-test-fixtures.ts +7 -1
  374. package/tests/review-transaction.test.ts +18 -1
  375. package/tests/run-test-suite.test.ts +83 -5
  376. package/tests/runtime-harness.mjs +42 -15
  377. package/tests/runtime-metrics-extension.test.ts +1 -1
  378. package/tests/runtime-metrics-native.test.ts +31 -1
  379. package/tests/selection-engine.test.ts +65 -5
  380. package/tests/session-change-capture.test.ts +10 -10
  381. package/tests/session-changes-shell.test.ts +242 -48
  382. package/tests/session-changes.test.ts +17 -24
  383. package/tests/session-profile-binding.test.ts +163 -0
  384. package/tests/session-profile-freeze.test.ts +394 -0
  385. package/tests/session-profile-persistence.test.ts +334 -0
  386. package/tests/session-subject-routing-contract.test.ts +34 -0
  387. package/tests/session-worktree-registry.test.ts +57 -2
  388. package/tests/shell-bar.test.ts +40 -0
  389. package/tests/shell-changes-view.test.ts +85 -34
  390. package/tests/shell-changes.test.ts +79 -62
  391. package/tests/shell-render-cache-runtime.test.ts +69 -0
  392. package/tests/shell-render-cache.test.ts +129 -0
  393. package/tests/shell-sidebar-layout.test.ts +27 -2
  394. package/tests/shell-todo.test.ts +132 -11
  395. package/tests/shell-usage-view.test.ts +1 -1
  396. package/tests/shell-usage.test.ts +35 -0
  397. package/tests/short-check-routing-contract.test.ts +49 -0
  398. package/tests/skill-registry-watcher.test.ts +119 -0
  399. package/tests/skill-registry.test.ts +311 -5
  400. package/tests/startup-banner.test.ts +117 -3
  401. package/tests/startup-home-repo.test.ts +170 -0
  402. package/tests/status-timing-diagnostics.test.ts +283 -0
  403. package/tests/support/orchestrator-modules.ts +19 -0
  404. package/tests/task-size-routing-contract.test.ts +241 -0
  405. package/tests/telemetry-trigger.test.ts +27 -1
  406. package/tests/test-strength-contract.test.ts +124 -0
  407. package/tests/verify-agent-contract.test.ts +90 -0
  408. package/tests/verify-first-launch-contract.test.ts +139 -0
  409. package/tests/verify-package-files.test.ts +56 -0
  410. package/tests/vim-editor-adapter.test.ts +47 -21
  411. package/tests/visual-customization-policy.test.ts +32 -3
  412. package/tests/visual-customize-view.test.ts +288 -10
  413. package/tests/writer-edit-surface-scope.test.ts +59 -2
  414. package/tests/writer-surfaces.test.ts +199 -0
  415. package/tests/yolo-customize.test.ts +18 -3
  416. package/tests/yolo-mode-runtime.test.ts +37 -10
  417. package/tests/yolo-mode.test.ts +38 -5
  418. package/tests/yolo-session-policy.test.ts +1 -1
  419. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
  420. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
  421. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
  422. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  423. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
  424. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
  425. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
@@ -15,14 +15,18 @@ interface Token {
15
15
  }
16
16
  const separators = new Set([";", "&&", "||", "|", "&", "\n", "(", ")"]);
17
17
 
18
- /** Keep quoted arguments intact so harmless prose is never treated as a command. */
19
- function tokenize(command: string): Token[] {
20
- const tokens: Token[] = [];
21
- // Literal, named heredocs are data, not executable shell segments.
22
- command = command.replace(/<<-?\s*(['"]?)([A-Za-z_]\w*)\1[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g, (raw) => {
18
+ /** Literal, named heredocs are data, not executable shell segments; offsets are preserved. */
19
+ function blankHeredocs(command: string): string {
20
+ return command.replace(/<<-?\s*(['"]?)([A-Za-z_]\w*)\1[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g, (raw) => {
23
21
  const headerLength = raw.indexOf("\n") + 1;
24
22
  return raw.slice(0, headerLength) + " ".repeat(raw.length - headerLength);
25
23
  });
24
+ }
25
+
26
+ /** Keep quoted arguments intact so harmless prose is never treated as a command. */
27
+ function tokenize(command: string, blank = true): Token[] {
28
+ const tokens: Token[] = [];
29
+ if (blank) command = blankHeredocs(command);
26
30
  const pattern = /(?:"(?:\\.|[^"\\])*"|'[^']*'|\\.|[^\s;&|()'"\\])+|&&|\|\||[;&|()\n]/g;
27
31
  for (const match of command.matchAll(pattern)) {
28
32
  const raw = match[0];
@@ -38,6 +42,100 @@ function tokenize(command: string): Token[] {
38
42
  }
39
43
  const executable = (value: string) => value.split("/").at(-1) ?? value;
40
44
 
45
+ const WRAPPER_VALUE_OPTIONS: Record<string, readonly string[]> = {
46
+ env: ["-u", "--unset", "-C", "--chdir"],
47
+ sudo: ["-u", "-g", "-h", "-p", "-C", "--user", "--group", "--host", "--prompt", "--chdir"],
48
+ timeout: ["-s", "--signal", "-k", "--kill-after"],
49
+ xargs: ["-n", "-I", "-P", "--max-args", "--replace", "--max-procs"],
50
+ };
51
+ const WRAPPERS = ["env", "sudo", "command", "exec", "nohup", "timeout", "xargs"];
52
+
53
+ /** Index of the executable token after assignments and familiar wrappers. */
54
+ function headCursor(segment: readonly Token[]): number {
55
+ let cursor = 0;
56
+ while (cursor < segment.length) {
57
+ const value = segment[cursor].value;
58
+ if (/^[A-Za-z_][A-Za-z_0-9]*=/.test(value)) {
59
+ cursor++;
60
+ continue;
61
+ }
62
+ const name = executable(value);
63
+ if (!WRAPPERS.includes(name)) break;
64
+ cursor++;
65
+ while (cursor < segment.length && segment[cursor].value.startsWith("-")) {
66
+ const flag = segment[cursor++].value;
67
+ if (flag === "--") break;
68
+ if (WRAPPER_VALUE_OPTIONS[name]?.includes(flag)) cursor++;
69
+ }
70
+ if (name === "timeout") cursor++;
71
+ }
72
+ return cursor;
73
+ }
74
+
75
+ // gentle-shell#2174: commands whose arguments are searched, printed or viewed
76
+ // text, never executed and never fed to an executor. `sed` is absent on
77
+ // purpose: GNU sed executes scripts through `e` with any delimiter or address.
78
+ // The exemption applies only when EVERY segment head is in DATA_COMMANDS or
79
+ // INERT_COMMANDS, so an unknown consumer (`| at now`, `&& ./x.sh`) keeps the
80
+ // lexical guard: a missing entry costs a dialog, never a bypass.
81
+ const DATA_COMMANDS = new Set([
82
+ "grep", "egrep", "fgrep", "rg", "ag", "ack", "echo", "printf", "cat", "head", "tail", "less", "more",
83
+ "wc", "sort", "uniq", "cut", "tr", "jq", "diff", "ls", "file", "stat",
84
+ ]);
85
+ const INERT_COMMANDS = new Set(["cd", "pwd", "true", "false", "test", "[", "mkdir", "touch"]);
86
+ const HEREDOC_RE = /<<-?\s*(['"]?)([A-Za-z_]\w*)\1[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g;
87
+
88
+ /**
89
+ * Byte ranges of the command that are plain arguments of a known data command
90
+ * (gentle-shell#2174). A guarded or denied pattern matching only inside these
91
+ * ranges is prose, not an operation. Returns no ranges, keeping every lexical
92
+ * match, when the command is opaque: command or process substitution, an
93
+ * interpreter or remote shell anywhere, or quoting the tokenizer cannot
94
+ * account for.
95
+ */
96
+ export function dataArgumentRanges(command: string): Array<readonly [number, number]> {
97
+ if (/\$\(|`|[<>]\(/.test(command)) return [];
98
+ // A heredoc counts only when its `<<` starts an unquoted token of the raw
99
+ // command; quoted text such as `echo '<<x'` must not blank following lines.
100
+ const rawStarts = new Set(tokenize(command, false).filter((token) => token.raw.startsWith("<<")).map((token) => token.index));
101
+ const heredocs = [...command.matchAll(HEREDOC_RE)];
102
+ if (heredocs.some((heredoc) => !rawStarts.has(heredoc.index))) return [];
103
+ const blanked = blankHeredocs(command);
104
+ const tokens = tokenize(command);
105
+ const covered = new Uint8Array(blanked.length);
106
+ for (const token of tokens) covered.fill(1, token.index, token.index + token.raw.length);
107
+ for (let index = 0; index < blanked.length; index++) {
108
+ if (!covered[index] && !/\s/.test(blanked[index])) return [];
109
+ }
110
+ const ranges: Array<readonly [number, number]> = [];
111
+ let start = 0;
112
+ for (let end = 0; end <= tokens.length; end++) {
113
+ if (end < tokens.length && !tokens[end].operator) continue;
114
+ const segment = tokens.slice(start, end);
115
+ start = end + 1;
116
+ if (segment.length === 0) continue;
117
+ // Only plain assignments, optionally after a flagless `env`, may precede
118
+ // the head; every other wrapper or wrapper flag can change what runs.
119
+ let cursor = 0;
120
+ if (segment[0].value === "env") cursor++;
121
+ while (cursor < segment.length && /^[A-Za-z_][A-Za-z_0-9]*=/.test(segment[cursor].value)) cursor++;
122
+ const head = segment[cursor];
123
+ if (head === undefined || head.value !== head.raw) return [];
124
+ const name = head.value;
125
+ if (INERT_COMMANDS.has(name)) continue;
126
+ if (!DATA_COMMANDS.has(name)) return [];
127
+ ranges.push([head.index + head.raw.length, tokens[end]?.index ?? command.length]);
128
+ }
129
+ // A heredoc body belongs to the segment of its `<<` header, which the
130
+ // header's trailing newline would otherwise split off. Only the body is
131
+ // covered: the rest of the header line can hold further commands.
132
+ for (const heredoc of heredocs) {
133
+ const bodyStart = heredoc.index + heredoc[0].indexOf("\n") + 1;
134
+ if (ranges.some(([from, to]) => heredoc.index >= from && heredoc.index < to)) ranges.push([bodyStart, heredoc.index + heredoc[0].length]);
135
+ }
136
+ return ranges;
137
+ }
138
+
41
139
  function sqlDestroys(sql: string): boolean {
42
140
  // Mask quoted identifiers as non-keyword atoms, and remove comments and
43
141
  // strings. A table named "where" is not a predicate or a statement boundary.
@@ -60,30 +158,8 @@ export function recognizeDestructiveCommands(command: string, depth = 0): Destru
60
158
  for (let end = 0; end <= tokens.length; end++) {
61
159
  if (end < tokens.length && !tokens[end].operator) continue;
62
160
  const segment = tokens.slice(start, end);
63
- let cursor = 0;
64
161
  // Assignments and familiar executable wrappers preserve command position.
65
- while (cursor < segment.length) {
66
- const value = segment[cursor].value;
67
- if (/^[A-Za-z_][A-Za-z_0-9]*=/.test(value)) {
68
- cursor++;
69
- continue;
70
- }
71
- const name = executable(value);
72
- if (!["env", "sudo", "command", "exec", "nohup", "timeout", "xargs"].includes(name)) break;
73
- const valueOptions: Record<string, readonly string[]> = {
74
- env: ["-u", "--unset", "-C", "--chdir"],
75
- sudo: ["-u", "-g", "-h", "-p", "-C", "--user", "--group", "--host", "--prompt", "--chdir"],
76
- timeout: ["-s", "--signal", "-k", "--kill-after"],
77
- xargs: ["-n", "-I", "-P", "--max-args", "--replace", "--max-procs"],
78
- };
79
- cursor++;
80
- while (cursor < segment.length && segment[cursor].value.startsWith("-")) {
81
- const flag = segment[cursor++].value;
82
- if (flag === "--") break;
83
- if (valueOptions[name]?.includes(flag)) cursor++;
84
- }
85
- if (name === "timeout") cursor++;
86
- }
162
+ const cursor = headCursor(segment);
87
163
  const head = segment[cursor];
88
164
  const name = executable(head?.value ?? "");
89
165
  const args = segment.slice(cursor + 1).map((token) => token.value);
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
- import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
+ import { dirname, isAbsolute, join, posix, relative, resolve, win32 } from "node:path";
5
5
  import {
6
6
  GENTLE_AI_INSTALL_METHOD,
7
7
  GENTLE_AI_WINDOWS_SOURCE_MODULE,
@@ -13,6 +13,8 @@ import {
13
13
  isWindowsGoSumdbSourceTarget,
14
14
  resolveGentleAiReleaseAsset,
15
15
  } from "../scripts/gentle-ai-installer.mjs";
16
+ import { engramPinVersion } from "../scripts/installer-downloads.mjs";
17
+ import { bundledGentlePiConfigHome, bundledPiConfigDir, bundledPiConfigParent, runningBundledPrefix } from "./gentle-shell-bundled.ts";
16
18
  import { fileURLToPath } from "node:url";
17
19
 
18
20
  export const GENTLE_AI_BINARY_MISSING_CODE = "package-local-binary-missing";
@@ -45,6 +47,84 @@ export function gentleAiBinaryPath(
45
47
  );
46
48
  }
47
49
 
50
+ // ---------------------------------------------------------------------------
51
+ // Bundled isolation (S9). The bundled launcher (bin/gentle-shell.mjs) gives Pi
52
+ // and setup GENTLE_SHELL_PREFIX, its prefix. Every gentle-ai run a bundled
53
+ // gentle-pi starts gets Gentle AI's own variables (gentle-ai #5462): its state,
54
+ // backups and telemetry under <prefix>/gentle-ai, the gentle-pi config folder
55
+ // <prefix>/config/gentle-ai for its persona and background subagent files
56
+ // (and, for Gentle AI 4.0.0, GENTLE_PI_CONFIG_HOME=<prefix>/config), the pinned
57
+ // Engram in runtime/ with no Engram acquisition or dependency probe, and no
58
+ // self-update. They are never put in Pi's own environment: the agent's shell
59
+ // inherits it, and a gentle-ai the user runs there stays the user's. A
60
+ // gentle-ai release without #5462 ignores them.
61
+ // ---------------------------------------------------------------------------
62
+
63
+ export interface BundledGentleAiPaths {
64
+ /** GENTLE_AI_HOME. */
65
+ home: string;
66
+ /** GENTLE_PI_CONFIG_HOME for Gentle AI only: the parent Gentle AI 4.0.0 appends gentle-ai to. */
67
+ piConfigParent: string;
68
+ /** GENTLE_AI_PI_CONFIG_DIR: gentle-pi's own bundled config home, <piConfigParent>/gentle-ai. */
69
+ piConfig: string;
70
+ /** The Engram scripts/bundled-install.mjs publishes (prefixLayout's `engram`). */
71
+ engram: string;
72
+ }
73
+
74
+ export function bundledGentleAiPaths(prefix: string, platform: string = process.platform): BundledGentleAiPaths {
75
+ const path = platform === "win32" ? win32 : posix;
76
+ return {
77
+ home: path.join(prefix, "gentle-ai"),
78
+ piConfigParent: bundledPiConfigParent(prefix, platform),
79
+ piConfig: bundledPiConfigDir(prefix, platform),
80
+ engram: path.join(prefix, "runtime", `engram-${engramPinVersion}`, platform === "win32" ? "engram.exe" : "engram"),
81
+ };
82
+ }
83
+
84
+ /**
85
+ * `env` plus GENTLE_AI_HOME, GENTLE_AI_PI_CONFIG_DIR, GENTLE_AI_ENGRAM_BIN
86
+ * (only when the pinned Engram is published), GENTLE_AI_NO_SELF_UPDATE=1 and
87
+ * GENTLE_PI_CONFIG_HOME (the parent of GENTLE_AI_PI_CONFIG_DIR)
88
+ * when this gentle-pi runs from the valid bundled install GENTLE_SHELL_PREFIX
89
+ * names (runningBundledPrefix). Otherwise `env` itself, unchanged: a gentle-pi
90
+ * installed elsewhere (the user's own, started from the agent's shell, which
91
+ * inherits the variable) keeps its own Gentle AI.
92
+ */
93
+ export function gentleAiIsolationEnv(
94
+ env: NodeJS.ProcessEnv,
95
+ options: { packageRoot?: string; platform?: string } = {},
96
+ ): NodeJS.ProcessEnv {
97
+ const platform = options.platform ?? process.platform;
98
+ const prefix = runningBundledPrefix(env, { packageRoot: options.packageRoot, platform });
99
+ if (prefix === undefined) return env;
100
+ const paths = bundledGentleAiPaths(prefix, platform);
101
+ let engram = false;
102
+ try {
103
+ engram = lstatSync(paths.engram).isFile();
104
+ } catch {
105
+ // Not published (a prefix from before Engram shipped): Gentle AI gets none.
106
+ }
107
+ // Gentle AI 4.0.0 ignores GENTLE_AI_ENGRAM_BIN and looks `engram` up on PATH
108
+ // first, then falls back to Homebrew (`brew tap`, #2105): the pinned one goes first.
109
+ const pathKey = Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? (platform === "win32" ? "Path" : "PATH");
110
+ const pathModule = platform === "win32" ? win32 : posix;
111
+ const path = engram
112
+ ? { [pathKey]: [pathModule.dirname(paths.engram), ...(env[pathKey] ?? "").split(pathModule.delimiter).filter((entry) => entry.length > 0)].join(pathModule.delimiter) }
113
+ : {};
114
+ return {
115
+ ...env,
116
+ ...path,
117
+ GENTLE_AI_HOME: paths.home,
118
+ GENTLE_AI_PI_CONFIG_DIR: paths.piConfig,
119
+ ...(engram ? { GENTLE_AI_ENGRAM_BIN: paths.engram } : {}),
120
+ GENTLE_AI_NO_SELF_UPDATE: "1",
121
+ // Gentle AI 4.0.0 knows no GENTLE_AI_PI_CONFIG_DIR: it writes gentle-pi's
122
+ // background-subagents policy to <GENTLE_PI_CONFIG_HOME>/gentle-ai, the same
123
+ // folder. Only these Gentle AI processes get it, never Pi or its shell.
124
+ GENTLE_PI_CONFIG_HOME: paths.piConfigParent,
125
+ };
126
+ }
127
+
48
128
  function sha256(value: Buffer): string {
49
129
  return createHash("sha256").update(value).digest("hex");
50
130
  }
@@ -55,7 +135,8 @@ function sha256(value: Buffer): string {
55
135
  // Two explicit activation paths, in precedence order:
56
136
  // 1. GENTLE_PI_GENTLE_AI_DEV_BINARY (session override, absolute path), then
57
137
  // 2. the persistent registration file at
58
- // <GENTLE_PI_CONFIG_HOME|~/.pi/gentle-ai>/dev-binary.json with the strict
138
+ // <config home>/dev-binary.json (a bundled prefix's gentle-pi, else
139
+ // GENTLE_PI_CONFIG_HOME, else ~/.pi/gentle-ai) with the strict
59
140
  // shape {"schema":"gentle-pi.dev-binary/v1","path":"<absolute path>"}.
60
141
  //
61
142
  // The registration deliberately pins no digest: it is the unpinned field-test
@@ -113,7 +194,7 @@ function ambientDevBinaryEnvironment(): GentleAiDevBinaryEnvironment {
113
194
  }
114
195
 
115
196
  export function gentleAiDevBinaryRegistrationPath(environment: GentleAiDevBinaryEnvironment = ambientDevBinaryEnvironment()): string {
116
- const configHome = environment.env.GENTLE_PI_CONFIG_HOME ?? join(environment.home, ".pi", "gentle-ai");
197
+ const configHome = bundledGentlePiConfigHome(environment.env) ?? environment.env.GENTLE_PI_CONFIG_HOME ?? join(environment.home, ".pi", "gentle-ai");
117
198
  return join(configHome, "dev-binary.json");
118
199
  }
119
200
 
@@ -268,6 +349,30 @@ function sameFile(before: ReturnType<typeof lstatSync>, after: ReturnType<typeof
268
349
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs;
269
350
  }
270
351
 
352
+ // Reading and hashing the whole binary on every native call is expensive, and
353
+ // on Windows antivirus scans each read. The digest is reused only while the
354
+ // file keeps its full identity; any rewrite changes ctime, which callers cannot
355
+ // set, so a replaced binary is always hashed again. Keyed per reader so an
356
+ // injected reader never sees another reader's digest.
357
+ const binaryDigestCache = new WeakMap<(path: string) => Buffer, Map<string, { readonly identity: string; readonly sha256: string }>>();
358
+
359
+ function binaryIdentity(path: string): string {
360
+ const details = lstatSync(path, { bigint: true });
361
+ return [details.dev, details.ino, details.size, details.mtimeNs, details.ctimeNs].join(":");
362
+ }
363
+
364
+ function binaryDigest(path: string, readBinary: (path: string) => Buffer): string {
365
+ let entries = binaryDigestCache.get(readBinary);
366
+ if (entries === undefined) binaryDigestCache.set(readBinary, entries = new Map());
367
+ const identity = binaryIdentity(path);
368
+ const cached = entries.get(path);
369
+ if (cached?.identity === identity) return cached.sha256;
370
+ const digest = sha256(readBinary(path));
371
+ if (binaryIdentity(path) === identity) entries.set(path, { identity, sha256: digest });
372
+ else entries.delete(path);
373
+ return digest;
374
+ }
375
+
271
376
  export function resolveGentleAiBinary(
272
377
  packageRoot = dirname(dirname(fileURLToPath(import.meta.url))),
273
378
  platform = process.platform,
@@ -296,7 +401,7 @@ export function resolveGentleAiBinary(
296
401
  const beforeManifest = lstatSync(manifestPath);
297
402
  const manifestContents = readFileSync(manifestPath, "utf8");
298
403
  const manifest = JSON.parse(manifestContents) as Record<string, unknown>;
299
- const binarySha256 = sha256(readBinary(binaryPath));
404
+ const binarySha256 = binaryDigest(binaryPath, readBinary);
300
405
  const expected = expectedRuntimeManifest(platform, binarySha256, manifest);
301
406
  if (!isCanonicalManifest(manifestContents, manifest, expected)) throw new Error("invalid runtime integrity manifest");
302
407
  const afterBinary = lstatSync(binaryPath);