gentle-pi 3.7.0 → 4.1.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 (590) hide show
  1. package/README.md +155 -10
  2. package/assets/agents/gentle-ai-explore.md +13 -6
  3. package/assets/agents/gentle-ai-verify.md +27 -6
  4. package/assets/agents/gentle-ai-worker.md +13 -15
  5. package/assets/install-wizard/index.html +47 -0
  6. package/assets/install-wizard/wizard.css +1279 -0
  7. package/assets/install-wizard/wizard.js +951 -0
  8. package/assets/orchestrator-delegation.md +75 -169
  9. package/assets/orchestrator-memory.md +12 -26
  10. package/assets/orchestrator-prompts.md +48 -0
  11. package/assets/orchestrator-skills.md +1 -1
  12. package/assets/orchestrator-tracking.md +53 -0
  13. package/assets/orchestrator-verification.md +28 -0
  14. package/assets/orchestrator-writer.md +38 -0
  15. package/assets/orchestrator.md +30 -38
  16. package/assets/sounds/LICENSE.md +52 -0
  17. package/assets/sounds/attention.wav +0 -0
  18. package/assets/sounds/error.wav +0 -0
  19. package/assets/sounds/success.wav +0 -0
  20. package/assets/support/strict-tdd-verify.md +4 -266
  21. package/assets/support/strict-tdd.md +8 -360
  22. package/bin/engram-guard +9 -0
  23. package/bin/gentle-shell-install.mjs +302 -0
  24. package/bin/gentle-shell.mjs +956 -50
  25. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
  26. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
  27. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
  28. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
  29. package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
  30. package/docs/bundled-install.md +397 -0
  31. package/docs/delegated-verification.md +27 -4
  32. package/docs/gentle-agents-activity.md +537 -0
  33. package/docs/gentle-shell-uninstall.md +136 -0
  34. package/docs/gentle-shell.md +165 -51
  35. package/docs/install-wizard.md +2040 -0
  36. package/docs/native-authority-architecture.md +3 -3
  37. package/docs/permission-modes.md +82 -0
  38. package/docs/prompt-history.md +280 -0
  39. package/docs/readme-reference.md +364 -231
  40. package/docs/render-cache-performance.md +28 -0
  41. package/docs/sound-notifications-proposal.md +191 -0
  42. package/docs/sound-notifications.md +114 -0
  43. package/docs/telemetry.md +1 -1
  44. package/docs/yolo-mode.md +86 -0
  45. package/extensions/child-context.ts +35 -0
  46. package/extensions/child-safety.ts +25 -0
  47. package/extensions/codegraph-tools.ts +18 -5
  48. package/extensions/gentle-agents.ts +1093 -326
  49. package/extensions/gentle-ai.ts +1636 -890
  50. package/extensions/gentle-herdr-activity.ts +81 -0
  51. package/extensions/gentle-notifications.ts +104 -0
  52. package/extensions/gentle-shell.ts +1619 -136
  53. package/extensions/gentle-stats.ts +101 -0
  54. package/extensions/gentle-todo.ts +126 -20
  55. package/extensions/history/atomic-write.ts +38 -0
  56. package/extensions/history/hide-prompts.ts +183 -0
  57. package/extensions/history/index.ts +1424 -0
  58. package/extensions/history/load-shared-history.ts +39 -0
  59. package/extensions/history/selector-helpers.ts +538 -0
  60. package/extensions/history/session-scan.ts +233 -0
  61. package/extensions/history/store.ts +1119 -0
  62. package/extensions/nan-provider.ts +6 -0
  63. package/extensions/pi-pretty.ts +39 -3
  64. package/extensions/quiet-tools.ts +254 -92
  65. package/extensions/resume-hint.ts +60 -0
  66. package/extensions/skill-registry.ts +191 -58
  67. package/extensions/startup-banner.ts +71 -33
  68. package/lib/agent-assets.ts +604 -0
  69. package/lib/agent-home.ts +10 -1
  70. package/lib/agent-model-resolution.ts +80 -0
  71. package/lib/agent-profile-pin.ts +12 -0
  72. package/lib/agent-profiles.ts +13 -3
  73. package/lib/agents-completion-delivery.ts +11 -3
  74. package/lib/agents-message-delivery.ts +181 -0
  75. package/lib/agents-messaging.ts +1 -1
  76. package/lib/agents-protocol.ts +99 -0
  77. package/lib/agents-runner.ts +180 -107
  78. package/lib/agents-view.ts +26 -3
  79. package/lib/agents-widget.ts +16 -9
  80. package/lib/append-system-prompt.ts +21 -0
  81. package/lib/auto-permission-classifier.ts +280 -0
  82. package/lib/auto-permission-runtime.ts +268 -0
  83. package/lib/background-jobs-tools.ts +202 -0
  84. package/lib/background-jobs.ts +253 -0
  85. package/lib/background-monitor.ts +154 -0
  86. package/lib/bounded-writer-admission.ts +220 -0
  87. package/lib/builtin-codemode-optout.ts +166 -0
  88. package/lib/card-content-policy.ts +85 -0
  89. package/lib/card-style-policy.ts +60 -0
  90. package/lib/child-context-files.ts +166 -0
  91. package/lib/child-package-injection.ts +62 -0
  92. package/lib/codemode-renderer.ts +195 -0
  93. package/lib/command-palette-catalog.ts +5 -10
  94. package/lib/command-palette.ts +25 -14
  95. package/lib/context-threshold.ts +26 -0
  96. package/lib/destructive-command-guard.ts +144 -0
  97. package/lib/gentle-ai-binary.ts +109 -4
  98. package/lib/gentle-ai-elapsed-store.ts +87 -0
  99. package/lib/gentle-ai-renderer.ts +196 -39
  100. package/lib/gentle-shell-bundled.ts +517 -0
  101. package/lib/gentle-shell-launcher.ts +89 -27
  102. package/lib/gentle-shell-resume-hint.ts +176 -0
  103. package/lib/gentle-shell-startup-flow.ts +54 -0
  104. package/lib/gentle-shell-startup-update.ts +92 -0
  105. package/lib/gentle-shell-uninstall.ts +840 -0
  106. package/lib/gentle-shell-update-panel.ts +231 -0
  107. package/lib/herdr-activity.ts +183 -0
  108. package/lib/history-capture-policy.ts +95 -0
  109. package/lib/inprocess-reviewer.ts +3 -0
  110. package/lib/jobs-sidebar-state.ts +20 -0
  111. package/lib/jobs-view.ts +161 -0
  112. package/lib/model-price-ratio.ts +83 -0
  113. package/lib/model-routing-authority.ts +5 -1
  114. package/lib/nan-provider.ts +249 -0
  115. package/lib/native-review-cli.ts +242 -180
  116. package/lib/notification-audio-native.ts +162 -0
  117. package/lib/notification-audio-windows.ts +399 -0
  118. package/lib/notification-audio.ts +332 -0
  119. package/lib/notification-customize.ts +241 -0
  120. package/lib/notification-events.ts +113 -0
  121. package/lib/notification-policy.ts +148 -0
  122. package/lib/notification-pulse-client.ts +367 -0
  123. package/lib/notification-pulse-protocol.ts +305 -0
  124. package/lib/notification-pulse-stream.ts +213 -0
  125. package/lib/notification-pulse-worker.ts +66 -0
  126. package/lib/notification-scheduler.ts +173 -0
  127. package/lib/notification-service.ts +202 -0
  128. package/lib/notification-ui.ts +87 -0
  129. package/lib/odd-phase-inference.ts +231 -0
  130. package/lib/odd-phase.ts +141 -0
  131. package/lib/orchestrator-alias.ts +20 -0
  132. package/lib/orchestrator-catalog.ts +79 -0
  133. package/lib/orchestrator-consultation.ts +61 -0
  134. package/lib/orchestrator-discovery.ts +49 -0
  135. package/lib/orchestrator-helper-consent.ts +122 -0
  136. package/lib/orchestrator-helper.ts +167 -0
  137. package/lib/orchestrator-message-card.ts +105 -0
  138. package/lib/orchestrator-presence.ts +115 -14
  139. package/lib/orchestrator-scope.ts +80 -0
  140. package/lib/orchestrator-state.ts +107 -0
  141. package/lib/orchestrator-tool-card.ts +280 -0
  142. package/lib/orchestrator-work-search.ts +218 -0
  143. package/lib/orchestrator-work.ts +79 -0
  144. package/lib/overlay-repaint.ts +26 -0
  145. package/lib/permission-mode-policy.ts +59 -0
  146. package/lib/pi-tui-keys.ts +53 -0
  147. package/lib/review-candidate-view-owner.ts +73 -21
  148. package/lib/review-candidate-view.ts +115 -27
  149. package/lib/review-compact-contract.ts +2 -2
  150. package/lib/review-host-relay.ts +7 -2
  151. package/lib/review-integration-v2.ts +100 -22
  152. package/lib/review-publication-gate.ts +1 -1
  153. package/lib/review-reminder-receipt.ts +48 -8
  154. package/lib/review-risk-assessment.ts +227 -56
  155. package/lib/review-sidebar-state.ts +224 -0
  156. package/lib/review-transaction.ts +72 -3
  157. package/lib/runtime-metrics-children.ts +1 -1
  158. package/lib/runtime-metrics-native.ts +3 -2
  159. package/lib/selection-engine.ts +527 -0
  160. package/lib/session-change-capture.ts +5 -4
  161. package/lib/session-changes.ts +27 -22
  162. package/lib/session-messaging-grants.ts +135 -0
  163. package/lib/session-profile-binding.ts +83 -0
  164. package/lib/session-profile-freeze.ts +266 -0
  165. package/lib/session-worktree-registry.ts +87 -9
  166. package/lib/shell-bar.ts +215 -30
  167. package/lib/shell-card.ts +287 -18
  168. package/lib/shell-changes-view.ts +42 -14
  169. package/lib/shell-changes.ts +86 -71
  170. package/lib/shell-prompt.ts +102 -6
  171. package/lib/shell-sidebar-layout.ts +83 -15
  172. package/lib/shell-sidebar.ts +15 -1
  173. package/lib/shell-todo.ts +84 -31
  174. package/lib/shell-usage-view.ts +9 -4
  175. package/lib/shell-usage.ts +70 -13
  176. package/lib/stats-collector.ts +381 -0
  177. package/lib/stats-view.ts +431 -0
  178. package/lib/status-timing-diagnostics.ts +201 -0
  179. package/lib/telemetry-trigger.ts +3 -1
  180. package/lib/theme-customization.ts +52 -0
  181. package/lib/vim-editor-adapter.ts +383 -0
  182. package/lib/vim-normal-engine.ts +154 -0
  183. package/lib/vim-operator-engine.ts +416 -0
  184. package/lib/vim-policy.ts +49 -0
  185. package/lib/vim-visual-engine.ts +107 -0
  186. package/lib/visual-customization-policy.ts +119 -0
  187. package/lib/visual-customize-view.ts +481 -0
  188. package/lib/visual-profiles.ts +228 -0
  189. package/lib/writer-surfaces.ts +162 -0
  190. package/lib/yolo-session-policy.ts +317 -0
  191. package/package.json +20 -8
  192. package/runtime/child-package-injection.mjs +63 -0
  193. package/runtime/gentle-ai-binary.mjs +109 -4
  194. package/runtime/gentle-shell-bundled.mjs +518 -0
  195. package/runtime/gentle-shell-launcher.mjs +87 -25
  196. package/runtime/gentle-shell-resume-hint.mjs +177 -0
  197. package/runtime/gentle-shell-startup-flow.mjs +55 -0
  198. package/runtime/gentle-shell-startup-update.mjs +93 -0
  199. package/runtime/gentle-shell-uninstall.mjs +841 -0
  200. package/runtime/gentle-shell-update-panel.mjs +232 -0
  201. package/runtime/native-review-cli.mjs +239 -177
  202. package/runtime/review-integration-v2.mjs +100 -22
  203. package/runtime/review-risk-assessment.mjs +225 -54
  204. package/runtime/status-timing-diagnostics.mjs +202 -0
  205. package/runtime/telemetry-trigger.mjs +3 -1
  206. package/scripts/bootstrap.cmd +255 -0
  207. package/scripts/bootstrap.sh +168 -0
  208. package/scripts/build-distribution.mjs +157 -0
  209. package/scripts/build-installer-bundles.mjs +136 -0
  210. package/scripts/build-runtime-modules.mjs +8 -0
  211. package/scripts/bundled-install.mjs +1308 -0
  212. package/scripts/bundled-wizard.mjs +399 -0
  213. package/scripts/check-types.mjs +4 -6
  214. package/scripts/gentle-ai-installer.mjs +83 -35
  215. package/scripts/install-gentle-ai.mjs +48 -1
  216. package/scripts/install-tui-mode-setting.mjs +10 -1
  217. package/scripts/install-wizard-preview.mjs +154 -0
  218. package/scripts/installer-downloads.mjs +648 -0
  219. package/scripts/installer-preflight.mjs +387 -0
  220. package/scripts/installer-probes.mjs +564 -0
  221. package/scripts/installer-runner.mjs +1102 -0
  222. package/scripts/installer-server.mjs +881 -0
  223. package/scripts/installer-windows-artifacts.json +15 -0
  224. package/scripts/installer-windows.mjs +752 -0
  225. package/scripts/main-channel.mjs +452 -0
  226. package/scripts/mirror-odd-routing.mjs +2 -2
  227. package/scripts/npm-pack-result.mjs +31 -0
  228. package/scripts/render-cache-performance-worker.mjs +205 -0
  229. package/scripts/render-cache-performance.mjs +115 -0
  230. package/scripts/run-test-suite.mjs +90 -0
  231. package/scripts/test-installer-acceptance.sh +161 -0
  232. package/scripts/test-packed-runner.mjs +54 -19
  233. package/scripts/types-baseline.json +2 -92
  234. package/scripts/verify-package-files.mjs +91 -38
  235. package/skills/branch-pr/SKILL.md +24 -52
  236. package/skills/chained-pr/SKILL.md +31 -15
  237. package/skills/chained-pr/references/chaining-details.md +31 -20
  238. package/skills/gentle-ai/SKILL.md +16 -21
  239. package/skills/issue-creation/SKILL.md +8 -2
  240. package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
  241. package/skills/work-unit-commits/SKILL.md +4 -3
  242. package/tests/agent-home.test.ts +56 -3
  243. package/tests/agent-model-resolution.test.ts +113 -0
  244. package/tests/agent-profiles.test.ts +30 -0
  245. package/tests/agent-risk-escalation.test.ts +123 -0
  246. package/tests/agents-completion-delivery.test.ts +17 -0
  247. package/tests/agents-fake-child.ts +2 -2
  248. package/tests/agents-message-delivery.test.ts +106 -0
  249. package/tests/agents-protocol.test.ts +97 -0
  250. package/tests/agents-queries.test.ts +9 -9
  251. package/tests/agents-runner-process.test.ts +31 -0
  252. package/tests/agents-runner.test.ts +467 -93
  253. package/tests/agents-view-thread-identity.test.ts +169 -0
  254. package/tests/agents-view.test.ts +8 -2
  255. package/tests/agents-widget.test.ts +154 -15
  256. package/tests/append-system-prompt-route.test.ts +160 -0
  257. package/tests/append-system-prompt.test.ts +46 -0
  258. package/tests/artifact-language.test.ts +19 -213
  259. package/tests/ask-user-question.test.ts +44 -1
  260. package/tests/asset-installation-runtime.test.ts +5 -16
  261. package/tests/auto-mode-invariants.test.ts +258 -0
  262. package/tests/auto-mode-sdk.test.ts +57 -0
  263. package/tests/auto-mode.test.ts +49 -0
  264. package/tests/auto-permission-classifier.test.ts +283 -0
  265. package/tests/auto-permission-runtime.test.ts +196 -0
  266. package/tests/autonomous-guard.test.ts +69 -1
  267. package/tests/background-cache-warming.test.ts +2 -1
  268. package/tests/background-jobs.test.ts +227 -0
  269. package/tests/background-monitor.test.ts +195 -0
  270. package/tests/background-subagents-default-mode.test.ts +28 -5
  271. package/tests/bounded-stop-contract.test.ts +85 -0
  272. package/tests/bounded-writer-admission.test.ts +146 -0
  273. package/tests/branch-pr-skill.test.ts +43 -0
  274. package/tests/bridge-wake-lifecycle.test.ts +129 -0
  275. package/tests/bridge-wake-rendering.test.ts +56 -0
  276. package/tests/build-distribution.test.ts +269 -0
  277. package/tests/builtin-codemode-optout.test.ts +242 -0
  278. package/tests/bundled-install.test.ts +1319 -0
  279. package/tests/bundled-wizard.test.ts +505 -0
  280. package/tests/card-content-policy.test.ts +68 -0
  281. package/tests/card-style-policy.test.ts +87 -0
  282. package/tests/chained-pr-skill.test.ts +124 -0
  283. package/tests/child-codegraph-bootstrap.test.ts +74 -0
  284. package/tests/child-context-files.test.ts +255 -0
  285. package/tests/child-package-entrypoints.test.ts +65 -0
  286. package/tests/child-package-injection.test.ts +74 -0
  287. package/tests/child-safety.test.ts +94 -0
  288. package/tests/child-web-package.test.ts +224 -0
  289. package/tests/codegraph-tools.test.ts +35 -0
  290. package/tests/codemode-rendering.test.ts +596 -0
  291. package/tests/command-palette.test.ts +43 -7
  292. package/tests/context-threshold.test.ts +31 -0
  293. package/tests/delegate-for-reason-contract.test.ts +327 -0
  294. package/tests/delegated-key-learnings-contract.test.ts +3 -78
  295. package/tests/destructive-command-guard.test.ts +84 -0
  296. package/tests/devbinary/native-review-parity.devtest.ts +171 -3
  297. package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
  298. package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
  299. package/tests/engram-safety.test.ts +288 -0
  300. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
  301. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
  302. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
  303. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
  304. package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
  305. package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
  306. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
  307. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
  308. package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
  309. package/tests/generic-agent-tools.test.ts +69 -0
  310. package/tests/gentle-agents.test.ts +2804 -346
  311. package/tests/gentle-ai-binary-isolation.test.ts +135 -0
  312. package/tests/gentle-ai-binary.test.ts +21 -4
  313. package/tests/gentle-ai-child-guards.test.ts +202 -0
  314. package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
  315. package/tests/gentle-ai-elapsed-store.test.ts +68 -0
  316. package/tests/gentle-ai-installer.test.ts +566 -62
  317. package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
  318. package/tests/gentle-ai-renderer.test.ts +493 -14
  319. package/tests/gentle-ai.test.ts +1427 -236
  320. package/tests/gentle-card-text.ts +2 -1
  321. package/tests/gentle-herdr-activity.test.ts +100 -0
  322. package/tests/gentle-notifications.test.ts +427 -0
  323. package/tests/gentle-shell-bin.test.ts +896 -115
  324. package/tests/gentle-shell-bundled.test.ts +1002 -0
  325. package/tests/gentle-shell-launcher.test.ts +333 -65
  326. package/tests/gentle-shell-resume-hint.test.ts +270 -0
  327. package/tests/gentle-shell-startup-flow.test.ts +75 -0
  328. package/tests/gentle-shell-startup-update.test.ts +85 -0
  329. package/tests/gentle-shell-uninstall.test.ts +978 -0
  330. package/tests/gentle-shell-update-panel.test.ts +158 -0
  331. package/tests/gentle-shell.test.ts +4216 -119
  332. package/tests/gentle-stats.test.ts +152 -0
  333. package/tests/gentle-theme.test.ts +4 -1
  334. package/tests/gentle-todo.test.ts +333 -10
  335. package/tests/herdr-activity.test.ts +243 -0
  336. package/tests/history-agent-home.test.ts +81 -0
  337. package/tests/history-atomic-write.test.ts +57 -0
  338. package/tests/history-capture-policy.test.ts +102 -0
  339. package/tests/history-child-guard.test.ts +70 -0
  340. package/tests/history-command-registration.test.ts +164 -0
  341. package/tests/history-dedupe-entries.test.ts +123 -0
  342. package/tests/history-delete-backfill.test.ts +190 -0
  343. package/tests/history-delete-confirm.test.ts +460 -0
  344. package/tests/history-dispatch.test.ts +180 -0
  345. package/tests/history-drain-hidden.test.ts +110 -0
  346. package/tests/history-drain-order.test.ts +98 -0
  347. package/tests/history-expanded-globals.test.ts +62 -0
  348. package/tests/history-gc.test.ts +832 -0
  349. package/tests/history-header-layout.test.ts +265 -0
  350. package/tests/history-hide-prompts.test.ts +275 -0
  351. package/tests/history-lazy-windowing.test.ts +508 -0
  352. package/tests/history-legacy-migrate-v2.test.ts +297 -0
  353. package/tests/history-load-shared-history.test.ts +53 -0
  354. package/tests/history-max-results-cap.test.ts +76 -0
  355. package/tests/history-multi-reader.test.ts +203 -0
  356. package/tests/history-off-path.test.ts +170 -0
  357. package/tests/history-openflow-integration.test.ts +173 -0
  358. package/tests/history-overlay-margin.test.ts +326 -0
  359. package/tests/history-preview-layout.test.ts +93 -0
  360. package/tests/history-registry.test.ts +143 -0
  361. package/tests/history-scope-delete.test.ts +411 -0
  362. package/tests/history-search-caret-keys.test.ts +142 -0
  363. package/tests/history-seed-bootstrap.test.ts +170 -0
  364. package/tests/history-seed-regen.test.ts +129 -0
  365. package/tests/history-selector-windowing.test.ts +94 -0
  366. package/tests/history-session-scan-directory.test.ts +87 -0
  367. package/tests/history-session-scan-extract.test.ts +583 -0
  368. package/tests/history-session-writer.test.ts +351 -0
  369. package/tests/history-store-paths.test.ts +79 -0
  370. package/tests/history-tombstone-exact.test.ts +139 -0
  371. package/tests/history-wheel-mouse.test.ts +242 -0
  372. package/tests/inprocess-reviewer.test.ts +49 -19
  373. package/tests/install-gentle-ai-failure.test.ts +69 -0
  374. package/tests/install-tui-mode-setting.test.ts +15 -0
  375. package/tests/install-wizard.test.ts +1223 -0
  376. package/tests/installer-bundles.test.ts +122 -0
  377. package/tests/installer-posix-bootstrap.test.ts +1201 -0
  378. package/tests/installer-preflight.test.ts +750 -0
  379. package/tests/installer-probes.test.ts +1106 -0
  380. package/tests/installer-runner.test.ts +2158 -0
  381. package/tests/installer-server.test.ts +1567 -0
  382. package/tests/installer-windows-bootstrap.test.ts +2410 -0
  383. package/tests/issue-creation-skill.test.ts +61 -0
  384. package/tests/jobs-view.test.ts +160 -0
  385. package/tests/main-channel-upgrade.test.ts +389 -0
  386. package/tests/main-channel.test.ts +344 -0
  387. package/tests/maintainer/provider-relay.maintest.ts +13 -12
  388. package/tests/model-price-ratio.test.ts +116 -0
  389. package/tests/model-profiles-preservation.test.ts +153 -0
  390. package/tests/model-routing-authority.test.ts +16 -0
  391. package/tests/model-routing-package.test.ts +10 -0
  392. package/tests/nan-provider-runtime.test.ts +156 -0
  393. package/tests/nan-provider.test.ts +481 -0
  394. package/tests/native-binary-gate.test.ts +2 -0
  395. package/tests/native-review-capability-contract.test.ts +13 -1
  396. package/tests/native-review-cli.test.ts +239 -118
  397. package/tests/native-review-consent.test.ts +2 -2
  398. package/tests/native-review-parity-runtime.test.ts +101 -4
  399. package/tests/native-review-parity.test.ts +12 -48
  400. package/tests/native-risk-evidence-parity.test.ts +21 -0
  401. package/tests/no-self-pause-contract.test.ts +87 -0
  402. package/tests/notification-audio-native.test.ts +281 -0
  403. package/tests/notification-audio-windows.test.ts +581 -0
  404. package/tests/notification-audio.test.ts +548 -0
  405. package/tests/notification-customize.test.ts +655 -0
  406. package/tests/notification-events.test.ts +115 -0
  407. package/tests/notification-policy.test.ts +201 -0
  408. package/tests/notification-pulse-client.test.ts +356 -0
  409. package/tests/notification-pulse-protocol.test.ts +213 -0
  410. package/tests/notification-pulse-stream.test.ts +320 -0
  411. package/tests/notification-scheduler.test.ts +258 -0
  412. package/tests/notification-ui.test.ts +157 -0
  413. package/tests/npm-pack-result.test.ts +41 -0
  414. package/tests/odd-integration.test.ts +76 -0
  415. package/tests/odd-phase-inference.test.ts +213 -0
  416. package/tests/odd-phase-loader.test.ts +253 -0
  417. package/tests/odd-phase.test.ts +307 -0
  418. package/tests/odd-routing-canonical-ratchet.test.ts +60 -14
  419. package/tests/odd-routing-contract.test.ts +158 -50
  420. package/tests/orchestrator-budget.test.ts +43 -45
  421. package/tests/orchestrator-catalog.test.ts +146 -0
  422. package/tests/orchestrator-consultation-sdk.test.ts +614 -0
  423. package/tests/orchestrator-consultation.test.ts +111 -0
  424. package/tests/orchestrator-discovery.test.ts +241 -0
  425. package/tests/orchestrator-helper-consent.test.ts +171 -0
  426. package/tests/orchestrator-helper.test.ts +297 -0
  427. package/tests/orchestrator-message-card.test.ts +190 -0
  428. package/tests/orchestrator-presence.test.ts +23 -0
  429. package/tests/orchestrator-rdd-ownership.test.ts +8 -7
  430. package/tests/orchestrator-scope.test.ts +107 -0
  431. package/tests/orchestrator-state.test.ts +114 -0
  432. package/tests/orchestrator-status-publication-contract.test.ts +34 -0
  433. package/tests/orchestrator-tool-card.test.ts +398 -0
  434. package/tests/orchestrator-work-search.test.ts +318 -0
  435. package/tests/orchestrator-work.test.ts +147 -0
  436. package/tests/overlay-repaint.test.ts +74 -0
  437. package/tests/package-manifest.test.ts +344 -123
  438. package/tests/packed-runner-owned-path.test.ts +80 -0
  439. package/tests/parallel-reason-contract.test.ts +21 -0
  440. package/tests/parallel-writer-admission.test.ts +210 -0
  441. package/tests/permission-mode-policy.test.ts +194 -0
  442. package/tests/persona-single-channel.test.ts +10 -18
  443. package/tests/pi-pretty.test.ts +56 -0
  444. package/tests/provider-defect-handoff.test.ts +7 -14
  445. package/tests/quiet-bash-runtime.test.ts +76 -0
  446. package/tests/quiet-tool-rendering.test.ts +569 -187
  447. package/tests/rdd-aware-verification-contract.test.ts +103 -40
  448. package/tests/rdd-status-line.test.ts +9 -4
  449. package/tests/render-cache-performance.test.ts +48 -0
  450. package/tests/resume-hint-extension.test.ts +122 -0
  451. package/tests/review-agent-end-preflight.test.ts +237 -21
  452. package/tests/review-candidate-owner-retry.test.ts +22 -1
  453. package/tests/review-candidate-view.test.ts +399 -12
  454. package/tests/review-contract-prompt.test.ts +138 -44
  455. package/tests/review-controller-lock-status.test.ts +0 -1
  456. package/tests/review-controller-native-recovery.test.ts +327 -17
  457. package/tests/review-controller-native-routing.test.ts +866 -6
  458. package/tests/review-controller-workspace-root.test.ts +206 -6
  459. package/tests/review-controller.test.ts +2 -2
  460. package/tests/review-correction-lifecycle.test.ts +2 -1
  461. package/tests/review-gate.test.ts +7 -4
  462. package/tests/review-graph-schema.test.ts +2 -2
  463. package/tests/review-host-relay-routing.test.ts +342 -4
  464. package/tests/review-host-relay.test.ts +49 -2
  465. package/tests/review-integration-v2-forward.test.ts +221 -1
  466. package/tests/review-json-arguments.test.ts +167 -0
  467. package/tests/review-last-event-closure.test.ts +157 -8
  468. package/tests/review-ledger-contract.test.ts +14 -37
  469. package/tests/review-policy-judgment-day.test.ts +1 -1
  470. package/tests/review-policy-ordinary.test.ts +2 -2
  471. package/tests/review-relay-transport-agent.test.ts +66 -1
  472. package/tests/review-reminder-receipt.test.ts +47 -1
  473. package/tests/review-risk-assessment.test.ts +594 -26
  474. package/tests/review-session-standing-permission-controller.test.ts +16 -17
  475. package/tests/review-session-standing-permission-ipc.test.ts +6 -5
  476. package/tests/review-sidebar-state.test.ts +402 -0
  477. package/tests/review-test-fixtures.ts +7 -1
  478. package/tests/review-transaction.test.ts +18 -1
  479. package/tests/run-test-suite.test.ts +202 -0
  480. package/tests/runtime-harness.mjs +186 -800
  481. package/tests/runtime-metrics-children.test.ts +16 -23
  482. package/tests/runtime-metrics-extension.test.ts +1 -1
  483. package/tests/runtime-metrics-native.test.ts +31 -1
  484. package/tests/selection-engine.test.ts +451 -0
  485. package/tests/session-change-capture.test.ts +10 -10
  486. package/tests/session-changes-shell.test.ts +242 -48
  487. package/tests/session-changes.test.ts +17 -24
  488. package/tests/session-messaging-grants.test.ts +255 -0
  489. package/tests/session-profile-binding.test.ts +163 -0
  490. package/tests/session-profile-freeze.test.ts +381 -0
  491. package/tests/session-subject-routing-contract.test.ts +34 -0
  492. package/tests/session-worktree-registry.test.ts +134 -2
  493. package/tests/shell-bar.test.ts +422 -1
  494. package/tests/shell-card.test.ts +353 -1
  495. package/tests/shell-changes-view.test.ts +135 -32
  496. package/tests/shell-changes.test.ts +61 -62
  497. package/tests/shell-prompt.test.ts +94 -2
  498. package/tests/shell-render-cache-runtime.test.ts +69 -0
  499. package/tests/shell-render-cache.test.ts +129 -0
  500. package/tests/shell-sidebar-layout.test.ts +351 -22
  501. package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
  502. package/tests/shell-todo.test.ts +207 -3
  503. package/tests/shell-usage-view.test.ts +28 -1
  504. package/tests/shell-usage.test.ts +108 -0
  505. package/tests/short-check-routing-contract.test.ts +49 -0
  506. package/tests/skill-registry-watcher.test.ts +119 -0
  507. package/tests/skill-registry.test.ts +357 -2
  508. package/tests/startup-banner.test.ts +246 -4
  509. package/tests/startup-home-repo.test.ts +170 -0
  510. package/tests/stats-collector.test.ts +195 -0
  511. package/tests/stats-view.test.ts +202 -0
  512. package/tests/status-timing-diagnostics.test.ts +283 -0
  513. package/tests/support/orchestrator-modules.ts +19 -0
  514. package/tests/task-size-routing-contract.test.ts +241 -0
  515. package/tests/telemetry-trigger.test.ts +107 -20
  516. package/tests/test-strength-contract.test.ts +124 -0
  517. package/tests/theme-customization.test.ts +72 -0
  518. package/tests/verify-agent-contract.test.ts +90 -0
  519. package/tests/verify-first-launch-contract.test.ts +139 -0
  520. package/tests/verify-package-files.test.ts +56 -0
  521. package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
  522. package/tests/vim-editor-adapter.test.ts +804 -0
  523. package/tests/vim-normal-engine.test.ts +101 -0
  524. package/tests/vim-operator-engine.test.ts +215 -0
  525. package/tests/vim-policy.test.ts +19 -0
  526. package/tests/vim-visual-engine.test.ts +52 -0
  527. package/tests/visual-customization-policy.test.ts +139 -0
  528. package/tests/visual-customize-view.test.ts +696 -0
  529. package/tests/visual-profiles.test.ts +87 -0
  530. package/tests/writer-edit-surface-scope.test.ts +59 -2
  531. package/tests/writer-surfaces.test.ts +199 -0
  532. package/tests/yolo-customize.test.ts +271 -0
  533. package/tests/yolo-mode-runtime.test.ts +188 -0
  534. package/tests/yolo-mode.test.ts +289 -0
  535. package/tests/yolo-session-policy.test.ts +59 -0
  536. package/themes/Gentle.json +2 -1
  537. package/themes/Gentleman-Cute.json +2 -1
  538. package/themes/Gentleman-Sexy.json +2 -1
  539. package/assets/agents/sdd-apply.md +0 -159
  540. package/assets/agents/sdd-archive.md +0 -228
  541. package/assets/agents/sdd-design.md +0 -49
  542. package/assets/agents/sdd-explore.md +0 -48
  543. package/assets/agents/sdd-init.md +0 -56
  544. package/assets/agents/sdd-onboard.md +0 -52
  545. package/assets/agents/sdd-proposal.md +0 -64
  546. package/assets/agents/sdd-remediate.md +0 -37
  547. package/assets/agents/sdd-research.md +0 -49
  548. package/assets/agents/sdd-spec.md +0 -192
  549. package/assets/agents/sdd-status.md +0 -54
  550. package/assets/agents/sdd-tasks.md +0 -108
  551. package/assets/agents/sdd-verify.md +0 -124
  552. package/assets/chains/sdd-full.chain.md +0 -83
  553. package/assets/chains/sdd-plan.chain.md +0 -56
  554. package/assets/chains/sdd-verify.chain.md +0 -43
  555. package/assets/sdd-orchestrator-workflow.md +0 -319
  556. package/assets/support/sdd-status-contract.md +0 -77
  557. package/docs/assets/diagrams/sdd-cycle.svg +0 -14
  558. package/extensions/sdd-init.ts +0 -816
  559. package/lib/openspec-deltas.ts +0 -156
  560. package/lib/sdd-preflight.ts +0 -1066
  561. package/lib/sdd-research-capabilities.ts +0 -94
  562. package/lib/sdd-status.ts +0 -26
  563. package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
  564. package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
  565. package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
  566. package/tests/openspec-deltas.test.ts +0 -209
  567. package/tests/sdd-agent-tools.test.ts +0 -156
  568. package/tests/sdd-archive-replay.test.ts +0 -82
  569. package/tests/sdd-classical-continuation.test.ts +0 -74
  570. package/tests/sdd-execution-routing-contract.test.ts +0 -44
  571. package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
  572. package/tests/sdd-native-managed-uptake.test.ts +0 -243
  573. package/tests/sdd-no-attempts-contract.test.ts +0 -15
  574. package/tests/sdd-odd-integration.test.ts +0 -33
  575. package/tests/sdd-optional-research.test.ts +0 -124
  576. package/tests/sdd-planning-routing-contract.test.ts +0 -45
  577. package/tests/sdd-preflight-rpc-input.test.ts +0 -125
  578. package/tests/sdd-preflight.test.ts +0 -541
  579. package/tests/sdd-research-capabilities.test.ts +0 -114
  580. package/tests/sdd-research-live.test.ts +0 -241
  581. package/tests/sdd-selection-transport.test.ts +0 -653
  582. package/tests/sdd-status.test.ts +0 -9
  583. package/tests/sdd-task-truth.test.ts +0 -43
  584. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
  585. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
  586. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
  587. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  588. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
  589. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
  590. /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
