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
@@ -78,8 +78,8 @@ export const REVIEW_START_STATE = {
78
78
  const START_ACTIONS = ["created", "resumed", "closed", "blocked-scope-action"] ;
79
79
  const RISK_LEVELS = ["low", "medium", "high"] ;
80
80
  const REVIEW_LENSES = ["review-risk", "review-resilience", "review-readability", "review-reliability"] ;
81
- const RISK_REASON_CODES = ["configuration_change", "empty_content", "executable_change", "executable_mode", "hot_path", "large_change", "non_executable_only", "process_boundary", "process_scan_limit", "service_token", "shell_source"] ;
82
- const RISK_SIGNALS = ["auth", "update", "security", "payments", "permissions", "shell_process"] ;
81
+ const RISK_REASON_CODES = ["agent_escalation", "configuration_change", "dangerous_sink", "empty_content", "executable_change", "executable_mode", "hot_path", "large_change", "non_executable_only", "process_boundary", "process_scan_limit", "service_token", "shell_source"] ;
82
+ const RISK_SIGNALS = ["auth", "update", "security", "payments", "permissions", "shell_process", "dangerous_sink", "agent_escalation"] ;
83
83
  // "collect" and "execute" are the v2 envelope's projection of a live
84
84
  // transaction whose next_transition is mandatory: the root action names the
85
85
  // transition kind instead of reading as a terminal stop.
@@ -637,6 +637,23 @@ const ESCALATION_REFUTER_OUTCOMES = ["corroborated", "refuted", "inconclusive"]
637
637
 
638
638
 
639
639
 
640
+ function decodeReviewEscalationV1(value , label ) {
641
+ const source = exactRecord(value, label, ["cause", "finding_ids"], ["refuter_outcomes"]);
642
+ const escalation = {
643
+ cause: enumeration(source.cause, ESCALATION_CAUSES, `${label}.cause`),
644
+ findingIds: stringArray(source.finding_ids, `${label}.finding_ids`),
645
+ };
646
+ if (Object.hasOwn(source, "refuter_outcomes")) escalation.refuterOutcomes = array(source.refuter_outcomes, `${label}.refuter_outcomes`, (entry, itemLabel) => {
647
+ const row = exactRecord(entry, itemLabel, ["finding_id", "outcome", "proof"]);
648
+ return {
649
+ findingId: nonempty(row.finding_id, `${itemLabel}.finding_id`),
650
+ outcome: enumeration(row.outcome, ESCALATION_REFUTER_OUTCOMES, `${itemLabel}.outcome`),
651
+ proof: nonempty(row.proof, `${itemLabel}.proof`),
652
+ };
653
+ });
654
+ return escalation;
655
+ }
656
+
640
657
 
641
658
 
642
659
 
@@ -2105,23 +2122,9 @@ export function decodeReviewStatusV3(value ) {
2105
2122
  };
2106
2123
  }
2107
2124
 
2108
- let escalation ;
2109
- if (Object.hasOwn(body, "escalation")) {
2110
- const label = "status.escalation";
2111
- const source = exactRecord(body.escalation, label, ["cause", "finding_ids"], ["refuter_outcomes"]);
2112
- escalation = {
2113
- cause: enumeration(source.cause, ESCALATION_CAUSES, `${label}.cause`),
2114
- findingIds: stringArray(source.finding_ids, `${label}.finding_ids`),
2115
- };
2116
- if (Object.hasOwn(source, "refuter_outcomes")) escalation.refuterOutcomes = array(source.refuter_outcomes, `${label}.refuter_outcomes`, (entry, itemLabel) => {
2117
- const row = exactRecord(entry, itemLabel, ["finding_id", "outcome", "proof"]);
2118
- return {
2119
- findingId: nonempty(row.finding_id, `${itemLabel}.finding_id`),
2120
- outcome: enumeration(row.outcome, ESCALATION_REFUTER_OUTCOMES, `${itemLabel}.outcome`),
2121
- proof: nonempty(row.proof, `${itemLabel}.proof`),
2122
- };
2123
- });
2124
- }
2125
+ const escalation = Object.hasOwn(body, "escalation")
2126
+ ? decodeReviewEscalationV1(body.escalation, "status.escalation")
2127
+ : undefined;
2125
2128
 
