gentle-pi 4.3.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 (208) hide show
  1. package/README.md +8 -5
  2. package/assets/agents/gentle-ai-worker.md +1 -1
  3. package/assets/design-canvas/canvas.css +648 -0
  4. package/assets/design-canvas/canvas.js +1497 -0
  5. package/assets/design-canvas/index.html +88 -0
  6. package/assets/design-canvas/inspector.js +290 -0
  7. package/assets/orchestrator-delegation-reference.md +2 -2
  8. package/assets/orchestrator-verification.md +1 -1
  9. package/assets/orchestrator.md +2 -2
  10. package/bin/engram-guard +1 -1
  11. package/bin/gentle-shell.mjs +101 -151
  12. package/docs/bundled-install.md +47 -42
  13. package/docs/delegated-verification.md +6 -0
  14. package/docs/gentle-agents-activity.md +5 -1
  15. package/docs/gentle-music.md +61 -0
  16. package/docs/gentle-shell.md +77 -7
  17. package/docs/install-wizard.md +30 -6
  18. package/docs/permission-modes.md +7 -2
  19. package/docs/readme-reference.md +19 -15
  20. package/docs/session-profile-format.md +26 -0
  21. package/docs/voice.md +259 -0
  22. package/docs/yolo-mode.md +17 -2
  23. package/extensions/ask-user-choice.ts +4 -1
  24. package/extensions/gentle-agents.ts +173 -38
  25. package/extensions/gentle-ai.ts +214 -69
  26. package/extensions/gentle-design.ts +158 -0
  27. package/extensions/gentle-music.ts +289 -0
  28. package/extensions/gentle-shell.ts +332 -73
  29. package/extensions/gentle-voice.ts +11 -0
  30. package/extensions/session-scratch.ts +41 -22
  31. package/extensions/skill-registry.ts +63 -8
  32. package/lib/agent-profiles.ts +26 -0
  33. package/lib/agents-config.ts +35 -0
  34. package/lib/agents-protocol.ts +57 -1
  35. package/lib/agents-runner.ts +52 -3
  36. package/lib/agents-session-transport.ts +101 -6
  37. package/lib/agents-widget.ts +14 -1
  38. package/lib/auto-permission-memory.ts +48 -18
  39. package/lib/auto-permission-runtime.ts +21 -6
  40. package/lib/clipboard-image.ts +147 -0
  41. package/lib/command-palette-catalog.ts +2 -0
  42. package/lib/commit-slice-signal.ts +1 -1
  43. package/lib/context-backstop.ts +36 -0
  44. package/lib/design-canvas-server.ts +947 -0
  45. package/lib/design-comments.ts +437 -0
  46. package/lib/design-handoffs.ts +313 -0
  47. package/lib/design-open-browser.ts +58 -0
  48. package/lib/design-sources.ts +120 -0
  49. package/lib/external-editor.ts +31 -0
  50. package/lib/gentle-design.ts +513 -0
  51. package/lib/gentle-shell-bundled.ts +123 -49
  52. package/lib/gentle-shell-launcher.ts +9 -4
  53. package/lib/gentle-shell-startup-update.ts +61 -9
  54. package/lib/gentle-shell-uninstall.ts +33 -1
  55. package/lib/image-attachments.ts +150 -0
  56. package/lib/inprocess-reviewer.ts +33 -2
  57. package/lib/nan-provider.ts +3 -0
  58. package/lib/native-review-cli.ts +11 -4
  59. package/lib/notification-audio-native.ts +149 -13
  60. package/lib/notification-audio.ts +4 -0
  61. package/lib/notification-pulse-worker.ts +82 -18
  62. package/lib/notification-service.ts +5 -1
  63. package/lib/orchestrator-presence.ts +78 -8
  64. package/lib/orchestrator-scope.ts +10 -0
  65. package/lib/orchestrator-state.ts +11 -3
  66. package/lib/permission-mode-policy.ts +2 -2
  67. package/lib/review-host-relay.ts +11 -2
  68. package/lib/review-reminder-receipt.ts +24 -1
  69. package/lib/session-profile-append-controller.ts +713 -0
  70. package/lib/session-profile-disk-reader.ts +182 -0
  71. package/lib/session-profile-persistence.ts +15 -7
  72. package/lib/session-scratch.ts +52 -3
  73. package/lib/session-worktree-registry.ts +7 -0
  74. package/lib/shell-bar.ts +4 -3
  75. package/lib/shell-changes.ts +81 -5
  76. package/lib/shell-prompt.ts +4 -4
  77. package/lib/shell-sidebar-layout.ts +26 -6
  78. package/lib/shell-todo.ts +1 -1
  79. package/lib/verifier-admission.ts +5 -4
  80. package/lib/vim-editor-adapter.ts +11 -8
  81. package/lib/voice-artifacts.ts +188 -0
  82. package/lib/voice-command.ts +576 -0
  83. package/lib/voice-install.ts +455 -0
  84. package/lib/voice-platform.ts +99 -0
  85. package/lib/voice-push-to-talk.ts +262 -0
  86. package/lib/voice-session.ts +460 -0
  87. package/lib/voice-settings.ts +50 -0
  88. package/lib/voice-ui.ts +158 -0
  89. package/lib/yolo-session-policy.ts +58 -22
  90. package/package.json +1 -1
  91. package/runtime/gentle-shell-bundled.mjs +122 -48
  92. package/runtime/gentle-shell-launcher.mjs +9 -4
  93. package/runtime/gentle-shell-startup-update.mjs +61 -9
  94. package/runtime/gentle-shell-uninstall.mjs +33 -1
  95. package/runtime/native-review-cli.mjs +11 -4
  96. package/scripts/bundled-engram-migration.mjs +0 -6
  97. package/scripts/gentle-ai-installer.mjs +10 -10
  98. package/scripts/installer-downloads.mjs +7 -7
  99. package/scripts/installer-preflight.mjs +5 -3
  100. package/scripts/installer-windows.mjs +37 -7
  101. package/scripts/main-channel.mjs +91 -17
  102. package/scripts/pi-update.mjs +34 -6
  103. package/scripts/verify-package-files.mjs +26 -2
  104. package/scripts/voice-worker.cjs +476 -0
  105. package/skills/design/SKILL.md +110 -0
  106. package/tests/agent-profiles.test.ts +24 -0
  107. package/tests/agents-config.test.ts +61 -0
  108. package/tests/agents-fake-child.ts +8 -1
  109. package/tests/agents-protocol.test.ts +32 -0
  110. package/tests/agents-runner.test.ts +226 -5
  111. package/tests/agents-session-transport.test.ts +53 -3
  112. package/tests/ask-user-choice.test.ts +30 -0
  113. package/tests/auto-mode.test.ts +17 -0
  114. package/tests/auto-permission-memory-diagnostics.test.ts +56 -0
  115. package/tests/auto-permission-runtime.test.ts +85 -1
  116. package/tests/autonomous-guard.test.ts +20 -0
  117. package/tests/bundled-install.test.ts +3 -3
  118. package/tests/clipboard-image.test.ts +114 -0
  119. package/tests/command-palette.test.ts +1 -1
  120. package/tests/context-backstop.test.ts +70 -4
  121. package/tests/delegate-for-reason-contract.test.ts +2 -1
  122. package/tests/design-canvas-assets.test.ts +710 -0
  123. package/tests/design-canvas-server.test.ts +1096 -0
  124. package/tests/design-comments.test.ts +201 -0
  125. package/tests/design-handoffs.test.ts +282 -0
  126. package/tests/design-open-browser.test.ts +70 -0
  127. package/tests/design-sources.test.ts +132 -0
  128. package/tests/engram-safety.test.ts +132 -57
  129. package/tests/external-editor.test.ts +84 -0
  130. package/tests/fixtures/primary-session-prompt/gentleman.golden.md +3 -3
  131. package/tests/gentle-agents.test.ts +365 -20
  132. package/tests/gentle-ai-binary.test.ts +1 -1
  133. package/tests/gentle-ai-installer.test.ts +51 -51
  134. package/tests/gentle-ai.test.ts +292 -13
  135. package/tests/gentle-design.test.ts +1313 -0
  136. package/tests/gentle-music.test.ts +335 -0
  137. package/tests/gentle-shell-bundled.test.ts +301 -248
  138. package/tests/gentle-shell-launcher.test.ts +28 -2
  139. package/tests/gentle-shell-startup-update.test.ts +77 -1
  140. package/tests/gentle-shell-uninstall.test.ts +59 -0
  141. package/tests/gentle-shell.test.ts +183 -8
  142. package/tests/gentle-todo.test.ts +11 -0
  143. package/tests/image-attachments.test.ts +119 -0
  144. package/tests/image-paste-chips.test.ts +236 -0
  145. package/tests/inprocess-reviewer.test.ts +55 -2
  146. package/tests/installer-bundles.test.ts +12 -6
  147. package/tests/installer-posix-bootstrap.test.ts +8 -8
  148. package/tests/installer-preflight.test.ts +16 -0
  149. package/tests/installer-server.test.ts +2 -2
  150. package/tests/installer-windows-bootstrap.test.ts +61 -3
  151. package/tests/installer-windows-diagnostics.test.ts +111 -0
  152. package/tests/main-channel-upgrade.test.ts +232 -2
  153. package/tests/main-channel.test.ts +140 -21
  154. package/tests/nan-provider.test.ts +36 -0
  155. package/tests/native-review-capability-contract.test.ts +11 -4
  156. package/tests/native-review-parity.test.ts +1 -1
  157. package/tests/notification-audio-native.test.ts +128 -9
  158. package/tests/notification-customize.test.ts +17 -5
  159. package/tests/odd-routing-canonical-ratchet.test.ts +2 -1
  160. package/tests/orchestrator-budget.test.ts +5 -2
  161. package/tests/orchestrator-presence.test.ts +100 -2
  162. package/tests/orchestrator-rdd-ownership.test.ts +2 -2
  163. package/tests/orchestrator-scope.test.ts +14 -0
  164. package/tests/orchestrator-state.test.ts +29 -0
  165. package/tests/package-files.test.ts +34 -0
  166. package/tests/package-manifest.test.ts +6 -6
  167. package/tests/permission-mode-policy.test.ts +10 -1
  168. package/tests/pi-update.test.ts +99 -4
  169. package/tests/rdd-aware-verification-contract.test.ts +1 -1
  170. package/tests/review-host-relay-routing.test.ts +88 -1
  171. package/tests/review-host-relay.test.ts +9 -2
  172. package/tests/review-json-arguments.test.ts +20 -3
  173. package/tests/review-loadout-rdd-off-sdk.test.ts +23 -2
  174. package/tests/review-relay-transport-agent.test.ts +26 -0
  175. package/tests/review-reminder-receipt.test.ts +31 -1
  176. package/tests/session-changes.test.ts +2 -1
  177. package/tests/session-profile-append-controller-fork.test.ts +458 -0
  178. package/tests/session-profile-append-controller-pi.test.ts +290 -0
  179. package/tests/session-profile-append-controller.test.ts +731 -0
  180. package/tests/session-profile-disk-reader-pi.test.ts +183 -0
  181. package/tests/session-profile-disk-reader.test.ts +311 -0
  182. package/tests/session-scratch.test.ts +68 -2
  183. package/tests/session-worktree-registry.test.ts +13 -1
  184. package/tests/shell-bar.test.ts +14 -2
  185. package/tests/shell-changes.test.ts +148 -1
  186. package/tests/shell-prompt.test.ts +3 -3
  187. package/tests/shell-render-cache.test.ts +134 -1
  188. package/tests/shell-sidebar-layout.test.ts +50 -0
  189. package/tests/short-check-routing-contract.test.ts +1 -1
  190. package/tests/skill-collision-prefixes.test.ts +3 -1
  191. package/tests/skill-registry-watcher.test.ts +17 -5
  192. package/tests/skill-registry.test.ts +47 -3
  193. package/tests/task-size-routing-contract.test.ts +1 -1
  194. package/tests/test-strength-contract.test.ts +1 -1
  195. package/tests/verifier-admission.test.ts +18 -1
  196. package/tests/voice-command.test.ts +773 -0
  197. package/tests/voice-install.test.ts +323 -0
  198. package/tests/voice-platform.test.ts +176 -0
  199. package/tests/voice-push-to-talk.test.ts +444 -0
  200. package/tests/voice-session.test.ts +434 -0
  201. package/tests/voice-settings.test.ts +49 -0
  202. package/tests/voice-ui.test.ts +97 -0
  203. package/tests/voice-worker.test.ts +445 -0
  204. package/tests/weak-model-economy-prompt.test.ts +32 -0
  205. package/tests/windows-hidden-processes.test.ts +25 -2
  206. package/tests/yolo-customize.test.ts +5 -1
  207. package/tests/yolo-mode.test.ts +1 -1
  208. package/tests/yolo-session-policy-capture.test.ts +104 -0
