gsd-pi 2.51.0 → 2.52.0-dev.585e355

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 (399) hide show
  1. package/README.md +4 -4
  2. package/dist/headless-events.d.ts +18 -0
  3. package/dist/headless-events.js +36 -0
  4. package/dist/headless-types.d.ts +28 -0
  5. package/dist/headless-types.js +7 -0
  6. package/dist/headless.d.ts +8 -3
  7. package/dist/headless.js +47 -16
  8. package/dist/help-text.js +16 -5
  9. package/dist/onboarding.js +5 -4
  10. package/dist/remote-questions-config.js +1 -1
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
  12. package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
  14. package/dist/resources/extensions/gsd/auto/phases.js +6 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +18 -0
  16. package/dist/resources/extensions/gsd/auto-start.js +2 -0
  17. package/dist/resources/extensions/gsd/auto-timers.js +24 -2
  18. package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
  19. package/dist/resources/extensions/gsd/auto-worktree.js +21 -0
  20. package/dist/resources/extensions/gsd/auto.js +8 -4
  21. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +105 -70
  22. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
  24. package/dist/resources/extensions/gsd/claude-import.js +60 -9
  25. package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
  26. package/dist/resources/extensions/gsd/commands-config.js +10 -5
  27. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
  28. package/dist/resources/extensions/gsd/detection.js +6 -6
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  30. package/dist/resources/extensions/gsd/error-classifier.js +105 -0
  31. package/dist/resources/extensions/gsd/git-service.js +4 -3
  32. package/dist/resources/extensions/gsd/gitignore.js +7 -7
  33. package/dist/resources/extensions/gsd/gsd-db.js +298 -45
  34. package/dist/resources/extensions/gsd/init-wizard.js +2 -2
  35. package/dist/resources/extensions/gsd/key-manager.js +7 -16
  36. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
  37. package/dist/resources/extensions/gsd/memory-store.js +28 -13
  38. package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
  39. package/dist/resources/extensions/gsd/preferences-models.js +1 -13
  40. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  41. package/dist/resources/extensions/gsd/preferences.js +13 -13
  42. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  43. package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
  44. package/dist/resources/extensions/gsd/rule-registry.js +1 -1
  45. package/dist/resources/extensions/gsd/service-tier.js +13 -2
  46. package/dist/resources/extensions/gsd/state.js +33 -19
  47. package/dist/resources/extensions/gsd/status-guards.js +12 -0
  48. package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
  49. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
  50. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
  51. package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
  52. package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
  53. package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
  54. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
  55. package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
  56. package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
  57. package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
  58. package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
  59. package/dist/resources/extensions/gsd/validation.js +21 -0
  60. package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
  61. package/dist/resources/extensions/remote-questions/config.js +1 -1
  62. package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
  63. package/dist/resources/extensions/search-the-web/native-search.js +1 -1
  64. package/dist/resources/extensions/search-the-web/provider.js +1 -1
  65. package/dist/resources/extensions/shared/rtk.js +5 -3
  66. package/dist/rtk.js +3 -1
  67. package/dist/web/standalone/.next/BUILD_ID +1 -1
  68. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  69. package/dist/web/standalone/.next/build-manifest.json +4 -4
  70. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  71. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  72. package/dist/web/standalone/.next/required-server-files.json +3 -3
  73. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  74. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  76. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  84. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  93. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  100. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  112. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  140. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  146. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  160. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  162. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  164. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  166. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/index.html +1 -1
  176. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  177. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  178. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  179. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  180. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  181. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  182. package/dist/web/standalone/.next/server/app/page.js +2 -2
  183. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  185. package/dist/web/standalone/.next/server/chunks/2229.js +3 -3
  186. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  187. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/middleware.js +2 -2
  190. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  192. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  193. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  194. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  195. package/dist/web/standalone/.next/static/chunks/4024.21054f459af5cc78.js +9 -0
  196. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  197. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  198. package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
  199. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  200. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  201. package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-024d82be84800e52.js} +1 -1
  202. package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
  203. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  204. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  205. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  206. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  207. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  208. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  209. package/dist/web/standalone/server.js +1 -1
  210. package/dist/wizard.js +4 -1
  211. package/package.json +2 -2
  212. package/packages/mcp-server/README.md +202 -0
  213. package/packages/mcp-server/package.json +36 -0
  214. package/packages/mcp-server/src/cli.ts +68 -0
  215. package/packages/mcp-server/src/index.ts +14 -0
  216. package/packages/mcp-server/src/mcp-server.test.ts +628 -0
  217. package/packages/mcp-server/src/server.ts +278 -0
  218. package/packages/mcp-server/src/session-manager.ts +328 -0
  219. package/packages/mcp-server/src/types.ts +107 -0
  220. package/packages/mcp-server/tsconfig.json +24 -0
  221. package/packages/pi-ai/dist/models.d.ts +14 -3
  222. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  223. package/packages/pi-ai/dist/models.js +53 -10
  224. package/packages/pi-ai/dist/models.js.map +1 -1
  225. package/packages/pi-ai/dist/models.test.js +102 -1
  226. package/packages/pi-ai/dist/models.test.js.map +1 -1
  227. package/packages/pi-ai/dist/types.d.ts +30 -0
  228. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  229. package/packages/pi-ai/dist/types.js.map +1 -1
  230. package/packages/pi-ai/src/models.test.ts +114 -1
  231. package/packages/pi-ai/src/models.ts +70 -13
  232. package/packages/pi-ai/src/types.ts +31 -0
  233. package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
  234. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/cli/args.js +3 -0
  236. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
  238. package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
  239. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  240. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
  242. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  243. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
  244. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
  245. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
  246. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
  247. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
  249. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  251. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  252. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  253. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/main.js +5 -3
  255. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
  257. package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
  259. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
  261. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
  263. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
  265. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
  267. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
  269. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
  271. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
  272. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
  273. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
  274. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
  275. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  276. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  277. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  278. package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
  279. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  280. package/packages/pi-coding-agent/package.json +1 -1
  281. package/packages/pi-coding-agent/src/cli/args.ts +4 -0
  282. package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
  283. package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
  284. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
  285. package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
  286. package/packages/pi-coding-agent/src/index.ts +3 -0
  287. package/packages/pi-coding-agent/src/main.ts +5 -3
  288. package/packages/pi-coding-agent/src/modes/index.ts +8 -1
  289. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
  290. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
  291. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
  292. package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
  293. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
  294. package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
  295. package/packages/rpc-client/package.json +20 -0
  296. package/pkg/package.json +1 -1
  297. package/scripts/ensure-workspace-builds.cjs +36 -8
  298. package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
  299. package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
  300. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
  301. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
  302. package/src/resources/extensions/gsd/auto/phases.ts +6 -0
  303. package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
  304. package/src/resources/extensions/gsd/auto-start.ts +2 -0
  305. package/src/resources/extensions/gsd/auto-timers.ts +25 -1
  306. package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
  307. package/src/resources/extensions/gsd/auto-worktree.ts +21 -0
  308. package/src/resources/extensions/gsd/auto.ts +10 -4
  309. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -73
  310. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
  311. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
  312. package/src/resources/extensions/gsd/claude-import.ts +58 -9
  313. package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
  314. package/src/resources/extensions/gsd/commands-config.ts +11 -5
  315. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
  316. package/src/resources/extensions/gsd/detection.ts +6 -6
  317. package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  318. package/src/resources/extensions/gsd/error-classifier.ts +139 -0
  319. package/src/resources/extensions/gsd/git-service.ts +4 -3
  320. package/src/resources/extensions/gsd/gitignore.ts +7 -7
  321. package/src/resources/extensions/gsd/gsd-db.ts +355 -63
  322. package/src/resources/extensions/gsd/init-wizard.ts +2 -2
  323. package/src/resources/extensions/gsd/key-manager.ts +7 -16
  324. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
  325. package/src/resources/extensions/gsd/memory-store.ts +29 -18
  326. package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
  327. package/src/resources/extensions/gsd/preferences-models.ts +1 -13
  328. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  329. package/src/resources/extensions/gsd/preferences.ts +12 -13
  330. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  331. package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
  332. package/src/resources/extensions/gsd/rule-registry.ts +1 -1
  333. package/src/resources/extensions/gsd/service-tier.ts +14 -2
  334. package/src/resources/extensions/gsd/state.ts +34 -20
  335. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  336. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
  337. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
  338. package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
  339. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
  340. package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
  341. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  342. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
  343. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  344. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
  345. package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
  346. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
  347. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
  348. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
  349. package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
  350. package/src/resources/extensions/gsd/tests/git-service.test.ts +37 -4
  351. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  352. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
  353. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
  354. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
  355. package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
  356. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  357. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  358. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
  359. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
  360. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
  361. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
  362. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
  363. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
  364. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
  365. package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
  366. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
  367. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
  368. package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
  369. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
  370. package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
  371. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
  372. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
  373. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
  374. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
  375. package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
  376. package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
  377. package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
  378. package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
  379. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
  380. package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
  381. package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
  382. package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
  383. package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
  384. package/src/resources/extensions/gsd/validation.ts +23 -0
  385. package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
  386. package/src/resources/extensions/remote-questions/config.ts +1 -1
  387. package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
  388. package/src/resources/extensions/search-the-web/native-search.ts +1 -1
  389. package/src/resources/extensions/search-the-web/provider.ts +1 -1
  390. package/src/resources/extensions/shared/rtk.ts +12 -3
  391. package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
  392. package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
  393. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  394. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  395. package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
  396. /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
  397. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_buildManifest.js +0 -0
  398. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_ssgManifest.js +0 -0
  399. /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