2126
2129
  // status/v7 top-level optional digest (gentle-ai v2.6.0): resolves #4066's
2127
2130
  // closed loop where `sdd-attempt finish` named a digest status never
@@ -2584,7 +2587,11 @@ function assertReviewApprovedAcknowledgementExecuteShapeV1(execute
2584
2587
  if (execute.arguments.length !== REVIEW_APPROVED_ACKNOWLEDGEMENT_ARGUMENTS.length) throw new TypeError(`acknowledgement.execute.arguments must carry exactly ${REVIEW_APPROVED_ACKNOWLEDGEMENT_ARGUMENTS.length} provider-issued arguments`);
2585
2588
  const values = execute.arguments.map((argument, index) => { const name = REVIEW_APPROVED_ACKNOWLEDGEMENT_ARGUMENTS[index] ; if (argument.name !== name) throw new TypeError(`acknowledgement.execute.arguments[${index}].name must be ${name}`); const value = nonempty(argument.value, `acknowledgement.execute.arguments[${index}].value`); if (nonempty(argument.token, `acknowledgement.execute.arguments[${index}].token`) !== `--${name}=${value}`) throw new TypeError(`acknowledgement.execute.arguments[${index}].token must exactly match ${name}`); return value; });
2586
2589
  if (execute.preconditions.length !== 1 || execute.preconditions[0]?.name !== "state" || execute.preconditions[0]?.value !== "approved") throw new TypeError("acknowledgement.execute.preconditions must be the single approved state precondition");
2587
- return { tokens: execute.arguments.map((argument) => argument.token ) , values: values , lineageId: lineage(execute.binding.lineageId, "acknowledgement.execute.binding.lineage_id"), targetIdentity: sha256(execute.binding.targetIdentity, "acknowledgement.execute.binding.target_identity"), revision: sha256(execute.binding.revision, "acknowledgement.execute.binding.revision") };
2590
+ const tokens = execute.arguments.map((argument) => argument.token );
2591
+ // The exact five-argument check above validates both vectors before tuple construction.
2592
+ const tokenTuple = [tokens[0] , tokens[1] , tokens[2] , tokens[3] , tokens[4] ];
2593
+ const valueTuple = [values[0] , values[1] , values[2] , values[3] , values[4] ];
2594
+ return { tokens: tokenTuple, values: valueTuple, lineageId: lineage(execute.binding.lineageId, "acknowledgement.execute.binding.lineage_id"), targetIdentity: sha256(execute.binding.targetIdentity, "acknowledgement.execute.binding.target_identity"), revision: sha256(execute.binding.revision, "acknowledgement.execute.binding.revision") };
2588
2595
  }
2589
2596
 
