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
@@ -293,6 +293,18 @@ export function evaluateProfilePin(
293
293
  return evaluation;
294
294
  }
295
295
 
296
+ // Admission-only lookup at an explicitly bound non-Git project. Do not create
297
+ // a fake commonDir or change public pin/status semantics outside repositories.
298
+ export function resolveUnversionedProjectProfile(cwd: string, configHome: string): (ProfilePinSelection & { modelProfiles: AgentModelConfig }) | undefined {
299
+ const path = repoProfileDeclarationPath(cwd);
300
+ const pin = readProfilePinResult(path);
301
+ if (pin.status !== "valid") return undefined;
302
+ const store = readProfilesFileResult(profilesFilePath(configHome));
303
+ if (store.status !== "valid" || !hasProfile(store.file.profiles, pin.profile)) return undefined;
304
+ const config = store.file.profiles[pin.profile];
305
+ return { source: "repo", profile: pin.profile, path, modelProfiles: Object.fromEntries(profileRoleEntries(config).map(([agent, entry]) => [agent, { ...entry }])) };
306
+ }
307
+
296
308
  export interface ProfilePinResolveOptions {
297
309
  cwd: string;
298
310
  configHome: string;
@@ -406,18 +406,28 @@ export interface ProfileListItem {
406
406
  description: string;
407
407
  }
408
408
 
409
+ /** One list item per saved profile, named with the markers that explain what a launch would use: the global `(active)` claim, plus `(pinned)` for the pin winner or `(session)` for this session's binding when either applies. */
409
410
  export function buildProfileListItems(
410
411
  file: AgentProfilesFile,
411
412
  pinned?: string,
413
+ sessionBound?: string,
412
414
  ): ProfileListItem[] {
413
415
  return Object.entries(file.profiles).map(([name, config]) => {
414
416
  const roles = profileRoleEntries(config).length;
415
417
  const active = name === file.active ? `${name} (active)` : name;
418
+ // A pinned profile is the one this repository launches with, which is not the
419
+ // same thing as the globally active profile, so both are named. A session
420
+ // binding outranks the pin in this list (slice 1 stores it; slice 2,
421
+ // gentle-shell#1558, makes launches resolve it), so its marker wins on the
422
+ // same profile.
423
+ const label = name === sessionBound
424
+ ? `${active} (session)`
425
+ : name === pinned
426
+ ? `${active} (pinned)`
427
+ : active;
416
428
  return {
417
429
  id: name,
418
- // A pinned profile is the one this repository launches with, which is not the
419
- // same thing as the globally active profile, so both are named.
420
- label: name === pinned ? `${active} (pinned)` : active,
430
+ label,
421
431
  description: `${roles} ${roles === 1 ? "role" : "roles"}`,
422
432
  };
423
433
  });
@@ -11,9 +11,10 @@
11
11
  // behavior is unit-testable against a fake clock.
12
12
 
13
13
  /**
14
- * A completion older than this at flush time is stale: the parent had many
15
- * turns to pull the result with subagent_result, so replaying it into the
16
- * model context would re-enter state the conversation may already have used.
14
+ * A completion older than this at flush time is stale: replaying its full
15
+ * report into the model context could re-enter state the conversation may
16
+ * already have used, so the parent gets a compact notice instead. The parent
17
+ * is told never to poll, so a stale completion must still announce itself.
17
18
  * Ninety seconds comfortably covers one slow orchestrator turn (model latency
18
19
  * plus a few tool calls) while staying orders of magnitude below the 50-58
19
20
  * minute followUp delays measured in issue #867.
@@ -36,6 +37,8 @@ export interface CompletionQueue<T extends { id: string }> {
36
37
  consume(taskId: string): void;
37
38
  /** Remove and return the still-pending completions in settle order, each with its staleness decision at `now`. Consumed entries are dropped and never returned. */
38
39
  takeDeliverable(now: number): Array<DeliverableCompletion<T>>;
40
+ /** Return a taken completion whose forwarding failed to the pending queue, so a later flush retries it. A consumed id stays dropped. */
41
+ requeue(entry: DeliverableCompletion<T>): void;
39
42
  /** Discard everything: pending completions and consumed/delivered bookkeeping. Used on session start and shutdown so nothing replays after a resume. */
40
43
  dropAll(): void;
41
44
  }
@@ -63,6 +66,11 @@ export function createCompletionQueue<T extends { id: string }>(): CompletionQue
63
66
  }
64
67
  return deliverable;
65
68
  },
