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
@@ -1,10 +1,10 @@
1
1
  // Generated by scripts/build-runtime-modules.mjs. Do not edit.
2
- import { execFile } from "node:child_process";
2
+ import { execFile, spawn } from "node:child_process";
3
3
  import { chmod, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
4
4
  import { tmpdir } from "node:os";
5
5
  import { dirname, isAbsolute, join, posix, win32 } from "node:path";
6
6
  import { promisify } from "node:util";
7
- import { PackageLocalGentleAiBinaryMissingError, resolveGentleAiBinary } from "./gentle-ai-binary.mjs";
7
+ import { gentleAiIsolationEnv, PackageLocalGentleAiBinaryMissingError, resolveGentleAiBinary } from "./gentle-ai-binary.mjs";
8
8
  import { GENTLE_PI_REVIEW_RELAY_CONTRACT, GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV } from "./review-relay-contract.mjs";
9
9
  import { decodeReviewAssessmentV1, } from "./review-risk-assessment.mjs";
10
10
  import {
@@ -32,6 +32,8 @@ import {
32
32
 
33
33
  } from "./review-integration-v2.mjs";
34
34
 
35
+ import { observeNativeStatus, measureStatusSync, measureStatusAsync } from "./status-timing-diagnostics.mjs";
36
+
35
37
  const execFileAsync = promisify(execFile);
36
38
 
37
39
  // Negotiated review/status responses can carry a complete authority inventory.
@@ -258,6 +260,8 @@ export const NATIVE_REVIEW_RECOVER_DISPOSITION = ["scope_changed", "invalidated"
258
260
 
259
261
 
260
262
 
263
+
264
+
261
265
 
262
266
 
263
267
 
@@ -276,6 +280,13 @@ export const NATIVE_REVIEW_LEGACY_ALIAS_REPAIR = {
276
280
  } ;
277
281
 
278
282
 
283
+ export const NATIVE_REVIEW_ABANDON_REASONS = ["operator_disposition", "retired_schema"] ;
284
+
285
+
286
+ export function isNativeReviewAbandonReason(value ) {
287
+ return value === "operator_disposition" || value === "retired_schema";
288
+ }
289
+
279
290
 
280
291
 
281
292
 
@@ -472,6 +483,24 @@ export const NATIVE_REVIEW_UNACHIEVABLE_LENS_DETAIL_LIMIT = 512;
472
483
  // gentle-pi#638 fail-open capability gate: `review capture-unachievable` is younger than every released binary pinned in NATIVE_CLI_CONTRACTS, so the verb is gated invocation-adjacent instead of by a capability row. An older binary renders Go's exact `unknown review command "capture-unachievable"` refusal (internal/cli/review_facade.go) on stderr with no stdout, so the invocation rejects before any decode and the captured diagnostics are the only place that text survives. Every other failure -- a typed binding-mismatch refusal, a timeout, a decode failure -- is a real outcome the caller must surface, never a capability signal. Duck-typed on purpose: the classifier must survive a duplicated module instance exactly like the error it inspects.
473
484
  const NATIVE_REVIEW_UNKNOWN_UNACHIEVABLE_VERB_REFUSAL = /unknown review command "capture-unachievable"/;
474
485
 
486
+ // A provider older than START lens selection refuses the STATUS preflight
487
+ // flags with this exact typed preflight cause, before reading the repository.
488
+ const NATIVE_REVIEW_UNKNOWN_LENS_FLAG_CAUSE = /^flag provided but not defined: -lenses(?:-reason)?$/;
489
+
490
+ /** Reports a not-started STATUS refusal caused only by an unknown --lenses flag. */
491
+ export function isNativeReviewLensSelectionUnsupported(error ) {
492
+ if (!(error instanceof NativeReviewIntegrationError)) return false;
493
+ const failure = error.failureEnvelope;
494
+ return failure.code === "invalid_request" && failure.mutationOutcome === "not_started" &&
495
+ typeof failure.cause === "string" && NATIVE_REVIEW_UNKNOWN_LENS_FLAG_CAUSE.test(failure.cause);
496
+ }
497
+
498
+ function nativeLensSelectionArguments(request ) {
499
+ if (request.lenses === undefined && request.lensesReason === undefined) return [];
500
+ if (request.lenses === undefined || request.lenses.length === 0 || request.lensesReason === undefined) throw new TypeError("Native lens selection requires lenses and lensesReason together");
501
+ return ["--lenses", request.lenses.join(","), "--lenses-reason", request.lensesReason];
502
+ }
503
+
475
504
  export function isNativeReviewUnachievableVerbRefused(error ) {
476
505
  if (typeof error !== "object" || error === null) return false;
477
506
  const stderr = (error ).diagnostics?.stderr;
@@ -498,6 +527,16 @@ export const NATIVE_UNTRACKED_SCOPE = {
498
527
 
499
528
 
500
529
 
530
+ /**
531
+ * The agent's own review lens choice (gentle-ai START --lenses): canonical
532
+ * lens names in 4R order and one reason. Providers that predate it refuse
533
+ * the flag, and START then falls back to the tier default.
534
+ */
535
+
536
+
537
+
538
+
539
+
501
540
 
502
541
 
503
542
 
@@ -753,6 +792,8 @@ const REVIEW_RISK_SUBJECT_BY_CODE = Object.fre
753
792
  shell_source: "shell scripting",
754
793
  process_boundary: "code that starts other processes",
755
794
  process_scan_limit: "code that starts other processes",
795
+ dangerous_sink: "a dangerous code pattern",
796
+ agent_escalation: "the agent that made this change flagged it as high risk",
756
797
  executable_mode: "an executable permission change",
757
798
  executable_change: "an executable change",
758
799
  configuration_change: "a configuration change",
@@ -1035,6 +1076,13 @@ export const NATIVE_CLI_CONTRACTS = Object.freeze({
1035
1076
  // to /v4 without a review-integration/v2 change. No new negotiated
1036
1077
  // capability is asserted.
1037
1078
  "4.0.0": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
1079
+ // v4.1.1 repeats 4.0.0: the published binary still advertises
1080
+ // capabilities/v2.6 under review-integration/v2. The provider contract moved
1081
+ // from 1.2.0 to 1.3.0 (additive agent lens selection, gentle-ai#5348), with
1082
+ // published tarball SHA-256
1083
+ // b04b6b75d1a446c942a816fbf92932d16f8c3d3001f93159dee2a92bbf2ebe44.
1084
+ // No new negotiated capability identity is asserted.
1085
+ "4.1.1": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
1038
1086
  });
1039
1087
 
1040
1088
 
@@ -1076,21 +1124,98 @@ export class NativeReviewCliError extends Error {
1076
1124
  // makes. It declares the Pi host relay handshake on each spawn: gentle-ai
1077
1125
  // refuses pi admission pre-authority without it (gentle-pi#311 P4), and a
1078
1126
  // single injection point keeps the declaration impossible to forget on any
1079
- // individual operation.
1127
+ // individual operation. In a bundled install it also keeps Gentle AI's state
1128
+ // inside the prefix (gentleAiIsolationEnv, S9).
1080
1129
  export function gentleAiProcessEnvironment(base = process.env) {
1081
- return { ...base, [GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV]: GENTLE_PI_REVIEW_RELAY_CONTRACT };
1130
+ return { ...gentleAiIsolationEnv(base), [GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV]: GENTLE_PI_REVIEW_RELAY_CONTRACT };
1131
+ }
1132
+
1133
+
1134
+
1135
+
1136
+
1137
+ // A bare "taskkill" resolves from the current directory before PATH on
1138
+ // Windows, so a repository shipping its own taskkill.exe would run on any
1139
+ // abort. Always use the System32 binary, from System32 itself.
1140
+ export function windowsTaskkillPath(env = process.env) {
1141
+ const systemRoot = env.SystemRoot || env.windir;
1142
+ return win32.join(systemRoot && win32.isAbsolute(systemRoot) ? systemRoot : "C:\\Windows", "System32", "taskkill.exe");
1143
+ }
1144
+
1145
+ // taskkill walks the tree from a live parent, so it must run before the
1146
+ // parent dies; Node's own abort/timeout kill would orphan the git children.
1147
+ export function createWindowsProcessTreeKiller(env = process.env, spawnProcess = spawn) {
1148
+ const taskkill = windowsTaskkillPath(env);
1149
+ return (pid) => {
1150
+ try {
1151
+ spawnProcess(taskkill, ["/pid", String(pid), "/T", "/F"], { cwd: win32.dirname(taskkill), stdio: "ignore", windowsHide: true }).on("error", () => {});
1152
+ } catch { /* Best effort: the call still settles through its own child. */ }
1153
+ };
1154
+ }
1155
+
1156
+ export const windowsProcessTreeKiller = (pid) => createWindowsProcessTreeKiller()(pid);
1157
+
1158
+ const WINDOWS_TREE_KILL_GRACE_MS = 1_000;
1159
+
1160
+ function abortError(signal ) {
1161
+ return Object.assign(new Error("The operation was aborted", { cause: signal.reason }), { name: "AbortError", code: "ABORT_ERR" });
1082
1162
  }
1083
1163
 
1084
- export function createNodeExecFileAdapter() {
1164
+ function execFileResultFromError(error , timedOut ) {
1165
+ const detail = error ;
1166
+ if (detail.code === "ENOENT" || detail.code === "EACCES" || detail.name === "AbortError") throw error;
1167
+ const outputLimitExceeded = detail.code === "ENOBUFS" || detail.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
1168
+ return { stdout: detail.stdout ?? "", stderr: detail.stderr ?? "", exitCode: typeof detail.code === "number" ? detail.code : 1, signal: detail.signal ?? null, timedOut: !outputLimitExceeded && (timedOut || detail.killed === true), outputLimitExceeded };
1169
+ }
1170
+
1171
+ // Windows: abort and timeout kill the whole process tree instead of only
1172
+ // gentle-ai, with the same observable results as the POSIX path.
1173
+ function windowsExecFile(request , killProcessTree ) {
1174
+ const { signal } = request;
1175
+ if (signal?.aborted) return Promise.reject(abortError(signal));
1176
+ return new Promise((resolve, reject) => {
1177
+ let timedOut = false, aborted = false, pipesCut = false;
1178
+ let timer ;
1179
+ let fallback ;
1180
+ const child = execFile(request.file, [...request.arguments], { cwd: request.cwd, encoding: "utf8", shell: false, windowsHide: true, maxBuffer: request.maxBufferBytes, env: gentleAiProcessEnvironment() }, (error, stdout, stderr) => {
1181
+ if (timer !== undefined) clearTimeout(timer);
1182
+ if (fallback !== undefined) clearTimeout(fallback);
1183
+ signal?.removeEventListener("abort", onAbort);
1184
+ if (aborted && signal) { reject(abortError(signal)); return; }
1185
+ if (error === null) { resolve({ stdout, stderr, exitCode: 0, signal: null, timedOut: pipesCut, outputLimitExceeded: false }); return; }
1186
+ try { resolve(execFileResultFromError(Object.assign(error, { stdout, stderr }), timedOut)); } catch (failure) { reject(failure); }
1187
+ });
1188
+ // If taskkill cannot run (AppLocker, a broken PATH), still end gentle-ai
1189
+ // itself so the deadline holds; its children may then outlive it. A
1190
+ // surviving descendant can still hold the inherited stdout/stderr open,
1191
+ // which would keep execFile from calling back, so cut the pipes too.
1192
+ const killTree = () => {
1193
+ if (child.pid === undefined) { child.kill(); return; }
1194
+ killProcessTree(child.pid);
1195
+ fallback = setTimeout(() => {
1196
+ if (child.exitCode === null && child.signalCode === null) child.kill();
1197
+ pipesCut = true;
1198
+ child.stdout?.destroy();
1199
+ child.stderr?.destroy();
1200
+ }, WINDOWS_TREE_KILL_GRACE_MS);
1201
+ };
1202
+ function onAbort() { if (aborted || timedOut) return; aborted = true; killTree(); }
1203
+ signal?.addEventListener("abort", onAbort, { once: true });
1204
+ if (request.timeoutMs !== undefined && request.timeoutMs > 0) timer = setTimeout(() => { if (aborted) return; timedOut = true; killTree(); }, request.timeoutMs);
1205
+ });
1206
+ }
1207
+
1208
+ export function createNodeExecFileAdapter(options = {}) {
1209
+ if ((options.platform ?? process.platform) === "win32") {
1210
+ const killProcessTree = options.killProcessTree ?? windowsProcessTreeKiller;
1211
+ return (request) => windowsExecFile(request, killProcessTree);
1212
+ }
1085
1213
  return async (request) => {
1086
1214
  try {
1087
1215
  const output = await execFileAsync(request.file, [...request.arguments], { cwd: request.cwd, encoding: "utf8", shell: false, windowsHide: true, timeout: request.timeoutMs, maxBuffer: request.maxBufferBytes, signal: request.signal, env: gentleAiProcessEnvironment() });
1088
1216
  return { stdout: output.stdout, stderr: output.stderr, exitCode: 0, signal: null, timedOut: false, outputLimitExceeded: false };
1089
1217
  } catch (error) {
1090
- const detail = error ;
1091
- if (detail.code === "ENOENT" || detail.code === "EACCES" || detail.name === "AbortError") throw error;
1092
- const outputLimitExceeded = detail.code === "ENOBUFS" || detail.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
1093
- return { stdout: detail.stdout ?? "", stderr: detail.stderr ?? "", exitCode: typeof detail.code === "number" ? detail.code : 1, signal: detail.signal ?? null, timedOut: !outputLimitExceeded && detail.killed === true, outputLimitExceeded };
1218
+ return execFileResultFromError(error, false);
1094
1219
  }
1095
1220
  };
1096
1221
  }
@@ -1187,8 +1312,10 @@ function nativeProcessDiagnostics(operation , code
1187
1312
  }
1188
1313
 
1189
1314
  function parseJson(stdout , operation , mutating , diagnostics ) {
1190
- if (stdout.length === 0) throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.EMPTY_OUTPUT, operation, true, mutating, "native command returned empty output", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.EMPTY_OUTPUT });
1191
- try { return object(JSON.parse(stdout)); } catch { throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.MALFORMED_JSON, operation, true, mutating, "native command returned malformed JSON", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.MALFORMED_JSON }); }
1315
+ return measureStatusSync("decode", () => {
1316
+ if (stdout.length === 0) throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.EMPTY_OUTPUT, operation, true, mutating, "native command returned empty output", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.EMPTY_OUTPUT });
1317
+ try { return object(JSON.parse(stdout)); } catch { throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.MALFORMED_JSON, operation, true, mutating, "native command returned malformed JSON", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.MALFORMED_JSON }); }
1318
+ });
1192
1319
  }