@@ -53,14 +53,15 @@ Changing the default does **not** change the current session. To return this ses
53
53
  | **Normal** | Existing permission behavior; the default when no valid global preference exists. |
54
54
  | **Auto** | A separate reviewer using the current session model checks eligible actions against live user intent before execution. Local reads are classified too; locality does not exempt an action from explicit restrictions. |
55
55
  | **YOLO** | Standing permission for ordinary already-authorized work and delivery; no Auto classifier. Destructive and independent consent boundaries remain, except recursive removal of the session's own [temp scratch](yolo-mode.md#temp-scratch-removal). [Full YOLO details](yolo-mode.md). |
56
+ | **Super Yolo** | No permission prompts at all, including data-loss confirmations and configured guardrail actions. The only remaining line is the sensitive-path guard: read/write/edit of `.env`, `.ssh`, keychains, `*.pem/*.key/*.p12/*.pfx`, `secrets/` and cloud credential files stays hard-blocked. Code-level catastrophic denies (`rm -rf /`, `--no-preserve-root`, `git reset --hard`, forced clean, force push) also remain. |
56
57
 
57
58
  ## Commands and menu
58
59
 
59
60
  | Command | Effect |
60
61
  | --- | --- |
61
- | `/gentle:mode normal`, `auto`, or `yolo` | Select exactly one mode for the current interactive primary session. |
62
+ | `/gentle:mode normal`, `auto`, `yolo`, or `super-yolo` | Select exactly one mode for the current interactive primary session. |
62
63
  | `/gentle:mode status` or `/gentle:mode` | Show current mode and saved default. |