2590
2597
  /**
@@ -2689,6 +2696,28 @@ const REVIEW_LAST_EVENT_CAUSAL_DISPOSITIONS = ["introduced", "behavior-activated
2689
2696
 
2690
2697
 
2691
2698
 
2699
+
2700
+
2701
+
2702
+
2703
+
2704
+
2705
+
2706
+
2707
+
2708
+
2709
+
2710
+
2711
+
2712
+
2713
+
2714
+
2715
+
2716
+
2717
+
2718
+
2719
+
2720
+
2692
2721
 
2693
2722
 
2694
2723
 
@@ -2836,18 +2865,67 @@ function decodeReviewLastEventReviewerResultV1(value , label )
2836
2865
  };
2837
2866
  }
2838
2867
 
2868
+ function decodeReviewTargetedValidatorCheckEvidenceV1(value , label ) {
2869
+ const check = exactRecord(value, label, ["passed", "evidence"], ["regressions"]);
2870
+ const regressions = Object.hasOwn(check, "regressions")
2871
+ ? array(check.regressions, `${label}.regressions`, (entry, entryLabel) => {
2872
+ const row = exactRecord(entry, entryLabel, ["location", "claim", "proof_refs"], ["id"]);
2873
+ return {
2874
+ ...(Object.hasOwn(row, "id") ? { id: nonempty(row.id, `${entryLabel}.id`) } : {}),
2875
+ location: nonempty(row.location, `${entryLabel}.location`),
2876
+ claim: nonempty(row.claim, `${entryLabel}.claim`),
2877
+ proofRefs: stringArray(row.proof_refs, `${entryLabel}.proof_refs`, { minimum: 1 }),
2878
+ };
2879
+ }) : undefined;
2880
+ return {
2881
+ passed: boolean(check.passed, `${label}.passed`),
2882
+ // Native admission rejects observations containing only Unicode whitespace.
2883
+ evidence: stringArray(check.evidence, `${label}.evidence`, { minimum: 1, pattern: /[^\p{White_Space}]/u }),
2884
+ ...(regressions === undefined ? {} : { regressions }),
2885
+ };
2886
+ }
2887
+
2888
+ function decodeReviewTargetedValidatorEvidenceV1(value , label ) {
2889
+ const body = exactRecord(value, label, ["targeted_validation_request_hash", "correction_target_identity", "original_criteria", "correction_regression", "follow_ups"]);
2890
+ return {
2891
+ targetedValidationRequestHash: sha256(body.targeted_validation_request_hash, `${label}.targeted_validation_request_hash`),
2892
+ correctionTargetIdentity: sha256(body.correction_target_identity, `${label}.correction_target_identity`),
2893
+ originalCriteria: decodeReviewTargetedValidatorCheckEvidenceV1(body.original_criteria, `${label}.original_criteria`),
2894
+ correctionRegression: decodeReviewTargetedValidatorCheckEvidenceV1(body.correction_regression, `${label}.correction_regression`),
2895
+ followUps: array(body.follow_ups, `${label}.follow_ups`, (entry, entryLabel) => {
2896
+ const row = exactRecord(entry, entryLabel, ["observation", "proof_refs"]);
2897
+ return { observation: nonempty(row.observation, `${entryLabel}.observation`), proofRefs: stringArray(row.proof_refs, `${entryLabel}.proof_refs`, { minimum: 1 }) };
2898
+ }),
2899
+ raw: body,
2900
+ };
2901
+ }
2902
+
2839
2903
  export function decodeReviewLastEventClosureV1(value ) {
2840
- const body = exactRecord(value, "last_event_closure", ["schema", "operation", "lineage_id", "state", "store_revision"], ["target_identity", "request_hash", "correction_lines", "action", "advisory_findings", "reviewer_results", "status_continuation", "acknowledgement"]);
2904
+ const body = exactRecord(value, "last_event_closure", ["schema", "operation", "lineage_id", "state", "store_revision"], ["target_identity", "request_hash", "correction_lines", "action", "escalation", "targeted_validator_evidence", "advisory_findings", "reviewer_results", "status_continuation", "acknowledgement"]);
2841
2905
  if (body.schema !== REVIEW_LAST_EVENT_CLOSURE_SCHEMA) throw new TypeError(`last_event_closure.schema must be ${REVIEW_LAST_EVENT_CLOSURE_SCHEMA}`);
2842
2906
  const operation = enumeration(body.operation, Object.values(REVIEW_LAST_EVENT_CLOSURE_OPERATION), "last_event_closure.operation") ;
2843
2907
  const state = enumeration(body.state, REVIEW_LAST_EVENT_TERMINAL_STATES, "last_event_closure.state") ;
2908
+ if (Object.hasOwn(body, "targeted_validator_evidence") && (operation !== REVIEW_LAST_EVENT_CLOSURE_OPERATION.CAPTURE_VALIDATION || state !== "escalated")) {
2909
+ throw new TypeError("last_event_closure targeted_validator_evidence requires escalated review/capture-validation");
2910
+ }
2911
+ // Diagnostic evidence is optional for older providers and never controls terminal authority.
2912
+ const targetedValidatorEvidence = Object.hasOwn(body, "targeted_validator_evidence")
2913
+ ? decodeReviewTargetedValidatorEvidenceV1(body.targeted_validator_evidence, "last_event_closure.targeted_validator_evidence")
2914
+ : undefined;
2915
+ if (Object.hasOwn(body, "escalation") && state !== "escalated") throw new TypeError("last_event_closure escalation requires escalated state");
2916
+ // Older native closures omit this diagnostic; its presence never grants a continuation.
2917
+ const escalation = Object.hasOwn(body, "escalation")
2918
+ ? decodeReviewEscalationV1(body.escalation, "last_event_closure.escalation")
2919
+ : undefined;
2844
2920
  const shared = {
2845
2921
  schema: REVIEW_LAST_EVENT_CLOSURE_SCHEMA,
2846
2922
  operation,
2847
2923
  lineageId: lineage(body.lineage_id, "last_event_closure.lineage_id"),
2848
2924
  state,
2849
2925
  storeRevision: sha256(body.store_revision, "last_event_closure.store_revision"),
2850
- };
2926
+ ...(escalation === undefined ? {} : { escalation }),
2927
+ ...(targetedValidatorEvidence === undefined ? {} : { targetedValidatorEvidence }),
2928
+ } ;
2851
2929
  if (operation === REVIEW_LAST_EVENT_CLOSURE_OPERATION.CAPTURE_CORRECTION_PLAN) {
2852
2930
  if (body.reviewer_results !== undefined) throw new TypeError("last_event_closure reviewer_results requires approved state");
2853
2931
  if (body.action !== undefined || body.advisory_findings !== undefined || body.status_continuation !== undefined) throw new TypeError("last_event_closure correction-plan cannot carry action, advisory_findings, or status_continuation");
@@ -140,18 +140,23 @@ const DUE_REVIEW_REASONS = [REVIEW_DUE_REASON.HIGH_R
140
140
 
141
141
  /**
142
142
  * Rejects a review_due/review_due_reason/consumed combination the native
143
- * schema can never produce. `already_reviewed` takes precedence exactly when
144
- * the candidate is consumed, so the two must agree in both directions. A
145
- * contradictory envelope is never trusted, least of all as closure evidence.
146
- * Envelopes without the pair (older binaries) are not checked here.
143
+ * schema can never produce. A consumed candidate must report
144
+ * `already_reviewed`, but that reason can also describe an acknowledged
145
+ * committed predecessor followed by a passive delta (#1954). It does not
146
+ * prove this exact candidate was consumed; preserve native's explicit false.
147
+ * Native v2 requires consumed, so missing evidence with already_reviewed is
148
+ * still rejected. Envelopes without the pair (older binaries) are unchecked.
147
149
  */