@@ -11,6 +11,13 @@ import type { SessionStats } from "../../core/agent-session.js";
11
11
  import type { BashResult } from "../../core/bash-executor.js";
12
12
  import type { CompactionResult } from "../../core/compaction/index.js";
13
13
 
14
+ // ============================================================================
15
+ // RPC Protocol Versioning
16
+ // ============================================================================
17
+
18
+ /** Supported protocol versions. v1 is the implicit default; v2 requires an init handshake. */
19
+ export type RpcProtocolVersion = 1 | 2;
20
+
14
21
  // ============================================================================
15
22
  // RPC Commands (stdin)
16
23
  // ============================================================================
@@ -69,7 +76,12 @@ export type RpcCommand =
69
76
  // Bridge-hosted native terminal
70
77
  | { id?: string; type: "terminal_input"; data: string }
71
78
  | { id?: string; type: "terminal_resize"; cols: number; rows: number }
72
- | { id?: string; type: "terminal_redraw" };
79
+ | { id?: string; type: "terminal_redraw" }
80
+
81
+ // v2 Protocol
82
+ | { id?: string; type: "init"; protocolVersion: 2; clientId?: string }
83
+ | { id?: string; type: "shutdown"; graceful?: boolean }
84
+ | { id?: string; type: "subscribe"; events: string[] };
73
85
 