69
+ requeue(entry) {
70
+ delivered.delete(entry.task.id);
71
+ if (consumed.has(entry.task.id) || pending.some((pendingEntry) => pendingEntry.task.id === entry.task.id)) return;
72
+ pending.push({ task: entry.task, settledAt: entry.settledAt });
73
+ },
66
74
  dropAll() {
67
75
  pending = [];
68
76
  consumed.clear();
@@ -0,0 +1,181 @@
1
+ // Gentle Agents subagent message delivery queue (issue #1162).
2
+ //
3
+ // Background and foreground child notifications and queries used to be handed
4
+ // straight to the host as `deliverAs: "followUp"` messages. Because the host
5
+ // only drains the follow-up queue when the parent agent stops calling tools
6
+ // entirely, stale notifications and already-answered or expired queries could
7
+ // re-enter the conversation long after the child task completed, re-triggering
8
+ // the parent LLM unnecessarily (#1162).
9
+ //
10
+ // This module owns the pending messages instead: the extension enqueues them,
11
+ // tracks query answer/timeout/cancellation lifecycle, invalidates messages on
12
+ // task completion, and flushes only live, fresh messages at turn boundaries or
13
+ // idle using `deliverAs: "steer"`.
14
+
15
+ export const STALE_NOTIFICATION_MS = 90_000;
16
+
17
+ export type AgentMessageKind = "notification" | "query";
18
+
19
+ export interface PendingAgentMessage {
20
+ taskId: string;
21
+ agent: string;
22
+ kind: AgentMessageKind;
23
+ content: string;
24
+ parentSessionId: string;
25
+ requestId?: string;
26
+ display: boolean;
27
+ details: Record<string, unknown>;
28
+ enqueuedAt: number;
29
+ }
30
+
31
+ export interface AgentMessageQueue {
32
+ /** Enqueue an ordinary notification from a subagent. */
33
+ enqueueNotification(
34
+ task: { id: string; agent: string; parentSessionId: string },
35
+ content: string,
36
+ enqueuedAt: number,
37
+ ): void;
38
+
39
+ /** Enqueue an interactive query from a subagent. */
40
+ enqueueQuery(
41
+ task: { id: string; agent: string; parentSessionId: string },
42
+ requestId: string,
43
+ question: string,
44
+ enqueuedAt: number,
45
+ ): void;
46
+
47
+ /** Mark a query as consumed (e.g. answered or directly handed off). Prevents subsequent delivery. */
48
+ consumeQuery(taskId: string, requestId: string): void;
49
+
50
+ /** Mark a query as expired (timed out or errored). Prevents delivery. */
51
+ expireQuery(taskId: string, requestId: string): void;
52
+
53
+ /** Invalidate all pending messages (notifications and queries) for a settled or cancelled task. */
54
+ invalidateTask(taskId: string): void;
55
+
56
+ /** Return deliverable messages in enqueue order for the active session, dropping stale/consumed ones. */
57
+ takeDeliverable(
58
+ now: number,
59
+ activeSessionId: string,
60
+ isTaskLive: (taskId: string) => boolean,
61
+ ): Array<PendingAgentMessage>;
62
+
63
+ /** Return number of pending messages waiting to be delivered. */
64
+ pendingCount(): number;
65
+
66
+ /** Discard all pending messages and tracking sets (on session change or shutdown). */
67
+ dropAll(): void;
68
+ }
69
+
70
+ /**
71
+ * Creates an agent message queue that manages pending notifications and queries
72
+ * from subagents, bounding their lifetime and invalidating stale messages upon
73
+ * task settlement or query resolution.
74
+ */
75
+ export function createAgentMessageQueue(): AgentMessageQueue {
76
+ let pending: Array<PendingAgentMessage> = [];
77
+ const consumedQueries = new Set<string>();
78
+ const expiredQueries = new Set<string>();
79
+ const invalidatedTasks = new Set<string>();
80
+
81
+ const queryKey = (taskId: string, requestId: string) => `${taskId}:${requestId}`;
82
+
83
+ return {
84
+ enqueueNotification(task, content, enqueuedAt) {
85
+ if (invalidatedTasks.has(task.id)) return;
86
+ pending.push({
87
+ taskId: task.id,
88
+ agent: task.agent,
89
+ kind: "notification",
90
+ content,
91
+ parentSessionId: task.parentSessionId,
92
+ display: false,
93
+ details: {
94
+ gentleAgents: {
95
+ taskId: task.id,
96
+ agent: task.agent,
97
+ parentSessionId: task.parentSessionId,
98
+ kind: "notification",
99
+ },
100
+ },
101
+ enqueuedAt,
102
+ });
103
+ },
104
+
105
+ enqueueQuery(task, requestId, question, enqueuedAt) {
106
+ const key = queryKey(task.id, requestId);
107
+ if (invalidatedTasks.has(task.id) || consumedQueries.has(key) || expiredQueries.has(key)) return;
108
+ if (pending.some((msg) => msg.kind === "query" && msg.taskId === task.id && msg.requestId === requestId)) return;
109
+ pending.push({
110
+ taskId: task.id,
111
+ agent: task.agent,
112
+ kind: "query",
113
+ content: `Subagent ${task.agent} asks:\nTask ID: ${task.id}\nRequest ID: ${requestId}\nQuestion: ${question}`,
114
+ parentSessionId: task.parentSessionId,
115
+ requestId,
116
+ display: true,
117
+ details: {
118
+ gentleAgents: {
119
+ taskId: task.id,
120
+ agent: task.agent,
121
+ parentSessionId: task.parentSessionId,
122
+ requestId,
123
+ kind: "query",
124
+ },
125
+ },
126
+ enqueuedAt,
127
+ });
128
+ },
129
+
130
+ consumeQuery(taskId, requestId) {
131
+ const key = queryKey(taskId, requestId);
132
+ consumedQueries.add(key);
133
+ pending = pending.filter((msg) => !(msg.kind === "query" && msg.taskId === taskId && msg.requestId === requestId));
134
+ },
135
+
136
+ expireQuery(taskId, requestId) {
137
+ const key = queryKey(taskId, requestId);
138
+ expiredQueries.add(key);
139
+ pending = pending.filter((msg) => !(msg.kind === "query" && msg.taskId === taskId && msg.requestId === requestId));
140
+ },
141
+
142
+ invalidateTask(taskId) {
143
+ invalidatedTasks.add(taskId);
144
+ pending = pending.filter((msg) => msg.taskId !== taskId);
145
+ },
146
+
147
+ takeDeliverable(now, activeSessionId, isTaskLive) {
148
+ const taken = pending;
149
+ pending = [];
150
+ const deliverable: Array<PendingAgentMessage> = [];
151
+
152
+ for (const msg of taken) {
153
+ if (msg.parentSessionId !== activeSessionId) continue;
154
+ if (invalidatedTasks.has(msg.taskId)) continue;
155
+ if (!isTaskLive(msg.taskId)) continue;
156
+
157
+ if (msg.kind === "query") {
158
+ const key = queryKey(msg.taskId, msg.requestId!);
159
+ if (consumedQueries.has(key) || expiredQueries.has(key)) continue;
160
+ } else if (msg.kind === "notification") {
161
+ if (now - msg.enqueuedAt >= STALE_NOTIFICATION_MS) continue;
162
+ }
163
+
164
+ deliverable.push(msg);
165
+ }
166
+
167
+ return deliverable;
168
+ },
169
+
170
+ pendingCount() {
171
+ return pending.length;
172
+ },
173
+
174
+ dropAll() {
175
+ pending = [];
176
+ consumedQueries.clear();
177
+ expiredQueries.clear();
178
+ invalidatedTasks.clear();
179
+ },
180
+ };
181
+ }
@@ -95,7 +95,7 @@ function parseAck(value: unknown): AckFrame | undefined {
95
95
  if (!record(value) || !exact(value, ["id", "kind", "accepted", "error"]) || !validChildId(value.id) || value.kind !== "ack" || typeof value.accepted !== "boolean") return undefined;
96
96
  if (value.accepted && value.error !== undefined) return undefined;
97
97
  if (value.error !== undefined && !boundedText(value.error, CHILD_ACK_ERROR_MAX_BYTES)) return undefined;
98
- return { id: value.id, accepted: value.accepted, ...(value.error === undefined ? {} : { error: value.error }) };
98
+ return { id: value.id, accepted: value.accepted, ...(typeof value.error === "string" ? { error: value.error } : {}) };
99
99
  }
100
100
 
101
101
  function parseReply(value: unknown): ReplyFrame | undefined {
@@ -1,3 +1,4 @@
1
+ import { createHash, randomUUID } from "node:crypto";
1
2
  import { sanitizeTerminalText } from "./terminal-theme.ts";
2
3
 
3
4
  // Gentle Agents protocol. A child pi process streams RPC events; the host
@@ -147,10 +148,23 @@ export interface TaskSummary {
147
148
 
148
149
  export type TaskListener = (task: TaskRecord, thread: TaskThread) => void;
149
150
  export type SummaryListener = (summary: TaskSummary) => void;
151
+ export interface TaskStatusChange {
152
+ readonly producerId: string;
153
+ readonly sessionId: string;
154
+ readonly parentSessionId: string;
155
+ readonly taskId: string;
156
+ readonly runId: string;
157
+ readonly status: TaskStatus;
158
+ readonly previousStatus?: TaskStatus;
159
+ readonly sequence: number;
160
+ }
161
+ export type StatusChangeListener = (change: TaskStatusChange) => void | Promise<void>;
150
162
 
151
163
  const DEFAULT_LIMITS: ThreadLimits = { maxItems: 400, maxOutputChars: 16_000 };
152
164
  /** Child UI requests that block on an answer; everything else (notify, setStatus, setWidget) is noise here. */
153
165
  export const DIALOG_METHODS: ReadonlySet<string> = new Set(["select", "confirm", "input", "editor"]);
166
+ /** The one child notify that reaches the task thread: requested --tools the child does not have (#1690). */
167
+ export const MISSING_TOOLS_NOTE_PREFIX = "gentle-agents: requested tools missing in child:";
154
168
  const LABEL_MAX = 72;
155
169
  const TEXT_CAP = 20_000;
156
170
  const ELLIPSIS = "…";
@@ -226,6 +240,65 @@ function childResponse(message: Raw): ChildResponseObservation | undefined {
226
240
  });
227
241
  }
228
242
 
243
+ const ARGUMENT_PROGRESS_MAX = 4096;
244
+
245
+ /** Per-child liveness evidence, separate from display events and usage.
246
+ * RPC deltas have no sequence number. Reject duplicate fingerprints rather
247
+ * than claiming indistinguishable repeated bytes are fresh progress. Retain
248
+ * only hashes, never arguments; exhaustion fails closed until a new message.
249
+ */
250
+ export class ToolArgumentProgress {
251
+ private timestamp = -1;
252
+ private active = false;
253
+ private readonly blocks = new Map<number, boolean>();
254
+ private readonly fingerprints = new Set<string>();
255
+
256
+ observe(raw: Record<string, unknown>): boolean {
257
+ if (raw.type === "message_start") {
258
+ const message = raw.message as Raw | undefined;
259
+ if (message?.role === "assistant" && typeof message.timestamp === "number" && Number.isSafeInteger(message.timestamp)
260
+ && message.timestamp >= 0 && message.timestamp > this.timestamp && !this.active) {
261
+ this.timestamp = message.timestamp;
262
+ this.active = true;
263
+ this.blocks.clear();
264
+ this.fingerprints.clear();
265
+ }
266
+ return false;
267
+ }
268
+ if (raw.type === "message_end" || raw.type === "agent_end" || raw.type === "agent_settled" || raw.type === "turn_end") {
269
+ this.active = false;
270
+ this.blocks.clear();
271
+ this.fingerprints.clear();
272
+ return false;
273
+ }
274
+ if (!this.active || raw.type !== "message_update") return false;
275
+ const inner = raw.assistantMessageEvent as Raw | undefined;
276
+ if (!inner || typeof inner !== "object") return false;
277
+ const index = inner.contentIndex;
278
+ if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0 || index >= ARGUMENT_PROGRESS_MAX) return false;
279
+ if (inner.type === "toolcall_start") {
280
+ // Older Pi RPC forwards SDK partial snapshots; current RPC puts the
281
+ // identity directly on toolcall_start. Neither shape is persisted.
282
+ const partial = inner.partial as Raw | undefined;
283
+ const block = Array.isArray(partial?.content) ? partial.content[index] as Raw | undefined : undefined;
284
+ const id = inner.id ?? (block?.type === "toolCall" ? block.id : undefined);
285
+ const name = inner.toolName ?? (block?.type === "toolCall" ? block.name : undefined);
286
+ if (typeof id === "string" && id.length > 0 && typeof name === "string" && name.length > 0 && !this.blocks.has(index)) this.blocks.set(index, true);
287
+ return false;
288
+ }
289
+ if (inner.type === "toolcall_end") {
290
+ if (this.blocks.has(index)) this.blocks.set(index, false);
291
+ return false;
292
+ }
293
+ if (inner.type !== "toolcall_delta" || this.blocks.get(index) !== true || typeof inner.delta !== "string" || inner.delta.length === 0) return false;
294
+ if (this.fingerprints.size >= ARGUMENT_PROGRESS_MAX) return false;
295
+ const fingerprint = createHash("sha256").update(`${index}:`).update(inner.delta).digest("hex");
296
+ if (this.fingerprints.has(fingerprint)) return false;
297
+ this.fingerprints.add(fingerprint);
298
+ return true;
299
+ }
300
+ }
301
+
229
302
  // One RPC line in, zero or more deltas out. Streaming deltas carry only the