63
- | `/gentle:mode default normal`, `auto`, or `yolo` | Save the default for new primary sessions, leaving the current mode unchanged. |
64
+ | `/gentle:mode default normal`, `auto`, `yolo`, or `super-yolo` | Save the default for new primary sessions, leaving the current mode unchanged. |
64
65
  | `/gentle:yolo enable`, `disable`, or `status` | Existing shortcut; enable selects YOLO, disable selects Normal. |
65
66
 
66
67
  In `/gentle:customize` → **Editor**, use **Session permissions** to change the current mode, or **Default permissions** to save a future-session preference. The original **YOLO: ON/OFF · session only** row still toggles the live mode; it does not save a default. Browsing, previews, and closing the menu do not grant permission.
@@ -116,6 +117,10 @@ The **Auto checks** status and `gentle-auto-permission` session entries account
116
117
 
117
118
  The dedicated `permission-mode.json` in Gentle Pi's resolved user config home contains a strict versioned preference. Saving uses a private atomic replacement; malformed or unreadable existing preferences fall back to Normal and refuse an overwrite until repaired. Use the human command/menu path rather than asking model tools to edit this authority file.
118
119
 
120
+ Model `write`/`edit` calls keep the config home protected except for the main-channel updater's `dev-binary.json`, `channel.json`, `main/` and `tools/go`. Those destinations select or build executable code, so they still require authorization under the current mode's ordinary checks; Auto classifies them and can deny an update. They are not automatically allowed. Permission preferences, global and project `runtime-guardrails.json`, their parent directories and symlink aliases resolving to them stay blocked even if a classifier would allow the action. Other config-home state, including model routing, persona and privacy preferences, remains protected.
121
+
122
+ Shell recognition remains deliberately conservative: commands mentioning the config home or policy filenames can still be blocked. This change does not relax shell protection, authorize routing around a denial, or turn the hook into a sandbox.
123
+
119
124
  Live mode selection is bound to the SessionManager, session ID and canonical Git clone identity, or the canonical workspace directory outside Git. Session replacement, shutdown and reload revoke live callbacks and pending actions. A newly initialized primary session may select its saved default; this is a new application of the global preference, not restoration of an old session grant. Disable the saved default as well if future sessions must stay Normal.
120
125
 
121
126
  ## Limits
@@ -157,7 +157,7 @@ otherwise `herdr` from `PATH`, without a shell.
157
157
  | **Skill creation workflow** | Provides the `gentle-ai-skill-creator`/`gentle-ai-skill-improver` skills, `/skill-creation` prompt, and packaged style guide for LLM-first skills. |
158
158
  | **Delivery skills** | Includes issue-first PRs, chained PRs, work-unit commits, cognitive docs, comment writing, and Judgment Day review. |
159
159
  | **Bounded native review** | Freezes one candidate, dispatches only controller-selected lenses, and records native authority. Review outcomes are informational; delivery follows ordinary repository policy. |
160
- | **Verified native runtime** | The current source checkout provisions the exact package-local Gentle AI v4.2.0 runtime: signed, SHA-256-pinned release archives on Darwin/Linux and a Go SumDB-verified source build on Windows x64/arm64. It validates package-local integrity and rejects PATH, global, sibling, symlink, and mode fallbacks. |
160
+ | **Verified native runtime** | The current source checkout provisions the exact package-local Gentle AI v4.3.1 runtime: signed, SHA-256-pinned release archives on Darwin/Linux and a Go SumDB-verified source build on Windows x64/arm64. It validates package-local integrity and rejects PATH, global, sibling, symlink, and mode fallbacks. |
161
161
  | **Runtime safety** | Blocks destructive shell commands, asks for confirmation for sensitive operations, and blocks direct read/write/edit access to sensitive paths. Blocks writing a test file that reads git history at a literal commit or points `TMPDIR` inside the working directory (including a relative path such as `.tmp`, `./.tmp`, or `tmp/x`), in the primary session and in subagents. Refuses native review tools (`gentle_review*`) called from inside another tool, such as a codemode script: they run only as direct tool calls. |
