gentle-pi 4.2.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/README.md +340 -138
  2. package/assets/agents/gentle-ai-explore.md +1 -0
  3. package/assets/agents/gentle-ai-verify.md +6 -4
  4. package/assets/agents/gentle-ai-worker.md +7 -1
  5. package/assets/design-canvas/canvas.css +648 -0
  6. package/assets/design-canvas/canvas.js +1497 -0
  7. package/assets/design-canvas/index.html +88 -0
  8. package/assets/design-canvas/inspector.js +290 -0
  9. package/assets/orchestrator-delegation-reference.md +163 -0
  10. package/assets/orchestrator-delegation.md +25 -176
  11. package/assets/orchestrator-tracking.md +1 -1
  12. package/assets/orchestrator-verification.md +14 -8
  13. package/assets/orchestrator-writer.md +5 -1
  14. package/assets/orchestrator.md +11 -11
  15. package/bin/engram-guard +1 -1
  16. package/bin/gentle-shell.mjs +237 -139
  17. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
  18. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/README.md +7 -0
  19. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/manifest.json +11 -2
  20. package/contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md +92 -0
  21. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-capabilities.baseline.json +8 -1
  22. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-roles.baseline.json +1 -1
  23. package/docs/assets/diagrams/agent-orchestration.svg +54 -15
  24. package/docs/assets/diagrams/gentleman-workflow.svg +69 -11
  25. package/docs/assets/diagrams/measured-routing.svg +49 -0
  26. package/docs/assets/diagrams/native-review.svg +52 -12
  27. package/docs/assets/diagrams/odd-workflow.svg +128 -66
  28. package/docs/assets/diagrams/permission-modes.svg +51 -0
  29. package/docs/assets/features/audio-notifications.png +0 -0
  30. package/docs/assets/features/command-palette.png +0 -0
  31. package/docs/bundled-install.md +65 -30
  32. package/docs/delegated-verification.md +16 -8
  33. package/docs/gentle-agents-activity.md +16 -6
  34. package/docs/gentle-music.md +61 -0
  35. package/docs/gentle-shell-uninstall.md +8 -1
  36. package/docs/gentle-shell.md +80 -9
  37. package/docs/install-wizard.md +37 -6
  38. package/docs/permission-modes.md +8 -3
  39. package/docs/readme-reference.md +120 -28
  40. package/docs/session-profile-format.md +26 -0
  41. package/docs/voice.md +259 -0
  42. package/docs/yolo-mode.md +38 -10
  43. package/extensions/ask-user-choice.ts +4 -1
  44. package/extensions/codegraph-tools.ts +68 -6
  45. package/extensions/gentle-agents.ts +270 -48
  46. package/extensions/gentle-ai.ts +490 -113
  47. package/extensions/gentle-design.ts +158 -0
  48. package/extensions/gentle-herdr-activity.ts +2 -3
  49. package/extensions/gentle-music.ts +289 -0
  50. package/extensions/gentle-shell.ts +359 -85
  51. package/extensions/gentle-todo.ts +43 -9
  52. package/extensions/gentle-voice.ts +11 -0
  53. package/extensions/quiet-tools.ts +1 -1
  54. package/extensions/session-scratch.ts +384 -0
  55. package/extensions/skill-registry.ts +63 -8
  56. package/extensions/startup-banner.ts +2 -3
  57. package/lib/agent-home.ts +8 -3
  58. package/lib/agent-profiles.ts +26 -0
  59. package/lib/agents-config.ts +35 -0
  60. package/lib/agents-protocol.ts +60 -1
  61. package/lib/agents-runner.ts +107 -9
  62. package/lib/agents-session-transport.ts +101 -6
  63. package/lib/agents-view.ts +3 -1
  64. package/lib/agents-widget.ts +14 -1
  65. package/lib/auto-permission-memory.ts +48 -18
  66. package/lib/auto-permission-runtime.ts +21 -6
  67. package/lib/bounded-writer-admission.ts +51 -0
  68. package/lib/clipboard-image.ts +147 -0
  69. package/lib/command-palette-catalog.ts +2 -4
  70. package/lib/commit-slice-signal.ts +100 -0
  71. package/lib/context-backstop.ts +120 -0
  72. package/lib/delivered-test-lint.ts +80 -0
  73. package/lib/design-canvas-server.ts +947 -0
  74. package/lib/design-comments.ts +437 -0
  75. package/lib/design-handoffs.ts +313 -0
  76. package/lib/design-open-browser.ts +58 -0
  77. package/lib/design-sources.ts +120 -0
  78. package/lib/destructive-command-guard.ts +120 -9
  79. package/lib/external-editor.ts +31 -0
  80. package/lib/gauge-theme.ts +146 -0
  81. package/lib/gentle-ai-binary.ts +28 -7
  82. package/lib/gentle-design.ts +513 -0
  83. package/lib/gentle-shell-bundled.ts +146 -36
  84. package/lib/gentle-shell-import-pi.ts +233 -0
  85. package/lib/gentle-shell-launcher.ts +154 -15
  86. package/lib/gentle-shell-startup-update.ts +61 -9
  87. package/lib/gentle-shell-uninstall.ts +51 -2
  88. package/lib/herdr-activity.ts +24 -0
  89. package/lib/image-attachments.ts +150 -0
  90. package/lib/inprocess-reviewer.ts +33 -2
  91. package/lib/nan-provider.ts +3 -0
  92. package/lib/native-review-cli.ts +20 -5
  93. package/lib/notification-audio-native.ts +149 -13
  94. package/lib/notification-audio.ts +4 -0
  95. package/lib/notification-pulse-worker.ts +82 -18
  96. package/lib/notification-service.ts +5 -1
  97. package/lib/orchestrator-discovery.ts +5 -1
  98. package/lib/orchestrator-presence.ts +87 -8
  99. package/lib/orchestrator-scope.ts +31 -3
  100. package/lib/orchestrator-state.ts +11 -3
  101. package/lib/permission-mode-policy.ts +2 -2
  102. package/lib/provider-contract-bundle.ts +99 -53
  103. package/lib/review-candidate-view-owner.ts +29 -19
  104. package/lib/review-candidate-view.ts +36 -2
  105. package/lib/review-host-relay.ts +11 -2
  106. package/lib/review-reminder-receipt.ts +24 -1
  107. package/lib/review-risk-assessment.ts +43 -38
  108. package/lib/review-session-standing-permission.ts +3 -1
  109. package/lib/session-profile-append-controller.ts +713 -0
  110. package/lib/session-profile-disk-reader.ts +182 -0
  111. package/lib/session-profile-freeze.ts +30 -1
  112. package/lib/session-profile-persistence.ts +15 -7
  113. package/lib/session-scratch.ts +523 -0
  114. package/lib/session-worktree-registry.ts +7 -0
  115. package/lib/shell-bar.ts +29 -28
  116. package/lib/shell-changes-view.ts +80 -13
  117. package/lib/shell-changes.ts +81 -5
  118. package/lib/shell-gauge.ts +49 -6
  119. package/lib/shell-prompt.ts +4 -4
  120. package/lib/shell-sidebar-layout.ts +26 -6
  121. package/lib/shell-todo.ts +24 -5
  122. package/lib/shell-usage-view.ts +5 -1
  123. package/lib/shell-usage.ts +7 -7
  124. package/lib/verifier-admission.ts +30 -0
  125. package/lib/vim-editor-adapter.ts +11 -8
  126. package/lib/voice-artifacts.ts +188 -0
  127. package/lib/voice-command.ts +576 -0
  128. package/lib/voice-install.ts +455 -0
  129. package/lib/voice-platform.ts +99 -0
  130. package/lib/voice-push-to-talk.ts +262 -0
  131. package/lib/voice-session.ts +460 -0
  132. package/lib/voice-settings.ts +50 -0
  133. package/lib/voice-ui.ts +158 -0
  134. package/lib/yolo-session-policy.ts +58 -22
  135. package/lib/yolo-temp-scratch.ts +69 -0
  136. package/package.json +1 -2
  137. package/runtime/gentle-ai-binary.mjs +28 -7
  138. package/runtime/gentle-shell-bundled.mjs +145 -35
  139. package/runtime/gentle-shell-import-pi.mjs +234 -0
  140. package/runtime/gentle-shell-launcher.mjs +152 -13
  141. package/runtime/gentle-shell-startup-update.mjs +61 -9
  142. package/runtime/gentle-shell-uninstall.mjs +51 -2
  143. package/runtime/native-review-cli.mjs +19 -4
  144. package/runtime/review-risk-assessment.mjs +43 -38
  145. package/scripts/build-runtime-modules.mjs +1 -0
  146. package/scripts/bundled-engram-migration.mjs +119 -0
  147. package/scripts/gentle-ai-installer.mjs +10 -10
  148. package/scripts/installer-downloads.mjs +7 -7
  149. package/scripts/installer-preflight.mjs +5 -3
  150. package/scripts/installer-server.mjs +14 -4
  151. package/scripts/installer-windows.mjs +37 -7
  152. package/scripts/main-channel.mjs +94 -19
  153. package/scripts/pi-update.mjs +34 -6
  154. package/scripts/run-test-suite.mjs +5 -0
  155. package/scripts/verify-package-files.mjs +43 -13
  156. package/scripts/voice-worker.cjs +476 -0
  157. package/skills/design/SKILL.md +110 -0
  158. package/skills/gentle-ai/SKILL.md +3 -3
  159. package/skills/work-unit-commits/SKILL.md +5 -3
  160. package/tests/agent-profiles.test.ts +24 -0
  161. package/tests/agent-risk-escalation.test.ts +37 -10
  162. package/tests/agents-config.test.ts +61 -0
  163. package/tests/agents-fake-child.ts +8 -1
  164. package/tests/agents-protocol.test.ts +32 -0
  165. package/tests/agents-runner.test.ts +485 -7
  166. package/tests/agents-session-transport.test.ts +53 -3
  167. package/tests/ask-user-choice.test.ts +30 -0
  168. package/tests/auto-mode.test.ts +17 -0
  169. package/tests/auto-permission-memory-diagnostics.test.ts +56 -0
  170. package/tests/auto-permission-runtime.test.ts +85 -1
  171. package/tests/autonomous-guard.test.ts +142 -0
  172. package/tests/bounded-writer-admission.test.ts +11 -3
  173. package/tests/bundled-install.test.ts +3 -3
  174. package/tests/card-content-policy.test.ts +19 -9
  175. package/tests/child-safety.test.ts +39 -0
  176. package/tests/clipboard-image.test.ts +114 -0
  177. package/tests/codegraph-tools.test.ts +123 -2
  178. package/tests/command-palette.test.ts +15 -5
  179. package/tests/commit-slice-signal.test.ts +162 -0
  180. package/tests/context-backstop.test.ts +225 -0
  181. package/tests/delegate-for-reason-contract.test.ts +92 -24
  182. package/tests/delivered-test-lint.test.ts +105 -0
  183. package/tests/design-canvas-assets.test.ts +710 -0
  184. package/tests/design-canvas-server.test.ts +1096 -0
  185. package/tests/design-comments.test.ts +201 -0
  186. package/tests/design-handoffs.test.ts +282 -0
  187. package/tests/design-open-browser.test.ts +70 -0
  188. package/tests/design-sources.test.ts +132 -0
  189. package/tests/destructive-command-guard.test.ts +85 -1
  190. package/tests/engram-safety.test.ts +132 -57
  191. package/tests/external-editor.test.ts +84 -0
  192. package/tests/fixtures/primary-session-prompt/gentleman.golden.md +143 -0
  193. package/tests/fixtures/provider-contract-bundle/v1.4.0/README.md +29 -0
  194. package/tests/fixtures/provider-contract-bundle/v1.4.0/manifest.json +83 -0
  195. package/tests/fixtures/provider-contract-bundle/v1.4.0/orchestration/pi.md +70 -0
  196. package/tests/fixtures/provider-contract-bundle/v1.4.0/routing/pi.md +92 -0
  197. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/lens.schema.json +16 -0
  198. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/refuter.schema.json +1 -0
  199. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/targeted-validator.schema.json +1 -0
  200. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/lens.json +1 -0
  201. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/refuter.json +1 -0
  202. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/targeted-validator.json +1 -0
  203. package/tests/gauge-theme.test.ts +120 -0
  204. package/tests/gentle-agents.test.ts +645 -21
  205. package/tests/gentle-ai-binary.test.ts +1 -1
  206. package/tests/gentle-ai-child-guards.test.ts +61 -0
  207. package/tests/gentle-ai-doctor-herdr.test.ts +94 -0
  208. package/tests/gentle-ai-installer.test.ts +51 -51
  209. package/tests/gentle-ai.test.ts +292 -13
  210. package/tests/gentle-design.test.ts +1313 -0
  211. package/tests/gentle-music.test.ts +335 -0
  212. package/tests/gentle-shell-bin.test.ts +163 -0
  213. package/tests/gentle-shell-bundled.test.ts +392 -176
  214. package/tests/gentle-shell-import-pi.test.ts +262 -0
  215. package/tests/gentle-shell-launcher.test.ts +107 -2
  216. package/tests/gentle-shell-startup-update.test.ts +77 -1
  217. package/tests/gentle-shell-uninstall.test.ts +89 -0
  218. package/tests/gentle-shell.test.ts +225 -17
  219. package/tests/gentle-todo.test.ts +190 -0
  220. package/tests/headless-delivery-question.test.ts +58 -0
  221. package/tests/herdr-activity.test.ts +34 -1
  222. package/tests/image-attachments.test.ts +119 -0
  223. package/tests/image-paste-chips.test.ts +236 -0
  224. package/tests/inprocess-reviewer.test.ts +55 -2
  225. package/tests/installer-bundles.test.ts +12 -6
  226. package/tests/installer-posix-bootstrap.test.ts +8 -8
  227. package/tests/installer-preflight.test.ts +16 -0
  228. package/tests/installer-server.test.ts +10 -2
  229. package/tests/installer-windows-bootstrap.test.ts +94 -7
  230. package/tests/installer-windows-diagnostics.test.ts +111 -0
  231. package/tests/main-channel-upgrade.test.ts +232 -2
  232. package/tests/main-channel.test.ts +140 -21
  233. package/tests/nan-provider.test.ts +36 -0
  234. package/tests/native-review-capability-contract.test.ts +17 -4
  235. package/tests/native-review-parity.test.ts +23 -2
  236. package/tests/no-self-pause-contract.test.ts +5 -2
  237. package/tests/notification-audio-native.test.ts +128 -9
  238. package/tests/notification-customize.test.ts +17 -5
  239. package/tests/odd-integration.test.ts +5 -1
  240. package/tests/odd-routing-canonical-ratchet.test.ts +128 -162
  241. package/tests/odd-routing-contract.test.ts +115 -30
  242. package/tests/orchestrator-budget.test.ts +22 -8
  243. package/tests/orchestrator-discovery.test.ts +29 -0
  244. package/tests/orchestrator-presence.test.ts +100 -2
  245. package/tests/orchestrator-rdd-ownership.test.ts +3 -1
  246. package/tests/orchestrator-scope.test.ts +80 -0
  247. package/tests/orchestrator-state.test.ts +29 -0
  248. package/tests/orchestrator-status-publication-contract.test.ts +12 -8
  249. package/tests/package-files.test.ts +34 -0
  250. package/tests/package-manifest.test.ts +24 -20
  251. package/tests/permission-mode-policy.test.ts +10 -1
  252. package/tests/pi-update-launcher.test.ts +21 -2
  253. package/tests/pi-update.test.ts +99 -4
  254. package/tests/primary-session-prompt.test.ts +101 -0
  255. package/tests/provider-contract-bundle.test.ts +118 -0
  256. package/tests/provider-contract-mirror.test.ts +24 -0
  257. package/tests/quiet-tool-rendering.test.ts +23 -0
  258. package/tests/rdd-aware-verification-contract.test.ts +29 -11
  259. package/tests/rdd-status-line.test.ts +79 -0
  260. package/tests/review-agent-end-preflight.test.ts +6 -0
  261. package/tests/review-candidate-view.test.ts +49 -0
  262. package/tests/review-contract-prompt.test.ts +5 -2
  263. package/tests/review-controller-native-routing.test.ts +14 -1
  264. package/tests/review-controller.test.ts +1 -1
  265. package/tests/review-host-relay-routing.test.ts +147 -3
  266. package/tests/review-host-relay.test.ts +9 -2
  267. package/tests/review-json-arguments.test.ts +20 -3
  268. package/tests/review-loadout-rdd-off-sdk.test.ts +258 -0
  269. package/tests/review-owner-identity.test.ts +132 -0
  270. package/tests/review-relay-transport-agent.test.ts +26 -0
  271. package/tests/review-reminder-receipt.test.ts +31 -1
  272. package/tests/review-risk-assessment.test.ts +163 -76
  273. package/tests/review-session-standing-permission-controller.test.ts +14 -1
  274. package/tests/review-session-standing-permission.test.ts +27 -1
  275. package/tests/run-test-suite.test.ts +15 -0
  276. package/tests/runtime-harness.mjs +16 -6
  277. package/tests/session-changes-shell.test.ts +1 -1
  278. package/tests/session-changes.test.ts +2 -1
  279. package/tests/session-profile-append-controller-fork.test.ts +458 -0
  280. package/tests/session-profile-append-controller-pi.test.ts +290 -0
  281. package/tests/session-profile-append-controller.test.ts +731 -0
  282. package/tests/session-profile-disk-reader-pi.test.ts +183 -0
  283. package/tests/session-profile-disk-reader.test.ts +311 -0
  284. package/tests/session-profile-freeze.test.ts +23 -0
  285. package/tests/session-scratch.test.ts +1011 -0
  286. package/tests/session-subject-routing-contract.test.ts +2 -1
  287. package/tests/session-worktree-registry.test.ts +13 -1
  288. package/tests/shell-bar.test.ts +21 -2
  289. package/tests/shell-changes-view.test.ts +137 -1
  290. package/tests/shell-changes.test.ts +148 -1
  291. package/tests/shell-gauge.test.ts +42 -0
  292. package/tests/shell-prompt.test.ts +3 -3
  293. package/tests/shell-render-cache.test.ts +134 -1
  294. package/tests/shell-sidebar-layout.test.ts +50 -0
  295. package/tests/short-check-routing-contract.test.ts +1 -1
  296. package/tests/skill-collision-prefixes.test.ts +3 -1
  297. package/tests/skill-registry-watcher.test.ts +17 -5
  298. package/tests/skill-registry.test.ts +47 -3
  299. package/tests/small-task-inline-contract.test.ts +37 -0
  300. package/tests/standalone-config-home.test.ts +324 -0
  301. package/tests/startup-banner.test.ts +5 -4
  302. package/tests/startup-home-repo.test.ts +34 -0
  303. package/tests/support/orchestrator-modules.ts +1 -0
  304. package/tests/task-size-routing-contract.test.ts +70 -10
  305. package/tests/test-scratch-contract.test.ts +102 -0
  306. package/tests/test-strength-contract.test.ts +36 -1
  307. package/tests/tool-batching-contract.test.ts +64 -0
  308. package/tests/verification-proportionality.test.ts +46 -0
  309. package/tests/verifier-admission.test.ts +154 -0
  310. package/tests/verify-agent-contract.test.ts +8 -8
  311. package/tests/voice-command.test.ts +773 -0
  312. package/tests/voice-install.test.ts +323 -0
  313. package/tests/voice-platform.test.ts +176 -0
  314. package/tests/voice-push-to-talk.test.ts +444 -0
  315. package/tests/voice-session.test.ts +434 -0
  316. package/tests/voice-settings.test.ts +49 -0
  317. package/tests/voice-ui.test.ts +97 -0
  318. package/tests/voice-worker.test.ts +445 -0
  319. package/tests/weak-model-economy-prompt.test.ts +32 -0
  320. package/tests/windows-hidden-processes.test.ts +30 -2
  321. package/tests/writer-edit-surface-scope.test.ts +3 -0
  322. package/tests/writer-feature-document-admission.test.ts +156 -0
  323. package/tests/yolo-customize.test.ts +5 -1
  324. package/tests/yolo-mode.test.ts +19 -1
  325. package/tests/yolo-session-policy-capture.test.ts +104 -0
  326. package/tests/yolo-temp-scratch.test.ts +71 -0
  327. package/scripts/mirror-odd-routing.mjs +0 -242
  328. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/orchestration/pi.md +0 -0
  329. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/lens.schema.json +0 -0
  330. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/refuter.schema.json +0 -0
  331. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  332. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/lens.json +0 -0
  333. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/refuter.json +0 -0
  334. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/targeted-validator.json +0 -0