230
303
  // chunk; whole-message payloads that pi repeats on every update are ignored.
231
304
  export function normalizeRpcEvent(raw: unknown, options: { observeResponses?: boolean } = {}): TaskEvent[] {
@@ -270,6 +343,7 @@ export function normalizeRpcEvent(raw: unknown, options: { observeResponses?: bo
270
343
  case "agent_settled":
271
344
  return [{ type: TASK_EVENT.AGENT_SETTLED }];
272
345
  case "extension_ui_request":
346
+ if (event.method === "notify" && typeof event.message === "string" && event.message.startsWith(MISSING_TOOLS_NOTE_PREFIX)) return [{ type: TASK_EVENT.NOTE, text: clean(event.message) }];
273
347
  return DIALOG_METHODS.has(String(event.method)) ? [{ type: TASK_EVENT.ASK, request: askRequest(event) }] : [];
274
348
  case "auto_retry_start":
275
349
  return [{ type: TASK_EVENT.NOTE, text: `retrying (${String(event.attempt ?? "?")}/${String(event.maxAttempts ?? "?")})` }];
@@ -400,14 +474,21 @@ export class TaskStore {
400
474
  private readonly listeners = new Map<string, Set<TaskListener>>();
401
475
  private readonly summaryListeners = new Set<SummaryListener>();
402
476
  private readonly limits: Partial<ThreadLimits>;
477
+ private readonly producerId = randomUUID();
478
+ private sequence = 0;
479
+ private readonly runIds = new Map<string, string>();
480
+ private readonly statusListeners = new Set<StatusChangeListener>();
403
481
 
404
482
  constructor(limits: Partial<ThreadLimits> = {}) {
405
483
  this.limits = limits;
406
484
  }
407
485
 
408
486
  add(task: TaskRecord): void {
487
+ const previous = this.tasks.get(task.id)?.status;
488
+ if (!this.tasks.has(task.id)) this.runIds.set(task.id, randomUUID());
409
489
  this.tasks.set(task.id, task);
410
490
  this.threads.set(task.id, emptyThread(this.limits));
491
+ this.notifyStatus(task, previous);
411
492
  this.notifySummary();
412
493
  }
413
494
 
@@ -416,6 +497,7 @@ export class TaskStore {
416
497
  restore(task: TaskRecord, thread: TaskThread): boolean {
417
498
  if (this.tasks.has(task.id)) return false;
418
499
  this.tasks.set(task.id, task);
500
+ this.runIds.set(task.id, randomUUID());
419
501
  this.threads.set(task.id, { ...thread, limits: { ...DEFAULT_LIMITS, ...this.limits } });
420
502
  this.notifySummary();
421
503
  return true;
@@ -446,6 +528,7 @@ export class TaskStore {
446
528
  if (!current) return undefined;
447
529
  const next = { ...current, ...patch };
448
530
  this.tasks.set(id, next);
531
+ this.notifyStatus(next, current.status);
449
532
  this.notifyTask(next);
450
533
  if (next.status !== current.status) this.notifySummary();
451
534
  return next;
@@ -473,6 +556,22 @@ export class TaskStore {
473
556
  return () => this.summaryListeners.delete(listener);
474
557
  }
475
558
 
559
+ /** Optional, content-free observer. Restores and unchanged snapshots are silent. */
560
+ subscribeStatusChanges(listener: StatusChangeListener): () => void {
561
+ this.statusListeners.add(listener);
562
+ return () => { this.statusListeners.delete(listener); };
563
+ }
564
+
565
+ private notifyStatus(task: TaskRecord, previousStatus?: TaskStatus): void {
566
+ if (task.status === previousStatus) return;
567
+ const change: TaskStatusChange = Object.freeze({ producerId: this.producerId,
568
+ sessionId: task.parentSessionId, parentSessionId: task.parentSessionId, taskId: task.id,
569
+ runId: this.runIds.get(task.id)!, status: task.status, previousStatus, sequence: ++this.sequence });
570
+ for (const listener of [...this.statusListeners]) {
571
+ try { void Promise.resolve(listener(change)).catch(() => {}); } catch { /* Observability cannot fail a mutation. */ }
572
+ }
573
+ }
574
+
476
575
  private notifyTask(task: TaskRecord): void {
477
576
  const set = this.listeners.get(task.id);
478
577
  if (!set) return;