148
150
  function validateReviewDueConsistency(reviewDue , reviewDueReason , candidate ) {
149
151
  if (reviewDue !== DUE_REVIEW_REASONS.includes(reviewDueReason)) {
150
152
  throw new TypeError(`review assessment review_due ${reviewDue} contradicts review_due_reason ${reviewDueReason}`);
151
153
  }
152
154
  const alreadyReviewed = reviewDueReason === REVIEW_DUE_REASON.ALREADY_REVIEWED;
153
- if (alreadyReviewed !== (candidate.consumed === true)) {
154
- throw new TypeError("review assessment review_due_reason already_reviewed must be reported exactly when candidate.consumed is true");
155
+ if (candidate.consumed === true && !alreadyReviewed) {
156
+ throw new TypeError("review assessment candidate.consumed true requires review_due_reason already_reviewed");
157
+ }
158
+ if (alreadyReviewed && candidate.consumed === undefined) {
159
+ throw new TypeError("review assessment review_due_reason already_reviewed requires explicit candidate.consumed evidence");
155
160
  }
156
161
  }
157
162
 
@@ -260,6 +265,53 @@ export const VERIFICATION_TIER = {
260
265
  } ;
261
266
 
262
267
 
268
+ // ---------------------------------------------------------------------------
269
+ // Agent-raised risk (gentle-shell#1494). The agent that made the change may
270
+ // raise its risk to high by citing one item of the high-risk list in the
271
+ // always-on Task Size section; it can never lower a tier. Lowering stays with
272
+ // the deterministic native assessment.
273
+ // ---------------------------------------------------------------------------
274
+
275
+ export const HIGH_RISK_ITEMS = Object.freeze({
276
+ 1: "data or irreversible effects",
277
+ 2: "security",
278
+ 3: "changing or removing contracts others already consume",
279
+ 4: "concurrency",
280
+ 5: "delivery or environment",
281
+ 6: "no test would catch a regression",
282
+ } );
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+ export const AGENT_RISK_ESCALATION_REASON_MAX = 500;
291
+
292
+ export function decodeAgentRiskEscalation(value ) {
293
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
294
+ throw new Error("escalate must be an object with item and reason");
295
+ }
296
+ const record = value ;
297
+ const unexpected = Object.keys(record).find((key) => key !== "item" && key !== "reason");
298
+ if (unexpected !== undefined) throw new Error(`escalate does not accept ${unexpected}`);
299
+ const { item, reason } = record;
300
+ if (typeof item !== "number" || !Number.isInteger(item) || !(item in HIGH_RISK_ITEMS)) {
301
+ throw new Error("escalate item must be an integer from 1 to 6 naming a high-risk item");
302
+ }
303
+ if (typeof reason !== "string" || reason.trim() === "" || reason.length > AGENT_RISK_ESCALATION_REASON_MAX) {
304
+ throw new Error(`escalate reason must be a non-empty string of at most ${AGENT_RISK_ESCALATION_REASON_MAX} characters`);
305
+ }
306
+ return Object.freeze({ item: item , reason });
307
+ }
308
+
309
+ /** Raises passive or medium to high when the agent escalated; never lowers. */
310
+ export function escalatedRisk(risk , escalation ) {
311
+ if (escalation === undefined) return risk;
312
+ return risk === VERIFICATION_TIER.PASSIVE || risk === VERIFICATION_TIER.MEDIUM ? VERIFICATION_TIER.HIGH : risk;
313
+ }
314
+
263
315
  export const RDD_LINE = {
264
316
  ON: "on",
265
317
  OFF: "off",
@@ -364,59 +416,33 @@ function nonClosedOutcomeClause(outcome ) {
364
416
  }
365
417
 
366
418
  /**
367
- * Computes who verifies a delegated writer's change, exactly as
368
- * gentle-pi#662/#668 specify:
419
+ * Computes who verifies a delegated writer's change. Verification follows the
420
+ * native risk tier whatever the RDD line and native review outcome
421
+ * (verify-always-rdd-high S1, superseding the gentle-pi#662/#668 on-path): the
422
+ * native review is an additional outside view and never replaces or skips the
423
+ * tier's verification. `nativeReviewOutcome` only shapes the reason text.
369
424
  *
370
- * - `rdd: "on"` AND the native review reached `NATIVE_REVIEW_OUTCOME.CLOSED`
371
- * for this candidate: the writer self-verifies and no independent verifier
372
- * runs, because the closed native review is the check -- except `passive`,
373
- * which gets a structural readback by the parent instead.
374
- * - `rdd: "on"` with any other outcome (`declined`, `unavailable`, or the
375
- * fail-closed `unknown` default for an omitted/unrecognized outcome): the
376
- * `on` branch never held for this candidate, so the risk-gated path below
377
- * applies exactly as `off` -- declining a review is candidate-scoped and
378
- * never lowers the bar below the RDD-off path.
379
- * - `rdd: "off"` or `"unknown"` (both gate identically, so an unknown RDD line
380
- * never lowers a tier relative to `off`; `nativeReviewOutcome` is ignored
381
- * entirely on these lines):
382
- * - `passive`: structural readback by the parent only; no verifier, no
383
- * tests.
384
- * - `medium`: the writer self-verifies; a separate independent verifier
385
- * runs only when `writerProfile` is `"small"` -- the small-model bias
386
- * raises the tier by one for verification purposes (medium -> high). An
387
- * unknown or omitted writer profile resolves to `"small"` before it
388
- * reaches this function (see `resolveWriterProfile`), so an unrecognized
389
- * profile never lowers a tier below what a known small model would get.
390
- * - `high` or `unassessable` (a failed or unrecognized native assessment is
391
- * always treated as high): the writer self-verifies and an independent
392
- * verifier always runs.
425
+ * - `passive`: structural readback by the parent only; no verifier, no tests.
426
+ * - `medium`: the writer self-verifies; a separate independent verifier runs
427
+ * only when `writerProfile` is `"small"` -- the small-model bias raises the
428
+ * tier by one for verification purposes (medium -> high). An unknown or
429
+ * omitted writer profile resolves to `"small"` before it reaches this
430
+ * function (see `resolveWriterProfile`), so an unrecognized profile never
431
+ * lowers a tier below what a known small model would get.
432
+ * - `high` or `unassessable` (a failed or unrecognized native assessment is
433
+ * always treated as high): the writer self-verifies and an independent
434
+ * verifier always runs.
393
435
  */
394
436
  export function verificationPlan(input ) {
395
437
  const { rddLine, risk, writerProfile } = input;
396
438
 
397
439
  if (rddLine === RDD_LINE.ON) {
398
440
  const outcome = input.nativeReviewOutcome ?? NATIVE_REVIEW_OUTCOME.UNKNOWN;
399
- if (outcome === NATIVE_REVIEW_OUTCOME.CLOSED) {
400
- if (risk === VERIFICATION_TIER.PASSIVE) {
401
- return Object.freeze({
402
- writerSelfVerification: false,
403
- structuralReadbackOnly: true,
404
- independentVerifier: false,
405
- reason: "receipt-driven development is on and the native review closed for this candidate; the assessment also reports passive risk: a structural readback by the parent replaces both the writer's self-verification and any separate verifier.",
406
- });
407
- }
408
- return Object.freeze({
409
- writerSelfVerification: true,
410
- structuralReadbackOnly: false,
411
- independentVerifier: false,
412
- reason: "receipt-driven development is on and the native review closed for this candidate: the writer's self-verification is the record and the closed native review was the independent check the writer cannot influence, so no separate verifier is required.",
413
- });
414
- }
415
- // The on branch holds only while the native review reaches a terminal
416
- // outcome for this candidate. Anything else -- declined, unavailable, or
417
- // unknown -- falls back to the exact same risk-gated path as off.
441
+ const clause = outcome === NATIVE_REVIEW_OUTCOME.CLOSED
442
+ ? "the native review closed for this candidate"
443
+ : nonClosedOutcomeClause(outcome);
418
444
  return riskGatedPlan(
419
- `receipt-driven development is on, but ${nonClosedOutcomeClause(outcome)}, so the risk-gated path applies exactly as receipt-driven development off, and`,
445
+ `receipt-driven development is on and ${clause}; the native review is an additional outside view that never replaces verification, so the risk-gated path applies exactly as receipt-driven development off, and`,
420
446
  risk,
421
447
  writerProfile,
422
448
  );
@@ -0,0 +1,202 @@
1
+ // Generated by scripts/build-runtime-modules.mjs. Do not edit.
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+
4
+
5
+
6
+ const MAX_STATUSES = 8;
7
+ const MAX_ACTIVE_OBSERVATIONS = 8;
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ const hostScope = new AsyncLocalStorage ();
38
+ const nativeScope = new AsyncLocalStorage ();
39
+
40
+ // Observer failures must never replace the operation's result or exception.
41
+ function safely (run ) {
42
+ try { return run(); } catch { return undefined; }
43
+ }
44
+ function now(host ) {
45
+ const value = safely(host.clock);
46
+ if (value !== undefined && Number.isFinite(value) && value >= 0) return value;
47
+ host.summary.clock_unavailable = true;
48
+ return 0;
49
+ }
50
+ function elapsed(start , end ) { return Math.max(0, end - start); }
51
+
52
+ // Failure/v2 codes and next_action are extensible strings, not safe diagnostic
53
+ // text. Only recognized values enter this summary; no messages or raw envelopes.
54
+ const CLI_CODES = new Set(["unavailable", "timeout", "non-zero", "signal", "unexpected-stderr", "output-limit", "empty-output", "malformed-json", "schema-incompatible", "identity-mismatch", "version-incompatible", "cancelled", "package-local-binary-missing", "unsupported-transition-operation"]);
55
+ const NATIVE_CODES = new Set(["operation_timeout", "invalid_request", "binding_revision_conflict", "internal_error", "cancelled"]);
56
+ function outcome(error ) {
57
+ return safely(() => {
58
+ if (typeof error !== "object" || error === null) return { kind: "unknown" };
59
+ const value = error ;
60
+ if (value.name === "NativeReviewCliError") return { kind: "cli_error", code: typeof value.code === "string" && CLI_CODES.has(value.code) ? value.code : "other" };
61
+ if (value.name !== "NativeReviewIntegrationError" || !value.failureEnvelope) return { kind: "unknown" };
62
+ const failure = value.failureEnvelope;
63
+ const result = { kind: "native_failure", code: typeof failure.code === "string" && NATIVE_CODES.has(failure.code) ? failure.code : "other" };
64
+ if (["preflight", "pre_native", "native_running", "native_committed", "reconciliation"].includes(String(failure.phase))) result.phase = String(failure.phase);
65
+ if (["not_started", "committed", "unknown"].includes(String(failure.mutationOutcome))) result.mutation_outcome = String(failure.mutationOutcome);
66
+ if (typeof failure.retrySafe === "boolean") result.retry_safe = failure.retrySafe;
67
+ if (["stop", "review.status"].includes(String(failure.nextAction))) result.next_action = String(failure.nextAction);
68
+ return result;
69
+ }) ?? { kind: "unknown" };
70
+ }
71
+
72
+ /** The next already-authorized STATUS-bearing tool call consumes the arm. */
73
+ export class StatusTimingDiagnostics {
74
+ armed = false;
75
+ epoch = 0;
76
+ manager ;
77
+ session ;
78
+ last ;
79
+ active = new Set ();
80
+ clock ;
81
+ constructor(clock = () => performance.now()) { this.clock = clock; }
82
+
83
+ reset(ctx ) {
84
+ this.epoch++;
85
+ this.armed = false;
86
+ this.last = undefined;
87
+ for (const host of this.active) host.summary.statuses.length = 0;
88
+ this.active.clear();
89
+ this.manager = ctx?.sessionManager;
90
+ this.session = safely(() => ctx?.sessionManager.getSessionId());
91
+ }
92
+ matches(ctx ) {
93
+ return safely(() => this.manager === ctx.sessionManager && !!this.session && this.session === ctx.sessionManager.getSessionId()) === true;
94
+ }
95
+ command(args , ctx ) {
96
+ if (!this.matches(ctx)) this.reset(ctx);
97
+ const action = args.trim() || "show";
98
+ if (action === "enable") {
99
+ this.reset(ctx);
100
+ this.armed = !!this.session;
101
+ ctx.ui.notify(this.armed
102
+ ? "STATUS timing armed for the next separately authorized STATUS-bearing tool call. No STATUS is invoked or retried. Use /gentle:status-timing show to consult the completed summary."
103
+ : "STATUS timing requires a live session.", "info");
104
+ } else if (action === "disable") {
105
+ this.reset(ctx);
106
+ ctx.ui.notify("STATUS timing disabled; in-memory observations cleared.", "info");
107
+ } else if (action === "show") {
108
+ ctx.ui.notify(this.last ? JSON.stringify(this.last, null, 2) : `No completed STATUS timing summary. Diagnostic is ${this.armed ? "armed" : "off"}.`, "info");
109
+ } else {
110
+ ctx.ui.notify("Unknown /gentle:status-timing sub-action. Use enable, disable, or show.", "warning");
111
+ }
112
+ }
113
+ tool (definition ) {
114
+ return {
115
+ ...definition,
116
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
117
+ const run = () => definition.execute(toolCallId, params, signal, onUpdate, ctx);
118
+ if (!this.matches(ctx)) this.reset(ctx);
119
+ if (!this.armed || this.active.size >= MAX_ACTIVE_OBSERVATIONS) return run();
120
+ const epoch = this.epoch;
121
+ const host = {
122
+ clock: this.clock,
123
+ current: () => this.epoch === epoch && this.matches(ctx),
124
+ claim: () => {
125
+ if (!host.current() || !this.armed) return false;
126
+ this.armed = false;
127
+ return true;
128
+ },
129
+ claimed: false, started: 0, lastNativeEnd: 0,
130
+ summary: { schema: "gentle-pi.status-timing/v1", host_total_ms: 0, dispatch_ms: 0, sidebar_ms: 0, completion_ms: 0, truncated: false, statuses: [] },
131
+ };
132
+ this.active.add(host);
133
+ host.started = now(host);
134
+ return hostScope.run(host, async () => {
135
+ try { return await run(); }
136
+ finally {
137
+ this.active.delete(host);
138
+ safely(() => {
139
+ if (!host.claimed || !host.current()) return;
140
+ const ended = now(host);
141
+ host.summary.host_total_ms = elapsed(host.started, ended);
142
+ host.summary.completion_ms = elapsed(host.lastNativeEnd, ended);
143
+ this.last = host.summary;
144
+ });
145
+ }
146
+ });
147
+ },
148
+ };
149
+ }
150
+ }
151
+
152
+ /** Only called by the existing STATUS entry points; never invokes one itself. */
153
+ export async function observeNativeStatus (timeoutMs , run ) {
154
+ const host = hostScope.getStore();
155
+ if (!host || !safely(host.current)) return run();
156
+ if (!host.claimed) {
157
+ if (!safely(host.claim)) return run();
158
+ host.claimed = true;
159
+ host.summary.dispatch_ms = elapsed(host.started, now(host));
160
+ }
161
+ const sample = { timeout_ms: timeoutMs, total_ms: 0, resolution_ms: 0, adapter_ms: 0, decode_ms: 0, outcome: { kind: "success" } };
162
+ if (host.summary.statuses.length < MAX_STATUSES) host.summary.statuses.push(sample);
163
+ else host.summary.truncated = true;
164
+ const started = now(host);
165
+ return nativeScope.run({ host, sample }, async () => {
166
+ try { return await run(); }
167
+ catch (error) { safely(() => { sample.outcome = outcome(error); }); throw error; }
168
+ finally {
169
+ safely(() => {
170
+ const ended = now(host);
171
+ sample.total_ms = elapsed(started, ended);
172
+ host.lastNativeEnd = ended;
173
+ });
174
+ }
175
+ });
176
+ }
177
+
178
+ function activeNative() {
179
+ const observation = nativeScope.getStore();
180
+ return observation && safely(observation.host.current) ? observation : undefined;
181
+ }
182
+ export function measureStatusSync (stage , run ) {
183
+ const observation = activeNative();
184
+ if (!observation) return run();
185
+ const started = now(observation.host);
186
+ try { return run(); }
187
+ finally { safely(() => { observation.sample[`${stage}_ms`] += elapsed(started, now(observation.host)); }); }
188
+ }
189
+ export async function measureStatusAsync (stage , run ) {
190
+ const observation = activeNative();
191
+ if (!observation) return run();
192
+ const started = now(observation.host);
193
+ try { return await run(); }
194
+ finally { safely(() => { observation.sample[`${stage}_ms`] += elapsed(started, now(observation.host)); }); }
195
+ }
196
+ export function measureStatusSidebar (run ) {
197
+ const host = hostScope.getStore();
198
+ if (!host || !safely(host.current)) return run();
199
+ const started = now(host);
200
+ try { return run(); }
201
+ finally { safely(() => { host.summary.sidebar_ms += elapsed(started, now(host)); }); }
202
+ }
@@ -1,5 +1,6 @@
1
1
  // Generated by scripts/build-runtime-modules.mjs. Do not edit.
2
2
  import { spawn as nodeSpawn } from "node:child_process";
3
+ import { gentleAiIsolationEnv } from "./gentle-ai-binary.mjs";
3
4
 
4
5
  // gentle-ai#4309 owns anonymous usage telemetry end to end: install and
5
6
  // heartbeat events, the notice, every kill switch, and the wire contract.
@@ -129,7 +130,8 @@ export function spawnTelemetryTrigger(options )
129
130
  try {
130
131
  const child = spawnImpl(options.executable, ["telemetry", "trigger", "--json"], {
131
132
  cwd: options.cwd,
132
- env: options.env,
133
+ // S9: a bundled install keeps Gentle AI's telemetry state inside its prefix.
134
+ env: gentleAiIsolationEnv(options.env),
133
135
  detached: true,
134
136
  windowsHide: true,
135
137
  stdio: "ignore",