1193
1320
  function decodeNativeMaintenanceResult(value , expectedOperation ) {
1194
1321
  const body = exactObject(value, ["operation", "record"]);
@@ -1219,7 +1346,7 @@ function assertSupportedNextTransitionOperation(body )
1219
1346
  }
1220
1347
  }
1221
1348
  function decode (operation , mutating , callback , diagnostics = nativeProcessDiagnostics(operation, NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE)) {
1222
- try { return callback(); } catch (error) { if (error instanceof NativeReviewCliError) throw error; throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE, operation, true, mutating, "native response is schema incompatible", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE }); }
1349
+ try { return measureStatusSync("decode", callback); } catch (error) { if (error instanceof NativeReviewCliError) throw error; throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE, operation, true, mutating, "native response is schema incompatible", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE }); }
1223
1350
  }
1224
1351
  function decodeReviewStartResponse(value ) {
1225
1352
  const body = object(value);
@@ -1232,50 +1359,6 @@ function decodeReleaseEvidence(value ) {
1232
1359
  for (const field of ["release_tree", "configuration_hash", "generated_artifact_hash", "provenance_hash", "publication_boundary_hash", "evidence_freshness_hash"]) requiredString(release[field]);
1233
1360
  if (release.publication_state !== "sealed" || release.evidence_freshness_state !== "current") throw new Error("invalid release evidence");
1234
1361
  }
1235
- function decodeNonDecidingGateContext(value , expectedGate ) {
1236
- const context = exactObject(value, ["gate"]);
1237
- const gate = enumString(context.gate, NATIVE_GATE);
1238
- if (gate !== expectedGate) throw new Error("native non-deciding gate context does not match the requested gate");
1239
- return { lineageId: "", storeRevision: "", raw: context };
1240
- }
1241
- function decodeGateContext(value ) {
1242
- const context = exactObject(
1243
- value,
1244
- ["gate", "lineage_id", "generation", "base_tree", "candidate_tree", "paths_digest", "fix_delta_hash", "policy_hash", "ledger_hash", "evidence_hash", "base_relationship_valid"],
1245
- ["store_revision", "genesis_revision", "chain_identity", "bundle_digest", "external_evidence", "base_advanced_compatible", "release", "pre_pr_boundary", "denial"],
1246
- );
1247
- const gate = stringValue(context.gate);
1248
- if (gate !== "" && !(NATIVE_GATE ).includes(gate)) throw new Error("invalid gate context gate");
1249
- for (const field of ["lineage_id", "base_tree", "candidate_tree", "paths_digest", "fix_delta_hash", "policy_hash", "ledger_hash", "evidence_hash"]) stringValue(context[field]);
1250
- for (const field of ["store_revision", "genesis_revision", "chain_identity", "bundle_digest"]) if (context[field] !== undefined) stringValue(context[field]);
1251
- nonNegativeInteger(context.generation);
1252
- booleanValue(context.base_relationship_valid);
1253
- if (context.external_evidence !== undefined) enumString(context.external_evidence, ["invalidating", "escalating"]);
1254
- let sanitizedContext = context;
1255
- if (context.denial !== undefined) {
1256
- const denial = exactObject(context.denial, ["stage", "code"]);
1257
- const stage = sanitizeNativeDiagnosticText(requiredString(denial.stage), NATIVE_REVIEW_DENIAL_TEXT_LIMIT);
1258
- const code = sanitizeNativeDiagnosticText(requiredString(denial.code), NATIVE_REVIEW_DENIAL_TEXT_LIMIT);
1259
- if (!isCanonicalProcessString(stage) || !isCanonicalProcessString(code)) throw new Error("non-canonical denial evidence");
1260
- sanitizedContext = { ...context, denial: { stage, code } };
1261
- }
1262
- if (context.pre_pr_boundary !== undefined) {
1263
- const boundary = exactObject(context.pre_pr_boundary, ["source", "selector", "commit"], ["remote", "remote_ref", "remote_identity"]);
1264
- enumString(boundary.source, ["explicit", "publication-default"]); requiredString(boundary.selector); stringValue(boundary.commit);
1265
- for (const field of ["remote", "remote_ref", "remote_identity"]) if (boundary[field] !== undefined) requiredString(boundary[field]);
1266
- }
1267
- if (context.base_advanced_compatible !== undefined) {
1268
- const proof = exactObject(context.base_advanced_compatible, ["status", "compatible", "old_base_tree", "new_base_tree", "original_patch_identity", "delivered_patch_identity", "delivered_paths_digest", "base_advance_paths_digest", "paths_disjoint", "merged_result_tree", "ci_attestation_artifact_hash", "ci_attestation_issuer", "ci_status"]);
1269
- for (const field of ["status", "old_base_tree", "new_base_tree", "original_patch_identity", "delivered_patch_identity", "delivered_paths_digest", "base_advance_paths_digest", "merged_result_tree", "ci_attestation_artifact_hash", "ci_attestation_issuer", "ci_status"]) requiredString(proof[field]);
1270
- booleanValue(proof.compatible); booleanValue(proof.paths_disjoint);
1271
- }
1272
- if (context.release !== undefined) decodeReleaseEvidence(context.release);
1273
- return {
1274
- lineageId: stringValue(context.lineage_id),
1275
- storeRevision: context.store_revision === undefined ? "" : stringValue(context.store_revision),
1276
- raw: sanitizedContext,
1277
- };
1278
- }
1279
1362
  function decodeNativeReviewRecovery(value ) {
1280
1363
  const recovery = exactObject(value, ["predecessor_lineage_id", "predecessor_revision", "disposition", "reason", "actor", "recovered_at"], ["maintainer_authorization"]);
1281
1364
  return {
@@ -1366,7 +1449,7 @@ function decodeNativeReviewStatus(value ) {
1366
1449
  const complete = booleanValue(body.complete);
1367
1450
  const authoritative = booleanValue(body.authoritative);
1368
1451
  if (authoritative && !complete) throw new Error("incomplete inventory cannot be authoritative");
1369
- if (!Array.isArray(body.entries) || !Array.isArray(body.locks)) throw new Error("invalid native status inventory");
1452
+ if (!Array.isArray(body.entries) || !Array.isArray(body.locks) || !Array.isArray(body.diagnostics)) throw new Error("invalid native status inventory");
1370
1453
  return {
1371
1454
  repository: requiredString(body.repository),
1372
1455
  complete,
@@ -1538,7 +1621,10 @@ class NativeReviewPlainCli {
1538
1621
  // membership in the frozen set.
1539
1622
  async execute(operation , cwd , arguments_ , mutating , signal , toleratedStderr = []) {
1540
1623
  let result ;
1541
- try { result = await this.adapter({ file: this.executablePath(operation, mutating), arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal }); }
1624
+ try {
1625
+ const file = measureStatusSync("resolution", () => this.executablePath(operation, mutating));
1626
+ result = await measureStatusAsync("adapter", () => this.adapter({ file, arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal }));
1627
+ }
1542
1628
  catch (error) {
1543
1629
  if (error instanceof NativeReviewCliError) throw nativeError(error.code, operation, mutating, error.message, undefined, error.launchAttempted);
1544
1630
  if (error instanceof Error && error.name === "AbortError") throw nativeError(NATIVE_REVIEW_ERROR_CODE.CANCELLED, operation, mutating, "native process was cancelled");
@@ -1548,7 +1634,7 @@ class NativeReviewPlainCli {
1548
1634
  if (result.outputLimitExceeded) throw nativeError(NATIVE_REVIEW_ERROR_CODE.OUTPUT_LIMIT, operation, mutating, "native process output exceeded limit", result, true, undefined, this.maxBufferBytes);
1549
1635
  if (result.timedOut) throw nativeError(NATIVE_REVIEW_ERROR_CODE.TIMEOUT, operation, mutating, "native process timed out", result);
1550
1636
  if (result.signal) throw nativeError(NATIVE_REVIEW_ERROR_CODE.SIGNAL, operation, mutating, "native process was signalled", result);
1551
- const maintenancePartialFailure = [NATIVE_REVIEW_OPERATION.ABANDON, NATIVE_REVIEW_OPERATION.QUARANTINE_LEGACY, NATIVE_REVIEW_OPERATION.RECONCILE_AUTHORITY, NATIVE_REVIEW_OPERATION.REPAIR_LEGACY_ALIAS].includes(operation) && result.exitCode !== 0;
1637
+ const maintenancePartialFailure = new Set ([NATIVE_REVIEW_OPERATION.ABANDON, NATIVE_REVIEW_OPERATION.QUARANTINE_LEGACY, NATIVE_REVIEW_OPERATION.RECONCILE_AUTHORITY, NATIVE_REVIEW_OPERATION.REPAIR_LEGACY_ALIAS]).has(operation) && result.exitCode !== 0;
1552
1638
  const toleratedNotice = stderrIsTolerated(result.stderr, toleratedStderr);
1553
1639
  if (result.exitCode !== 0 && !maintenancePartialFailure) throw nativeError(NATIVE_REVIEW_ERROR_CODE.NON_ZERO, operation, mutating, "native process failed", result);
1554
1640
  if (result.stderr.trim().length > 0 && !maintenancePartialFailure && !toleratedNotice) throw nativeError(NATIVE_REVIEW_ERROR_CODE.UNEXPECTED_STDERR, operation, mutating, "native process wrote stderr", result);
@@ -1556,6 +1642,10 @@ class NativeReviewPlainCli {
1556
1642
  }
1557
1643
 
1558
1644
  async reviewStatus(request ) {
1645
+ return observeNativeStatus(this.timeoutMs, () => this.observedReviewStatus(request));
1646
+ }
1647
+
1648
+ async observedReviewStatus(request ) {
1559
1649
  const { body: result } = await this.execute(NATIVE_REVIEW_OPERATION.STATUS, request.cwd, ["review", "status", "--cwd", request.cwd], false, request.signal);
1560
1650
  const status = decode(NATIVE_REVIEW_OPERATION.STATUS, false, () => decodeNativeReviewStatus(result));
1561
1651
  if (!await repositoriesMatch(request.cwd, status.repository)) throw nativeError(NATIVE_REVIEW_ERROR_CODE.IDENTITY_MISMATCH, NATIVE_REVIEW_OPERATION.STATUS, false, "native review status repository mismatch");
@@ -1603,9 +1693,13 @@ class NativeReviewPlainCli {
1603
1693
  if (request.maintainerAuthorization !== undefined && (request.maintainerAuthorization.length === 0 || /[\u0000-\u0009\u000b-\u001f\u007f]/.test(request.maintainerAuthorization))) {
1604
1694
  throw new TypeError("Native RECOVER maintainerAuthorization must be a non-empty LF-only binding");
1605
1695
  }
1696
+ if (request.baseRef !== undefined && !isCanonicalProcessString(request.baseRef)) throw new TypeError("Native RECOVER baseRef must be a non-empty, trimmed, NUL-free string");
1697
+ if (request.baseRef !== undefined && request.committedOnly !== true) throw new TypeError("Native RECOVER baseRef requires explicit committedOnly acknowledgement");
1698
+ if (request.baseRef === undefined && request.committedOnly !== undefined) throw new TypeError("Native RECOVER committedOnly requires an explicit baseRef");
1606
1699
  if (!(NATIVE_REVIEW_RECOVER_DISPOSITION ).includes(request.disposition)) throw new TypeError("Native RECOVER disposition must be scope_changed, invalidated, or escalated");
1607
1700
  const { body } = await this.execute(NATIVE_REVIEW_OPERATION.RECOVER, request.cwd, [
1608
1701
  "review", "recover", "--cwd", request.cwd,
1702
+ ...(request.baseRef === undefined ? [] : ["--base-ref", request.baseRef, "--committed-only"]),
1609
1703
  "--predecessor-lineage", request.predecessorLineage,
1610
1704
  "--expected-predecessor-revision", request.expectedPredecessorRevision,
1611
1705
  "--successor-lineage", request.successorLineage,
@@ -1618,6 +1712,7 @@ class NativeReviewPlainCli {
1618
1712
  }
1619
1713
 
1620
1714
  async abandon(request ) {
1715
+ if (!isNativeReviewAbandonReason(request.reason)) throw new TypeError('Native ABANDON reason must be "operator_disposition" or "retired_schema"');
1621
1716
  for (const [name, value] of [["lineage", request.lineage], ["expectedRevision", request.expectedRevision], ["snapshotIdentity", request.snapshotIdentity], ["actor", request.actor], ["reason", request.reason]] ) {
1622
1717
  if (!isCanonicalProcessString(value)) throw new TypeError(`Native ABANDON ${name} must be a non-empty, trimmed, NUL-free string`);
1623
1718
  }
@@ -1716,6 +1811,10 @@ export function nativeReviewAbandonAuthorization(request
1716
1811
  // so the native v2 gate verifies an exact eight-line binding (schema, lineage,
1717
1812
  // revision, snapshot_identity, reason, captured_lens_results, findings_present,
1718
1813
  // actor) — there is no evidence_records_present line to derive or relay.
1814
+ // capturedLensResults must arrive verbatim from the native authority
1815
+ // inventory projection (issue #1159): the gate recomputes this line from its
1816
+ // own ordered record, whose entries carry the ordinal prefix, so caller- or
1817
+ // facade-authored lens names cannot reproduce it.
1719
1818
  return [
1720
1819
  "gentle-ai.review-abandon-authorization/v2",
1721
1820
  `lineage=${request.lineage}`,
@@ -1856,10 +1955,11 @@ function splitNativeConsentInvocation(invocation ) {
1856
1955
  continue;
1857
1956
  }
1858
1957
  // Provider invocations are not shell commands. Keep every path backslash
1859
- // verbatim, including quoted UNC and drive-root paths. Outside quotes,
1860
- // only a backslash before whitespace joins a space-containing token.
1958
+ // verbatim, including quoted UNC and drive-root paths. Outside quotes, a
1959
+ // backslash escapes only whitespace or a quote, so a space-containing token
1960
+ // and a POSIX-quoted apostrophe (`'it'\''s'`) split exactly.
1861
1961
  const next = source[index + 1];
1862
- if (character === "\\" && quote === undefined && next !== undefined && /\s/.test(next)) {
1962
+ if (character === "\\" && quote === undefined && next !== undefined && (/\s/.test(next) || next === "'" || next === '"')) {
1863
1963
  escaping = true;
1864
1964
  started = true;
1865
1965
  continue;
@@ -2102,7 +2202,7 @@ export class NativeReviewCliV216 {
2102
2202
  ) {
2103
2203
  let result ;
2104
2204
  try {
2105
- result = await this.adapter({ file: path, arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal });
2205
+ result = await measureStatusAsync("adapter", () => this.adapter({ file: path, arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal }));
2106
2206
  } catch (error) {
2107
2207
  if (error instanceof Error && error.name === "AbortError") throw nativeError(NATIVE_REVIEW_ERROR_CODE.CANCELLED, operation, mutating, "native process was cancelled");
2108
2208
  throw nativeError(NATIVE_REVIEW_ERROR_CODE.UNAVAILABLE, operation, mutating, "native process could not start");
@@ -2118,7 +2218,7 @@ export class NativeReviewCliV216 {
2118
2218
  const body = parseJson(result.stdout, operation, mutating, diagnostics);
2119
2219
  if (result.exitCode !== 0) {
2120
2220
  try {
2121
- throw new NativeReviewIntegrationError(decodeReviewFailureV2(body));
2221
+ throw new NativeReviewIntegrationError(measureStatusSync("decode", () => decodeReviewFailureV2(body)));
2122
2222
  } catch (error) {
2123
2223
  if (error instanceof NativeReviewIntegrationError) throw error;
2124
2224
  throw nativeError(NATIVE_REVIEW_ERROR_CODE.NON_ZERO, operation, mutating, "native negotiated operation failed without a valid failure envelope", result);
@@ -2137,7 +2237,7 @@ export class NativeReviewCliV216 {
2137
2237
  signal ,
2138
2238
  toleratedStderr = [],
2139
2239
  ) {
2140
- return this.invoke(operation, cwd, arguments_, mutating, signal, this.executablePath(operation, mutating), toleratedStderr);
2240
+ return this.invoke(operation, cwd, arguments_, mutating, signal, measureStatusSync("resolution", () => this.executablePath(operation, mutating)), toleratedStderr);
2141
2241
  }
2142
2242
 
2143
2243
  async start(request ) {
@@ -2150,7 +2250,7 @@ export class NativeReviewCliV216 {
2150
2250
  // drift; Pi never rebuilds that vector from request fields.
2151
2251
  const projection = request.projection ?? "workspace";
2152
2252
  const selection = nativeUntrackedSelection(request);
2153
- const status = await this.targetStatus({
2253
+ const statusRequest = {
2154
2254
  cwd: request.cwd,
2155
2255
  projection,
2156
2256
  ...(request.baseRef === undefined ? {} : { baseRef: request.baseRef, committedOnly: true }),
@@ -2159,7 +2259,19 @@ export class NativeReviewCliV216 {
2159
2259
  ...(request.intendedUntrackedSelection === undefined ? {} : { intendedUntrackedSelection: request.intendedUntrackedSelection }),
2160
2260
  agent: "pi",
2161
2261
  ...(request.signal === undefined ? {} : { signal: request.signal }),
2162
- });
2262
+ };
2263
+ const lensSelection = request.lenses === undefined && request.lensesReason === undefined
2264
+ ? {}
2265
+ : { lenses: request.lenses, lensesReason: request.lensesReason };
2266
+ let status ;
2267
+ try {
2268
+ status = await this.targetStatus({ ...statusRequest, ...lensSelection });
2269
+ } catch (error) {
2270
+ // Phase A: a provider that predates lens selection still reviews,
2271
+ // with the tier default, instead of refusing the candidate.
2272
+ if (Object.keys(lensSelection).length === 0 || !isNativeReviewLensSelectionUnsupported(error)) throw error;
2273
+ status = await this.targetStatus(statusRequest);
2274
+ }
2163
2275
  const transition = status.nextTransition?.kind === "execute" && status.nextTransition.execute?.operation === "review.start"
2164
2276
  ? status.nextTransition.execute
2165
2277
  : undefined;
@@ -2256,6 +2368,10 @@ export class NativeReviewCliV216 {
2256
2368
  }
2257
2369
 
2258
2370
  async targetStatus(request ) {
2371
+ return observeNativeStatus(this.timeoutMs, () => this.observedTargetStatus(request));
2372
+ }
2373
+
2374
+ async observedTargetStatus(request ) {
2259
2375
  if (request.baseRef !== undefined && !isCanonicalProcessString(request.baseRef)) throw new TypeError("Native STATUS baseRef must be a non-empty, trimmed, NUL-free string");
2260
2376
  if (request.baseRef !== undefined && request.committedOnly !== true) throw new TypeError("Native STATUS baseRef requires explicit committedOnly acknowledgement");
2261
2377
  if (request.baseRef === undefined && request.committedOnly !== undefined) throw new TypeError("Native STATUS committedOnly requires an explicit baseRef");
@@ -2294,10 +2410,11 @@ export class NativeReviewCliV216 {
2294
2410
  ...(request.baseRef === undefined ? [] : ["--base-ref", request.baseRef, "--committed-only"]),
2295
2411
  ...(request.lineageId === undefined ? [] : ["--lineage", request.lineageId]),
2296
2412
  ...(request.agent === undefined ? [] : ["--agent", request.agent]),
2413
+ ...nativeLensSelectionArguments(request),
2297
2414
  "--next-transition",
2298
- ] : ["review", "status", "--cwd", request.cwd, ...submittedTokens, ...forwardedBaseRef, ...forwardedLineage];
2415
+ ] : ["review", "status", "--cwd", request.cwd, ...submittedTokens, ...forwardedBaseRef, ...forwardedLineage, ...nativeLensSelectionArguments(request)];
2299
2416
  const execution = await this.negotiated(NATIVE_REVIEW_OPERATION.STATUS, request.cwd, statusArguments, false, request.signal);
2300
- assertSupportedNextTransitionOperation(execution.body);
2417
+ measureStatusSync("decode", () => assertSupportedNextTransitionOperation(execution.body));
2301
2418
  return decode(NATIVE_REVIEW_OPERATION.STATUS, false, () => decodeReviewStatusV3(execution.body));
2302
2419
  }
2303
2420
 
@@ -2486,13 +2603,17 @@ export class NativeReviewCliV216 {
2486
2603
  // mutually exclusive with a path.
2487
2604
 
2488
2605
  async reviewStatus(request ) {
2606
+ return observeNativeStatus(this.timeoutMs, () => this.observedReviewStatus(request));
2607
+ }
2608
+
2609
+ async observedReviewStatus(request ) {
2489
2610
  const execution = await this.invoke(
2490
2611
  NATIVE_REVIEW_OPERATION.STATUS,
2491
2612
  request.cwd,
2492
2613
  ["review", "status", "--cwd", request.cwd],
2493
2614
  false,
2494
2615
  request.signal,
2495
- this.executablePath(NATIVE_REVIEW_OPERATION.STATUS, false),
2616
+ measureStatusSync("resolution", () => this.executablePath(NATIVE_REVIEW_OPERATION.STATUS, false)),
2496
2617
  );
2497
2618
  const status = decode(NATIVE_REVIEW_OPERATION.STATUS, false, () => decodeNativeReviewStatus(execution.body));
2498
2619
  if (!await repositoriesMatch(request.cwd, status.repository)) throw nativeError(NATIVE_REVIEW_ERROR_CODE.IDENTITY_MISMATCH, NATIVE_REVIEW_OPERATION.STATUS, false, "native review status repository mismatch");