74
86
  // ============================================================================
75
87
  // RPC Slash Command (for get_commands response)
@@ -120,9 +132,9 @@ export interface RpcSessionState {
120
132
  // Success responses with data
121
133
  export type RpcResponse =
122
134
  // Prompting (async - events follow)
123
- | { id?: string; type: "response"; command: "prompt"; success: true }
124
- | { id?: string; type: "response"; command: "steer"; success: true }
125
- | { id?: string; type: "response"; command: "follow_up"; success: true }
135
+ | { id?: string; type: "response"; command: "prompt"; success: true; runId?: string }
136
+ | { id?: string; type: "response"; command: "steer"; success: true; runId?: string }
137
+ | { id?: string; type: "response"; command: "follow_up"; success: true; runId?: string }
126
138
  | { id?: string; type: "response"; command: "abort"; success: true }
127
139
  | { id?: string; type: "response"; command: "new_session"; success: true; data: { cancelled: boolean } }
128
140
 
@@ -216,9 +228,54 @@ export type RpcResponse =
216
228
  | { id?: string; type: "response"; command: "terminal_resize"; success: true }
217
229
  | { id?: string; type: "response"; command: "terminal_redraw"; success: true }
218
230
 
231
+ // v2 Protocol
232
+ | { id?: string; type: "response"; command: "init"; success: true; data: RpcInitResult }
233
+ | { id?: string; type: "response"; command: "shutdown"; success: true }
234
+ | { id?: string; type: "response"; command: "subscribe"; success: true }
235
+
219
236
  // Error response (any command can fail)
220
237
  | { id?: string; type: "response"; command: string; success: false; error: string };
221
238
 
239
+ // ============================================================================
240
+ // v2 Protocol Types
241
+ // ============================================================================
242
+
243
+ /** Result of the init handshake (v2 only) */
244
+ export interface RpcInitResult {
245
+ protocolVersion: 2;
246
+ sessionId: string;
247
+ capabilities: {
248
+ events: string[];
249
+ commands: string[];
250
+ };
251
+ }
252
+
253
+ /** v2 execution_complete event — emitted when a prompt/steer/follow_up finishes */
254
+ export interface RpcExecutionCompleteEvent {
255
+ type: "execution_complete";
256
+ runId: string;
257
+ status: "completed" | "error" | "cancelled";
258
+ reason?: string;
259
+ stats: SessionStats;
260
+ }
261
+
262
+ /** v2 cost_update event — emitted per-turn with running cost data */
263
+ export interface RpcCostUpdateEvent {
264
+ type: "cost_update";
265
+ runId: string;
266
+ turnCost: number;
267
+ cumulativeCost: number;
268
+ tokens: {
269
+ input: number;
270
+ output: number;
271
+ cacheRead: number;
272
+ cacheWrite: number;
273
+ };
274
+ }
275
+
276
+ /** Discriminated union of all v2-only event types */
277
+ export type RpcV2Event = RpcExecutionCompleteEvent | RpcCostUpdateEvent;
278
+
222
279
  // ============================================================================
223
280
  // Extension UI Events (stdout)
224
281
  // ============================================================================
@@ -192,7 +192,6 @@ export function killProcessTree(pid: number): void {
192
192
  try {
193
193
  spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
194
194
  stdio: "ignore",
195
- detached: true,
196
195
  });
197
196
  } catch {
198
197
  // Ignore errors if taskkill fails
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@gsd/rpc-client",
3
+ "version": "2.51.0",
4
+ "description": "Standalone RPC client SDK for GSD — zero internal dependencies",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "engines": {
18
+ "node": ">=22.0.0"
19
+ }
20
+ }
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.51.0",
3
+ "version": "2.52.0",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -3,15 +3,18 @@
3
3
  * ensure-workspace-builds.cjs