162
162
 
163
163
  ## Audio notifications
@@ -218,7 +218,7 @@ This installs the current npm release; select an explicit version if you need a
218
218
 
219
219
  ### Source checkout
220
220
 
221
- This checkout declares `gentle-pi` `4.0.0` with a package-local Gentle AI `v4.2.0` pin. Checkout metadata alone is not proof of npm publication; verify the registry version and its release workflow.
221
+ This checkout declares `gentle-pi` `4.0.0` with a package-local Gentle AI `v4.3.1` pin. Checkout metadata alone is not proof of npm publication; verify the registry version and its release workflow.
222
222
 
223
223
  ### Pi compatibility
224
224
 
@@ -234,7 +234,7 @@ Project-local installs (`pi install -l`), Git installs outside that exact global
234
234
 
235
235
  Malformed/nonobject JSON, symlink/nonregular settings, unsafe paths, or a busy settings lock fail without replacing settings. The installer coordinates with Pi's cooperative settings lock and uses atomic replacement; it does not guarantee safety against noncooperating writers or malicious concurrent directory replacement. Already-fullscreen settings remain byte-identical. Native installation failure leaves settings untouched; `GENTLE_PI_SKIP_GENTLE_AI_INSTALL=1` skips only native provisioning, not the recognized global fullscreen setting.
236
236
 
237
- ### RDD history and opt-in
237
+ ### RDD history and default
238
238
 
239
239
  Native RDD was introduced in `gentle-pi` `v0.15.0` on 2026-07-10 with bounded review transactions. The current stable release, [`v3.5.1`](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v3.5.1), includes native RDD:
240
240
 
@@ -243,9 +243,9 @@ Native RDD was introduced in `gentle-pi` `v0.15.0` on 2026-07-10 with bounded re
243
243
  pi install npm:gentle-pi@3.5.1