@@ -0,0 +1,146 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdirSync, mkdtempSync, realpathSync, renameSync, rmSync, symlinkSync } from "node:fs";
3
+ import { execFileSync } from "node:child_process";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import test from "node:test";
7
+ import { __testing } from "../extensions/gentle-ai.ts";
8
+ import { allowedEditSurfaces, inheritAllowedEditSurfaces, isBoundedWriter, bindSessionRepositoryPreparation, boundSessionRepositoryAuthorityCurrent, captureBoundSessionRepositoryAuthority, isDevelopmentSurface, prepareBoundSessionRepository, safeBootstrapDirectory, sessionRepositoryAuthority } from "../lib/bounded-writer-admission.ts";
9
+ import type { NativeReviewCli } from "../lib/native-review-cli.ts";
10
+
11
+ test("development admission excludes sensitive, config and bookkeeping surfaces structurally", () => {
12
+ for (const path of ["README.md", "odd/tasks/feature.md", ".pi/settings.json", "docs/app.ts", ".ssh/app.ts", ".aws/app.ts", ".gnupg/app.ts", "credentials/app.ts", "tokens/app.ts", "secrets/app.ts", "vite.config.ts", ".env", "private.key"]) assert.equal(isDevelopmentSurface(path), false, path);
13
+ for (const path of ["src/app.ts", "lib/core.go", "tests/feature.test.ts", "src/**/*"]) assert.equal(isDevelopmentSurface(path), true, path);
14
+ });
15
+
16
+ test("authority capture is synchronous and retains root/common-dir across misses", () => {
17
+ let identity: { root: string; commonDir: string } | undefined = { root: "/fixture", commonDir: "/fixture/git" };
18
+ const current = sessionRepositoryAuthority("/fixture", () => identity);
19
+ identity = undefined;
20
+ assert.equal(current(), false);
21
+ identity = { root: "/fixture", commonDir: "/other/git" };
22
+ assert.equal(current(), false);
23
+ identity = { root: "/other", commonDir: "/fixture/git" };
24
+ assert.equal(current(), false);
25
+ });
26
+
27
+ test("captured authority cannot fall back to missing or a replacement incarnation", () => {
28
+ const cwd = realpathSync(tmpdir());
29
+ const manager = { getSessionId: () => "same-id", getCwd: () => cwd };
30
+ const neverBound = captureBoundSessionRepositoryAuthority(manager, cwd);
31
+ assert.equal(neverBound(), true, "never-bound compatibility callers remain permitted");
32
+ const unbind = bindSessionRepositoryPreparation(manager, cwd, async () => true, () => true);
33
+ assert.equal(neverBound(), false, "capture cannot silently adopt a subsequently minted binding");
34
+ const captured = captureBoundSessionRepositoryAuthority(manager, cwd);
35
+ assert.equal(captured(), true);
36
+ unbind();
37
+ assert.equal(captured(), false, "revoked binding never becomes permissive absence");
38
+ assert.equal(boundSessionRepositoryAuthorityCurrent(manager, cwd), false, "new calls from revoked owners are also rejected");
39
+ const replacement = bindSessionRepositoryPreparation(manager, cwd, async () => true, () => true);
40
+ assert.equal(captured(), false, "same manager and ID cannot substitute another incarnation");
41
+ const fresh = captureBoundSessionRepositoryAuthority(manager, cwd);
42
+ assert.equal(fresh(), true);
43
+ manager.getSessionId = () => "changed-id";
44
+ assert.equal(fresh(), false);
45
+ replacement();
46
+ });
47
+
48
+ for (const race of ["loss", "replacement", "cancellation"] as const) {
49
+ test(`pending preparation revalidates established authority and owner before STATUS: ${race}`, async t => {
50
+ const cwd = realpathSync(mkdtempSync(join(tmpdir(), "bootstrap-authority-race-")));
51
+ t.after(() => rmSync(cwd, { recursive: true, force: true }));
52
+ const manager = { getSessionId: () => "fixture", getCwd: () => cwd };
53
+ let release!: () => void;
54
+ const gate = new Promise<void>(resolve => { release = resolve; });
55
+ let calls = 0;
56
+ const abort = new AbortController();
57
+ const unbind = bindSessionRepositoryPreparation(manager, cwd, async (_root, current) => {
58
+ execFileSync("git", ["init", "--quiet", cwd]);
59
+ assert.equal(current(), true, "first authorized bootstrap establishes retained identity");
60
+ await gate;
61
+ if (!current()) return false;
62
+ calls++;
63
+ return true;
64
+ }, () => true);
65
+ const pending = prepareBoundSessionRepository(manager, cwd, abort.signal);
66
+ let successor: (() => void) | undefined;
67
+ if (race === "loss") renameSync(join(cwd, ".git"), join(cwd, "saved-git"));
68
+ if (race === "replacement") successor = bindSessionRepositoryPreparation(manager, cwd, async () => true, () => true);
69
+ if (race === "cancellation") abort.abort();
70
+ release();
71
+ assert.equal(await pending, false);
72
+ assert.equal(calls, 0, "no STATUS after authority/owner/cancellation drift during await");
73
+ unbind(); successor?.();
74
+ });
75
+ }
76
+
77
+ // Never invoke native code against the real HOME or filesystem root.
78
+ test("explicit review protects canonical HOME aliases, filesystem root and sensitive directories", async t => {
79
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "bootstrap-safety-")));
80
+ const home = join(root, "home");
81
+ const alias = join(root, "home-alias");
82
+ mkdirSync(home);
83
+ mkdirSync(join(home, ".ssh"));
84
+ symlinkSync(home, alias);
85
+ const previous = process.env.HOME;
86
+ process.env.HOME = home;
87
+ t.after(() => { if (previous === undefined) delete process.env.HOME; else process.env.HOME = previous; rmSync(root, { recursive: true, force: true }); });
88
+ let calls = 0;
89
+ const native = { reviewMode: async () => ({ operation: "status", status: { effective: "on", source: "global", global: "on", cloneLocal: "" } }), targetStatus: async () => { calls++; throw new Error("protected native invocation"); } } as unknown as NativeReviewCli;
90
+ for (const target of [home, alias, "/", join(home, ".ssh")]) {
91
+ try { await __testing.executeReviewControllerOperation({ operation: "inspect", workspaceRoot: target }, home, native, undefined, null); }
92
+ catch { /* The public boundary can reject before returning an envelope. */ }
93
+ }
94
+ assert.equal(calls, 0, "no native STATUS against protected directories");
95
+ });
96
+
97
+ // A Git repository created in HOME, an ancestor of it, or a broad user folder
98
+ // turns every later Git call in the user's tree into a scan of everything
99
+ // below it (a Windows user measured ~47s startups). Bootstrap refuses them.
100
+ test("native bootstrap refuses HOME ancestors and broad user folders but keeps project folders", t => {
101
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "bootstrap-broad-")));
102
+ t.after(() => rmSync(root, { recursive: true, force: true }));
103
+ const home = join(root, "home");
104
+ const broad = ["Documents", "Desktop", "Downloads", "OneDrive", "OneDrive - Contoso", join("OneDrive", "Documents")];
105
+ for (const folder of [...broad, join("Documents", "project"), "project"]) mkdirSync(join(home, folder), { recursive: true });
106
+ for (const target of [root, home, ...broad.map(folder => join(home, folder))]) assert.equal(safeBootstrapDirectory(target, { home }), undefined, target);
107
+ for (const target of [join(home, "project"), join(home, "Documents", "project")]) assert.equal(safeBootstrapDirectory(target, { home }), target, target);
108
+ });
109
+
110
+ test("native bootstrap compares HOME case-insensitively on Windows only", t => {
111
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "bootstrap-case-")));
112
+ t.after(() => rmSync(root, { recursive: true, force: true }));
113
+ const home = join(root, "home");
114
+ mkdirSync(join(home, "project"), { recursive: true });
115
+ const differentlyCasedHome = home.toUpperCase();
116
+ assert.equal(safeBootstrapDirectory(home, { home: differentlyCasedHome, platform: "win32" }), undefined);
117
+ assert.equal(safeBootstrapDirectory(home, { home: differentlyCasedHome, platform: "linux" }), home);
118
+ assert.equal(safeBootstrapDirectory(join(home, "project"), { home: differentlyCasedHome, platform: "win32" }), join(home, "project"));
119
+ });
120
+
121
+ // gentle-shell#1713: a writer continuation without its own section was
122
+ // rejected, and every retry shortened the follow-up. A continuation resumes
123
+ // the same delegated task, so it inherits the surfaces that launch admitted.
124
+ test("writer continuations inherit the original surfaces only when they carry none", () => {
125
+ const original = "Implement it.\n\n## Allowed edit surfaces\n- src/model.ts\n- `docs/with space.md`\n\n## Return\nReport";
126
+ const inherited = inheritAllowedEditSurfaces("gentle-ai-worker", "Continue with the remaining specs.", undefined, original);
127
+ assert.deepEqual(allowedEditSurfaces(inherited), ["docs/with space.md", "src/model.ts"]);
128
+ assert.ok(inherited.startsWith("Continue with the remaining specs."));
129
+ const own = "Continue.\n## Allowed edit surfaces\nsrc/other.ts";
130
+ assert.equal(inheritAllowedEditSurfaces("gentle-ai-worker", own, undefined, original), own, "a follow-up section is validated as written, never merged");
131
+ const viaContext = "## Allowed edit surfaces\nsrc/other.ts";
132
+ assert.equal(inheritAllowedEditSurfaces("worker", "Continue.", viaContext, original), "Continue.");
133
+ assert.equal(inheritAllowedEditSurfaces("gentle-ai-explore", "Continue.", undefined, original), "Continue.", "non-writers are untouched");
134
+ assert.equal(inheritAllowedEditSurfaces("jd-fix-agent", "Continue.", undefined, original), "Continue.", "Judgment Day fix batches keep their exact protocol");
135
+ assert.equal(inheritAllowedEditSurfaces("gentle-ai-worker", "Continue.", undefined, "No surfaces here."), "Continue.", "nothing to inherit stays rejectable");
136
+ // Review R3-002: an entry admitted only when quoted must round-trip quoted.
137
+ const quotedOnly = "## Allowed edit surfaces\n- `-`\n- src/model.ts";
138
+ assert.deepEqual(allowedEditSurfaces(quotedOnly), ["-", "src/model.ts"]);
139
+ assert.deepEqual(allowedEditSurfaces(inheritAllowedEditSurfaces("worker", "Continue.", undefined, quotedOnly)), ["-", "src/model.ts"]);
140
+ });
141
+
142
+ // gentle-shell#1731: only agents behind the surfaces guard claim surfaces at runtime.
143
+ test("bounded writers are exactly the agents behind the Allowed edit surfaces guard", () => {
144
+ for (const name of ["gentle-ai-worker", "worker", "jd-fix-agent"]) assert.equal(isBoundedWriter(name), true, name);
145
+ for (const name of ["explore", "gentle-ai-explore", "gentle-ai-verify", "reviewer", ""]) assert.equal(isBoundedWriter(name), false, name);
146
+ });
@@ -0,0 +1,43 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import test from "node:test";
5
+
6
+ const skill = readFileSync(join(import.meta.dirname, "..", "skills/branch-pr/SKILL.md"), "utf8");
7
+
8
+ test("target-host reads require explicit destination, operation and credential/session authorization", () => {
9
+ assert.match(skill, /explicit.*(?:destination|target).*operation.*credential\/session.*before.*target-host reads/i);
10
+ assert.match(skill, /reuse fresh target-bound.*issue.*default branch.*type.*labels.*checks/i);
11
+ assert.match(skill, /(?:do not|never).*ambient SSH/i);
12
+ });
13
+
14
+ test("issue linkage and protected labels retain human decisions without extra proof", () => {
15
+ assert.match(skill, /human-selected.*closing.*nonclosing.*Refs/i);
16
+ assert.match(skill, /protected labels.*exact direct instruction.*MAINTAIN\/ADMIN/i);
17
+ assert.match(skill, /human-selected.*size:exception.*rationale.*no separate instructor proof/i);
18
+ assert.doesNotMatch(skill, /Every PR body MUST contain:[\s\S]*?```markdown\s*Closes #<issue-number>/i);
19
+ });
20
+
21
+ test("oversized single PR does not impose Gentle size labels on generic users", () => {
22
+ assert.match(skill, /size:exception.*Gentle-owned.*not.*universal/i);
23
+ assert.match(skill, /destination repository's documented contribution\/size policy/i);
24
+ assert.match(skill, /only when.*policy.*size:exception/i);
25
+ assert.match(skill, /(?:never|do not).*request.*(?:create|add).*label.*generic/i);
26
+ assert.match(skill, /single-pr.*not.*(?:waive|bypass).*authorization/i);
27
+ });
28
+
29
+ test("required checks are target-policy based and actions are not automatic", () => {
30
+ assert.match(skill, /REQUIRED CI.*target policy/i);
31
+ assert.match(skill, /CodeRabbit.*optional.*unless required/i);
32
+ assert.match(skill, /(?:do not|never).*automatically commit.*push.*open.*PR.*merge/i);
33
+ assert.match(skill, /applicable test-first/i);
34
+ assert.match(skill, /advisory 400/i);
35
+ assert.doesNotMatch(skill, /\bSDD\b|OpenSpec|TUI Strict TDD/i);
36
+ });
37
+
38
+ test("template checklist reflects evidence rather than pre-checked assertions", () => {
39
+ assert.match(skill, /(?:do not|never).*unverified `?\[x\]`?/i);
40
+ assert.doesNotMatch(skill, /^- \[x\]/m);
41
+ assert.match(skill, /shellcheck on modified scripts/i);
42
+ assert.match(skill, /skills.*at least one agent/i);
43
+ });
@@ -0,0 +1,129 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import test from "node:test";
6
+ import { createAssistantMessageEventStream, getCurrentSystemPrompt, getCurrentTools, type AssistantMessage, type TranscriptContext } from "@earendil-works/pi-ai";
7
+ import { createAgentSession, DefaultResourceLoader, ModelRuntime, SettingsManager, SessionManager } from "@earendil-works/pi-coding-agent";
8
+ import gentleAgents, { type SessionTransportFactory } from "../extensions/gentle-agents.ts";
9
+ import { fakeChild, type FakeChild } from "./agents-fake-child.ts";
10
+
11
+ const sdk = import.meta.resolve("@earendil-works/pi-coding-agent");
12
+ const { UserMessageComponent } = await import(new URL("./modes/interactive/components/user-message.js", sdk).href);
13
+ const { initTheme } = await import(new URL("./modes/interactive/theme/theme.js", sdk).href);
14
+ initTheme("dark");
15
+ const transport: SessionTransportFactory = {
16
+ createRegistry: async () => ({ list: async () => [], listActivations: async () => [] }),
17
+ createListener: registry => ({ registry, start: async () => {}, close: async () => {} }),
18
+ createClient: () => ({ close() {}, sendNotification: async () => { throw new Error("Offline fixture forbids transport"); } }),
19
+ };
20
+ const tick = () => new Promise<void>(resolve => setImmediate(resolve));
21
+ async function until(predicate: () => boolean) {
22
+ const deadline = Date.now() + 2000;
23
+ while (!predicate() && Date.now() < deadline) await tick();
24
+ assert.ok(predicate(), "offline lifecycle did not settle");
25
+ }
26
+
27
+ test("actual SDK Bridge wake retains preparation, history, reload and resumed rendering offline", async () => {
28
+ const root = mkdtempSync(join(tmpdir(), "bridge-lifecycle-"));
29
+ const home = join(root, "home"), cwd = join(root, "project"), agentDir = join(root, "agent");
30
+ mkdirSync(home, { recursive: true });
31
+ mkdirSync(cwd); mkdirSync(join(agentDir, "agents"), { recursive: true });
32
+ writeFileSync(join(agentDir, "agents", "explore.md"), "---\ndescription: Offline fixture\n---\nReturn a fixture answer.");
33
+ const children: FakeChild[] = [], requests: TranscriptContext[] = [];
34
+ let preparations = 0;
35
+ const modelRuntime = await ModelRuntime.create({ authPath: join(agentDir, "offline-auth.json"), modelsPath: null, refreshOnCreate: false });
36
+ // The provider name tests routing, but this model has no live implementation.
37
+ modelRuntime.registerProvider("claude-bridge", {
38
+ api: "bridge-offline-test", apiKey: "explicit-offline-dummy", baseUrl: "http://offline.invalid",
39
+ models: [{ id: "offline-recording", name: "Offline recording", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 100000, maxTokens: 1000 }],
40
+ streamSimple: (model, context) => {
41
+ assert.equal(model.id, "offline-recording");
42
+ requests.push(structuredClone(context));
43
+ const stream = createAssistantMessageEventStream();
44
+ const message: AssistantMessage = { role: "assistant", api: model.api, provider: model.provider, model: model.id, timestamp: Date.now(), content: [{ type: "text", text: "Offline continued" }], stopReason: "stop", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } } };
45
+ queueMicrotask(() => { stream.push({ type: "start", partial: message }); stream.push({ type: "done", reason: "stop", message }); stream.end(); });
46
+ return stream;
47
+ },
48
+ });
49
+ const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false }, retry: { enabled: false } });
50
+ const loader = new DefaultResourceLoader({ cwd, agentDir, settingsManager, noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true,
51
+ extensionFactories: [pi => {
52
+ gentleAgents(pi, {}, { home, agentHome: agentDir, env: { PATH: "/usr/bin:/bin" }, pi: { command: "offline-child", args: [] }, spawn: () => { const child = fakeChild(); children.push(child); return child.child; }, schedule: () => () => {}, sessionTransport: transport, resolveWorktree: () => undefined });
53
+ pi.registerTool({ name: "sentinel", label: "Sentinel", description: "Offline sentinel tool", parameters: { type: "object", properties: {} } as never, execute: async () => ({ content: [], details: undefined }) });
54
+ pi.on("before_agent_start", event => {
55
+ preparations++;
56
+ pi.setActiveTools(["sentinel"]);
57
+ event.systemPromptOptions.appendSystemPrompt += `\nBRIDGE_PREPARATION_SENTINEL_${preparations}`;
58
+ event.systemPromptOptions.selectedTools = ["sentinel"];
59
+ });
60
+ }],
61
+ });
62
+ await loader.reload();
63
+ const manager = SessionManager.create(cwd, join(root, "sessions"));
64
+ const model = modelRuntime.getModel("claude-bridge", "offline-recording")!;
65
+ const errors: string[] = [];
66
+ const create = async (sessionManager: SessionManager) => {
67
+ const result = await createAgentSession({ cwd, agentDir, modelRuntime, model, tools: ["sentinel"], settingsManager, resourceLoader: loader, sessionManager });
68
+ await result.session.bindExtensions({ mode: "rpc", onError: error => errors.push(error.error) });
69
+ return result.session;
70
+ };
71
+ let session = await create(manager);
72
+ try {
73
+ await session.prompt("Ordinary human turn");
74
+ const launch = async (id: string) => {
75
+ const tool = session.extensionRunner.getAllRegisteredTools().find(tool => tool.definition.name === "subagent_run")!.definition;
76
+ const count = children.length;
77
+ const result = await tool.execute(id, { agent: "explore", task: "Offline work", mode: "background" }, undefined, undefined, session.extensionRunner.createToolContext(id, undefined));
78
+ assert.ok(JSON.stringify(result).includes("taskId"), JSON.stringify(result));
79
+ await until(() => children.length > count);
80
+ return children.at(-1)!;
81
+ };
82
+ const child = await launch("completion");
83
+ child.emit({ type: "agent_end", messages: [{ role: "assistant", content: [{ type: "text", text: "OFFLINE_CHILD_RESULT" }] }] });
84
+ child.emit({ type: "agent_settled" });
85
+ await until(() => requests.length === 2 && session.isIdle);
86
+ assert.equal(preparations, 2, "wake traverses normal prompt/before_agent_start");
87
+ assert.match(getCurrentSystemPrompt(requests[1].messages), /BRIDGE_PREPARATION_SENTINEL_2/);
88
+ assert.deepEqual(getCurrentTools(requests[1].messages).map(tool => tool.name), ["sentinel"]);
89
+ const wakeEntries = manager.getEntries().filter(entry => entry.type === "custom" && entry.customType === "gentle-agents.wake-identity");
90
+ assert.equal(wakeEntries.length, 1);
91
+ const identity = (wakeEntries[0] as { data: { text: string } }).data;
92
+ assert.ok(session.messages.some(message => message.role === "user" && JSON.stringify(message.content).includes(identity.text)), "wake is still user-role history");
93
+ assert.ok(JSON.stringify(requests[1].messages).includes(identity.text), "wake remains model-visible");
94
+ assert.equal(manager.getEntries().filter(entry => entry.type === "custom_message" && entry.customType === "gentle-agents.result").length, 1);
95
+ const render = (text: string) => new UserMessageComponent(text, undefined, 1, session.extensionRunner.getMarkdownTransformers()).render(80);
96
+ assert.deepEqual(render(identity.text), []);
97
+ assert.ok(render("Ordinary human turn").length > 0);
98
+ await session.reload();
99
+ assert.equal(session.extensionRunner.getMarkdownTransformers().length, 1);
100
+ assert.deepEqual(render(identity.text), [], "reload reconstructs the durable identity");
101
+ const file = manager.getSessionFile()!;
102
+ await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
103
+ session.dispose();
104
+ await loader.reload();
105
+ session = await create(SessionManager.open(file));
106
+ assert.equal(session.extensionRunner.getMarkdownTransformers().length, 1);
107
+ assert.deepEqual(render(identity.text), [], "opening saved history restores suppression");
108
+ const queryChild = await launch("query");
109
+ queryChild.message({ id: "q1", kind: "query", message: "OFFLINE_CHILD_QUERY" });
110
+ await until(() => requests.length === 3 && session.isIdle);
111
+ assert.equal(preparations, 3);
112
+ assert.match(getCurrentSystemPrompt(requests[2].messages), /BRIDGE_PREPARATION_SENTINEL_3/);
113
+ assert.deepEqual(getCurrentTools(requests[2].messages).map(tool => tool.name), ["sentinel"]);
114
+ assert.equal(session.sessionManager.getEntries().filter(entry => entry.type === "custom" && entry.customType === "gentle-agents.wake-identity").length, 1, "resume reuses one identity");
115
+ assert.equal(session.sessionManager.getEntries().filter(entry => entry.type === "custom_message" && entry.customType === "gentle-agents.message").length, 1, "query stored once");
116
+ await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
117
+ session.dispose();
118
+ await loader.reload();
119
+ session = await create(SessionManager.inMemory(cwd));
120
+ assert.equal(session.extensionRunner.getMarkdownTransformers().length, 1);
121
+ assert.ok(render(identity.text).length > 0, "switching to another session releases the old identity");
122
+ assert.ok(render("Ordinary human turn").length > 0);
123
+ assert.deepEqual(errors, []);
124
+ } finally {
125
+ await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
126
+ session.dispose();
127
+ rmSync(root, { recursive: true, force: true });
128
+ }
129
+ });
@@ -0,0 +1,56 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import test from "node:test";
6
+ import { createAgentSession, ModelRuntime, DefaultResourceLoader, SettingsManager, SessionManager } from "@earendil-works/pi-coding-agent";
7
+ import gentleAgents from "../extensions/gentle-agents.ts";
8
+
9
+ // Resolve from the installed SDK entry, never from a vendored runtime copy.
10
+ const sdk = import.meta.resolve("@earendil-works/pi-coding-agent");
11
+ const { UserMessageComponent } = await import(new URL("./modes/interactive/components/user-message.js", sdk).href);
12
+ const { initTheme } = await import(new URL("./modes/interactive/theme/theme.js", sdk).href);
13
+ initTheme("dark");
14
+ const oldWake = "[System-generated Gentle Agents notification, not written by the user] Subagent output was delivered to this session above. Review it and continue.";
15
+
16
+ test("real user renderer preserves ordinary content and removes only the owned reserved wake", async () => {
17
+ // Every path is owned by this test so CI never depends on ambient TMPDIR, HOME, or agent directories.
18
+ const root = mkdtempSync(join(tmpdir(), "bridge-rendering-"));
19
+ try {
20
+ const home = join(root, "home"), cwd = join(root, "project"), agentDir = join(root, "agent");
21
+ mkdirSync(home); mkdirSync(cwd); mkdirSync(agentDir);
22
+ const manager = SessionManager.inMemory(cwd);
23
+ const wake = `${oldWake} [gentle-agents wake: 67da57d9-7fbe-47b6-9187-c31109fe4eba]`;
24
+ manager.appendCustomEntry("gentle-agents.wake-identity", { sessionId: manager.getSessionId(), nonce: "invalid", text: "Human continuation" });
25
+ manager.appendCustomEntry("gentle-agents.wake-identity", { sessionId: "other-session", nonce: "67da57d9-7fbe-47b6-9187-c31109fe4eba", text: "Human continuation" });
26
+ manager.appendCustomEntry("gentle-agents.wake-identity", { sessionId: manager.getSessionId(), nonce: "67da57d9-7fbe-47b6-9187-c31109fe4eba", text: wake });
27
+ const settingsManager = SettingsManager.inMemory();
28
+ const loader = new DefaultResourceLoader({
29
+ cwd, agentDir, settingsManager,
30
+ noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true,
31
+ extensionFactories: [pi => gentleAgents(pi, {}, { home, agentHome: agentDir, schedule: () => () => {} })],
32
+ });
33
+ await loader.reload();
34
+ const loaded = loader.getExtensions();
35
+ assert.deepEqual(loaded.errors, []);
36
+ const modelRuntime = await ModelRuntime.create({ authPath: join(agentDir, "offline-auth.json"), modelsPath: null, refreshOnCreate: false });
37
+ const { session } = await createAgentSession({ cwd, agentDir, resourceLoader: loader, sessionManager: manager, settingsManager, modelRuntime, tools: [] });
38
+ await session.bindExtensions({});
39
+ try {
40
+ const transformers = session.extensionRunner.getMarkdownTransformers();
41
+ assert.equal(transformers.length, 1);
42
+ for (const width of [12, 80, 140]) {
43
+ assert.deepEqual(new UserMessageComponent(wake, undefined, 1, transformers).render(width), [], "no lines, padding, bubble, or OSC markers");
44
+ for (const ordinary of [oldWake, `Quoted: ${wake}`, ` ${wake}`, `${wake}\n`, "Human continuation"]) {
45
+ assert.ok(new UserMessageComponent(ordinary, undefined, 1, transformers).render(width).length > 0);
46
+ }
47
+ }
48
+ assert.equal(transformers[0](wake, { messageType: "assistant", isStreaming: false, availableWidth: 80 }), wake);
49
+ } finally {
50
+ await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
51
+ session.dispose();
52
+ }
53
+ } finally {
54
+ rmSync(root, { recursive: true, force: true });
55
+ }
56
+ });