4
4
  *
5
5
  * Checks whether workspace packages have been compiled (dist/ exists with
6
- * index.js). If any are missing, runs the build for those packages.
6
+ * index.js) and that the build is not stale (no src/ file newer than dist/).
7
+ * If any are missing or stale, runs the build for those packages.
7
8
  *
8
9
  * Designed for the postinstall hook so that `npm install` in a fresh clone
9
- * produces a working runtime without a manual `npm run build` step.
10
+ * produces a working runtime without a manual `npm run build` step. Also
11
+ * catches the common case where `git pull` updates package sources but the
12
+ * old dist/ remains, causing TypeScript type errors.
10
13
  *
11
14
  * Skipped in CI (where the full build pipeline handles this) and when
12
15
  * installing as an end-user dependency (no packages/ directory).
13
16
  */
14
- const { existsSync } = require('fs')
17
+ const { existsSync, statSync, readdirSync } = require('fs')
15
18
  const { resolve, join } = require('path')
16
19
  const { execSync } = require('child_process')
17
20
 
@@ -34,19 +37,44 @@ const WORKSPACE_PACKAGES = [
34
37
  'pi-coding-agent',
35
38
  ]
36
39
 
37
- const missing = []
40
+ /**
41
+ * Returns the most recent mtime (ms) of any .ts file under dir, recursively.
42
+ * Returns 0 if no .ts files found.
43
+ */
44
+ function newestSrcMtime(dir) {
45
+ if (!existsSync(dir)) return 0
46
+ let newest = 0
47
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
48
+ if (entry.name === 'node_modules') continue
49
+ const full = join(dir, entry.name)
50
+ if (entry.isDirectory()) {
51
+ newest = Math.max(newest, newestSrcMtime(full))
52
+ } else if (entry.isFile() && entry.name.endsWith('.ts')) {
53
+ newest = Math.max(newest, statSync(full).mtimeMs)
54
+ }
55
+ }
56
+ return newest
57
+ }
58
+
59
+ const stale = []
38
60
  for (const pkg of WORKSPACE_PACKAGES) {
39
61
  const distIndex = join(packagesDir, pkg, 'dist', 'index.js')
40
62
  if (!existsSync(distIndex)) {
41
- missing.push(pkg)
63
+ stale.push(pkg)
64
+ continue
65
+ }
66
+ const distMtime = statSync(distIndex).mtimeMs
67
+ const srcMtime = newestSrcMtime(join(packagesDir, pkg, 'src'))
68
+ if (srcMtime > distMtime) {
69
+ stale.push(pkg)
42
70
  }
43
71
  }
44
72
 
45
- if (missing.length === 0) process.exit(0)
73
+ if (stale.length === 0) process.exit(0)
46
74
 
47
- process.stderr.write(` Building ${missing.length} workspace package(s) missing dist/: ${missing.join(', ')}\n`)
75
+ process.stderr.write(` Building ${stale.length} workspace package(s) with stale or missing dist/: ${stale.join(', ')}\n`)
48
76
 
