gentle-pi 4.2.0 → 4.4.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 (334) hide show
  1. package/README.md +340 -138
  2. package/assets/agents/gentle-ai-explore.md +1 -0
  3. package/assets/agents/gentle-ai-verify.md +6 -4
  4. package/assets/agents/gentle-ai-worker.md +7 -1
  5. package/assets/design-canvas/canvas.css +648 -0
  6. package/assets/design-canvas/canvas.js +1497 -0
  7. package/assets/design-canvas/index.html +88 -0
  8. package/assets/design-canvas/inspector.js +290 -0
  9. package/assets/orchestrator-delegation-reference.md +163 -0
  10. package/assets/orchestrator-delegation.md +25 -176
  11. package/assets/orchestrator-tracking.md +1 -1
  12. package/assets/orchestrator-verification.md +14 -8
  13. package/assets/orchestrator-writer.md +5 -1
  14. package/assets/orchestrator.md +11 -11
  15. package/bin/engram-guard +1 -1
  16. package/bin/gentle-shell.mjs +237 -139
  17. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
  18. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/README.md +7 -0
  19. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/manifest.json +11 -2
  20. package/contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md +92 -0
  21. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-capabilities.baseline.json +8 -1
  22. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-roles.baseline.json +1 -1
  23. package/docs/assets/diagrams/agent-orchestration.svg +54 -15
  24. package/docs/assets/diagrams/gentleman-workflow.svg +69 -11
  25. package/docs/assets/diagrams/measured-routing.svg +49 -0
  26. package/docs/assets/diagrams/native-review.svg +52 -12
  27. package/docs/assets/diagrams/odd-workflow.svg +128 -66
  28. package/docs/assets/diagrams/permission-modes.svg +51 -0
  29. package/docs/assets/features/audio-notifications.png +0 -0
  30. package/docs/assets/features/command-palette.png +0 -0
  31. package/docs/bundled-install.md +65 -30
  32. package/docs/delegated-verification.md +16 -8
  33. package/docs/gentle-agents-activity.md +16 -6
  34. package/docs/gentle-music.md +61 -0
  35. package/docs/gentle-shell-uninstall.md +8 -1
  36. package/docs/gentle-shell.md +80 -9
  37. package/docs/install-wizard.md +37 -6
  38. package/docs/permission-modes.md +8 -3
  39. package/docs/readme-reference.md +120 -28
  40. package/docs/session-profile-format.md +26 -0
  41. package/docs/voice.md +259 -0
  42. package/docs/yolo-mode.md +38 -10
  43. package/extensions/ask-user-choice.ts +4 -1
  44. package/extensions/codegraph-tools.ts +68 -6
  45. package/extensions/gentle-agents.ts +270 -48
  46. package/extensions/gentle-ai.ts +490 -113
  47. package/extensions/gentle-design.ts +158 -0
  48. package/extensions/gentle-herdr-activity.ts +2 -3
  49. package/extensions/gentle-music.ts +289 -0
  50. package/extensions/gentle-shell.ts +359 -85
  51. package/extensions/gentle-todo.ts +43 -9
  52. package/extensions/gentle-voice.ts +11 -0
  53. package/extensions/quiet-tools.ts +1 -1
  54. package/extensions/session-scratch.ts +384 -0
  55. package/extensions/skill-registry.ts +63 -8
  56. package/extensions/startup-banner.ts +2 -3
  57. package/lib/agent-home.ts +8 -3
  58. package/lib/agent-profiles.ts +26 -0
  59. package/lib/agents-config.ts +35 -0
  60. package/lib/agents-protocol.ts +60 -1
  61. package/lib/agents-runner.ts +107 -9
  62. package/lib/agents-session-transport.ts +101 -6
  63. package/lib/agents-view.ts +3 -1
  64. package/lib/agents-widget.ts +14 -1
  65. package/lib/auto-permission-memory.ts +48 -18
  66. package/lib/auto-permission-runtime.ts +21 -6
  67. package/lib/bounded-writer-admission.ts +51 -0
  68. package/lib/clipboard-image.ts +147 -0
  69. package/lib/command-palette-catalog.ts +2 -4
  70. package/lib/commit-slice-signal.ts +100 -0
  71. package/lib/context-backstop.ts +120 -0
  72. package/lib/delivered-test-lint.ts +80 -0
  73. package/lib/design-canvas-server.ts +947 -0
  74. package/lib/design-comments.ts +437 -0
  75. package/lib/design-handoffs.ts +313 -0
  76. package/lib/design-open-browser.ts +58 -0
  77. package/lib/design-sources.ts +120 -0
  78. package/lib/destructive-command-guard.ts +120 -9
  79. package/lib/external-editor.ts +31 -0
  80. package/lib/gauge-theme.ts +146 -0
  81. package/lib/gentle-ai-binary.ts +28 -7
  82. package/lib/gentle-design.ts +513 -0
  83. package/lib/gentle-shell-bundled.ts +146 -36
  84. package/lib/gentle-shell-import-pi.ts +233 -0
  85. package/lib/gentle-shell-launcher.ts +154 -15
  86. package/lib/gentle-shell-startup-update.ts +61 -9
  87. package/lib/gentle-shell-uninstall.ts +51 -2
  88. package/lib/herdr-activity.ts +24 -0
  89. package/lib/image-attachments.ts +150 -0
  90. package/lib/inprocess-reviewer.ts +33 -2
  91. package/lib/nan-provider.ts +3 -0
  92. package/lib/native-review-cli.ts +20 -5
  93. package/lib/notification-audio-native.ts +149 -13
  94. package/lib/notification-audio.ts +4 -0
  95. package/lib/notification-pulse-worker.ts +82 -18
  96. package/lib/notification-service.ts +5 -1
  97. package/lib/orchestrator-discovery.ts +5 -1
  98. package/lib/orchestrator-presence.ts +87 -8
  99. package/lib/orchestrator-scope.ts +31 -3
  100. package/lib/orchestrator-state.ts +11 -3
  101. package/lib/permission-mode-policy.ts +2 -2
  102. package/lib/provider-contract-bundle.ts +99 -53
  103. package/lib/review-candidate-view-owner.ts +29 -19
  104. package/lib/review-candidate-view.ts +36 -2
  105. package/lib/review-host-relay.ts +11 -2
  106. package/lib/review-reminder-receipt.ts +24 -1
  107. package/lib/review-risk-assessment.ts +43 -38
  108. package/lib/review-session-standing-permission.ts +3 -1
  109. package/lib/session-profile-append-controller.ts +713 -0
  110. package/lib/session-profile-disk-reader.ts +182 -0
  111. package/lib/session-profile-freeze.ts +30 -1
  112. package/lib/session-profile-persistence.ts +15 -7
  113. package/lib/session-scratch.ts +523 -0
  114. package/lib/session-worktree-registry.ts +7 -0
  115. package/lib/shell-bar.ts +29 -28
  116. package/lib/shell-changes-view.ts +80 -13
  117. package/lib/shell-changes.ts +81 -5
  118. package/lib/shell-gauge.ts +49 -6
  119. package/lib/shell-prompt.ts +4 -4
  120. package/lib/shell-sidebar-layout.ts +26 -6
  121. package/lib/shell-todo.ts +24 -5
  122. package/lib/shell-usage-view.ts +5 -1
  123. package/lib/shell-usage.ts +7 -7
  124. package/lib/verifier-admission.ts +30 -0
  125. package/lib/vim-editor-adapter.ts +11 -8
  126. package/lib/voice-artifacts.ts +188 -0
  127. package/lib/voice-command.ts +576 -0
  128. package/lib/voice-install.ts +455 -0
  129. package/lib/voice-platform.ts +99 -0
  130. package/lib/voice-push-to-talk.ts +262 -0
  131. package/lib/voice-session.ts +460 -0
  132. package/lib/voice-settings.ts +50 -0
  133. package/lib/voice-ui.ts +158 -0
  134. package/lib/yolo-session-policy.ts +58 -22
  135. package/lib/yolo-temp-scratch.ts +69 -0
  136. package/package.json +1 -2
  137. package/runtime/gentle-ai-binary.mjs +28 -7
  138. package/runtime/gentle-shell-bundled.mjs +145 -35
  139. package/runtime/gentle-shell-import-pi.mjs +234 -0
  140. package/runtime/gentle-shell-launcher.mjs +152 -13
  141. package/runtime/gentle-shell-startup-update.mjs +61 -9
  142. package/runtime/gentle-shell-uninstall.mjs +51 -2
  143. package/runtime/native-review-cli.mjs +19 -4
  144. package/runtime/review-risk-assessment.mjs +43 -38
  145. package/scripts/build-runtime-modules.mjs +1 -0
  146. package/scripts/bundled-engram-migration.mjs +119 -0
  147. package/scripts/gentle-ai-installer.mjs +10 -10
  148. package/scripts/installer-downloads.mjs +7 -7
  149. package/scripts/installer-preflight.mjs +5 -3
  150. package/scripts/installer-server.mjs +14 -4
  151. package/scripts/installer-windows.mjs +37 -7
  152. package/scripts/main-channel.mjs +94 -19
  153. package/scripts/pi-update.mjs +34 -6
  154. package/scripts/run-test-suite.mjs +5 -0
  155. package/scripts/verify-package-files.mjs +43 -13
  156. package/scripts/voice-worker.cjs +476 -0
  157. package/skills/design/SKILL.md +110 -0
  158. package/skills/gentle-ai/SKILL.md +3 -3
  159. package/skills/work-unit-commits/SKILL.md +5 -3
  160. package/tests/agent-profiles.test.ts +24 -0
  161. package/tests/agent-risk-escalation.test.ts +37 -10
  162. package/tests/agents-config.test.ts +61 -0
  163. package/tests/agents-fake-child.ts +8 -1
  164. package/tests/agents-protocol.test.ts +32 -0
  165. package/tests/agents-runner.test.ts +485 -7
  166. package/tests/agents-session-transport.test.ts +53 -3
  167. package/tests/ask-user-choice.test.ts +30 -0
  168. package/tests/auto-mode.test.ts +17 -0
  169. package/tests/auto-permission-memory-diagnostics.test.ts +56 -0
  170. package/tests/auto-permission-runtime.test.ts +85 -1
  171. package/tests/autonomous-guard.test.ts +142 -0
  172. package/tests/bounded-writer-admission.test.ts +11 -3
  173. package/tests/bundled-install.test.ts +3 -3
  174. package/tests/card-content-policy.test.ts +19 -9
  175. package/tests/child-safety.test.ts +39 -0
  176. package/tests/clipboard-image.test.ts +114 -0
  177. package/tests/codegraph-tools.test.ts +123 -2
  178. package/tests/command-palette.test.ts +15 -5
  179. package/tests/commit-slice-signal.test.ts +162 -0
  180. package/tests/context-backstop.test.ts +225 -0
  181. package/tests/delegate-for-reason-contract.test.ts +92 -24
  182. package/tests/delivered-test-lint.test.ts +105 -0
  183. package/tests/design-canvas-assets.test.ts +710 -0
  184. package/tests/design-canvas-server.test.ts +1096 -0
  185. package/tests/design-comments.test.ts +201 -0
  186. package/tests/design-handoffs.test.ts +282 -0
  187. package/tests/design-open-browser.test.ts +70 -0
  188. package/tests/design-sources.test.ts +132 -0
  189. package/tests/destructive-command-guard.test.ts +85 -1
  190. package/tests/engram-safety.test.ts +132 -57
  191. package/tests/external-editor.test.ts +84 -0
  192. package/tests/fixtures/primary-session-prompt/gentleman.golden.md +143 -0
  193. package/tests/fixtures/provider-contract-bundle/v1.4.0/README.md +29 -0
  194. package/tests/fixtures/provider-contract-bundle/v1.4.0/manifest.json +83 -0
  195. package/tests/fixtures/provider-contract-bundle/v1.4.0/orchestration/pi.md +70 -0
  196. package/tests/fixtures/provider-contract-bundle/v1.4.0/routing/pi.md +92 -0
  197. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/lens.schema.json +16 -0
  198. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/refuter.schema.json +1 -0
  199. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/targeted-validator.schema.json +1 -0
  200. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/lens.json +1 -0
  201. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/refuter.json +1 -0
  202. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/targeted-validator.json +1 -0
  203. package/tests/gauge-theme.test.ts +120 -0
  204. package/tests/gentle-agents.test.ts +645 -21
  205. package/tests/gentle-ai-binary.test.ts +1 -1
  206. package/tests/gentle-ai-child-guards.test.ts +61 -0
  207. package/tests/gentle-ai-doctor-herdr.test.ts +94 -0
  208. package/tests/gentle-ai-installer.test.ts +51 -51
  209. package/tests/gentle-ai.test.ts +292 -13
  210. package/tests/gentle-design.test.ts +1313 -0
  211. package/tests/gentle-music.test.ts +335 -0
  212. package/tests/gentle-shell-bin.test.ts +163 -0
  213. package/tests/gentle-shell-bundled.test.ts +392 -176
  214. package/tests/gentle-shell-import-pi.test.ts +262 -0
  215. package/tests/gentle-shell-launcher.test.ts +107 -2
  216. package/tests/gentle-shell-startup-update.test.ts +77 -1
  217. package/tests/gentle-shell-uninstall.test.ts +89 -0
  218. package/tests/gentle-shell.test.ts +225 -17
  219. package/tests/gentle-todo.test.ts +190 -0
  220. package/tests/headless-delivery-question.test.ts +58 -0
  221. package/tests/herdr-activity.test.ts +34 -1
  222. package/tests/image-attachments.test.ts +119 -0
  223. package/tests/image-paste-chips.test.ts +236 -0
  224. package/tests/inprocess-reviewer.test.ts +55 -2
  225. package/tests/installer-bundles.test.ts +12 -6
  226. package/tests/installer-posix-bootstrap.test.ts +8 -8
  227. package/tests/installer-preflight.test.ts +16 -0
  228. package/tests/installer-server.test.ts +10 -2
  229. package/tests/installer-windows-bootstrap.test.ts +94 -7
  230. package/tests/installer-windows-diagnostics.test.ts +111 -0
  231. package/tests/main-channel-upgrade.test.ts +232 -2
  232. package/tests/main-channel.test.ts +140 -21
  233. package/tests/nan-provider.test.ts +36 -0
  234. package/tests/native-review-capability-contract.test.ts +17 -4
  235. package/tests/native-review-parity.test.ts +23 -2
  236. package/tests/no-self-pause-contract.test.ts +5 -2
  237. package/tests/notification-audio-native.test.ts +128 -9
  238. package/tests/notification-customize.test.ts +17 -5
  239. package/tests/odd-integration.test.ts +5 -1
  240. package/tests/odd-routing-canonical-ratchet.test.ts +128 -162
  241. package/tests/odd-routing-contract.test.ts +115 -30
  242. package/tests/orchestrator-budget.test.ts +22 -8
  243. package/tests/orchestrator-discovery.test.ts +29 -0
  244. package/tests/orchestrator-presence.test.ts +100 -2
  245. package/tests/orchestrator-rdd-ownership.test.ts +3 -1
  246. package/tests/orchestrator-scope.test.ts +80 -0
  247. package/tests/orchestrator-state.test.ts +29 -0
  248. package/tests/orchestrator-status-publication-contract.test.ts +12 -8
  249. package/tests/package-files.test.ts +34 -0
  250. package/tests/package-manifest.test.ts +24 -20
  251. package/tests/permission-mode-policy.test.ts +10 -1
  252. package/tests/pi-update-launcher.test.ts +21 -2
  253. package/tests/pi-update.test.ts +99 -4
  254. package/tests/primary-session-prompt.test.ts +101 -0
  255. package/tests/provider-contract-bundle.test.ts +118 -0
  256. package/tests/provider-contract-mirror.test.ts +24 -0
  257. package/tests/quiet-tool-rendering.test.ts +23 -0
  258. package/tests/rdd-aware-verification-contract.test.ts +29 -11
  259. package/tests/rdd-status-line.test.ts +79 -0
  260. package/tests/review-agent-end-preflight.test.ts +6 -0
  261. package/tests/review-candidate-view.test.ts +49 -0
  262. package/tests/review-contract-prompt.test.ts +5 -2
  263. package/tests/review-controller-native-routing.test.ts +14 -1
  264. package/tests/review-controller.test.ts +1 -1
  265. package/tests/review-host-relay-routing.test.ts +147 -3
  266. package/tests/review-host-relay.test.ts +9 -2
  267. package/tests/review-json-arguments.test.ts +20 -3
  268. package/tests/review-loadout-rdd-off-sdk.test.ts +258 -0
  269. package/tests/review-owner-identity.test.ts +132 -0
  270. package/tests/review-relay-transport-agent.test.ts +26 -0
  271. package/tests/review-reminder-receipt.test.ts +31 -1
  272. package/tests/review-risk-assessment.test.ts +163 -76
  273. package/tests/review-session-standing-permission-controller.test.ts +14 -1
  274. package/tests/review-session-standing-permission.test.ts +27 -1
  275. package/tests/run-test-suite.test.ts +15 -0
  276. package/tests/runtime-harness.mjs +16 -6
  277. package/tests/session-changes-shell.test.ts +1 -1
  278. package/tests/session-changes.test.ts +2 -1
  279. package/tests/session-profile-append-controller-fork.test.ts +458 -0
  280. package/tests/session-profile-append-controller-pi.test.ts +290 -0
  281. package/tests/session-profile-append-controller.test.ts +731 -0
  282. package/tests/session-profile-disk-reader-pi.test.ts +183 -0
  283. package/tests/session-profile-disk-reader.test.ts +311 -0
  284. package/tests/session-profile-freeze.test.ts +23 -0
  285. package/tests/session-scratch.test.ts +1011 -0
  286. package/tests/session-subject-routing-contract.test.ts +2 -1
  287. package/tests/session-worktree-registry.test.ts +13 -1
  288. package/tests/shell-bar.test.ts +21 -2
  289. package/tests/shell-changes-view.test.ts +137 -1
  290. package/tests/shell-changes.test.ts +148 -1
  291. package/tests/shell-gauge.test.ts +42 -0
  292. package/tests/shell-prompt.test.ts +3 -3
  293. package/tests/shell-render-cache.test.ts +134 -1
  294. package/tests/shell-sidebar-layout.test.ts +50 -0
  295. package/tests/short-check-routing-contract.test.ts +1 -1
  296. package/tests/skill-collision-prefixes.test.ts +3 -1
  297. package/tests/skill-registry-watcher.test.ts +17 -5
  298. package/tests/skill-registry.test.ts +47 -3
  299. package/tests/small-task-inline-contract.test.ts +37 -0
  300. package/tests/standalone-config-home.test.ts +324 -0
  301. package/tests/startup-banner.test.ts +5 -4
  302. package/tests/startup-home-repo.test.ts +34 -0
  303. package/tests/support/orchestrator-modules.ts +1 -0
  304. package/tests/task-size-routing-contract.test.ts +70 -10
  305. package/tests/test-scratch-contract.test.ts +102 -0
  306. package/tests/test-strength-contract.test.ts +36 -1
  307. package/tests/tool-batching-contract.test.ts +64 -0
  308. package/tests/verification-proportionality.test.ts +46 -0
  309. package/tests/verifier-admission.test.ts +154 -0
  310. package/tests/verify-agent-contract.test.ts +8 -8
  311. package/tests/voice-command.test.ts +773 -0
  312. package/tests/voice-install.test.ts +323 -0
  313. package/tests/voice-platform.test.ts +176 -0
  314. package/tests/voice-push-to-talk.test.ts +444 -0
  315. package/tests/voice-session.test.ts +434 -0
  316. package/tests/voice-settings.test.ts +49 -0
  317. package/tests/voice-ui.test.ts +97 -0
  318. package/tests/voice-worker.test.ts +445 -0
  319. package/tests/weak-model-economy-prompt.test.ts +32 -0
  320. package/tests/windows-hidden-processes.test.ts +30 -2
  321. package/tests/writer-edit-surface-scope.test.ts +3 -0
  322. package/tests/writer-feature-document-admission.test.ts +156 -0
  323. package/tests/yolo-customize.test.ts +5 -1
  324. package/tests/yolo-mode.test.ts +19 -1
  325. package/tests/yolo-session-policy-capture.test.ts +104 -0
  326. package/tests/yolo-temp-scratch.test.ts +71 -0
  327. package/scripts/mirror-odd-routing.mjs +0 -242
  328. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/orchestration/pi.md +0 -0
  329. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/lens.schema.json +0 -0
  330. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/refuter.schema.json +0 -0
  331. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  332. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/lens.json +0 -0
  333. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/refuter.json +0 -0
  334. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/targeted-validator.json +0 -0