244
244
  ```
245
245
 
246
- RDD remains opt-in. Enable it only through an explicit user decision with `/gentle:review-mode enable`; `status` lets you inspect the mode without changing it. The `.git/gentle-ai/candidate-views` parent must sit on a filesystem that honors private POSIX modes (or equivalent Windows ACLs); WSL DrvFS mounts without metadata can reject START before lineage creation.
246
+ RDD is optional and on by default (since Gentle AI v4.0.0). Turn it off anytime with `/gentle:review-mode disable` for this clone or `gentle-ai review mode disable` machine-wide; `/gentle:review-mode enable` only clears a clone-local off, and `status` lets you inspect the mode without changing it. The `.git/gentle-ai/candidate-views` parent must sit on a filesystem that honors private POSIX modes (or equivalent Windows ACLs); WSL DrvFS mounts without metadata can reject START before lineage creation.
247
247
 
248
- The source checkout's RDD integration installs Gentle AI only into its private `.gentle-ai/` directory. Darwin and Linux use pinned release assets with asset and executable SHA-256 verification (signed archives for source pin `v4.2.0`; raw prerelease binaries only under a prerelease pin). Windows x64 and arm64 build the exact `v4.2.0` source tag with a local Go 1.25.10+ toolchain, a sealed Go environment, `GOTOOLCHAIN=local`, and `GOSUMDB=sum.golang.org`; it does not download Go automatically (the [installation wizard](install-wizard.md#pinned-go) can put its own pinned Go first on PATH for that build). Go runs in a short private directory under the system temp directory, never inside the package, because a deep pnpm store path pushes Go's working directories past MAX_PATH; a failed build prints its whole cause chain with the failing command's output tail. Windows provenance is Go-toolchain plus SumDB evidence and postinstall tamper detection, **not** Authenticode or protection against a malicious joint binary-and-manifest replacement. Package-private locks coordinate cooperative concurrent or crashed installers; their tombstones fail closed. A malicious same-user process with write access to package-private `node_modules` is outside that protocol because it can already replace package code, binary, or manifest, and portable Node has no pathname-delete CAS. It never uses `PATH` or a global `gentle-ai` installation. For development or offline installs only, set `GENTLE_PI_SKIP_GENTLE_AI_INSTALL=1`; native review operations then fail closed with an actionable `package-local-binary-missing` error. To recover explicitly, if `GENTLE_PI_SKIP_GENTLE_AI_INSTALL` is set, remove or unset it before changing to the installed `gentle-pi` package directory. Then run `node scripts/install-gentle-ai.mjs`. This invokes the package-owned installer without relying on a global binary or npm configuration change. A missing binary can result from skipped lifecycle scripts, but does not prove that lifecycle scripts were disabled.
248
+ The source checkout's RDD integration installs Gentle AI only into its private `.gentle-ai/` directory. Darwin and Linux use pinned release assets with asset and executable SHA-256 verification (signed archives for source pin `v4.3.1`; raw prerelease binaries only under a prerelease pin). Windows x64 and arm64 build the exact `v4.3.1` source tag with a local Go 1.25.10+ toolchain, a sealed Go environment, `GOTOOLCHAIN=local`, and `GOSUMDB=sum.golang.org`; it does not download Go automatically (the [installation wizard](install-wizard.md#pinned-go) can put its own pinned Go first on PATH for that build). Go runs in a short private directory under the system temp directory, never inside the package, because a deep pnpm store path pushes Go's working directories past MAX_PATH; a failed build prints its whole cause chain with the failing command's output tail. Windows provenance is Go-toolchain plus SumDB evidence and postinstall tamper detection, **not** Authenticode or protection against a malicious joint binary-and-manifest replacement. Package-private locks coordinate cooperative concurrent or crashed installers; their tombstones fail closed. A malicious same-user process with write access to package-private `node_modules` is outside that protocol because it can already replace package code, binary, or manifest, and portable Node has no pathname-delete CAS. It never uses `PATH` or a global `gentle-ai` installation. For development or offline installs only, set `GENTLE_PI_SKIP_GENTLE_AI_INSTALL=1`; native review operations then fail closed with an actionable `package-local-binary-missing` error. To recover explicitly, if `GENTLE_PI_SKIP_GENTLE_AI_INSTALL` is set, remove or unset it before changing to the installed `gentle-pi` package directory. Then run `node scripts/install-gentle-ai.mjs`. This invokes the package-owned installer without relying on a global binary or npm configuration change. A missing binary can result from skipped lifecycle scripts, but does not prove that lifecycle scripts were disabled.
249
249
 
250
250
  Recommended companion packages, into the standalone `gentle-shell` home:
251
251
 
@@ -378,7 +378,7 @@ Tradeoff: `--link` shares one credential store with pi; `import-pi` duplicates i
378
378
 
379
379
  `gentle-shell list` and extension updates follow that same home selection. `gentle-shell update --extensions-only` preserves Pi's extension-only command; `--extensions`, package targets, and model updates also stay with Pi.
380
380
 
381
- `gentle-shell update` updates the selected Pi through its verified npm or pnpm global owner, avoiding Pi's self-update refusal for pnpm v11 linked stores. Ownership requires successful global listing JSON, an exact canonical listed package-root/CLI match, and binding to the selected runtime. An adjacent peer (the launcher's `bundled` runtime) is supplied by the exact listed `gentle-pi` package: that Shell package is reinstalled at its current version so the manager re-resolves the peer, and a separate global Pi is never the target, because updating it leaves the peer a Shell install runs unchanged. A `PATH` or `GENTLE_SHELL_PI` runtime is supplied only by its own listed global Pi package; when that executable is a package-manager shim (pnpm's POSIX shim, a `.cmd`), Shell follows the single `dist/bundle/cli.js` it names, relative to the shim's directory, and refuses shims naming none or several. Pi and Shell are never installed together in a comma group, and unrelated packages are never reinstalled. Merely residing under `PNPM_HOME` proves nothing. Adjacent peers resolve where the package manager places them: nested under the Shell package (npm), beside it in pnpm's linked store, or in the global group's own `node_modules`. npm source/link entries are rejected. pnpm updates additionally require that the actual old global group contains only the target top-level package: aggregated listings may contain other, separate groups, but shared groups are rejected because pnpm can remove the whole old group. Unknown, malformed, failed, or ambiguous ownership refuses before installation; update source checkouts and other unsupported layouts with their supplying package manager.
381
+ `gentle-shell update` updates the selected Pi through its verified npm or pnpm global owner, avoiding Pi's self-update refusal for pnpm v11 linked stores. Ownership requires successful global listing JSON, an exact canonical listed package-root/CLI match, and binding to the selected runtime. An adjacent peer (the launcher's `bundled` runtime) is supplied by the exact listed `gentle-pi` package: that Shell package is reinstalled at its current version so the manager re-resolves the peer (a main-channel `<version>-main.<sha12>` Shell, which is never published, from the tarball `gentle-shell upgrade --channel main` packed under `<config home>/main/packages/` (an absolute `GENTLE_PI_CONFIG_HOME`, else `~/.pi/gentle-ai`; a relative override is refused), refusing without installation when it is missing and naming `gentle-shell upgrade --channel release` then `--channel main` as the rebuild path), and a separate global Pi is never the target, because updating it leaves the peer a Shell install runs unchanged. A `PATH` or `GENTLE_SHELL_PI` runtime is supplied only by its own listed global Pi package; when that executable is a package-manager shim (pnpm's POSIX shim, a `.cmd`), Shell follows the single `dist/bundle/cli.js` it names, relative to the shim's directory, and refuses shims naming none or several. Pi and Shell are never installed together in a comma group, and unrelated packages are never reinstalled. Merely residing under `PNPM_HOME` proves nothing. Adjacent peers resolve where the package manager places them: nested under the Shell package (npm), beside it in pnpm's linked store, or in the global group's own `node_modules`. npm source/link entries are rejected. pnpm updates additionally require that the actual old global group contains only the target top-level package: aggregated listings may contain other, separate groups, but shared groups are rejected because pnpm can remove the whole old group. Unknown, malformed, failed, or ambiguous ownership refuses before installation; update source checkouts and other unsupported layouts with their supplying package manager.
382
382
 
383
383
  After a successful install, Shell reads the supplied Pi version back from a fresh global listing and reports it, naming the previous version when it changed. An unchanged pnpm result also names pnpm's `minimumReleaseAge` setting: pnpm installs only releases older than that age (pnpm 11 defaults to 4320 minutes), so a very recent Pi release is picked up once it is old enough; Shell reports the rule rather than overriding it. Both the listing and the install run from the system temporary directory, so a project's `packageManager` pin in the current directory never changes which pnpm answers. When the version cannot be read back, the output says so and points at `gentle-shell --version`.
384
384
 
@@ -386,9 +386,9 @@ Prefix-bundled installations keep their existing `gentle-shell upgrade` route fo
386
386
 
387
387
  ### `setup` subcommand
388
388
 
389
- **Bundled Engram safety:** a fresh state uses the checksum-pinned Engram 3.4.0: before Pi starts, Shell provisions the new store's identity with that core and records it, so later launches keep using it, and a store Shell did not provision is never adopted. A running server is reused only through Engram's exact `GET /compatibility` ready contract, or when it is Shell's own store with the recorded `/health` instance and the exact bundled core version. Anything else (foreign or unknown data, provenance mismatch, a newer schema in the database header, a token-protected or unknown server) disables memory with an explanation of the missing safety evidence and backup precautions (matching versions or updating alone do not prove compatibility), and Shell points `ENGRAM_BIN` at its own guard (`bin/engram-guard`), which fails before any store access for every gentle-engram from 0.4.1 on, whichever Pi route loaded it. No compatibility is inferred from versions. An explicit `ENGRAM_BIN` or `ENGRAM_URL` is never replaced and bypasses the guard. Every launch also pins `npm:gentle-engram@0.4.2` through Pi's own `install` command and verifies `engram.startupPolicy: 1`; a failed check is reported and Shell still starts. See [bundled startup safety](bundled-install.md#engram-startup-safety) for the full rules and known limits.
389
+ **Bundled Engram safety:** Shell shares the global Engram server on `127.0.0.1:7437`, like other agents' hooks. A running server is reused only through Engram's exact `GET /compatibility` ready contract; when nothing listens, Shell starts the checksum-pinned Engram 3.5.2 core as a detached `engram serve` (Engram's default port and `~/.engram`, no update check, no GitHub tokens) and waits up to 10 seconds for that same contract, so you never start the server yourself. An existing store does not disable memory by itself, whoever created it; only a database header from a newer Engram schema keeps Shell from starting its older core on it. Anything else (a server that does not verify, a token-protected or unknown server, a start that fails or never reports ready) disables memory for that launch with an explanation of the missing safety evidence and backup precautions (matching versions or updating alone do not prove compatibility); Shell never stops or replaces a listening server. Shell always points `ENGRAM_BIN` at its own guard (`bin/engram-guard`), which fails before any store access for every gentle-engram from 0.4.1 on, whichever Pi route loaded it, so memory reaches the store only through the verified server's `ENGRAM_URL`. No compatibility is inferred from versions. An explicit `ENGRAM_BIN` or `ENGRAM_URL` is never replaced and bypasses the guard. Every launch also pins `npm:gentle-engram@0.4.2` through Pi's own `install` command and verifies `engram.startupPolicy: 1`; a failed check is reported and Shell still starts. See [bundled startup safety](bundled-install.md#engram-startup-safety) for the full rules and known limits.
390
390
 
391
- The memory decision never skips or fails bundled setup: it always publishes the verified core, runs Gentle AI and pins the companion, then only selects the Engram environment (core or guard) for setup children and the launched Pi. `--dry-run` installs nothing. Non-bundled setup keeps the existing behavior described below.
391
+ The memory decision never skips or fails bundled setup: it always publishes the verified core, runs Gentle AI and pins the companion, then only selects the Engram environment (the guard as `ENGRAM_BIN`, plus `ENGRAM_URL` when memory is allowed) for setup children and the launched Pi. `--dry-run` installs nothing. Non-bundled setup keeps the existing behavior described below.
392
392
 
393
393
  `gentle-shell setup` provisions the resolved home — the same home selection as any other invocation, `--isolated` by default, or `--link`/`--home <path>` when given before `setup` — with the same companion packages a regular `gentle-ai install --agent pi` installs into a Pi agent home: `npm:gentle-pi`, `npm:gentle-engram`, `npm:@juicesharp/rpiv-ask-user-question`, `npm:pi-web-access`, `npm:pi-btw`, plus running `pi-engram init`. It never touches `~/.pi/agent` unless you pass `--link`.
394
394
 
@@ -438,7 +438,7 @@ If none resolve, `gentle-shell` exits 1 naming all three options. Once a runtime
438
438
  | `GENTLE_SHELL_NO_AUTO_SETUP` | Set to `1` to skip automatic first-run provisioning (see "First run in an isolated or custom home" below). |
439
439
  | `ENGRAM_STARTUP_POLICY` | Bundled startup recomputes `allow` or `disabled` from safety evidence. Explicit empty/invalid values remain disabled; user `allow` does not override unknown data. Standalone provider behavior with the variable absent is unchanged. |
440
440
  | `ENGRAM_STARTUP_NOTICE` | Display-only recovery guidance; never executed. |
441
- | `ENGRAM_BIN`, `ENGRAM_URL`, `ENGRAM_PORT`, `ENGRAM_DATA_DIR`, `ENGRAM_DATABASE_URL` | Explicit selections are not probed by Shell and keep bundled memory disabled. An explicit `ENGRAM_BIN` or `ENGRAM_URL` is never replaced and bypasses the guard (an `ENGRAM_URL` alone still gets the verified core, or the guard, as `ENGRAM_BIN`); without them, bundled Shell sets `ENGRAM_BIN` (core or guard) and, for verified reuse, `ENGRAM_URL`. |
441
+ | `ENGRAM_BIN`, `ENGRAM_URL`, `ENGRAM_PORT`, `ENGRAM_DATA_DIR`, `ENGRAM_DATABASE_URL`, `ENGRAM_SERVER_MODE` | Explicit selections are not probed by Shell and keep bundled memory disabled. An explicit `ENGRAM_BIN` or `ENGRAM_URL` is never replaced and bypasses the guard (an `ENGRAM_URL` alone still gets the verified core, or the guard, as `ENGRAM_BIN`); without them, bundled Shell sets `ENGRAM_BIN` to the guard and, for verified reuse, `ENGRAM_URL`. |
442
442
 
443
443
  ### Loading the package
444
444
 
@@ -657,7 +657,7 @@ Synthetic tests prove the observer's behavior, not the cause of issue #213. A ne
657
657
  ```text