49
- for (const pkg of missing) {
77
+ for (const pkg of stale) {
50
78
  const pkgDir = join(packagesDir, pkg)
51
79
  try {
52
80
  execSync('npm run build', { cwd: pkgDir, stdio: 'pipe' })
@@ -14,7 +14,7 @@ import {
14
14
  DEFAULT_MAX_LINES,
15
15
  } from "@gsd/pi-coding-agent";
16
16
  import { Type } from "@sinclair/typebox";
17
- import { spawn } from "node:child_process";
17
+ import { spawn, spawnSync } from "node:child_process";
18
18
  import { createWriteStream } from "node:fs";
19
19
  import { tmpdir } from "node:os";
20
20
  import { join } from "node:path";
@@ -38,17 +38,24 @@ function getTempFilePath(): string {
38
38
  }
39
39
 
40
40
  /**
41
- * Kill a process and its children. Uses process group kill on Unix.
41
+ * Kill a process and its children (cross-platform).
42
+ * Uses process group kill on Unix; taskkill /F /T on Windows.
42
43
  */
43
44
  function killTree(pid: number): void {
44
- try {
45
- // Kill the process group (negative PID)
46
- process.kill(-pid, "SIGTERM");
47
- } catch {
45
+ if (process.platform === "win32") {
48
46
  try {
49
- process.kill(pid, "SIGTERM");
47
+ spawnSync("taskkill", ["/F", "/T", "/PID", String(pid)], {
48
+ timeout: 5_000,
49
+ stdio: "ignore",
50
+ });
51
+ } catch {
52
+ try { process.kill(pid, "SIGTERM"); } catch { /* already exited */ }
53
+ }
54
+ } else {
55
+ try {
56
+ process.kill(-pid, "SIGTERM");
50
57
  } catch {
51
- // Already exited
58
+ try { process.kill(pid, "SIGTERM"); } catch { /* already exited */ }
52
59
  }
53
60
  }
54
61
  }
@@ -118,9 +125,13 @@ function executeBashInBackground(
118
125
  const rewrittenCommand = rewriteCommandWithRtk(command);
119
126
  const resolvedCommand = sanitizeCommand(rewrittenCommand);
120
127
 
128
+ // On Windows, detached: true sets CREATE_NEW_PROCESS_GROUP which can
129
+ // cause EINVAL in VSCode/ConPTY terminal contexts. The bg-shell
130
+ // extension already guards this (process-manager.ts); align here.
131
+ // Process-tree cleanup uses taskkill /F /T on Windows regardless.
121
132
  const child = spawn(shell, [...args, resolvedCommand], {
122
133
  cwd,
123
- detached: true,
134
+ detached: process.platform !== "win32",
124
135
  env: { ...process.env },
125
136
  stdio: ["ignore", "pipe", "pipe"],
126
137
  });
@@ -143,8 +154,8 @@ function executeBashInBackground(
143
154
  // If the process ignores SIGTERM, escalate to SIGKILL
144
155
  sigkillHandle = setTimeout(() => {
145
156
  if (child.pid) {
146
- try { process.kill(-child.pid, "SIGKILL"); } catch { /* ignore */ }
147
- try { process.kill(child.pid, "SIGKILL"); } catch { /* ignore */ }
157
+ // killTree already uses taskkill /F /T on Windows
158
+ killTree(child.pid);
148
159
  }
149
160
 
150
161
  // Hard deadline: if even SIGKILL doesn't trigger 'close',
@@ -172,7 +172,10 @@ export class AsyncJobManager {
172
172
 
173
173
  private deliverResult(job: Job): void {
174
174
  if (!this.onJobComplete) return;
175
- this.onJobComplete(job);
175
+ // Defer delivery by one microtask so await_job's .then() chain runs first
176
+ // and can set job.awaited = true before onJobComplete checks it (#2762).
177
+ const cb = this.onJobComplete;
178
+ queueMicrotask(() => cb(job));
176
179
  }
177
180
 
178
181
  private scheduleEviction(id: string): void {
@@ -113,6 +113,20 @@ function makeErrorMessage(model: string, errorMsg: string): AssistantMessage {
113
113
  };
114
114
  }
115
115
 
116
+ /**
117
+ * Generator exhaustion without a terminal result means the SDK stream was
118
+ * interrupted mid-turn. Surface it as an error so downstream recovery logic
119
+ * can classify and retry it instead of treating it as a clean completion.
120
+ */
121
+ export function makeStreamExhaustedErrorMessage(model: string, lastTextContent: string): AssistantMessage {
122
+ const errorMsg = "stream_exhausted_without_result";
123
+ const message = makeErrorMessage(model, errorMsg);
124
+ if (lastTextContent) {
125
+ message.content = [{ type: "text", text: lastTextContent }];
126
+ }
127
+ return message;
128
+ }
129
+
116
130
  // ---------------------------------------------------------------------------
117
131
  // streamSimple implementation
118
132
  // ---------------------------------------------------------------------------
@@ -339,26 +353,11 @@ async function pumpSdkMessages(
339
353
  }
340
354
  }
341
355
 
342
- // Generator exhausted without a result message (unexpected)
343
- const fallbackContent: AssistantMessage["content"] = [];
344
- if (lastTextContent) {
345
- fallbackContent.push({ type: "text", text: lastTextContent });
346
- }
347
- if (fallbackContent.length === 0) {
348
- fallbackContent.push({ type: "text", text: "(Claude Code session ended without a response)" });
349
- }
350
-
351
- const fallback: AssistantMessage = {
352
- role: "assistant",
353
- content: fallbackContent,
354
- api: "anthropic-messages",
355
- provider: "claude-code",
356
- model: modelId,
357
- usage: { ...ZERO_USAGE },
358
- stopReason: "stop",
359
- timestamp: Date.now(),
360
- };
361
- stream.push({ type: "done", reason: "stop", message: fallback });
356
+ // Generator exhaustion without a terminal result is a stream interruption,
357
+ // not a successful completion. Emitting an error lets GSD classify it as a
358
+ // transient provider failure instead of advancing auto-mode state.
359
+ const fallback = makeStreamExhaustedErrorMessage(modelId, lastTextContent);
360
+ stream.push({ type: "error", reason: "error", error: fallback });
362
361
  } catch (err) {
363
362
  const errorMsg = err instanceof Error ? err.message : String(err);
364
363
  stream.push({
@@ -0,0 +1,21 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { makeStreamExhaustedErrorMessage } from "../stream-adapter.ts";
4
+
5
+ describe("stream-adapter — exhausted stream fallback (#2575)", () => {
6
+ test("generator exhaustion becomes an error message instead of clean completion", () => {
7
+ const message = makeStreamExhaustedErrorMessage("claude-sonnet-4-20250514", "partial answer");
8
+
9
+ assert.equal(message.stopReason, "error");
10
+ assert.equal(message.errorMessage, "stream_exhausted_without_result");
11
+ assert.deepEqual(message.content, [{ type: "text", text: "partial answer" }]);
12
+ });
13
+
14
+ test("generator exhaustion without prior text still exposes a classifiable error", () => {
15
+ const message = makeStreamExhaustedErrorMessage("claude-sonnet-4-20250514", "");
16
+
17
+ assert.equal(message.stopReason, "error");
18
+ assert.equal(message.errorMessage, "stream_exhausted_without_result");
19
+ assert.match(String((message.content[0] as any)?.text ?? ""), /Claude Code error: stream_exhausted_without_result/);
20
+ });
21
+ });
@@ -1069,6 +1069,12 @@ export async function runUnitPhase(
1069
1069
  }
1070
1070
 
1071
1071
  if (unitResult.status === "cancelled") {
1072
+ // Provider-error pause: pauseAuto already handled cleanup and scheduled
1073
+ // recovery. Don't hard-stop — just break out of the loop (#2762).
1074
+ if (unitResult.errorContext?.category === "provider") {
1075
+ debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext.isTransient });
1076
+ return { action: "break", reason: "provider-pause" };
1077
+ }
1072
1078
  ctx.ui.notify(
1073
1079
  `Session creation timed out or was cancelled for ${unitType} ${unitId}. Will retry.`,
1074
1080
  "warning",
@@ -626,6 +626,25 @@ export const DISPATCH_RULES: DispatchRule[] = [
626
626
  match: async ({ state, mid, midTitle, basePath }) => {
627
627
  if (state.phase !== "completing-milestone") return null;
628
628
 
629
+ // Safety guard (#2675): block completion when VALIDATION verdict is
630
+ // needs-remediation. The state machine treats needs-remediation as
631
+ // terminal (to prevent validate-milestone loops per #832), but
632
+ // completing-milestone should NOT proceed — remediation work is needed.
633
+ const validationFile = resolveMilestoneFile(basePath, mid, "VALIDATION");
634
+ if (validationFile) {
635
+ const validationContent = await loadFile(validationFile);
636
+ if (validationContent) {
637
+ const verdict = extractVerdict(validationContent);
638
+ if (verdict === "needs-remediation") {
639
+ return {
640
+ action: "stop",
641
+ reason: `Cannot complete milestone ${mid}: VALIDATION verdict is "needs-remediation". Address the remediation findings and re-run validation, or update the verdict manually.`,
642
+ level: "warning",
643
+ };
644
+ }
645
+ }
646
+ }
647
+
629
648
  // Safety guard (#1368): verify all roadmap slices have SUMMARY files.
630
649
  const missingSlices = findMissingSummaries(basePath, mid);
631
650
  if (missingSlices.length > 0) {
@@ -67,6 +67,7 @@ import {
67
67
  getDebugLogPath,
68
68
  } from "./debug-logger.js";
69
69
  import { parseUnitId } from "./unit-id.js";
70
+ import { setLogBasePath } from "./workflow-logger.js";
70
71
  import type { AutoSession } from "./auto/session.js";
71
72
  import {
72
73
  existsSync,
@@ -461,6 +462,7 @@ export async function bootstrapAutoSession(
461
462
  s.verbose = verboseMode;
462
463
  s.cmdCtx = ctx;
463
464
  s.basePath = base;
465
+ setLogBasePath(base);
464
466
  s.unitDispatchCount.clear();
465
467
  s.unitRecoveryCount.clear();
466
468
  s.lastBudgetAlertLevel = 0;
@@ -15,6 +15,8 @@ import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.j
15
15
  import {
16
16
  getInFlightToolCount,
17
17
  getOldestInFlightToolStart,
18
+ clearInFlightTools,
19
+ hasInteractiveToolInFlight,
18
20
  } from "./auto-tool-tracking.js";
19
21
  import { detectWorkingTreeActivity } from "./auto-supervisor.js";
20
22
  import { closeoutUnit, type CloseoutOptions } from "./auto-unit-closeout.js";
@@ -146,7 +148,17 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
146
148
 
147
149
  // Agent has tool calls currently executing — not idle, just waiting.
148
150
  // But only suppress recovery if the tool started recently.
151
+ let stalledToolDetected = false;
149
152
  if (getInFlightToolCount() > 0) {
153
+ // User-interactive tools (ask_user_questions, secure_env_collect) block
154
+ // waiting for human input by design — never treat them as stalled (#2676).
155
+ if (hasInteractiveToolInFlight()) {
156
+ writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
157
+ lastProgressAt: Date.now(),
158
+ lastProgressKind: "interactive-tool-waiting",
159
+ });
160
+ return;
161
+ }
150
162
  const oldestStart = getOldestInFlightToolStart()!;
151
163
  const toolAgeMs = Date.now() - oldestStart;
152
164
  if (toolAgeMs < idleTimeoutMs) {
@@ -156,6 +168,12 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
156
168
  });
157
169
  return;
158
170
  }
171
+ // Tool has been in-flight longer than idle timeout — treat as hung.
172
+ // Clear the stale entries so subsequent ticks don't re-detect them,
173
+ // and set the flag so the filesystem-activity check below does not
174
+ // override the stall verdict (#2527).
175
+ stalledToolDetected = true;
176
+ clearInFlightTools();
159
177
  ctx.ui.notify(
160
178
  `Stalled tool detected: a tool has been in-flight for ${Math.round(toolAgeMs / 60000)}min. Treating as hung — attempting idle recovery.`,
161
179
  "warning",
@@ -163,7 +181,9 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
163
181
  }
164
182
 
165
183
  // Check if the agent is producing work on disk.
166
- if (detectWorkingTreeActivity(s.basePath)) {
184
+ // Skip this when a stalled tool was just detected — filesystem changes
185
+ // from earlier in the task should not override the stall verdict (#2527).
186
+ if (!stalledToolDetected && detectWorkingTreeActivity(s.basePath)) {
167
187
  writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
168
188
  lastProgressAt: Date.now(),
169
189
  lastProgressKind: "filesystem-activity",
@@ -180,6 +200,10 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
180
200
  const recovery = await recoverTimedOutUnit(ctx, pi, unitType, unitId, "idle", buildRecoveryContext());
181
201
  if (recovery === "recovered") return;
182
202
 
203
+ // Guard: recoverTimedOutUnit is async — pauseAuto/stopAuto may have
204
+ // set s.currentUnit = null during the await (#2527).
205
+ if (!s.currentUnit) return;
206
+
183
207
  writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
184
208
  phase: "paused",
185
209
  });
@@ -4,15 +4,27 @@
4
4
  * can distinguish "waiting for tool completion" from "truly idle".
5
5
  */
6
6
 
7
- const inFlightTools = new Map<string, number>();
7
+ interface InFlightTool {
8
+ startedAt: number;
9
+ toolName: string;
10
+ }
11
+
12
+ const inFlightTools = new Map<string, InFlightTool>();
13
+
14
+ /**
15
+ * Tools that block waiting for human input by design.
16
+ * The idle watchdog must not treat these as stalled.
17
+ */
18
+ const INTERACTIVE_TOOLS = new Set(["ask_user_questions", "secure_env_collect"]);
8
19
 
9
20
  /**
10
21
  * Mark a tool execution as in-flight.
11
- * Records start time so the idle watchdog can detect tools hung longer than the idle timeout.
22
+ * Records start time and tool name so the idle watchdog can detect tools
23
+ * hung longer than the idle timeout while exempting interactive tools.
12
24
  */
13
- export function markToolStart(toolCallId: string, isActive: boolean): void {
25
+ export function markToolStart(toolCallId: string, isActive: boolean, toolName?: string): void {
14
26
  if (!isActive) return;
15
- inFlightTools.set(toolCallId, Date.now());
27
+ inFlightTools.set(toolCallId, { startedAt: Date.now(), toolName: toolName ?? "unknown" });
16
28
  }
17
29
 
18
30
  /**
@@ -29,7 +41,7 @@ export function getOldestInFlightToolAgeMs(): number {
29
41
  if (inFlightTools.size === 0) return 0;
30
42
  let oldestStart = Infinity;
31
43
  for (const t of inFlightTools.values()) {
32
- if (t < oldestStart) oldestStart = t;
44
+ if (t.startedAt < oldestStart) oldestStart = t.startedAt;
33
45
  }
34
46
  return Date.now() - oldestStart;
35
47
  }
@@ -48,11 +60,23 @@ export function getOldestInFlightToolStart(): number | undefined {
48
60
  if (inFlightTools.size === 0) return undefined;
49
61
  let oldest = Infinity;
50
62
  for (const t of inFlightTools.values()) {
51
- if (t < oldest) oldest = t;
63
+ if (t.startedAt < oldest) oldest = t.startedAt;
52
64
  }
53
65
  return oldest;
54
66
  }
55
67
 
68
+ /**
69
+ * Returns true if any currently in-flight tool is a user-interactive tool
70
+ * (e.g. ask_user_questions, secure_env_collect) that blocks waiting for
71
+ * human input. These must be exempt from idle stall detection.
72
+ */
73
+ export function hasInteractiveToolInFlight(): boolean {
74
+ for (const { toolName } of inFlightTools.values()) {
75
+ if (INTERACTIVE_TOOLS.has(toolName)) return true;
76
+ }
77
+ return false;
78
+ }
79
+
56
80
  /**
57
81
  * Clear all in-flight tool tracking state.
58
82
  */
@@ -82,6 +82,10 @@ const ROOT_STATE_FILES = [
82
82
  "QUEUE.md",
83
83
  "completed-units.json",
84
84
  "metrics.json",
85
+ // NOTE: preferences.md is intentionally NOT in ROOT_STATE_FILES.
86
+ // Forward-sync (main → worktree) is handled explicitly in syncGsdStateToWorktree().
87
+ // Back-sync (worktree → main) must NEVER overwrite the project root's copy
88
+ // because the project root is authoritative for preferences (#2684).
85
89
  ] as const;
86
90
 
87
91
  /**
@@ -416,6 +420,22 @@ export function syncGsdStateToWorktree(
416
420
  }
417
421
  }
418
422
 
423
+ // Forward-sync preferences.md from project root to worktree (additive only).
424
+ // NOT in ROOT_STATE_FILES because syncWorktreeStateBack() must never overwrite
425
+ // the project root's preferences — the project root is authoritative (#2684).
426
+ {
427
+ const src = join(mainGsd, "preferences.md");
428
+ const dst = join(wtGsd, "preferences.md");
429
+ if (existsSync(src) && !existsSync(dst)) {
430
+ try {
431
+ cpSync(src, dst);
432
+ synced.push("preferences.md");
433
+ } catch {
434
+ /* non-fatal */
435
+ }
436
+ }
437
+ }
438
+
419
439
  // Sync milestones: copy entire milestone directories that are missing
420
440
  const mainMilestonesDir = join(mainGsd, "milestones");
421
441
  const wtMilestonesDir = join(wtGsd, "milestones");
@@ -946,6 +966,7 @@ function copyPlanningArtifacts(srcBase: string, wtPath: string): void {
946
966
  "STATE.md",
947
967
  "KNOWLEDGE.md",
948
968
  "OVERRIDES.md",
969
+ "preferences.md",
949
970
  ]) {
950
971
  safeCopy(join(srcGsd, file), join(dstGsd, file), { force: true });
951
972
  }
@@ -73,6 +73,7 @@ import {
73
73
  getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs,
74
74
  getInFlightToolCount,
75
75
  getOldestInFlightToolStart,
76
+ hasInteractiveToolInFlight,
76
77
  clearInFlightTools,
77
78
  } from "./auto-tool-tracking.js";
78
79
  import { closeoutUnit } from "./auto-unit-closeout.js";
@@ -114,6 +115,7 @@ import {
114
115
  formatCost,
115
116
  formatTokenCount,
116
117
  } from "./metrics.js";
118
+ import { setLogBasePath } from "./workflow-logger.js";
117
119
  import { join } from "node:path";
118
120
  import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
119
121
  import { atomicWriteSync } from "./atomic-write.js";
@@ -184,7 +186,7 @@ import {
184
186
  postUnitPostVerification,
185
187
  } from "./auto-post-unit.js";
186
188
  import { bootstrapAutoSession, type BootstrapDeps } from "./auto-start.js";
187
- import { autoLoop, resolveAgentEnd, resolveAgentEndCancelled, _resetPendingResolve, isSessionSwitchInFlight, type LoopDeps } from "./auto-loop.js";
189
+ import { autoLoop, resolveAgentEnd, resolveAgentEndCancelled, _resetPendingResolve, isSessionSwitchInFlight, type LoopDeps, type ErrorContext } from "./auto-loop.js";
188
190
  import {
189
191
  WorktreeResolver,
190
192
  type WorktreeResolverDeps,
@@ -374,8 +376,8 @@ export function getAutoModeStartModel(): {
374
376
  }
375
377
 
376
378
  // Tool tracking — delegates to auto-tool-tracking.ts
377
- export function markToolStart(toolCallId: string): void {
378
- _markToolStart(toolCallId, s.active);
379
+ export function markToolStart(toolCallId: string, toolName?: string): void {
380
+ _markToolStart(toolCallId, s.active, toolName);
379
381
  }
380
382
 
381
383
  export function markToolEnd(toolCallId: string): void {
@@ -798,11 +800,14 @@ export async function stopAuto(
798
800
  export async function pauseAuto(
799
801
  ctx?: ExtensionContext,
800
802
  _pi?: ExtensionAPI,
803
+ _errorContext?: ErrorContext,
801
804
  ): Promise<void> {
802
805
  if (!s.active) return;
803
806
  clearUnitTimeout();
804
807
  // Unblock any pending unit promise so the auto-loop is not orphaned.
805
- resolveAgentEndCancelled();
808
+ // Pass errorContext so runUnitPhase can distinguish user-initiated pause
809
+ // from provider-error pause and avoid hard-stopping (#2762).
810
+ resolveAgentEndCancelled(_errorContext);
806
811
 
807
812
  s.pausedSessionFile = ctx?.sessionManager?.getSessionFile() ?? null;
808
813
 
@@ -1102,6 +1107,7 @@ export async function startAuto(
1102
1107
  s.stepMode = requestedStepMode;
1103
1108
  s.cmdCtx = ctx;
1104
1109
  s.basePath = base;
1110
+ setLogBasePath(base);
1105
1111
  s.unitDispatchCount.clear();
1106
1112
  s.unitLifetimeDispatches.clear();
1107
1113
  if (!getLedger()) initMetrics(base);