@@ -107,6 +107,36 @@ function exists(path) {
107
107
  try { lstatSync(path); return true; }
108
108
  catch (error) { if (error.code === "ENOENT") return false; throw error; }
109
109
  }
110
+ // Failure-only diagnostics, in UTF-16 code units. Suppress entire sensitive
111
+ // streams (including continuation lines), and individual path/SID/opaque lines.
112
+ // This is conservative diagnostic filtering, not a general-purpose secret parser.
113
+ function processDiagnosticText(value, limit) {
114
+ if (typeof value !== "string") return "";
115
+ if (/(?:^|[^a-z0-9])(?:authorization|bearer|token|password|passphrase|secret|credential|api[_-]?key|access[_-]?key|cookie)(?:$|[^a-z0-9])/i.test(value)) return "[redacted]";
116
+ return value.split(/\r\n?|\n/).map(line =>
117
+ /[A-Za-z]:[\\/]|\\|(?:^|[\s"'=])\/\S+|\bS-\d-(?:\d+-)+\d+\b|unsafe:[^|]*\||[A-Za-z0-9+/=_-]{64,}|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/i.test(line) ? "[redacted]" : line
118
+ ).join("\n").slice(0, limit);
119
+ }
120
+ function prerequisiteProcessCause(result) {
121
+ const failure = result.error;
122
+ const code = processDiagnosticText(failure?.code, 64);
123
+ const details = {
124
+ status: Number.isSafeInteger(result.status) ? result.status : null,
125
+ signal: processDiagnosticText(result.signal, 32) || null,
126
+ error: failure ? {
127
+ code, name: processDiagnosticText(failure.name, 64),
128
+ // Spawn text may contain a bare private executable name, not just paths.
129
+ message: /^E[A-Z0-9_]+$/.test(code) ? `Windows prerequisite process error: ${code}` : "Windows prerequisite process error",
130
+ errno: Number.isSafeInteger(failure.errno) ? failure.errno : processDiagnosticText(failure.errno, 64),
131
+ } : null,
132
+ stdout: processDiagnosticText(result.stdout, 1024), stderr: processDiagnosticText(result.stderr, 1024),
133
+ };
134
+ const cause = Object.assign(new Error("Windows prerequisite process diagnostics"), details);
135
+ // Existing evidence consumers read cause.stack only; omit local callsites and
136
+ // never attach the raw spawn Error (which carries command/path/argv fields).
137
+ cause.stack = `Error: ${cause.message}\n${JSON.stringify(details)}`.slice(0, 8192);
138
+ return cause;
139
+ }
110
140
  export function windowsProcessCheck(command, args, env) {
111
141
  // Owned direct children only, not a tree/global kill. Descendant-held stdio
112
142
  // is outside this guarantee. Never execute .cmd through a command interpreter.
@@ -114,7 +144,7 @@ export function windowsProcessCheck(command, args, env) {
114
144
  // A neutral cwd: inside a project (the bundle itself), pnpm 11 reports, and may
115
145
  // fetch, the version that project's packageManager pins instead of its own.
116
146
  const result = spawnSync(command, args, { cwd: parse(command).root || undefined, env, shell: false, encoding: "utf8", timeout: 15000, killSignal: "SIGKILL", maxBuffer: 1024 * 1024, windowsHide: true });
117
- if (result.error || result.status !== 0) throw new Error("Windows prerequisite process failed");
147
+ if (result.error || result.status !== 0) throw new Error("Windows prerequisite process failed", { cause: prerequisiteProcessCause(result) });
118
148
  return result.stdout.trim();
119
149
  }
120
150
 
@@ -151,9 +181,9 @@ try {
151
181
  $depth = 0;
152
182
  while ($path) {
153
183
  $role = 'ancestor'; if ($depth -eq 0) { $role = 'target' } elseif ($depth -eq 1) { $role = 'parent' };
154
- $item = Get-Item -LiteralPath $path -Force;
155
- if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { $detail = $path + '|||'; throw ($role + '-reparse') };
156
- if ($item.PSIsContainer) { $acl = [IO.Directory]::GetAccessControl($path) } else { $acl = [IO.File]::GetAccessControl($path) };
184
+ $attributes = [IO.File]::GetAttributes($path);
185
+ if ($attributes -band [IO.FileAttributes]::ReparsePoint) { $detail = $path + '|||'; throw ($role + '-reparse') };
186
+ if ($attributes -band [IO.FileAttributes]::Directory) { $acl = [IO.Directory]::GetAccessControl($path) } else { $acl = [IO.File]::GetAccessControl($path) };
157
187
  $owner = $acl.GetOwner([Security.Principal.SecurityIdentifier]);
158
188
  if ($trusted -notcontains $owner.Value) { $detail = $path + '|' + $owner.Value + '|' + (& $account $owner) + '|'; throw ($role + '-owner') };
159
189
  $allowedRights = 0x1200a9; if ($depth -ge 2) { $allowedRights = 0x1200ad };
@@ -186,9 +216,9 @@ foreach ($start in $env:GENTLE_WINDOWS_CHECKS.Split('|')) {
186
216
  $depth = 0;
187
217
  while ($path) {
188
218
  $role = 'ancestor'; if ($depth -eq 0) { $role = 'target' } elseif ($depth -eq 1) { $role = 'parent' };
189
- $item = Get-Item -LiteralPath $path -Force;
190
- if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { $detail = $path + '|||'; throw ($role + '-reparse') };
191
- if ($item.PSIsContainer) { $acl = [IO.Directory]::GetAccessControl($path) } else { $acl = [IO.File]::GetAccessControl($path) };
219
+ $attributes = [IO.File]::GetAttributes($path);
220
+ if ($attributes -band [IO.FileAttributes]::ReparsePoint) { $detail = $path + '|||'; throw ($role + '-reparse') };
221
+ if ($attributes -band [IO.FileAttributes]::Directory) { $acl = [IO.Directory]::GetAccessControl($path) } else { $acl = [IO.File]::GetAccessControl($path) };
192
222
  $owner = $acl.GetOwner([Security.Principal.SecurityIdentifier]);
193
223
  if ($trusted -notcontains $owner.Value) { $detail = $path + '|' + $owner.Value + '|' + (& $account $owner) + '|'; throw ($role + '-owner') };
194
224
  $allowedRights = 0x1200a9; if ($depth -ge 2) { $allowedRights = 0x1200ad };
@@ -4,9 +4,11 @@
4
4
  // an exact commit SHA resolved at install or upgrade time:
5
5
  // - Gentle AI: `go install <module>@<sha>` in a sealed Go environment, so the
6
6
  // module source is verified by Go's checksum database (sum.golang.org), as
7
- // the Windows release build already is. The binary must report a version
8
- // naming that commit, then it is activated through the existing dev-binary
9
- // override, which never falls back to the pinned binary silently.
7
+ // the Windows release build already is. Go must resolve the commit to a
8
+ // module version (a tag or a pseudo-version) whose origin is that commit,
9
+ // and the binary must embed that version and report it, then it is
10
+ // activated through the existing dev-binary override, which never falls
11
+ // back to the pinned binary silently.
10
12
  // - Gentle Shell: the exact commit's source tarball, versioned
11
13
  // `<version>-main.<sha12>` and packed without `prepack` (which runs the full
12
14
  // test suite), then installed globally like any local tarball.
@@ -18,13 +20,14 @@ import { goAcquisition, pnpmGlobalBin, requirements } from "./installer-prefligh
18
20
 
19
21
  export const SHELL_REPOSITORY = "Gentleman-Programming/gentle-shell";
20
22
  export const GENTLE_AI_REPOSITORY = "Gentleman-Programming/gentle-ai";
23
+ export const GENTLE_AI_MODULE = "github.com/gentleman-programming/gentle-ai/v4";
21
24
  export const GENTLE_AI_MAIN_PACKAGE = "github.com/gentleman-programming/gentle-ai/v4/cmd/gentle-ai";
22
25
  export const CHANNEL_SCHEMA = "gentle-shell.channel/v1";
23
26
  export const CHANNELS = Object.freeze(["release", "main"]);
24
27
 
25
28
  const SHA = /^[0-9a-f]{40}$/;
26
29
  const MINUTE = 60_000;
27
- const deadlines = Object.freeze({ build: 15 * MINUTE, version: 30_000, extract: 2 * MINUTE, pack: 5 * MINUTE });
30
+ const deadlines = Object.freeze({ build: 15 * MINUTE, resolve: 2 * MINUTE, version: 30_000, extract: 2 * MINUTE, pack: 5 * MINUTE });
28
31
  const MAX_SOURCE_BYTES = 100 * 1024 * 1024;
29
32
 
30
33
  export class MainChannelError extends Error {
@@ -35,9 +38,10 @@ export class MainChannelError extends Error {
35
38
  }
36
39
  }
37
40
 
38
- /** Same config home as the dev-binary override: GENTLE_PI_CONFIG_HOME or ~/.pi/gentle-ai. */
41
+ /** Same config home as the dev-binary override: a nonempty GENTLE_PI_CONFIG_HOME or ~/.pi/gentle-ai.
42
+ * Install-level state, so never the standalone launcher's per-home marker (#2201). */
39
43
  export function configHome({ env, home }) {
40
- return env.GENTLE_PI_CONFIG_HOME ?? join(home, ".pi", "gentle-ai");
44
+ return env.GENTLE_PI_CONFIG_HOME || join(home, ".pi", "gentle-ai");
41
45
  }
42
46
  export function channelStatePath(ctx) {
43
47
  return join(configHome(ctx), "channel.json");
@@ -135,23 +139,57 @@ export async function buildMainGentleAi({ commit, ctx, platform, goPath, run, fs
135
139
  const executable = platform === "win32" ? "gentle-ai.exe" : "gentle-ai";
136
140
  const directory = join(configHome(ctx), "main", "gentle-ai", commit);
137
141
  const buildDirectory = join(configHome(ctx), "main", ".build");
142
+ // Verified in the work folder before it replaces anything, so a failed rebuild of
143
+ // an already registered commit never touches the override; the final move stays
144
+ // on one filesystem, and uninstall already accepts a leftover .build.
145
+ const staging = join(buildDirectory, commit);
138
146
  await fs.rm(buildDirectory, { recursive: true, force: true });
139
147
  const env = sealedGoEnvironment(goPath, buildDirectory, platform);
140
148
  for (const path of [env.GOBIN, env.GOPATH, env.GOMODCACHE, env.GOCACHE, join(buildDirectory, "tmp")]) await fs.mkdir(path, { recursive: true });
141
149
  try {
142
150
  const install = await run(goPath, ["install", `${GENTLE_AI_MAIN_PACKAGE}@${commit}`], { env, cwd: buildDirectory, deadlineMs: deadlines.build, stderrTail: 4096 });
143
151
  if (!succeeded(install)) throw new MainChannelError("main-gentle-ai-build-failed", `go install ${GENTLE_AI_MAIN_PACKAGE}@${commit} failed`, stderrCause(install));
144
- await fs.mkdir(directory, { recursive: true });
145
- const binaryPath = join(directory, executable);
146
- await fs.copyFile(join(env.GOBIN, executable), binaryPath);
147
- if (platform !== "win32") await fs.chmod(binaryPath, 0o755);
148
- // Go stamps the module pseudo-version, whose suffix is the commit's first 12 characters.
149
- const probe = await run(binaryPath, ["version"], { env: { PATH: env.PATH }, deadlineMs: deadlines.version });
152
+ await fs.mkdir(staging, { recursive: true });
153
+ const stagedPath = join(staging, executable);
154
+ await fs.copyFile(join(env.GOBIN, executable), stagedPath);
155
+ if (platform !== "win32") await fs.chmod(stagedPath, 0o755);
156
+ const reject = async (message, cause) => {
157
+ throw new MainChannelError("main-gentle-ai-unverified", message, cause);
158
+ };
159
+ const short = commit.slice(0, 12);
160
+ const goOptions = { env, cwd: buildDirectory, deadlineMs: deadlines.version, stderrTail: 4096 };
161
+ // Go resolves the commit through the proxy and checksum database: a tag commit is
162
+ // stamped with its tag, any other with a pseudo-version, so the version is not guessed.
163
+ // Resolving may reach the proxy, so it gets a network deadline, not the local one.
164
+ const listed = await run(goPath, ["list", "-m", "-json", `${GENTLE_AI_MODULE}@${commit}`], { ...goOptions, deadlineMs: deadlines.resolve });
165
+ if (!succeeded(listed)) return await reject(`Go could not resolve Gentle AI commit ${short}`, stderrCause(listed));
166
+ let resolved = null;
167
+ try {
168
+ resolved = JSON.parse(String(listed.stdout ?? ""));
169
+ } catch {
170
+ return await reject(`Go returned unreadable module information for Gentle AI commit ${short}`);
171
+ }
172
+ const resolvedVersion = typeof resolved?.Version === "string" ? resolved.Version : "";
173
+ const originHash = resolved?.Origin?.Hash;
174
+ if (resolvedVersion === "") return await reject(`Go resolved Gentle AI commit ${short} to no module version`);
175
+ if (originHash !== commit) {
176
+ return await reject(`Go resolved Gentle AI commit ${short} to ${originHash ? `commit ${originHash}` : "no commit"}, not the requested commit`);
177
+ }
178
+ const embedded = await run(goPath, ["version", "-m", stagedPath], goOptions);
179
+ if (!succeeded(embedded)) return await reject("the built Gentle AI's embedded module version could not be read", stderrCause(embedded));
180
+ const modLine = String(embedded.stdout ?? "").split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((fields) => fields[0] === "mod" && fields[1] === GENTLE_AI_MODULE);
181
+ if (modLine?.[2] !== resolvedVersion) {
182
+ return await reject(`the built Gentle AI embeds module version ${modLine?.[2] ?? "none"}, expected ${resolvedVersion} for commit ${short}`);
183
+ }
184
+ const probe = await run(stagedPath, ["version"], { env: { PATH: env.PATH }, deadlineMs: deadlines.version });
150
185
  const match = succeeded(probe) && /^gentle-ai (\S+)$/m.exec(String(probe.stdout ?? ""));
151
- if (!match || !match[1].endsWith(`-${commit.slice(0, 12)}`)) {
152
- await fs.rm(directory, { recursive: true, force: true });
153
- throw new MainChannelError("main-gentle-ai-unverified", `the built Gentle AI does not report commit ${commit.slice(0, 12)}`);
186
+ const expectedVersion = resolvedVersion.replace(/^v/, "");
187
+ if (!match || match[1] !== expectedVersion) {
188
+ return await reject(`the built Gentle AI reports version ${match ? match[1] : "none"}, expected ${expectedVersion} for commit ${short}`);
154
189
  }
190
+ await fs.mkdir(directory, { recursive: true });
191
+ const binaryPath = join(directory, executable);
192
+ await fs.rename(stagedPath, binaryPath);
155
193
  registerGentleAiDevBinary(binaryPath, ctx, platform);
156
194
  return { binaryPath, version: match[1] };
157
195
  } finally {
@@ -386,6 +424,21 @@ async function mainGo({ userGo, pinnedGo, acquire, run }) {
386
424
  }
387
425
  }
388
426
 
427
+ /** A thrown value's message when it has one (an Error or not), otherwise the value as text. */
428
+ function describe(error) {
429
+ return typeof error?.message === "string" ? error.message : String(error);
430
+ }
431
+
432
+ /** `error` with `detail` appended to its message, keeping its code and cause; any
433
+ * other error becomes `main-upgrade-incomplete` with its message and itself as cause. */
434
+ function withDetail(error, detail) {
435
+ if (!(error instanceof MainChannelError)) {
436
+ return new MainChannelError("main-upgrade-incomplete", `${describe(error)}; ${detail}`, error);
437
+ }
438
+ const message = error.message.slice(`${error.code}: `.length);
439
+ return new MainChannelError(error.code, `${message}; ${detail}`, error.cause);
440
+ }
441
+
389
442
  /**
390
443
  * Updates Gentle Shell along its recorded channel (or the one `--channel` selects):
391
444
  * release goes to the latest npm release, main to the latest `main` commits of
@@ -440,11 +493,33 @@ export async function runUpgrade({ args, ctx, platform, arch = process.arch, pac
440
493
  out(`gentle-shell is already at the latest main: ${summary}.`);
441
494
  return 0;
442
495
  }
443
- if (!aiCurrent) await buildMainGentleAi({ commit: gentleAiCommit, ctx, platform, goPath: await mainGo({ userGo, pinnedGo, acquire, run }), run, fs });
496
+ // Fail forward (#2356): a Gentle AI step that fails still installs a main Shell that
497
+ // moved, so the retry runs the new Shell's code; the channel is recorded only when
498
+ // both steps succeed, so that retry rebuilds Gentle AI. Verification stays fail-closed.
499
+ // A flag, not the caught value: a rejection may be any value, even a falsy one.
500
+ let aiFailed = false;
501
+ let aiError;
502
+ if (!aiCurrent) {
503
+ try {
504
+ await buildMainGentleAi({ commit: gentleAiCommit, ctx, platform, goPath: await mainGo({ userGo, pinnedGo, acquire, run }), run, fs });
505
+ } catch (error) {
506
+ if (shellCurrent) throw error;
507
+ aiFailed = true;
508
+ aiError = error;
509
+ }
510
+ }
444
511
  if (!shellCurrent) {
445
- const manager = await ownerManager({ ctx, platform, packageRoot, adapters });
446
- const tgz = await packMainShell({ commit: shellCommit, ctx, fetch, run, pnpm, fs, platform });
447
- await installGlobal(manager, tgz, run);
512
+ try {
513
+ const manager = await ownerManager({ ctx, platform, packageRoot, adapters });
514
+ const tgz = await packMainShell({ commit: shellCommit, ctx, fetch, run, pnpm, fs, platform });
515
+ await installGlobal(manager, tgz, run);
516
+ } catch (error) {
517
+ throw aiFailed ? withDetail(error, `Gentle AI was not updated either (${describe(aiError)})`) : error;
518
+ }
519
+ }
520
+ if (aiFailed) {
521
+ out(`Updated Gentle Shell to the latest main (${shellCommit.slice(0, 12)}); Gentle AI was not updated.`);
522
+ throw withDetail(aiError, `Gentle Shell was updated to main ${shellCommit.slice(0, 12)} but Gentle AI was not: run gentle-shell upgrade --channel main to finish`);
448
523
  }
449
524
  await writeChannel(ctx, { channel: "main", shellCommit, gentleAiCommit }, fs);
450
525
  out(`Updated to the latest main: ${summary}.`);
@@ -1,6 +1,7 @@
1
1
  // Narrow update ownership proof. Deliberately independent of Shell upgrade/uninstall.
2
2
  import { posix, win32 } from 'node:path';
3
3
  import { bundledUpdatePlan } from '../runtime/gentle-shell-bundled.mjs';
4
+ import { setupErrorDetail } from './installer-runner.mjs';
4
5
 
5
6
  const PI = '@earendil-works/pi-coding-agent';
6
7
  const SHELL = 'gentle-pi';
@@ -8,6 +9,9 @@ const object = value => value !== null && typeof value === 'object' && !Array.is
8
9
  const succeeded = result => result?.code === 0 && !result.timedOut && result.signal == null;
9
10
  const refusal = detail => new Error(`gentle-shell update: ownership ${detail}; update Pi with the package manager or source checkout that supplies the selected runtime. No installation changed.`);
10
11
  const LINKED = /^(?:file|link|workspace):/i;
12
+ // A main-channel Shell (`<version>-main.<sha12>`, scripts/main-channel.mjs
13
+ // mainVersion) is never published; it exists only as the packed tarball.
14
+ const MAIN_BUILD = /^[0-9A-Za-z.+-]+-main\.[0-9a-f]{12}$/;
11
15
  const CLI_TAIL = /[\\/]dist[\\/]bundle[\\/]cli\.js$/i;
12
16
  // A quoted cli.js target inside a package-manager shim: pnpm's POSIX shim
13
17
  // (`"$basedir/../global/.../cli.js"`), its `.CMD`/npm's `.cmd` shim
@@ -136,7 +140,19 @@ async function listedEntries(groups, {adapters, path, key, canonical}) {
136
140
  * ownership. An adjacent runtime is owned by the Shell group that resolved
137
141
  * it: updating a separate global Pi never changes what that Shell runs.
138
142
  */
139
- export async function resolveUpdateOwner({runtime, packageRoot, platform = process.platform, adapters, cwd}) {
143
+ /** Where `gentle-shell upgrade --channel main` packed this Shell version: the
144
+ * same config home as scripts/main-channel.mjs configHome. A relative config
145
+ * home is refused, not resolved: `upgrade` resolved it from its own working
146
+ * directory, so it names no single folder, and the package manager runs from
147
+ * the neutral cwd.
148
+ */
149
+ function mainBuildTarball(version, {env, home, path}) {
150
+ const config = env?.GENTLE_PI_CONFIG_HOME || (typeof home === 'string' ? path.join(home, '.pi', 'gentle-ai') : null);
151
+ if (!config || !path.isAbsolute(config)) throw refusal('cannot locate the main-channel build without an absolute config home');
152
+ return path.join(config, 'main', 'packages', `${SHELL}-${version}.tgz`);
153
+ }
154
+
155
+ export async function resolveUpdateOwner({runtime, packageRoot, platform = process.platform, adapters, cwd, env, home}) {
140
156
  const {path, key} = pathsFor(platform);
141
157
  const canonical = canonicalWith(adapters, path);
142
158
  const selected = await selectedCliOf(runtime, {adapters, path, canonical});
@@ -154,7 +170,7 @@ export async function resolveUpdateOwner({runtime, packageRoot, platform = proce
154
170
  const root = await canonical(dependency.path);
155
171
  const manifest = await manifestOf(adapters, path, root);
156
172
  if (manifest.name !== pkg) throw refusal('listed package identity differs');
157
- let cli, spec;
173
+ let cli, spec, artifact = false;
158
174
  if (pkg === PI) {
159
175
  if (manifest.bin?.pi !== 'dist/bundle/cli.js') throw refusal('listed Pi bin is unsupported');
160
176
  cli = await canonical(path.join(root, manifest.bin.pi));
@@ -169,11 +185,12 @@ export async function resolveUpdateOwner({runtime, packageRoot, platform = proce
169
185
  // Reinstalling the same Shell version re-resolves its peer; `update`
170
186
  // never upgrades Shell itself (that is `gentle-shell upgrade`).
171
187
  if (typeof manifest.version !== 'string') throw refusal('listed Shell version is missing');
172
- spec = `${SHELL}@${manifest.version}`;
188
+ artifact = MAIN_BUILD.test(manifest.version);
189
+ spec = artifact ? mainBuildTarball(manifest.version, {env, home, path}) : `${SHELL}@${manifest.version}`;
173
190
  }
174
191
  if (key(cli) !== key(selected)) continue;
175
192
  if (name === 'pnpm' && members.get(key(groupRoot)).size !== 1) throw refusal('selected supplier shares its global install group');
176
- candidates.push({name, ...invocation, package:pkg, spec});
193
+ candidates.push({name, ...invocation, package:pkg, spec, artifact});
177
194
  }
178
195
  }
179
196
  if (candidates.length !== 1) throw refusal(candidates.length > 1 ? 'is ambiguous' : 'could not be verified from global listings');
@@ -207,11 +224,22 @@ export async function updateSelectedPi(input) {
207
224
  let owner;
208
225
  try { owner = await resolveUpdateOwner(input); }
209
226
  catch (error) { throw error.message?.includes('ownership') ? error : refusal('could not be resolved'); }
227
+ if (owner.artifact) {
228
+ try { await input.adapters.fs.realpath(owner.spec); }
229
+ catch { throw new Error(`gentle-shell update: the main-channel build ${owner.spec} is missing, and this unpublished build cannot be reinstalled from the registry; run 'gentle-shell upgrade --channel release' and then 'gentle-shell upgrade --channel main' to rebuild it. No installation changed.`); }
230
+ }
210
231
  // Exactly one approved package per command; never create a comma group or
211
232
  // reinstall arbitrary dependencies merely because they share a store.
212
233
  const args = owner.name === 'pnpm' ? ['add', '-g', owner.spec, ...(owner.package === SHELL ? ['--allow-build=gentle-pi'] : [])] : ['install', '-g', owner.spec, ...(owner.package === PI ? ['--ignore-scripts'] : [])];
213
- const result = await input.adapters.run(owner.command, [...owner.prefix, ...args], {deadlineMs:20 * 60_000, cwd: input.cwd});
214
- if (!succeeded(result)) throw new Error(`gentle-shell update: ${owner.name} could not install ${owner.spec}${result?.stderrTail ? `\n${result.stderrTail}` : ''}`);
234
+ const result = await input.adapters.run(owner.command, [...owner.prefix, ...args], {deadlineMs:20 * 60_000, cwd: input.cwd, stderrTail:4096});
235
+ if (!succeeded(result)) {
236
+ // One sanitized line of the manager's own error: npm prints it on stderr,
237
+ // pnpm 11 prints ERR_PNPM_* on stdout. Read as one text, stderr last, an
238
+ // error line in either stream wins over a stderr warning, stderr's on a tie.
239
+ const output = [result?.stdout, result?.stderrTail].filter(text => typeof text === 'string').join('\n');
240
+ const detail = setupErrorDetail(output, input.home, input.platform);
241
+ throw new Error(`gentle-shell update: ${owner.name} (${[owner.command, ...owner.prefix].join(' ')}) could not install ${owner.spec}${detail ? `\n${detail}` : ''}`);
242
+ }
215
243
  const after = await suppliedPiVersion(owner, input);
216
244
  const before = input.currentVersion;
217
245
  if (after === null) input.out?.(`${owner.name} installed ${owner.spec}. The Pi version it now supplies could not be read back; run 'gentle-shell --version' to check it.`);
@@ -30,6 +30,10 @@ export async function runStage(stage) {
30
30
  return !normalizedKey.startsWith("GIT_") && !normalizedKey.startsWith("GENTLE_PI_AGENTS_") && normalizedKey !== "GENTLE_PI_CONFIG_HOME";
31
31
  })) : process.env;
32
32
  if (configHome) env.GENTLE_PI_CONFIG_HOME = configHome;
33
+ // Unit fixtures also get a private temp dir, removed with the stage, so a
34
+ // fixture that leaves a subagent unfinished cannot leave its TMPDIR behind.
35
+ const unitTmp = configHome ? mkdtempSync(join(tmpdir(), "gentle-pi-unit-tmp-")) : undefined;
36
+ if (unitTmp) env.TMPDIR = unitTmp;
33
37
  try {
34
38
  return await new Promise((resolve) => {
35
39
  const child = spawn(stage.command, { shell: true, stdio: "inherit", env });
@@ -41,6 +45,7 @@ export async function runStage(stage) {
41
45
  });
42
46
  } finally {
43
47
  if (configHome) rmSync(configHome, { recursive: true, force: true });
48
+ if (unitTmp) rmSync(unitTmp, { recursive: true, force: true });
44
49
  }
45
50
  }
46
51
 
@@ -50,9 +50,33 @@ export const requiredPaths = [
50
50
  "assets/sounds/LICENSE.md",
51
51
  "docs/sound-notifications.md",
52
52
  "docs/sound-notifications-proposal.md",
53
+ "extensions/gentle-voice.ts",
54
+ "lib/voice-artifacts.ts",
55
+ "lib/voice-command.ts",
56
+ "lib/voice-install.ts",
57
+ "lib/voice-platform.ts",
58
+ "lib/voice-session.ts",
59
+ "lib/voice-ui.ts",
60
+ // The dictation worker is spawned by path, never imported, so nothing else
61
+ // would notice it missing from the packed tarball.
62
+ "scripts/voice-worker.cjs",
63
+ "docs/voice.md",
64
+ "extensions/gentle-design.ts",
65
+ "lib/gentle-design.ts",
66
+ // The design canvas server reads its page and scripts from assets/ by path.
67
+ "lib/design-canvas-server.ts",
68
+ "lib/design-open-browser.ts",
69
+ "lib/design-comments.ts",
70
+ "lib/design-handoffs.ts",
71
+ "lib/design-sources.ts",
72
+ "assets/design-canvas/index.html",
73
+ "assets/design-canvas/canvas.js",
74
+ "assets/design-canvas/canvas.css",
75
+ "assets/design-canvas/inspector.js",
53
76
  "scripts/npm-pack-result.mjs",
54
77
  "assets/orchestrator.md",
55
78
  "assets/orchestrator-delegation.md",
79
+ "assets/orchestrator-delegation-reference.md",
56
80
  "assets/orchestrator-tracking.md",
57
81
  "assets/orchestrator-verification.md",
58
82
  "assets/orchestrator-writer.md",
@@ -81,6 +105,7 @@ export const requiredPaths = [
81
105
  "docs/review-integration.md",
82
106
  "extensions/gentle-ai.ts",
83
107
  "extensions/resume-hint.ts",
108
+ "extensions/session-scratch.ts",
84
109
  "extensions/skill-registry.ts",
85
110
  "lib/child-package-injection.ts",
86
111
  "lib/gentle-ai-binary.ts",
@@ -90,6 +115,7 @@ export const requiredPaths = [
90
115
  "lib/gentle-shell-update-panel.ts",
91
116
  "lib/gentle-shell-startup-flow.ts",
92
117
  "lib/gentle-shell-resume-hint.ts",
118
+ "lib/gentle-shell-import-pi.ts",
93
119
  "lib/native-review-cli.ts",
94
120
  "lib/status-timing-diagnostics.ts",
95
121
  "lib/provider-contract-bundle.ts",
@@ -98,6 +124,7 @@ export const requiredPaths = [
98
124
  "lib/review-relay-contract.ts",
99
125
  "lib/agent-assets.ts",
100
126
  "lib/telemetry-trigger.ts",
127
+ "lib/session-scratch.ts",
101
128
  "runtime/child-package-injection.mjs",
102
129
  "runtime/gentle-ai-binary.mjs",
103
130
  "runtime/gentle-shell-launcher.mjs",
@@ -107,6 +134,7 @@ export const requiredPaths = [
107
134
  "runtime/gentle-shell-startup-flow.mjs",
108
135
  "runtime/gentle-shell-resume-hint.mjs",
109
136
  "runtime/gentle-shell-uninstall.mjs",
137
+ "runtime/gentle-shell-import-pi.mjs",
110
138
  "runtime/native-review-cli.mjs",
111
139
  "runtime/status-timing-diagnostics.mjs",
112
140
  "runtime/review-integration-v2.mjs",
@@ -131,23 +159,25 @@ export const requiredPaths = [
131
159
  // exact bytes are pinned by the lock-driven scripts/check-provider-contract.mjs
132
160
  // drift check, which runs in the same pnpm test flow.
133
161
  "contracts/review-provider-contract-mirror/provider-contract.lock.json",
134
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/README.md",
135
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/manifest.json",
136
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/orchestration/pi.md",
137
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/schemas/lens.schema.json",
138
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/schemas/refuter.schema.json",
139
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/schemas/targeted-validator.schema.json",
140
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/vectors/lens.json",
141
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/vectors/refuter.json",
142
- "contracts/review-provider-contract-mirror/v1.3.0/bundle/vectors/targeted-validator.json",
143
- "contracts/review-provider-contract-mirror/v1.3.0/generated/provider-capabilities.baseline.json",
144
- "contracts/review-provider-contract-mirror/v1.3.0/generated/provider-roles.baseline.json",
162
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/README.md",
163
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/manifest.json",
164
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/orchestration/pi.md",
165
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md",
166
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/schemas/lens.schema.json",
167
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/schemas/refuter.schema.json",
168
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/schemas/targeted-validator.schema.json",
169
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/vectors/lens.json",
170
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/vectors/refuter.json",
171
+ "contracts/review-provider-contract-mirror/v1.4.0/bundle/vectors/targeted-validator.json",
172
+ "contracts/review-provider-contract-mirror/v1.4.0/generated/provider-capabilities.baseline.json",
173
+ "contracts/review-provider-contract-mirror/v1.4.0/generated/provider-roles.baseline.json",
145
174
  "prompts/skill-creation.md",
146
175
  "skills/_shared/review-ledger-contract.md",
147
176
  "skills/branch-pr/SKILL.md",
148
177
  "skills/chained-pr/SKILL.md",
149
178
  "skills/cognitive-doc-design/SKILL.md",
150
179
  "skills/comment-writer/SKILL.md",
180
+ "skills/design/SKILL.md",
151
181
  "skills/gentle-ai/SKILL.md",
152
182
  "skills/issue-creation/SKILL.md",
153
183
  "skills/judgment-day/SKILL.md",
@@ -395,7 +425,7 @@ async function main() {
395
425
  });
396
426
 
397
427
  if (driftedContracts.length > 0) {
398
- console.error("gentle-pi packaged review-integration/v1 and review-integration/v2 contract bytes drifted from the pinned v4.1.1 runtime's vendored Gentle AI contract artifacts:");
428
+ console.error("gentle-pi packaged review-integration/v1 and review-integration/v2 contract bytes drifted from the pinned v4.3.1 runtime's vendored Gentle AI contract artifacts:");
399
429
  for (const drift of driftedContracts) console.error(`- ${drift.relativePath}: expected ${drift.expected}, got ${drift.actual}`);
400
430
  process.exit(1);
401
431
  }
@@ -440,7 +470,7 @@ async function main() {
440
470
  process.exit(1);
441
471
  }
442
472
 
443
- console.log(`gentle-pi package resource check passed (${requiredPaths.length} files; ${Object.keys(contractHashes).length} exact byte-pinned contract artifacts for the v4.1.1 runtime).`);
473
+ console.log(`gentle-pi package resource check passed (${requiredPaths.length} files; ${Object.keys(contractHashes).length} exact byte-pinned contract artifacts for the v4.3.1 runtime).`);
444
474
  }
445
475
 
446
476
  const isMainModule = process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;