package/docs/voice.md ADDED
@@ -0,0 +1,259 @@
1
+ # Optional local voice dictation
2
+
3
+ Voice dictation is optional. It transcribes speech locally with NVIDIA Parakeet
4
+ through sherpa-onnx, with no API key and no cloud inference. Everything happens
5
+ from `/gentle:voice`; nothing described here runs at startup.
6
+
7
+ ## Quick path
8
+
9
+ 1. Run `/gentle:voice` and choose **Download voice dictation**. The
10
+ confirmation lists each component, its size, the destination directory,
11
+ the licenses and the platform limitations. Esc during the download cancels it.
12
+ 2. Run `/gentle:voice` again and choose **Start dictation**. Speak, then press
13
+ Enter to stop. Esc cancels at any point.
14
+ 3. The transcript is pasted into the editor draft at the cursor. It is never
15
+ sent automatically. Pi collapses pastes over 1,000 characters or 10 lines into
16
+ a `[paste #N …]` marker; the full text is retained and expanded when you submit.
17
+ Long transcripts are therefore not displayed as editable text in the draft.
18
+ 4. Or hold Space in the editor: push-to-talk is turned on by a successful
19
+ download. See [Push-to-talk](#push-to-talk-hold-space).
20
+
21
+ ## The command
22
+
23
+ | Topic | Behavior |
24
+ |-------|----------|
25
+ | Menu | Built-in select and confirm dialogs. **Show status and notices** reports the target, both components, limitations and licenses. **Disable push-to-talk (hold Space)** appears while it is on; **Enable push-to-talk (hold Space)** appears only when voice is installed. |
26
+ | Download | Only after the confirmation; runtime first, then the model. A cancelled download keeps components that already finished. |
27
+ | Dictation | A compact overlay shows preparing, recording, transcribing. Enter stops recording; the overlay stays until the transcript is ready. |
28
+ | Recording limit | After 2 minutes recording stops by itself, and the transcript is pasted without a key press. |
29
+ | Insertion | The recognized text goes through Pi's editor paste pipeline, which may collapse long text into a paste marker. A blank result pastes nothing. |
30
+ | One at a time | A second `/gentle:voice` while a download or dictation runs is refused with a notice. A Space hold during either one types spaces as usual. |
31
+ | Cancel and shutdown | Esc, reload or session shutdown aborts the download or cancels the session. A transcript that arrives afterwards is dropped. |
32
+ | Where it runs | Interactive TUI parent sessions only. RPC, print, JSON and delegated child sessions refuse it. No model-callable tool exists. |
33
+ | Errors | Fixed messages with the action to take, such as installing ALSA or the Visual C++ runtime. Gentle AI never runs those installers. |
34
+
35
+ The command is tested with fake dialogs, downloads and sessions only. No real
36
+ download, native load or microphone recording has been exercised through it.
37
+
38
+ ## Push-to-talk (hold Space)
39
+
40
+ Hold Space in the editor to record; release it to stop. The transcript is
41
+ pasted at the cursor and never sent. Esc cancels while recording or
42
+ transcribing. A one-line footer status shows preparing, recording and
43
+ transcribing; no overlay opens, so the editor keeps the focus.
44
+
45
+ | Topic | Behavior |
46
+ |-------|----------|
47
+ | Toggle | Stored as `voice-settings.json` (`gentle-pi.voice-settings/v1`) in the Gentle AI config home. A successful download turns it on; the `/gentle:voice` menu turns it off and on, effective immediately. A missing or malformed file means off. |
48
+ | When it is armed | Only with the toggle on, in an interactive TUI parent session. Delegated child sessions, RPC, print and JSON modes never arm it. Reload or shutdown disarms it and cancels a running dictation. |
49
+ | Startup | Reads the toggle file only. No voice files are checked, nothing is downloaded or loaded, and no worker starts. |
50
+ | First hold | Checks that the runtime and model are installed; the spaces keep typing meanwhile. If voice is not installed, a notice points to `/gentle:voice` and push-to-talk stays off for the session; the saved choice is kept. |
51
+ | One at a time | A hold while a download or dictation runs types spaces normally. |
52
+
53
+ ### How a hold is recognized
54
+
55
+ With Pi's keyboard mode, terminals report Space as plain text and never report
56
+ its release, so push-to-talk relies on the operating system's key auto-repeat.
57
+ Pi lets an extension drop or replace a key as it arrives, but not type one
58
+ later, so every space reaches the editor unchanged:
59
+
60
+ - **Typing:** a tap, several spaces, an indent or Space then a letter are
61
+ typed exactly as without push-to-talk, with no delay.
62
+ - **Hold:** auto-repeat has a long initial delay followed by short, regular
63
+ repeats; typing does not. A hold needs a first gap of 180 to 1000 ms (macOS
64
+ allows at least about 225 ms, X11 defaults to 660 ms), then three repeats
65
+ each at most 250 ms apart and regular (the slowest at most 1.6 times the
66
+ fastest), with the first gap at least 1.5 times the slowest repeat. Each
67
+ space must have added exactly one space to the draft. The following repeats
68
+ are turned into Backspace, through the editor's normal key path, until the
69
+ spaces the hold typed are removed; when the hold followed a word, one space
70
+ stays as the separator before the transcript. Later repeats are dropped.
71
+ While recording starts, those spaces can be seen briefly.
72
+ - **Release:** no repeat for 2.5 times the observed repeat interval, between
73
+ 150 and 400 ms. The decision waits until input already queued is handled, so
74
+ a repeat delayed by a busy moment is not taken for a release.
75
+ - **Guards:** only single-space input counts. Pastes, escape sequences and
76
+ multi-character input pass through and end the attempt. If the draft changes
77
+ in any other way, for example Vim NORMAL mode where Space only moves the
78
+ cursor, autocomplete, or another key, the hold is not confirmed, or erasing
79
+ stops, and nothing else is removed. Spaces that were in the draft before the
80
+ hold are never removed.
81
+
82
+ Limitations: the timing depends on the keyboard repeat settings. An initial
83
+ repeat delay under 180 ms or over 1000 ms, or a repeat interval over 250 ms,
84
+ prevents a hold from being recognized. Five or more spaces typed with a pause
85
+ and then a very even fast rhythm could still look like a hold. Whether a space
86
+ precedes the hold is judged as if typing at the end of the draft; in the
87
+ middle of existing spaces the separator may be missing. Esc pressed while
88
+ repeats are still arriving, and a release right as the hold is confirmed, can
89
+ leave a stray space. Recording starts once the hold is confirmed and the
90
+ microphone is open; the model is verified while you speak (see below). A hold
91
+ while a dialog or overlay has the focus does not change the draft and is
92
+ ignored. Push-to-talk is tested with fake terminals, editors and sessions
93
+ only.
94
+
95
+ ## What gets downloaded
96
+
97
+ All versions, sizes and digests are pinned in `lib/voice-artifacts.ts`.
98
+
99
+ | Component | Source | Size | Verified by |
100
+ |-----------|--------|------|-------------|
101
+ | `sherpa-onnx-node@1.13.8` | npm registry | 12 KB | `dist.integrity` (SHA-512) |
102
+ | `sherpa-onnx-<platform>-<arch>@1.13.8` | npm registry | 9–14 MB | `dist.integrity` (SHA-512) |
103
+ | `node-cpal@1.0.0` (microphone capture) | npm registry | 7.7 MB | `dist.integrity` (SHA-512) |
104
+ | Parakeet TDT 0.6B v3, int8 | Hugging Face, commit `2bda32ec70b0` | about 670 MB | per-file size and SHA-256 |
105
+
106
+ The model is the sherpa-onnx export
107
+ `csukuangfj/sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8`: `encoder.int8.onnx`,
108
+ `decoder.int8.onnx`, `joiner.int8.onnx` and `tokens.txt`.
109
+
110
+ ## Where it is stored
111
+
112
+ Everything lives in `<Gentle AI config home>/voice` (by default
113
+ `~/.pi/gentle-ai/voice`; see `gentlePiConfigHome()`):
114
+
115
+ ```text
116
+ voice/
117
+ runtime/sherpa-onnx-1.13.8-node-cpal-1.0.0-<target>/node_modules/
118
+ sherpa-onnx-node/
119
+ sherpa-onnx-<platform>-<arch>/
120
+ node-cpal/
121
+ models/parakeet-tdt-0.6b-v3-int8-2bda32ec70b0/
122
+ ```
123
+
124
+ The platform package sits next to `sherpa-onnx-node` because its loader resolves
125
+ `../sherpa-onnx-<platform>-<arch>/sherpa-onnx.node`. node-cpal loads
126
+ `bin/<platform>-<arch>/index.node` from its own directory. The runtime directory
127
+ names both native versions, so a runtime installed with other pins is never
128
+ reported as the current one.
129
+
130
+ ## How an install stays safe
131
+
132
+ - **Explicit only.** Importing the voice modules performs no filesystem or
133
+ network access. Status checks never create the voice directory.
134
+ - **Streamed and bounded.** Each file is hashed while it streams to disk and is
135
+ cut off as soon as it exceeds its pinned size. A wrong length or digest fails
136
+ the install.
137
+ - **Pinned redirects.** npm tarballs are fetched without redirects. Hugging Face
138
+ redirects are followed only over HTTPS to the paths observed for this revision:
139
+ `huggingface.co/api/resolve-cache/...` and `us.aws.cdn.hf.co/xet-bridge-us/...`.
140
+ Any other host, including other CDN regions, is refused.
141
+ - **Cancellable and time-bounded.** An install can be aborted. It also stops
142
+ when no data arrives for 60 seconds, or when it runs past 20 minutes for the
143
+ runtime or 2 hours for the model.
144
+ - **Checked archives.** Every tarball is validated before anything is
145
+ extracted. Only regular files under `package/` are accepted, within the
146
+ registry's file count and unpacked size. Links, directories, traversal,
147
+ duplicates and Windows device names are refused. No package script runs, and
148
+ no downloaded code is loaded during installation.
149
+ - **Atomic publication.** Work happens in a private `.staging-*` directory. The
150
+ install marker is written last, and one rename publishes the component. A
151
+ failure or cancellation removes only that staging directory. An existing
152
+ installation is never partially overwritten: a valid one is kept as is, and an
153
+ invalid one is replaced only by a complete, verified copy.
154
+
155
+ ## Dictation session
156
+
157
+ `lib/voice-session.ts` records one dictation and returns its text. It never
158
+ submits the text or touches the editor; the caller decides what to do with it.
159
+
160
+ - **Separate process.** `start()` runs `scripts/voice-worker.cjs` with the same
161
+ Node executable, from the runtime directory, with no shell. Microphone
162
+ capture, the model load and the decode all happen in that process, so a
163
+ native crash or a long decode cannot freeze Gentle AI. Importing the module or
164
+ creating a session does nothing until `start()`.
165
+ - **Checked before loading.** `start()` first requires both components to be
166
+ reported installed. The worker then checks the files again before it loads
167
+ the native code that uses them: every entry in the runtime and model
168
+ directories must be a plain directory or regular file, owned by the current
169
+ user and not writable by everyone (ownership and modes are not checked on
170
+ Windows). Each required runtime file must be present, and each model file
171
+ must match its pinned size and SHA-256. The digests are recomputed on every
172
+ session, which reads the whole model (about 670 MB). Directories above the
173
+ voice home are not checked. These checks catch broken or swapped installs;
174
+ they cannot stop someone with the same user account from replacing the files,
175
+ which is the same as running code as that user.
176
+ - **Order.** So that the first words are recorded, the worker validates the
177
+ runtime files, loads node-cpal and opens the microphone, then reports
178
+ `recording` and verifies the model digests while recording, reading each
179
+ file in chunks so capture keeps running. sherpa-onnx and the model are loaded
180
+ only after every digest matched and recording stopped, because that load is
181
+ synchronous and would stall capture. A mismatch found while recording closes
182
+ the microphone at once and reports the model as invalid; one found after stop
183
+ is reported instead of a transcript. Cancel stops the verification and loads
184
+ nothing. The tradeoff: the recognizer load (about 0.85 seconds measured on an
185
+ Apple Silicon Mac) now happens after stop, so a transcript takes that much
186
+ longer to arrive, and any verification still running when you stop is
187
+ awaited too.
188
+ - **Lifecycle.** A session is used once: `start()` resolves when recording
189
+ begins, `stop()` resolves with `{ text, truncated }`, and `cancel()` (or
190
+ `dispose()`) discards everything, including a transcript that arrives late.
191
+ `start()` resolves while the worker keeps recording; `stop()`, `cancel()` and
192
+ a failed `start()` settle only after the worker has exited. A worker that does
193
+ not exit within 2 seconds of finishing or being cancelled is killed. If the parent
194
+ disappears, the worker sees its input close and stops capturing.
195
+ - **Bounds.** Recording stops by itself after 120 seconds, and the worker then
196
+ transcribes what it has (`truncated: true`). Starting (runtime checks and
197
+ opening the microphone) and transcribing (the rest of the model
198
+ verification, the model load and the decode) each have a 120-second limit. Audio is kept in memory
199
+ only up to the recording limit. All of these are options.
200
+ - **Audio.** The worker opens the default input device through node-cpal,
201
+ preferring 32-bit float samples and otherwise using the device's default
202
+ integer or float format. It mixes all channels down to mono and resamples to
203
+ 16 kHz in JavaScript (averaging when downsampling, linear interpolation when
204
+ upsampling). No SoX or FFmpeg is used. Less than 0.1 seconds of audio is
205
+ reported as empty, and a blank transcript as no speech.
206
+ - **Errors.** The worker reports a fixed code only, and the session turns it
207
+ into a fixed message. Native error text, file paths and environment values
208
+ are never included, and the worker's stderr is discarded.
209
+ - **Environment.** The worker gets only home, temp, locale and audio-session
210
+ variables (`HOME`, `USERPROFILE`, `SystemRoot`, `windir`, `TEMP`, `TMP`,
211
+ `TMPDIR`, `XDG_RUNTIME_DIR`, `PULSE_SERVER`, `PULSE_COOKIE`, `LANG`, `LC_ALL`,
212
+ `LC_CTYPE`). Credentials, `PATH`, `NODE_OPTIONS` and loader variables are
213
+ dropped.
214
+ - **Protocol.** JSON lines with schema `gentle.voice/v1`. The parent sends
215
+ `start` (with paths computed from the pinned manifests), then `stop` or
216
+ `cancel`. The worker answers `recording`, `transcribing`, and one final
217
+ `result` or `error`. Any other message, an unknown field, more than 16 lines
218
+ or a line over 64 KB stops the worker.
219
+
220
+ The session has been tested only with scripted fake workers and with the real
221
+ worker driving fake capture and recognizer modules. It has not loaded the real
222
+ native modules, opened a real microphone or transcribed real speech.
223
+
224
+ ## Platform support
225
+
226
+ All five targets get the pinned runtime and a `node-cpal@1.0.0` capture binary
227
+ built for their architecture (checked from the binary headers, not by loading).
228
+
229
+ | Target | Prerequisites (not checked before loading) |
230
+ |--------|--------------------------------------------|
231
+ | macOS arm64 | macOS 11 or newer |
232
+ | macOS x64 | macOS 10.12 or newer |
233
+ | Linux x64, glibc 2.34+ | ALSA `libasound.so.2` |
234
+ | Linux arm64, glibc 2.34+ | ALSA `libasound.so.2` |
235
+ | Windows x64 | Microsoft Visual C++ runtime (`vcruntime140.dll`) |
236
+
237
+ node-cpal requires Node 22 or newer, and a dictation session refuses to start
238
+ on an older Node. Gentle AI never downloads or installs the Visual C++ runtime;
239
+ a missing runtime is expected to surface as a runtime load failure, which has
240
+ not been observed on hardware. `/gentle:voice` then names the runtime to install.
241
+
242
+ None of these targets has been verified on real hardware yet: native loading,
243
+ library paths and microphone capture have not been executed. Linux with a glibc
244
+ older than 2.34 is unsupported and the diagnosis names the version found.
245
+ Windows ARM64 has no native sherpa-onnx package and is unsupported. musl Linux
246
+ is unsupported. WSL is reported as unproven.
247
+
248
+ ## Licenses and attribution
249
+
250
+ - **Model:** Parakeet TDT 0.6B v3 by NVIDIA, licensed under CC-BY-4.0. The int8
251
+ sherpa-onnx export is by csukuangfj. The export repository has no license file
252
+ of its own; its license is taken from the upstream NVIDIA model.
253
+ - **Runtime:** sherpa-onnx, Apache-2.0. The npm tarballs ship no LICENSE or
254
+ NOTICE file. The platform packages bundle onnxruntime (MIT) without its
255
+ license text.
256
+ - **Capture:** node-cpal 1.0.0, ISC. The tarball ships no LICENSE or NOTICE file. Its prebuilt
257
+ binaries link Rust crates whose notices are not included.
258
+
259
+ These gaps are unresolved and are listed in `VOICE_NOTICES`.
package/docs/yolo-mode.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # YOLO permission
2
2
 
3
- > 🚀 **Full speed, destructive actions still ask.** YOLO lets the agent implement, commit, push and open PRs within the already authorized task without asking each time, which suits long autonomous runs. Destructive operations still require fresh confirmation.
3
+ > 🚀 **Full speed, destructive actions still ask.** YOLO lets the agent implement, commit, push and open PRs within the already authorized task without asking each time, which suits long autonomous runs. Destructive operations still require fresh confirmation; the one exception is recursive removal of the session's own temp scratch (see [Temp scratch removal](#temp-scratch-removal)).
4
4
 
5
5
  Use `/gentle:yolo enable` in the interactive primary Pi TUI to stop repeated permission questions for ordinary work **within your already authorized task**. YOLO is **OFF** unless you select it or save YOLO as the user-global default. [Normal, Auto and global defaults](permission-modes.md) are managed separately from the live-session toggle. The active status and a separate editor widget both say **🚀 YOLO ON 🔥 — destructive confirmations remain**, even when Gentle Shell hides its status layout.
6
6
 
@@ -9,12 +9,27 @@ Use `/gentle:yolo enable` in the interactive primary Pi TUI to stop repeated per
9
9
  | `/gentle:yolo enable` | Select YOLO for this live session and workspace/clone. |
10
10
  | `/gentle:yolo disable` | Select Normal immediately and clear the YOLO indicators; saved defaults stay unchanged. |
11
11
  | `/gentle:yolo status` | Show the current state without activating. |
12
- | `/gentle:yolo` | Open a menu titled **🚀 Gentle YOLO 🔥 — full speed, destructive actions still ask (current: ON\|OFF)** with `enable`, `disable` and `status`. Cancelling changes nothing. Without an interactive menu UI it behaves like `status`. |
12
+ | `/gentle:yolo` | Open a menu titled **🚀 Gentle YOLO 🔥 — standing permission (current: ON\|OFF)** with `enable`, `disable` and `status`. Cancelling changes nothing. Without an interactive menu UI it behaves like `status`. |
13
13
 
14
14
  Invalid arguments leave state unchanged.
15
15
 
16
16
  The Session command palette also includes YOLO. Interactive selection requires an eligible primary TUI, live session identity and identifiable workspace. Outside Git, the canonical workspace directory supplies identity. There is no model-callable activation tool. `/gentle:mode default yolo` optionally saves a trusted user-global default for new primary sessions; `/gentle:mode default normal` removes that elevated default. Neither command changes the current mode.
17
17
 
18
+ ## Super Yolo
19
+
20
+ **💥 Super Yolo removes every permission prompt; the only line left is private material.** Select it with `/gentle:mode super-yolo` or save it for new sessions with `/gentle:mode default super-yolo`.
21
+
22
+ While it is active:
23
+
24
+ - Every shell-guard confirmation is waived, data loss included: recursive `rm`, SQL drops, ordinary pushes, rebases, forced branch deletion, `npm publish` and configured guardrail `confirm`/`block` rules all run without dialogs. Code-level hard denies stay blocked (`git reset --hard`, forced clean, force push); see below.
25
+ - The Auto classifier does not run.
26
+ - The **sensitive-path guard stays hard-blocked**: `read`, `write` and `edit` of `.env` files, `.ssh`, keychains, `*.pem/*.key/*.p12/*.pfx`, `secrets/` directories and cloud credential files are refused in every mode, Super Yolo included.
27
+ - Code-level catastrophic denies remain: `rm -rf /`, `rm -rf ~`, `--no-preserve-root`, `git reset --hard`, forced clean and force push are blocked without a dialog.
28
+ - `ask_user` tools, provider consent, maintenance/recovery authorization and opaque-token decisions are never automatically answered.
29
+ - The `/gentle:yolo` toggle shows **ON** while Super Yolo is active and turning it off returns to Normal. Children never inherit Super Yolo; their bounded delegation and child guards are unchanged.
30
+
31
+ Super Yolo is deliberate maximum autonomy for a workspace you accept the consequences of. The shell recognition remains lexical, not a sandbox: unrecognized script forms, custom tools and prompt injection can still reach destructive effects, and OS-level isolation remains your real containment boundary.
32
+
18
33
  ## Configuration menu path
19
34
 
20
35
  1. Open `/gentle:customize` → **Editor**.
@@ -23,23 +38,35 @@ The Session command palette also includes YOLO. Interactive selection requires a
23
38
 
24
39
  Navigation, the read-only preview and Escape never grant permission. The preview reminds you that ordinary scoped commits/push/PR are covered, destructive confirmations remain, review consent is unchanged and reload resets the live grant. **UNAVAILABLE** means the live primary owner or eligible session/workspace cannot be used; selecting it cannot activate YOLO.
25
40
 
26
- The live YOLO toggle does not save preferences. The separate **Default permissions** rows can save Normal, Auto or YOLO in the dedicated user-global permission store, never a repository preference or visual profile. **Session permissions** selects the current mode without changing that default. Closing the menu removes its observers and cancels unfinished menu activation; it does not revoke an already completed activation. Reload or session replacement invalidates old menu callbacks and resets the live permission.
41
+ The live YOLO toggle does not save preferences. The separate **Default permissions** rows can save Normal, Auto, YOLO or Super Yolo in the dedicated user-global permission store, never a repository preference or visual profile. **Session permissions** selects the current mode without changing that default. Closing the menu removes its observers and cancels unfinished menu activation; it does not revoke an already completed activation. Reload or session replacement invalidates old menu callbacks and resets the live permission.
27
42
 
28
43
  ## What activation authorizes
29
44
 
30
45
  YOLO supplies standing human permission for ordinary already-scoped implementation, checks, commits, non-force pushes and PR creation. Its active-only system instruction qualifies the default commit/push/PR confirmation clauses; it also tells the primary agent to make ordinary reversible implementation choices without needless interviews.
31
46
 
32
- The shell guard's routine default confirmation is skipped for reversible delivery only. Every guarded segment of the command is judged on its own, so `git commit -m "..." && git push -u origin feature`, `git push && gh pr create --fill`, `git -C <dir> push origin main` and `git fetch && git rebase origin/main && git push` run without a prompt, while the other segments keep their ordinary guard (`git push && rm -rf data` still asks for the `rm`). A push segment is eligible with plain remote/ref arguments, `src:dst` refspecs with both sides present, and the options `-u`/`--set-upstream`, `-q`/`--quiet`, `-v`/`--verbose`, `--tags`, `--follow-tags`, `--no-verify` and `--porcelain`. A rebase segment is eligible with plain refs, `--onto`, `--continue`, `--abort`, `--skip` and `--autostash`; it is recoverable from the reflog. Anything that deletes or rewrites a remote ref, publishes, or runs arbitrary commands never qualifies: `--delete`/`-d`, `:branch` and `+branch` refspecs, `--mirror`, `--prune`, `--all`, every force spelling (hard-blocked), `--receive-pack`/`--exec`, `--repo`, `-o`, `sudo`, shell wrappers such as `sh -c`, interactive or `--exec` rebases, `git branch -D`, `pi remove` and `npm publish`. Full command evaluation, including the recognized data-loss guards, runs first. Explicit push or rebase confirmations or blocks in either configuration layer remain restrictions, including when legacy persistent autonomy or its environment override would ignore them. YOLO never changes persistent `autonomousMode`.
47
+ The shell guard's routine default confirmation is skipped for reversible delivery only. Every guarded segment of the command is judged on its own, so `git commit -m "..." && git push -u origin feature`, `git push && gh pr create --fill`, `git -C <dir> push origin main` and `git fetch && git rebase origin/main && git push` run without a prompt, while the other segments keep their ordinary guard (`git push && rm -rf data` still asks for the `rm`). A push segment is eligible with plain remote/ref arguments, `src:dst` refspecs with both sides present, and the options `-u`/`--set-upstream`, `-q`/`--quiet`, `-v`/`--verbose`, `--tags`, `--follow-tags`, `--no-verify` and `--porcelain`. A rebase segment is eligible with plain refs, `--onto`, `--continue`, `--abort`, `--skip` and `--autostash`; it is recoverable from the reflog. Either segment may also carry output redirections that write nothing (`2>&1`, `>&2`, `>/dev/null`, `2>/dev/null`, `&>/dev/null`) and may be piped into the read-only filters `tail`, `head`, `cat`, `grep` and `wc` with plain or literally quoted arguments, so `git push -u origin feature 2>&1 | tail -5` runs without a prompt. Redirection to a file, a pipe into any other command (`| sh`, `| tee file`, `| xargs`) and a trailing background `&` still ask: Git output carries text the remote controls. Anything that deletes or rewrites a remote ref, publishes, or runs arbitrary commands never qualifies: `--delete`/`-d`, `:branch` and `+branch` refspecs, `--mirror`, `--prune`, `--all`, every force spelling (hard-blocked), `--receive-pack`/`--exec`, `--repo`, `-o`, `sudo`, shell wrappers such as `sh -c`, interactive or `--exec` rebases, `git branch -D`, `pi remove` and `npm publish`. Full command evaluation, including the recognized data-loss guards, runs first. Explicit push or rebase confirmations or blocks in either configuration layer remain restrictions, including when legacy persistent autonomy or its environment override would ignore them. YOLO never changes persistent `autonomousMode`.
33
48
 
34
49
  ## What still requires a decision
35
50
 
36
- - Destructive operations retain fresh confirmations or hard denial: recognized SQL DROP/TRUNCATE and broad deletion, recursive removal, destructive Git and other guarded operations are not waived. Explicit configured confirmations/blocks and sensitive-path protection remain.
51
+ - Destructive operations retain fresh confirmations or hard denial: recognized SQL DROP/TRUNCATE and broad deletion, recursive removal, destructive Git and other guarded operations are not waived. The only exception is [temp scratch removal](#temp-scratch-removal). Explicit configured confirmations/blocks and sensitive-path protection remain.
37
52
  - Current explicit human restrictions, repository policy, project trust and authorized scope still apply. Scope expansion, privacy-sensitive disclosure and genuinely unresolved consequential product choices require a human decision.
38
53
  - Ambiguous destinations or credentials remain unresolved. YOLO does not invent a remote, deployment destination, account or credential, nor authorize discovery or reuse of ambient credentials.
39
54
  - `ask_user` tools, provider consent, maintenance/recovery authorization and opaque-token choices are never automatically answered. Native review, RDD and review standing permission remain separate and unchanged.
40
55
 
41
56
  Children retain only their bounded delegated scope. They cannot activate or inherit YOLO, and receive no independent delivery permission.
42
57
 
58
+ ### Temp scratch removal
59
+
60
+ In the primary session with YOLO active, a recursive `rm` whose every target is the session's own temp scratch runs without the data-loss confirmation. For example, `rm -rf /tmp/tmp.8phya1BpFr /tmp/build.log; git log --oneline -1` runs directly when both targets qualify. A target qualifies only when all of these hold:
61
+
62
+ - The segment is a plain `rm` with no wrapper (`sudo`, `env`, `xargs`, assignments), not inside `sh -c` or `find -exec`, and without `--no-preserve-root`.
63
+ - The removal runs before anything else in the command. The command must start with one or more such `rm` segments joined by `;`, `&&`, `||` or newlines. Nothing may precede them: no `mv`, `cp`, `ln`, redirection, subshell or other command. They also may not be backgrounded with `&` or piped with `|`. Otherwise an earlier step could put other data into a target that looked like scratch when the command was checked, so `mv ~/data /tmp/tmp.abcdef1234 && rm -rf /tmp/tmp.abcdef1234` still asks. Commands after the removals, such as `cd … && git log`, are allowed.
64
+ - The target is an unquoted literal absolute path made only of letters, digits, `/` and `._-+@%=:,`. It has no `$`, backtick, `~`, glob, brace or redirection characters, no `.`/`..` components and no trailing `/`.
65
+ - The target is exactly one path component under `/tmp`, `/var/tmp` or the resolved `os.tmpdir()`. The temp root itself never qualifies, and a temp root that is `/`, your home directory, one of its ancestors or a directory inside your home is ignored.
66
+ - When the command is checked, the target is missing, a symlink, a regular file you own, or a directory you own named like `mktemp` output (`tmp.` followed by six or more letters or digits).
67
+
68
+ Anything else keeps asking, including another user's directory, a directory not named like `mktemp` output, an unreadable path and `$TMPDIR/...`. Every other effect in the same command keeps its ordinary guard: `rm -rf /tmp/tmp.abcdef1234 && rm -rf data` still asks, and `rm -rf /tmp/tmp.abcdef1234 ~/foo` is still hard-blocked. Normal and Auto modes still ask, and children still block. The check runs when the command is admitted; it does not lock the path until `rm` runs.
69
+
43
70
  ## Lifetime and limits
44
71
 
45
72
  Live permission is held in the extension instance, bound to the exact live SessionManager, session ID and canonical Git common-directory identity, or canonical workspace directory outside Git. Sibling worktrees in one clone may retain it; an unrelated workspace or session identity cannot. Scope loss revokes rather than hiding permission until you return.
@@ -50,7 +77,7 @@ This is permission guidance and shell-guard defense in depth, **not a sandbox or
50
77
 
51
78
  ## Recognized data-loss boundary
52
79
 
53
- Recognized destructive database and broad filesystem commands require **fresh primary confirmation** and are **blocked in package-owned children**, independently of YOLO and existing autonomous-mode configuration.
80
+ Recognized destructive database and broad filesystem commands require **fresh primary confirmation** and are **blocked in package-owned children**, independently of YOLO and existing autonomous-mode configuration. The only YOLO exception is [temp scratch removal](#temp-scratch-removal).
54
81
 
55
82
  | Recognized executable form | Primary session | Delegated child |
56
83
  |---|---|---|
@@ -58,12 +85,13 @@ Recognized destructive database and broad filesystem commands require **fresh pr
58
85
  | SQL `DELETE FROM` without `WHERE`, or a trailing literal `WHERE 1=1` / `WHERE TRUE` | Fresh confirmation | Block |
59
86
  | Recursive `rm` (`-r`, `-R`, combined short flags, `--recursive`), `find -delete`, recognized destructive `find -exec/-execdir` and `xargs` invocations | Fresh confirmation | Block |
60
87
  | Recursive `rm` targeting `/`, `/*`, `.`, `..`, home or home descendants | Hard block | Block |
88
+ | Temp scratch recursive removal ([conditions](#temp-scratch-removal)) | Waived under YOLO (primary only); fresh confirmation otherwise | Block |
61
89
  | Git force push, hard reset, forced clean | Hard block | Block |
62
90
  | Git forced branch deletion, reset/clean/restore/rebase, checkout with `--` or force, stash drop/clear | Existing primary policy remains | Block |
63
91
 
64
92
  SQL recognition is limited to literal arguments passed to `psql`, `mysql`, `mariadb`, or `sqlite3`, literal `echo`/`printf` pipelines into those clients, and simple named heredocs. SQL string literals and comments are excluded from the new SQL recognition; printing or searching destructive prose alone does not trigger it.
65
93
 
66
- Command recognition follows simple shell separators, pipelines and parenthesized command groups. It handles executable paths, leading assignments, common `env`, `sudo`, `command`, `exec`, `nohup`, `timeout` and `xargs` wrappers, and literal `sh/bash/zsh/dash -c` payloads (up to five recognition levels). Every recognized operation in a compound command participates; an earlier allowed delivery action cannot hide later data loss.
94
+ Command recognition follows simple shell separators, pipelines and parenthesized command groups. It handles executable paths, leading assignments, common `env`, `sudo`, `command`, `exec`, `nohup`, `timeout` and `xargs` wrappers, and literal `sh/bash/zsh/dash -c` payloads (up to five recognition levels). A command after the unquoted reserved words `then`, `else`, `elif`, `do`, `{`, `!`, `if`, `while` and `until` is recognized like a command at the start of a segment, so `if true; then rm -rf data; fi` and `for d in a b; do rm -rf "$d"; done` are guarded like `rm -rf data`, and a root or home target behind them is still hard-blocked. Backtick substitutions outside single quotes (also inside double quotes) and literal `eval` payloads, quoted or bare words, are recognized like literal `sh -c` payloads within the same recognition levels; `eval "$x"` and other expanded payloads are not resolved. A recursive `rm` behind a reserved word, in backticks or in `eval` never qualifies for [temp scratch removal](#temp-scratch-removal). Every recognized operation in a compound command participates; an earlier allowed delivery action cannot hide later data loss.
67
95
 
68
96
  Ordinary builds, tests, read-only SQL, predicate-bounded literal deletes, Git status/log and plain pushes are unchanged by the shared data-loss classifier. A build command that explicitly includes recursive deletion is still guarded.
69
97
 
@@ -71,15 +99,15 @@ Ordinary builds, tests, read-only SQL, predicate-bounded literal deletes, Git st
71
99
 
72
100
  1. Existing hard denies and newly recognized hard-deny forms win across the command.
73
101
  2. Explicit configured blocks retain their existing semantics and win before data-loss confirmation. Active YOLO additionally honors explicit push restrictions in either configuration layer.
74
- 3. Recognized database/filesystem data loss always requires fresh confirmation, even when autonomous mode or a matched delivery action is configured to allow.
102
+ 3. Recognized database/filesystem data loss always requires fresh confirmation, even when autonomous mode or a matched delivery action is configured to allow. The only exception is [temp scratch removal](#temp-scratch-removal) under active YOLO in the primary session.
75
103
  4. No UI, cancellation, a non-true answer or a dialog error cannot authorize execution. Approval is not cached; permission and Herdr blocker lifecycle events remain balanced.
76
104
 
77
105
  Children receive the lightweight `child-safety.ts` entry alongside `child-context.ts`; they do not need the full primary extension. The safety entry registers nothing outside `GENTLE_PI_AGENTS_CHILD=1`, preventing duplicate primary prompts during package auto-discovery. If the full primary extension is explicitly loaded in a child, its destructive-command path also blocks instead of prompting.
78
106
 
79
107
  ### Detection limitations
80
108
 
81
- - This is deterministic lexical recognition, **not** shell or SQL evaluation. Aliases, functions, dynamically assembled commands, variable/command expansion inside quoted payloads, unusual wrapper flags, complex heredocs and deeply nested wrappers can evade recognition. Simple recursive removal of a variable target is guarded, but its expanded target cannot reliably be identified as a hard-deny path.
82
- - Guarded-command confirmations and hard-denied patterns ignore text that only appears as an argument of a known data command (`grep`, `rg`, `cat`, `echo`, `printf`, `head`, `tail`, `jq`, `sort`, `uniq`, `wc` and similar), including the body of a real heredoc fed to one. Searching a file for a push or printing a reset command does not open a dialog. The exemption applies only when every command in the line is such a data command or an inert one (`cd`, `pwd`, `test`, `mkdir`, `touch`), optionally after plain `VAR=value` assignments or a flagless `env`. Anything else anywhere in the line keeps the full lexical match: any other command or wrapper (`sed`, `sh`, `python`, `awk`, `xargs`, `sudo`, `exec`, `at`, `./script`), command or process substitution, a `<<` that is not an unquoted heredoc, and quoting the tokenizer cannot account for. `sed` is excluded because GNU sed can execute its script.
109
+ - This is deterministic lexical recognition, **not** shell or SQL evaluation. Aliases, functions, other reserved words (`time`, `function`, `coproc`), commands inside double-quoted `$(...)`, dynamically assembled commands, variable/command expansion inside quoted payloads, unusual wrapper flags, complex heredocs and deeply nested wrappers can evade recognition. Simple recursive removal of a variable target is guarded, but its expanded target cannot reliably be identified as a hard-deny path.
110
+ - Guarded-command confirmations and hard-denied patterns ignore text that only appears as an argument of a known data command (`grep`, `rg`, `cat`, `echo`, `printf`, `head`, `tail`, `jq`, `sort`, `uniq`, `wc` and similar), including the body of a real heredoc fed to one. Searching a file for a push or printing a reset command does not open a dialog. The exemption applies only when every command in the line is such a data command or an inert one (`cd`, `pwd`, `test`, `mkdir`, `touch`), optionally after plain `VAR=value` assignments or a flagless `env`. Plain assignment segments (`f=x;`) and simple command substitutions whose body is itself only data or inert commands (`f=$(ls -t *.jsonl | head -40); grep -l "git push" $f`) keep the exemption. Anything else anywhere in the line keeps the full lexical match: any other command or wrapper (`sed`, `sh`, `python`, `awk`, `xargs`, `sudo`, `exec`, `at`, `./script`), a command substitution that runs anything else or nests quotes, expansions or another substitution, a command substitution together with a heredoc, process substitution, backticks, a `<<` that is not an unquoted heredoc, and quoting the tokenizer cannot account for. `sed` is excluded because GNU sed can execute its script.
83
111
  - Scripts, migration files, redirected SQL files, arbitrary interpreter programs (Python/Node/etc.), encoded payloads and remote execution are not inspected. Non-recursive wildcard deletion, file truncation, other database clients and nontrivial tautological SQL predicates are not comprehensively classified. A `WHERE` clause is not proof of bounded impact.
84
112
  - Only `bash` tool calls reach this data-loss boundary, including nested calls dispatched through Pi's tool-event pipeline. Direct process execution, user shell commands, MCP tools and other tools are not covered by these hooks.
85
113
  - The older primary regex safeguards remain authoritative and may be more conservative about text than the shared recognizer. Missing or explicitly overridden child extension paths remove the lightweight boundary; a valid package installation must include the safety entry.
@@ -326,7 +326,10 @@ export default function askUserChoice(pi: ExtensionAPI): void {
326
326
  if (details?.selection) {
327
327
  return new Text(theme.fg("success", `✓ ${details.selection.index}. ${details.selection.label}`), 0, 0);
328
328
  }
329
- if (details?.customResponse !== undefined) return new Text(theme.fg("success", "✓ Custom response"), 0, 0);
329
+ if (details?.customResponse !== undefined) {
330
+ const text = details.customResponse.trim();
331
+ return new Text(theme.fg("success", text ? `✓ Custom response — ${details.customResponse}` : "✓ Custom response"), 0, 0);
332
+ }
330
333
  return new Text(theme.fg("warning", "Cancelled"), 0, 0);
331
334
  },
332
335
  });
@@ -1,7 +1,7 @@
1
1
  import { execFile, execFileSync } from "node:child_process";
2
- import { existsSync, lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
2
+ import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, statSync } from "node:fs";
3
3
  import { homedir, tmpdir } from "node:os";
4
- import { dirname, join } from "node:path";
4
+ import { basename, dirname, join, resolve } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
7
7
 
@@ -38,11 +38,18 @@ export interface CodeGraphRunOptions {
38
38
  maxBuffer: number;
39
39
  }
40
40
 
41
+ interface CodeGraphLocalExclude {
42
+ status: "added" | "present" | "failed";
43
+ path?: string;
44
+ reason?: string;
45
+ }
46
+
41
47
  interface CodeGraphFallbackDetails {
42
48
  status: CodeGraphStatus;
43
49
  operation: CodeGraphOperation;
44
50
  cwd: string;
45
51
  fallback: string;
52
+ localExclude?: CodeGraphLocalExclude;
46
53
  }
47
54
 
48
55
  export type CodeGraphRunner = (
@@ -109,6 +116,55 @@ function assertSafeIndexDirectory(cwd: string): void {
109
116
  }
110
117
  }
111
118
 
119
+ function indexExists(cwd: string): boolean {
120
+ try {
121
+ return lstatSync(join(cwd, ".codegraph")).isDirectory();
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+
127
+ function gitOutput(cwd: string, args: readonly string[]): string {
128
+ return execFileSync("git", [...args], {
129
+ cwd,
130
+ encoding: "utf8",
131
+ stdio: ["ignore", "pipe", "ignore"],
132
+ windowsHide: true,
133
+ }).trim();
134
+ }
135
+
136
+ // CodeGraph writes .codegraph/.gitignore with `*` and `!.gitignore`, so that file
137
+ // stays untracked and enters every diff. Keep the whole index out of git through
138
+ // the worktree's local exclude file; tracked files and .gitignore are never edited.
139
+ const LOCAL_EXCLUDE_PATTERN = ".codegraph/";
140
+ const EQUIVALENT_EXCLUDE_PATTERNS = new Set([".codegraph", ".codegraph/", "/.codegraph", "/.codegraph/"]);
141
+
142
+ function excludeIndexLocally(cwd: string): CodeGraphLocalExclude | undefined {
143
+ let path: string | undefined;
144
+ try {
145
+ if (gitOutput(cwd, ["rev-parse", "--is-inside-work-tree"]) !== "true") return undefined;
146
+ // --git-path resolves the exclude file a linked worktree actually reads.
147
+ path = resolve(cwd, gitOutput(cwd, ["rev-parse", "--git-path", "info/exclude"]));
148
+ const current = existsSync(path) ? readFileSync(path, "utf8") : "";
149
+ mkdirSync(dirname(path), { recursive: true });
150
+ path = join(realpathSync(dirname(path)), basename(path));
151
+ if (current.split(/\r?\n/).some((line) => EQUIVALENT_EXCLUDE_PATTERNS.has(line.trim()))) {
152
+ return { status: "present", path };
153
+ }
154
+ const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
155
+ appendFileSync(path, `${separator}${LOCAL_EXCLUDE_PATTERN}\n`);
156
+ return { status: "added", path };
157
+ } catch (error: unknown) {
158
+ const reason = error instanceof Error ? error.message.split("\n")[0] : String(error);
159
+ return { status: "failed", ...(path ? { path } : {}), reason };
160
+ }
161
+ }
162
+
163
+ function withLocalExcludeNote(text: string, localExclude: CodeGraphLocalExclude | undefined): string {
164
+ if (localExclude?.status !== "failed") return text;
165
+ return `${text}\n\n.codegraph/ is not excluded from git locally (${localExclude.reason}). Keep .codegraph/ out of the change.`;
166
+ }
167
+
112
168
  function resolveLimit(limit: number | undefined): number {
113
169
  if (limit === undefined) return DEFAULT_LIMIT;
114
170
  if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
@@ -304,7 +360,7 @@ export function createCodeGraphTool(
304
360
  signal: AbortSignal | undefined,
305
361
  _onUpdate: undefined,
306
362
  ctx: ExtensionContext,
307
- ): Promise<{ content: { type: "text"; text: string }[]; details: { operation: CodeGraphOperation; cwd: string; args: string[] } | CodeGraphFallbackDetails }> {
363
+ ): Promise<{ content: { type: "text"; text: string }[]; details: { operation: CodeGraphOperation; cwd: string; args: string[]; localExclude?: CodeGraphLocalExclude } | CodeGraphFallbackDetails }> {
308
364
  const cwd = resolveWorkspaceCwd(ctx.cwd);
309
365
  assertSafeIndexDirectory(cwd);
310
366
  const args = commandArguments(parameters, cwd);
@@ -313,18 +369,24 @@ export function createCodeGraphTool(
313
369
  try {
314
370
  const result = await runner(args, { cwd, signal: runSignal, maxBuffer: PROCESS_MAX_BUFFER });
315
371
  const output = truncateOutput([result.stdout, result.stderr].filter(Boolean).join("\n"));
372
+ const localExclude = parameters.operation === CODEGRAPH_OPERATION.INIT || indexExists(cwd)
373
+ ? excludeIndexLocally(cwd)
374
+ : undefined;
316
375
  return {
317
- content: [{ type: "text" as const, text: output || "CodeGraph completed without output." }],
318
- details: { operation: parameters.operation, cwd, args },
376
+ content: [{ type: "text" as const, text: withLocalExcludeNote(output || "CodeGraph completed without output.", localExclude) }],
377
+ details: { operation: parameters.operation, cwd, args, ...(localExclude ? { localExclude } : {}) },
319
378
  };
320
379
  } catch (error: unknown) {
321
380
  const details = codeGraphFailureDetails(error, parameters.operation, cwd);
381
+ // A failed init can still leave a partial index behind.
382
+ const localExclude = indexExists(cwd) ? excludeIndexLocally(cwd) : undefined;
383
+ if (localExclude) details.localExclude = localExclude;
322
384
  const timedOut = timeout?.aborted === true && signal?.aborted !== true;
323
385
  const text = timedOut
324
386
  ? `CodeGraph timed out after ${queryTimeoutMs} ms (a slow filesystem such as a WSL /mnt mount, or a very large workspace). ${FALLBACK_INSTRUCTIONS}`
325
387
  : codeGraphFailureMessage(details.status);
326
388
  return {
327
- content: [{ type: "text" as const, text }],
389
+ content: [{ type: "text" as const, text: withLocalExcludeNote(text, localExclude) }],
328
390
  details,
329
391
  };
330
392
  }