658
658
  /gentle:status Check package assets and global model config.
659
659
  /gentle:doctor Run read-only diagnostics for assets, config, tools, guards, and Herdr setup.
660
- /gentle:models Assign global model/effort routing to packaged/custom agents.
660
+ /gentle:models Assign global agent model/effort routing and choose a profile orchestrator.
661
661
  /gentle:profiles Create, switch, and manage global agent-model profiles.
662
662
  /gentle:persona Switch between gentleman and neutral persona modes.
663
663
  /gentle:background-subagents Show or set the managed background-subagents policy, with its deciding source.
@@ -721,7 +721,7 @@ parent git/status + clarify → one worker writes authorized fixes → focused v
721
721
 
722
722
  A second admission check stops small-task delegation: in the primary session, a `gentle-ai-worker` or `worker` launch is admitted only when its task or context has a line that starts with `Spec: odd/tasks/<feature>.md` and names a task `T<n>` later on that line (for example `, T2, S3` or the delegation card's `(read until "## Log"). Do T2; S3.`), where the document is a regular file under the writer root's `odd/tasks/` after resolving symlinks, or when the parent's measured context reaches the context backstop described below. Otherwise the launch is rejected with an instruction to write the unit inline. Judgment Day fix agents, read-only subagents, and continuations are not affected.
723
723
 
724
- A third admission check keeps the independent verifier for high-risk changes: in the primary session, a `gentle-ai-verify` launch is admitted only when its task or context has a line that starts with `Risk:` (optionally as a list item) and names a Task Size high-risk item first, in the form `Risk: item N` with N from 1 to 6 (several items are allowed, for example `Risk: item 2, item 3 (reason)`), or when it carries a feature-document task line the writer check above accepts, or when the parent's measured context reaches the context backstop. `Risk: none` or no line is rejected with an instruction to run the change's focused test and suite inline, not to retry the launch, and never to add a Risk line or create a feature document only to get a verifier admitted. Subagent children, continuations, writers, Judgment Day agents, native review relays, and read-only explorers are not affected.
724
+ A third admission check keeps the independent verifier for high-risk changes: in the primary session, a `gentle-ai-verify` launch is admitted only when its task or context has a declaration that starts a line (optionally as a list item) or a sentence (after `.`, `!` or `?` and a space) with `Risk:` and names a Task Size high-risk item first, in the form `Risk: item N` with N from 1 to 6 (several items are allowed, for example `Risk: item 2, item 3 (reason)`), or when it carries a feature-document task line the writer check above accepts, or when the parent's measured context reaches the context backstop. `Risk: none`, a mention inside a sentence, or no declaration is rejected with an instruction to run the change's focused test and suite inline, not to retry the launch, and never to add a Risk line or create a feature document only to get a verifier admitted. Subagent children, continuations, writers, Judgment Day agents, native review relays, and read-only explorers are not affected.
725
725
 
726
726
  Every subagent child runs with its own `TMPDIR` (also `TMP` and `TEMP` on Windows): a fresh `gaXXXXXX` directory created under the inherited temp directory (joined to the inherited value as given, never its real path), so scratch the child makes with `mktemp` stays out of the worktree. The runtime removes that directory and everything in it when the child ends, including a failed launch. A quarantined child, whose process exit is unconfirmed, keeps its directory under the system temp directory, because a surviving descendant may still use it; once its exit is proven, including a process group that ends after the child's own exit, the directory is removed and the slot is freed. Each continuation is a new child with a new directory.
727
727
 
@@ -810,7 +810,7 @@ flowchart TD
810
810
 
811
811
  VALIDATE is informational. Commit, push, PR, and release commands follow ordinary repository policy; RDD never authorizes, rewrites, consumes review state for, or blocks them. Dangerous-command safety and destructive-review consent remain independent.
812
812
 
813
- For the source checkout, native contract pairing is exact: this adapter resolves only the integrity-verified package-local Gentle AI v4.2.0 executable, independently hashes it, then negotiates `gentle-ai.review-integration/v2` outside the repository. Capabilities are cached by that executable digest. Every START, target status, FINALIZE, and validate request passes the same contract identifier. Negotiated envelopes decode exactly against the vendored schemas; `recover` routes only the provider-selected `action_disposition`, and optional additions require a future compatible schema/minor that the provider explicitly advertises and the consumer negotiates.
813
+ For the source checkout, native contract pairing is exact: this adapter resolves only the integrity-verified package-local Gentle AI v4.3.1 executable, independently hashes it, then negotiates `gentle-ai.review-integration/v2` outside the repository. Capabilities are cached by that executable digest. Every START, target status, FINALIZE, and validate request passes the same contract identifier. Negotiated envelopes decode exactly against the vendored schemas; `recover` routes only the provider-selected `action_disposition`, and optional additions require a future compatible schema/minor that the provider explicitly advertises and the consumer negotiates.
814
814
 
815
815
  Contract `/v2` replaces the Base64 `candidate_diff` reviewer transport of `/v1` with immutable `base_tree`/`candidate_tree` plus an ordered `changed_path_manifest` and never an inline patch. `gentle-pi` negotiates `/v2` only, with no dual-lane fallback; the cutover landed as one atomic commit against gentle-ai v2.2.2 (tracked by the `migrate-review-integration-v2` change), and the `/v1` schemas stay packaged because the `/v2` schemas `$ref` into their fragments. This provider contract version is unrelated to Pi's own internal "compact-v2" review-authority naming used below — the shared digit is coincidental, not a version pairing.
816
816
 
@@ -820,7 +820,7 @@ Candidate views materialize tracked Git symlinks from their frozen blobs even wh
820
820
 
821
821
  On POSIX, if START rejects a group- or world-accessible `.git/gentle-ai/candidate-views` parent, Pi reports `candidate-owner-parent-privacy` before native START. When a sanitized probe shows the filesystem cannot represent private POSIX modes (for example WSL DrvFS mounts without `metadata`), Pi instead reports `candidate-owner-parent-chmod-ineffective` with guidance to move the Git common directory to a POSIX-metadata filesystem or enable metadata support. Inspect that parent's ownership and permissions and correct them out of band before retrying; Pi does not change them automatically. Other owner-preparation failures retain a generic diagnostic rather than exposing filesystem errors.
822
822
 
823
- Once the source checkout's pinned gentle-ai runtime (currently v4.2.0) has written review authority, rollback MUST preserve every native store and receipt and MUST NOT run a downgraded binary against that repository. Disable the Pi route or roll forward to a compatible authority-aware release instead; deleting authority data or reinstalling an older binary is not a rollback path.
823
+ Once the source checkout's pinned gentle-ai runtime (currently v4.3.1) has written review authority, rollback MUST preserve every native store and receipt and MUST NOT run a downgraded binary against that repository. Disable the Pi route or roll forward to a compatible authority-aware release instead; deleting authority data or reinstalling an older binary is not a rollback path.
824
824
 
825
825
  ### FINALIZE wrapper input
826
826
 
@@ -1037,7 +1037,9 @@ Startup and reload materialize saved non-empty routing, but never treat a saved
1037
1037
 
1038
1038
  Inside `/gentle:models`, press `x` to export the saved routing to `~/.pi/gentle-ai/models.export.json`, or `r` to restore from that file after confirmation. Export uses a versioned envelope and restore writes the normal `models.json` shape before applying routing to agents.
1039
1039
 
1040
- Press `u` to save global agent routing like `ctrl+s`, then capture that routing plus this session's orchestrator model and thinking level in the current profile. If this session has no model, `u` falls back to the orchestrator defaults in `settings.json`; it never changes those defaults. `/gentle:profiles` `s` follows the same rule for the orchestrator: the live session when available, the `settings.json` defaults otherwise. The panel names the profile `u` targets: the profile this repository pins when a pin wins, otherwise the globally active profile. When no profiles store exists yet, `u` seeds it with a `current` profile the way `/gentle:profiles` does on first open; when the store exists but nothing is active and nothing is pinned, the global save still happens and the panel points you to `/gentle:profiles`.
1040
+ The `orchestrator` row selects the model and thinking effort that `u` will store in the current profile. Its initial choice is the target profile's saved entry, or **leave untouched** if that profile has no orchestrator key. Without an existing target profile, it uses the live session model and effort, falling back to `settings.json` when no live model exists; changing the row never switches the live session. Choose **leave untouched** in its model picker, or press `i` on the row, to store no `orchestrator` key—even when replacing a profile that previously carried one. Select a model before editing effort. **Set all agents** never changes this row.
1041
+
1042
+ Press `u` to save global agent routing like `ctrl+s`, then snapshot that routing and the explicit orchestrator choice into the current profile. `ctrl+s` saves only agent routing: it neither updates a profile nor applies the chosen orchestrator. Both keys leave orchestrator defaults and the live model/effort unchanged; applying a profile with no orchestrator key preserves both. Custom-model, export and restore reopens retain the choice; exports/restores remain saved-agent-routing operations. `/gentle:profiles` `s` remains unchanged: it captures the live session model/effort when available, the `settings.json` defaults otherwise. The panel names the profile `u` targets: the profile this repository pins when a pin wins, otherwise the globally active profile. When no profiles store exists yet, `u` seeds it with a `current` profile the way `/gentle:profiles` does on first open; when the store exists but nothing is active and nothing is pinned, the global save still happens and the panel points you to `/gentle:profiles`.
1041
1043
 
1042
1044
  Config shape (per agent):
1043
1045
 
@@ -1080,7 +1082,7 @@ Profiles are named, switchable snapshots of the global agent-model routing from
1080
1082
  | `pgup`/`pgdn`, `ctrl+j`/`ctrl+k` | Scroll the detail pane by a page. |
1081
1083
  | `esc` | Close. |
1082
1084
 
1083
- Selecting a profile with Enter does not write `profiles.json`, `models.json`, Pi's global `settings.json`, agent frontmatter, `subagents.json`, or repository pins. Other open sessions keep their own bindings and live orchestrators. The session binding overrides pins and global routing as a complete snapshot, including reviewer routing; omitted roles do not fall back to another profile. The binding lasts in the current process (including `/reload`); resuming in another process requires selecting it again.
1085
+ Selecting a profile with Enter does not write `profiles.json`, `models.json`, Pi's global `settings.json`, agent frontmatter, `subagents.json`, or repository pins. Other open sessions keep their own bindings and live orchestrators. The session binding overrides pins and global routing as a complete snapshot, including reviewer routing; omitted roles do not fall back to another profile. The binding lasts in the current process (including `/reload`); resuming in another process requires selecting it again. Re-selecting re-copies the profile definition from `profiles.json`; later edits to `subagents.json` or agent frontmatter never reach a bound session, in that session or through a re-selection.
1084
1086
 
1085
1087
  Applying a profile with `a` writes `~/.pi/gentle-ai/models.json`, then reconciles agent frontmatter and `subagents.json` the same way `/gentle:models` does. A profile is a complete snapshot: every discoverable agent it omits returns to inherit, so routing materialized by a previous profile, by `/gentle:models`, or by a migration never survives a switch silently. The active profile is persisted so `/gentle:profiles` reopens with the applied profile marked.
1086
1088
 
@@ -1249,6 +1251,8 @@ With the policy `on`, `subagent_run` defaults to `mode: "background"` at the run
1249
1251
 
1250
1252
  Background delegation is off unless you turn it on. The policy is user-owned: only an explicit `/gentle:background-subagents enable` or `disable` writes it, and Pi automation never toggles it.
1251
1253
 
1254
+ An agent whose explicit `tools` frontmatter includes `bash_background` fails before spawn when the effective background-subagents policy is `off`, in either task or background mode. Admission resolves policy at dispatch using the request's environment and config home, including queued tasks. The error names the policy file, the exact opt-in shape `{"schema":"gentle-pi.background-subagents/v1","policy":"on"}`, and `/gentle:background-subagents enable|disable|status`. Agents without explicit tools are unchanged; the child missing-tools check remains the backstop for other names. Policy `on` passes this admission check only: it does not implement child background tools (future Slices 2–3).
1255
+
1252
1256
  ```text
1253
1257
  /gentle:background-subagents Report the effective policy, the deciding source, and the resolved capability.
1254
1258
  /gentle:background-subagents enable Write "on" to the global file.
@@ -37,3 +37,29 @@ does not establish persistence. The newest profile-family entry is terminal:
37
37
  Replay neither appends nor publishes bindings and never applies a model or
38
38
  thinking level. A subsequent disk-reader slice supplies corroboration; this
39
39
  codec alone cannot establish it.
40
+
41
+ ## Disk corroboration
42
+
43
+ `readSessionProfileDisk(source)` in `lib/session-profile-disk-reader.ts` is the
44
+ stateless synchronous check that supplies the corroboration replay requires.
45
+ It reads the session's public active branch and its JSONL file, selects the
46
+ newest profile-family entry on the branch (excluding caller-supplied known
47
+ failed append IDs), and admits it only when the record on disk is identical.
48
+ It performs no writes, fallback policy, caching, ancestry repair or fsync, and
49
+ a missing file is conservative: it never restores a profile found only in
50
+ memory.
51
+
52
+ The result is the decoder result plus `entryIndex` and `lineNumber`, or
53
+ `indeterminate` with one reason: `missing-source`, `invalid-candidate-metadata`,
54
+ `unreadable-file`, `source-changed`, `invalid-record`,
55
+ `session-header-mismatch`, `duplicate-id`, `missing-header`,
56
+ `selected-record-missing`, `selected-record-mismatch`, or
57
+ `unserializable-source` (exported as `DiskProfileIndeterminateReason`).
58
+ Reasons never carry record contents.
59
+
60
+ Malformed JSON lines are skipped, matching Pi's session parser, so a file Pi
61
+ opens never becomes permanently indeterminate here; `lineNumber` still counts
62
+ physical lines. A malformed selected record is therefore
63
+ `selected-record-missing`. `missing-header` means no line parsed at all (an
64
+ empty, all-blank or all-malformed file). Pi writes no byte order mark, so a
65
+ BOM-prefixed header line is skipped as malformed like any other.
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
@@ -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,7 +38,7 @@ 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
 
@@ -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
  });