gsd-pi 2.51.0 → 2.52.0-dev.655ad8a

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 (419) hide show
  1. package/README.md +59 -36
  2. package/dist/headless-events.d.ts +18 -0
  3. package/dist/headless-events.js +36 -0
  4. package/dist/headless-query.js +1 -1
  5. package/dist/headless-types.d.ts +28 -0
  6. package/dist/headless-types.js +7 -0
  7. package/dist/headless.d.ts +8 -3
  8. package/dist/headless.js +47 -16
  9. package/dist/help-text.js +16 -5
  10. package/dist/onboarding.js +5 -4
  11. package/dist/remote-questions-config.js +1 -1
  12. package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
  13. package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
  14. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
  15. package/dist/resources/extensions/get-secrets-from-user.js +7 -0
  16. package/dist/resources/extensions/gsd/auto/phases.js +34 -8
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +23 -1
  18. package/dist/resources/extensions/gsd/auto-start.js +2 -0
  19. package/dist/resources/extensions/gsd/auto-timers.js +24 -2
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
  21. package/dist/resources/extensions/gsd/auto-worktree.js +91 -14
  22. package/dist/resources/extensions/gsd/auto.js +30 -4
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +99 -70
  24. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
  26. package/dist/resources/extensions/gsd/claude-import.js +60 -9
  27. package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
  28. package/dist/resources/extensions/gsd/commands-config.js +10 -5
  29. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
  30. package/dist/resources/extensions/gsd/detection.js +6 -6
  31. package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  32. package/dist/resources/extensions/gsd/error-classifier.js +105 -0
  33. package/dist/resources/extensions/gsd/git-service.js +4 -3
  34. package/dist/resources/extensions/gsd/gitignore.js +7 -7
  35. package/dist/resources/extensions/gsd/gsd-db.js +298 -45
  36. package/dist/resources/extensions/gsd/guided-flow.js +4 -3
  37. package/dist/resources/extensions/gsd/init-wizard.js +2 -2
  38. package/dist/resources/extensions/gsd/key-manager.js +7 -16
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
  40. package/dist/resources/extensions/gsd/memory-store.js +28 -13
  41. package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
  42. package/dist/resources/extensions/gsd/parallel-orchestrator.js +18 -2
  43. package/dist/resources/extensions/gsd/preferences-models.js +1 -13
  44. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  45. package/dist/resources/extensions/gsd/preferences.js +13 -13
  46. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  47. package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
  48. package/dist/resources/extensions/gsd/rule-registry.js +1 -1
  49. package/dist/resources/extensions/gsd/service-tier.js +13 -2
  50. package/dist/resources/extensions/gsd/state.js +38 -30
  51. package/dist/resources/extensions/gsd/status-guards.js +12 -0
  52. package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
  53. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
  54. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
  55. package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
  56. package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
  57. package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
  58. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
  59. package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
  60. package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
  61. package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
  62. package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
  63. package/dist/resources/extensions/gsd/validation.js +21 -0
  64. package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
  65. package/dist/resources/extensions/remote-questions/config.js +1 -1
  66. package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
  67. package/dist/resources/extensions/search-the-web/native-search.js +1 -1
  68. package/dist/resources/extensions/search-the-web/provider.js +1 -1
  69. package/dist/resources/extensions/shared/rtk.js +14 -4
  70. package/dist/rtk.js +3 -1
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  73. package/dist/web/standalone/.next/build-manifest.json +4 -4
  74. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  75. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  76. package/dist/web/standalone/.next/required-server-files.json +3 -3
  77. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  78. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  80. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  88. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  97. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  116. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  144. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  150. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  164. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  170. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/index.html +1 -1
  180. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  181. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  182. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  183. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  184. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  185. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  186. package/dist/web/standalone/.next/server/app/page.js +2 -2
  187. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  189. package/dist/web/standalone/.next/server/chunks/2229.js +3 -3
  190. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  191. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/middleware.js +2 -2
  194. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  196. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  197. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  198. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  199. package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.js +9 -0
  200. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  202. package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-bca0e732db0dcec3.js} +1 -1
  206. package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
  207. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  208. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  209. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  210. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  211. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  212. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  213. package/dist/web/standalone/server.js +1 -1
  214. package/dist/wizard.js +4 -1
  215. package/package.json +2 -2
  216. package/packages/mcp-server/README.md +202 -0
  217. package/packages/mcp-server/package.json +36 -0
  218. package/packages/mcp-server/src/cli.ts +68 -0
  219. package/packages/mcp-server/src/index.ts +14 -0
  220. package/packages/mcp-server/src/mcp-server.test.ts +628 -0
  221. package/packages/mcp-server/src/server.ts +278 -0
  222. package/packages/mcp-server/src/session-manager.ts +328 -0
  223. package/packages/mcp-server/src/types.ts +107 -0
  224. package/packages/mcp-server/tsconfig.json +24 -0
  225. package/packages/pi-ai/dist/models.d.ts +14 -3
  226. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  227. package/packages/pi-ai/dist/models.js +53 -10
  228. package/packages/pi-ai/dist/models.js.map +1 -1
  229. package/packages/pi-ai/dist/models.test.js +102 -1
  230. package/packages/pi-ai/dist/models.test.js.map +1 -1
  231. package/packages/pi-ai/dist/types.d.ts +30 -0
  232. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  233. package/packages/pi-ai/dist/types.js.map +1 -1
  234. package/packages/pi-ai/src/models.test.ts +114 -1
  235. package/packages/pi-ai/src/models.ts +70 -13
  236. package/packages/pi-ai/src/types.ts +31 -0
  237. package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
  238. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/cli/args.js +3 -0
  240. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
  243. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
  246. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
  248. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
  249. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
  250. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
  251. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
  253. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  255. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  256. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  257. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/main.js +5 -3
  259. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
  261. package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
  265. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
  267. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
  269. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
  271. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  272. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
  273. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  274. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
  275. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
  276. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
  277. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
  278. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
  279. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  280. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  281. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
  283. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  284. package/packages/pi-coding-agent/package.json +1 -1
  285. package/packages/pi-coding-agent/src/cli/args.ts +4 -0
  286. package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
  287. package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
  288. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
  289. package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
  290. package/packages/pi-coding-agent/src/index.ts +3 -0
  291. package/packages/pi-coding-agent/src/main.ts +5 -3
  292. package/packages/pi-coding-agent/src/modes/index.ts +8 -1
  293. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
  294. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
  295. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
  296. package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
  297. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
  298. package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
  299. package/packages/rpc-client/package.json +20 -0
  300. package/pkg/package.json +1 -1
  301. package/scripts/ensure-workspace-builds.cjs +36 -8
  302. package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
  303. package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
  304. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
  305. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
  306. package/src/resources/extensions/get-secrets-from-user.ts +8 -0
  307. package/src/resources/extensions/gsd/auto/phases.ts +44 -7
  308. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -1
  309. package/src/resources/extensions/gsd/auto-start.ts +2 -0
  310. package/src/resources/extensions/gsd/auto-timers.ts +25 -1
  311. package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
  312. package/src/resources/extensions/gsd/auto-worktree.ts +94 -14
  313. package/src/resources/extensions/gsd/auto.ts +31 -4
  314. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +118 -73
  315. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
  316. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
  317. package/src/resources/extensions/gsd/claude-import.ts +58 -9
  318. package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
  319. package/src/resources/extensions/gsd/commands-config.ts +11 -5
  320. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
  321. package/src/resources/extensions/gsd/detection.ts +6 -6
  322. package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  323. package/src/resources/extensions/gsd/error-classifier.ts +139 -0
  324. package/src/resources/extensions/gsd/git-service.ts +4 -3
  325. package/src/resources/extensions/gsd/gitignore.ts +7 -7
  326. package/src/resources/extensions/gsd/gsd-db.ts +355 -63
  327. package/src/resources/extensions/gsd/guided-flow.ts +4 -3
  328. package/src/resources/extensions/gsd/init-wizard.ts +2 -2
  329. package/src/resources/extensions/gsd/key-manager.ts +7 -16
  330. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
  331. package/src/resources/extensions/gsd/memory-store.ts +29 -18
  332. package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
  333. package/src/resources/extensions/gsd/parallel-orchestrator.ts +23 -1
  334. package/src/resources/extensions/gsd/preferences-models.ts +1 -13
  335. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  336. package/src/resources/extensions/gsd/preferences.ts +12 -13
  337. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  338. package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
  339. package/src/resources/extensions/gsd/rule-registry.ts +1 -1
  340. package/src/resources/extensions/gsd/service-tier.ts +14 -2
  341. package/src/resources/extensions/gsd/state.ts +39 -30
  342. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  343. package/src/resources/extensions/gsd/tests/active-milestone-id-guard.test.ts +91 -0
  344. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
  345. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
  346. package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +87 -0
  347. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +80 -0
  348. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +1 -1
  349. package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
  350. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
  351. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +39 -0
  352. package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
  353. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  354. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
  355. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  356. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
  357. package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
  358. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
  359. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
  360. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
  361. package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
  362. package/src/resources/extensions/gsd/tests/git-service.test.ts +65 -31
  363. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
  365. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
  367. package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
  368. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  369. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  370. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +51 -0
  371. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
  372. package/src/resources/extensions/gsd/tests/parallel-orchestrator-zombie-cleanup.test.ts +277 -0
  373. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
  374. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +103 -0
  375. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
  376. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
  377. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
  378. package/src/resources/extensions/gsd/tests/rate-limit-model-fallback.test.ts +90 -0
  379. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
  380. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
  381. package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +9 -8
  382. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +125 -0
  383. package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
  384. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
  385. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
  386. package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
  387. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
  388. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -2
  389. package/src/resources/extensions/gsd/tests/validation-gate-patterns.test.ts +124 -0
  390. package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
  391. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
  392. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
  393. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
  394. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
  395. package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
  396. package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
  397. package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
  398. package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
  399. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
  400. package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
  401. package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
  402. package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
  403. package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
  404. package/src/resources/extensions/gsd/validation.ts +23 -0
  405. package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
  406. package/src/resources/extensions/remote-questions/config.ts +1 -1
  407. package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
  408. package/src/resources/extensions/search-the-web/native-search.ts +1 -1
  409. package/src/resources/extensions/search-the-web/provider.ts +1 -1
  410. package/src/resources/extensions/shared/rtk.ts +22 -4
  411. package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
  412. package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
  413. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  414. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  415. package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
  416. /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
  417. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_buildManifest.js +0 -0
  418. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_ssgManifest.js +0 -0
  419. /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
@@ -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
  /**
@@ -153,6 +157,25 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
153
157
  }
154
158
  }
155
159
 
160
+ // ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
161
+
162
+ /** Patterns for machine-generated build artifacts that can be safely
163
+ * auto-resolved by accepting --theirs during merge. These files are
164
+ * regenerable and never contain meaningful manual edits. */
165
+ export const SAFE_AUTO_RESOLVE_PATTERNS: RegExp[] = [
166
+ /\.tsbuildinfo$/,
167
+ /\.pyc$/,
168
+ /\/__pycache__\//,
169
+ /\.DS_Store$/,
170
+ /\.map$/,
171
+ ];
172
+
173
+ /** Returns true if the file path is safe to auto-resolve during merge.
174
+ * Covers `.gsd/` state files and common build artifacts. */
175
+ export const isSafeToAutoResolve = (filePath: string): boolean =>
176
+ filePath.startsWith(".gsd/") ||
177
+ SAFE_AUTO_RESOLVE_PATTERNS.some((re) => re.test(filePath));
178
+
156
179
  // ─── Dispatch-Level Sync (project root ↔ worktree) ──────────────────────────
157
180
 
158
181
  /**
@@ -416,6 +439,22 @@ export function syncGsdStateToWorktree(
416
439
  }
417
440
  }
418
441
 
442
+ // Forward-sync preferences.md from project root to worktree (additive only).
443
+ // NOT in ROOT_STATE_FILES because syncWorktreeStateBack() must never overwrite
444
+ // the project root's preferences — the project root is authoritative (#2684).
445
+ {
446
+ const src = join(mainGsd, "preferences.md");
447
+ const dst = join(wtGsd, "preferences.md");
448
+ if (existsSync(src) && !existsSync(dst)) {
449
+ try {
450
+ cpSync(src, dst);
451
+ synced.push("preferences.md");
452
+ } catch {
453
+ /* non-fatal */
454
+ }
455
+ }
456
+ }
457
+
419
458
  // Sync milestones: copy entire milestone directories that are missing
420
459
  const mainMilestonesDir = join(mainGsd, "milestones");
421
460
  const wtMilestonesDir = join(wtGsd, "milestones");
@@ -946,6 +985,7 @@ function copyPlanningArtifacts(srcBase: string, wtPath: string): void {
946
985
  "STATE.md",
947
986
  "KNOWLEDGE.md",
948
987
  "OVERRIDES.md",
988
+ "preferences.md",
949
989
  ]) {
950
990
  safeCopy(join(srcGsd, file), join(dstGsd, file), { force: true });
951
991
  }
@@ -1387,30 +1427,30 @@ export function mergeMilestoneToMain(
1387
1427
  : nativeConflictFiles(originalBasePath_);
1388
1428
 
1389
1429
  if (conflictedFiles.length > 0) {
1390
- // Separate .gsd/ state file conflicts from real code conflicts.
1391
- // GSD state files (STATE.md, auto.lock, etc.)
1392
- // diverge between branches during normal operation always prefer the
1393
- // milestone branch version since it has the latest execution state.
1394
- const gsdConflicts = conflictedFiles.filter((f) => f.startsWith(".gsd/"));
1430
+ // Separate auto-resolvable conflicts (GSD state files + build artifacts)
1431
+ // from real code conflicts. GSD state files diverge between branches
1432
+ // during normal operation. Build artifacts are machine-generated and
1433
+ // regenerable. Both are safe to accept from the milestone branch.
1434
+ const autoResolvable = conflictedFiles.filter(isSafeToAutoResolve);
1395
1435
  const codeConflicts = conflictedFiles.filter(
1396
- (f) => !f.startsWith(".gsd/"),
1436
+ (f) => !isSafeToAutoResolve(f),
1397
1437
  );
1398
1438
 
1399
- // Auto-resolve .gsd/ conflicts by accepting the milestone branch version
1400
- if (gsdConflicts.length > 0) {
1401
- for (const gsdFile of gsdConflicts) {
1439
+ // Auto-resolve safe conflicts by accepting the milestone branch version
1440
+ if (autoResolvable.length > 0) {
1441
+ for (const safeFile of autoResolvable) {
1402
1442
  try {
1403
- nativeCheckoutTheirs(originalBasePath_, [gsdFile]);
1404
- nativeAddPaths(originalBasePath_, [gsdFile]);
1443
+ nativeCheckoutTheirs(originalBasePath_, [safeFile]);
1444
+ nativeAddPaths(originalBasePath_, [safeFile]);
1405
1445
  } catch {
1406
1446
  // If checkout --theirs fails, try removing the file from the merge
1407
1447
  // (it's a runtime file that shouldn't be committed anyway)
1408
- nativeRmForce(originalBasePath_, [gsdFile]);
1448
+ nativeRmForce(originalBasePath_, [safeFile]);
1409
1449
  }
1410
1450
  }
1411
1451
  }
1412
1452
 
1413
- // If there are still non-.gsd conflicts, escalate
1453
+ // If there are still real code conflicts, escalate
1414
1454
  if (codeConflicts.length > 0) {
1415
1455
  // Pop stash before throwing so local work is not lost (#2151).
1416
1456
  if (stashed) {
@@ -1459,7 +1499,47 @@ export function mergeMilestoneToMain(
1459
1499
  encoding: "utf-8",
1460
1500
  });
1461
1501
  } catch {
1462
- // Stash pop conflict is non-fatal stash entry persists for manual resolution.
1502
+ // Stash pop after squash merge can conflict on .gsd/ state files that
1503
+ // diverged between branches. Left unresolved, these UU entries block
1504
+ // every subsequent merge. Auto-resolve them the same way we handle
1505
+ // .gsd/ conflicts during the merge itself: accept HEAD (the just-committed
1506
+ // version) and drop the now-applied stash.
1507
+ const uu = nativeConflictFiles(originalBasePath_);
1508
+ const gsdUU = uu.filter((f) => f.startsWith(".gsd/"));
1509
+ const nonGsdUU = uu.filter((f) => !f.startsWith(".gsd/"));
1510
+
1511
+ if (gsdUU.length > 0) {
1512
+ for (const f of gsdUU) {
1513
+ try {
1514
+ // Accept the committed (HEAD) version of the state file
1515
+ execFileSync("git", ["checkout", "HEAD", "--", f], {
1516
+ cwd: originalBasePath_,
1517
+ stdio: ["ignore", "pipe", "pipe"],
1518
+ encoding: "utf-8",
1519
+ });
1520
+ nativeAddPaths(originalBasePath_, [f]);
1521
+ } catch {
1522
+ // Last resort: remove the conflicted state file
1523
+ nativeRmForce(originalBasePath_, [f]);
1524
+ }
1525
+ }
1526
+ }
1527
+
1528
+ if (nonGsdUU.length === 0) {
1529
+ // All conflicts were .gsd/ files — safe to drop the stash
1530
+ try {
1531
+ execFileSync("git", ["stash", "drop"], {
1532
+ cwd: originalBasePath_,
1533
+ stdio: ["ignore", "pipe", "pipe"],
1534
+ encoding: "utf-8",
1535
+ });
1536
+ } catch { /* stash may already be consumed */ }
1537
+ } else {
1538
+ // Non-.gsd conflicts remain — leave stash for manual resolution
1539
+ logWarning("reconcile", "Stash pop conflict on non-.gsd files after merge", {
1540
+ files: nonGsdUU.join(", "),
1541
+ });
1542
+ }
1463
1543
  }
1464
1544
  }
1465
1545
 
@@ -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 {
@@ -412,6 +414,13 @@ export function stopAutoRemote(projectRoot: string): {
412
414
  const lock = readCrashLock(projectRoot);
413
415
  if (!lock) return { found: false };
414
416
 
417
+ // Never SIGTERM ourselves — a stale lock with our own PID is not a remote
418
+ // session, it is leftover from a prior loop exit in this process. (#2730)
419
+ if (lock.pid === process.pid) {
420
+ clearLock(projectRoot);
421
+ return { found: false };
422
+ }
423
+
415
424
  if (!isLockProcessAlive(lock)) {
416
425
  // Stale lock — clean it up
417
426
  clearLock(projectRoot);
@@ -443,6 +452,10 @@ export function checkRemoteAutoSession(projectRoot: string): {
443
452
  const lock = readCrashLock(projectRoot);
444
453
  if (!lock) return { running: false };
445
454
 
455
+ // Our own PID is not a "remote" session — it is a stale lock left by this
456
+ // process (e.g. after step-mode exit without full cleanup). (#2730)
457
+ if (lock.pid === process.pid) return { running: false };
458
+
446
459
  if (!isLockProcessAlive(lock)) {
447
460
  // Stale lock from a dead process — not a live remote session
448
461
  return { running: false };
@@ -546,6 +559,16 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
546
559
  s.active = false;
547
560
  clearUnitTimeout();
548
561
 
562
+ // Clear crash lock and release session lock so the next `/gsd next` does
563
+ // not see a stale lock with the current PID and treat it as a "remote"
564
+ // session (which would cause it to SIGTERM itself). (#2730)
565
+ try {
566
+ if (lockBase()) clearLock(lockBase());
567
+ if (lockBase()) releaseSessionLock(lockBase());
568
+ } catch {
569
+ /* best-effort — mirror stopAuto cleanup */
570
+ }
571
+
549
572
  ctx.ui.setStatus("gsd-auto", undefined);
550
573
  ctx.ui.setWidget("gsd-progress", undefined);
551
574
  ctx.ui.setFooter(undefined);
@@ -798,11 +821,14 @@ export async function stopAuto(
798
821
  export async function pauseAuto(
799
822
  ctx?: ExtensionContext,
800
823
  _pi?: ExtensionAPI,
824
+ _errorContext?: ErrorContext,
801
825
  ): Promise<void> {
802
826
  if (!s.active) return;
803
827
  clearUnitTimeout();
804
828
  // Unblock any pending unit promise so the auto-loop is not orphaned.
805
- resolveAgentEndCancelled();
829
+ // Pass errorContext so runUnitPhase can distinguish user-initiated pause
830
+ // from provider-error pause and avoid hard-stopping (#2762).
831
+ resolveAgentEndCancelled(_errorContext);
806
832
 
807
833
  s.pausedSessionFile = ctx?.sessionManager?.getSessionFile() ?? null;
808
834
 
@@ -1102,6 +1128,7 @@ export async function startAuto(
1102
1128
  s.stepMode = requestedStepMode;
1103
1129
  s.cmdCtx = ctx;
1104
1130
  s.basePath = base;
1131
+ setLogBasePath(base);
1105
1132
  s.unitDispatchCount.clear();
1106
1133
  s.unitLifetimeDispatches.clear();
1107
1134
  if (!getLedger()) initMetrics(base);
@@ -2,14 +2,56 @@ import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
2
2
 
3
3
  import { checkAutoStartAfterDiscuss } from "../guided-flow.js";
4
4
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto } from "../auto.js";
5
- import { getNextFallbackModel, isTransientNetworkError, resolveModelWithFallbacksForUnit } from "../preferences.js";
6
- import { classifyProviderError, pauseAutoForProviderError } from "../provider-error-pause.js";
5
+ import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
6
+ import { pauseAutoForProviderError } from "../provider-error-pause.js";
7
7
  import { isSessionSwitchInFlight, resolveAgentEnd } from "../auto-loop.js";
8
+ import { resolveModelId } from "../auto-model-selection.js";
8
9
  import { clearDiscussionFlowState } from "./write-gate.js";
10
+ import {
11
+ classifyError,
12
+ createRetryState,
13
+ resetRetryState,
14
+ isTransient,
15
+ type ErrorClass,
16
+ } from "../error-classifier.js";
9
17
 
10
- const networkRetryCounters = new Map<string, number>();
18
+ const retryState = createRetryState();
19
+ const MAX_NETWORK_RETRIES = 2;
11
20
  const MAX_TRANSIENT_AUTO_RESUMES = 3;
12
- let consecutiveTransientErrors = 0;
21
+
22
+ async function pauseTransientWithBackoff(
23
+ cls: ErrorClass,
24
+ pi: ExtensionAPI,
25
+ ctx: ExtensionContext,
26
+ errorDetail: string,
27
+ isRateLimit: boolean,
28
+ ): Promise<void> {
29
+ retryState.consecutiveTransientCount += 1;
30
+ const baseRetryAfterMs = "retryAfterMs" in cls ? cls.retryAfterMs : 15_000;
31
+ const retryAfterMs = baseRetryAfterMs * 2 ** Math.max(0, retryState.consecutiveTransientCount - 1);
32
+ const allowAutoResume = retryState.consecutiveTransientCount <= MAX_TRANSIENT_AUTO_RESUMES;
33
+ if (!allowAutoResume) {
34
+ ctx.ui.notify(`Transient provider errors persisted after ${MAX_TRANSIENT_AUTO_RESUMES} auto-resume attempts. Pausing for manual review.`, "warning");
35
+ }
36
+ await pauseAutoForProviderError(ctx.ui, errorDetail, () => pauseAuto(ctx, pi, {
37
+ message: `Provider error: ${errorDetail}`,
38
+ category: "provider",
39
+ isTransient: allowAutoResume,
40
+ retryAfterMs,
41
+ }), {
42
+ isRateLimit,
43
+ isTransient: allowAutoResume,
44
+ retryAfterMs,
45
+ resume: allowAutoResume
46
+ ? () => {
47
+ pi.sendMessage(
48
+ { customType: "gsd-auto-timeout-recovery", content: "Continue execution — provider error recovery delay elapsed.", display: false },
49
+ { triggerTurn: true },
50
+ );
51
+ }
52
+ : undefined,
53
+ });
54
+ }
13
55
 
14
56
  export async function handleAgentEnd(
15
57
  pi: ExtensionAPI,
@@ -31,17 +73,26 @@ export async function handleAgentEnd(
31
73
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
32
74
  const errorDetail = "errorMessage" in lastMsg && lastMsg.errorMessage ? `: ${lastMsg.errorMessage}` : "";
33
75
  const errorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
76
+ const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
77
+
78
+ // ── 1. Classify ──────────────────────────────────────────────────────
79
+ const cls = classifyError(errorMsg, explicitRetryAfterMs);
34
80
 
35
- if (isTransientNetworkError(errorMsg)) {
81
+ // ── 2. Decide & Act ──────────────────────────────────────────────────
82
+
83
+ // --- Network errors: same-model retry with backoff ---
84
+ if (cls.kind === "network") {
36
85
  const currentModelId = ctx.model?.id ?? "unknown";
37
- const retryKey = `network-retry:${currentModelId}`;
38
- const currentRetries = networkRetryCounters.get(retryKey) ?? 0;
39
- const maxRetries = 2;
40
- if (currentRetries < maxRetries) {
41
- networkRetryCounters.set(retryKey, currentRetries + 1);
42
- const attempt = currentRetries + 1;
43
- const delayMs = attempt * 3000;
44
- ctx.ui.notify(`Network error on ${currentModelId}${errorDetail}. Retry ${attempt}/${maxRetries} in ${delayMs / 1000}s...`, "warning");
86
+ if (retryState.currentRetryModelId !== currentModelId) {
87
+ retryState.networkRetryCount = 0;
88
+ retryState.currentRetryModelId = currentModelId;
89
+ }
90
+ if (retryState.networkRetryCount < MAX_NETWORK_RETRIES) {
91
+ retryState.networkRetryCount += 1;
92
+ retryState.consecutiveTransientCount += 1;
93
+ const attempt = retryState.networkRetryCount;
94
+ const delayMs = attempt * cls.retryAfterMs;
95
+ ctx.ui.notify(`Network error on ${currentModelId}${errorDetail}. Retry ${attempt}/${MAX_NETWORK_RETRIES} in ${delayMs / 1000}s...`, "warning");
45
96
  setTimeout(() => {
46
97
  pi.sendMessage(
47
98
  { customType: "gsd-auto-timeout-recovery", content: "Continue execution — retrying after transient network error.", display: false },
@@ -50,84 +101,79 @@ export async function handleAgentEnd(
50
101
  }, delayMs);
51
102
  return;
52
103
  }
53
- networkRetryCounters.delete(retryKey);
104
+ // Network retries exhausted — fall through to model fallback
105
+ retryState.networkRetryCount = 0;
106
+ retryState.currentRetryModelId = undefined;
54
107
  ctx.ui.notify(`Network retries exhausted for ${currentModelId}. Attempting model fallback.`, "warning");
55
108
  }
56
109
 
57
- const dash = getAutoDashboardData();
58
- if (dash.currentUnit) {
59
- const modelConfig = resolveModelWithFallbacksForUnit(dash.currentUnit.type);
60
- if (modelConfig && modelConfig.fallbacks.length > 0) {
61
- const availableModels = ctx.modelRegistry.getAvailable();
62
- const nextModelId = getNextFallbackModel(ctx.model?.id, modelConfig);
63
- if (nextModelId) {
64
- networkRetryCounters.clear();
65
- const slashIdx = nextModelId.indexOf("/");
66
- const modelToSet = slashIdx !== -1
67
- ? availableModels.find((m) => m.provider.toLowerCase() === nextModelId.substring(0, slashIdx).toLowerCase() && m.id.toLowerCase() === nextModelId.substring(slashIdx + 1).toLowerCase())
68
- : (availableModels.find((m) => m.id === nextModelId && m.provider === ctx.model?.provider) ?? availableModels.find((m) => m.id === nextModelId));
69
- if (modelToSet) {
70
- const ok = await pi.setModel(modelToSet, { persist: false });
71
- if (ok) {
72
- ctx.ui.notify(`Model error${errorDetail}. Switched to fallback: ${nextModelId} and resuming.`, "warning");
73
- pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
74
- return;
110
+ // --- Transient errors: try model fallback first, then pause ---
111
+ // Rate limits are often per-model, so switching models can bypass them.
112
+ if (cls.kind === "rate-limit" || cls.kind === "network" || cls.kind === "server" || cls.kind === "connection" || cls.kind === "stream") {
113
+ // Try model fallback
114
+ const dash = getAutoDashboardData();
115
+ if (dash.currentUnit) {
116
+ const modelConfig = resolveModelWithFallbacksForUnit(dash.currentUnit.type);
117
+ if (modelConfig && modelConfig.fallbacks.length > 0) {
118
+ const availableModels = ctx.modelRegistry.getAvailable();
119
+ const nextModelId = getNextFallbackModel(ctx.model?.id, modelConfig);
120
+ if (nextModelId) {
121
+ retryState.networkRetryCount = 0;
122
+ retryState.currentRetryModelId = undefined;
123
+ const modelToSet = resolveModelId(nextModelId, availableModels, ctx.model?.provider);
124
+ if (modelToSet) {
125
+ const ok = await pi.setModel(modelToSet, { persist: false });
126
+ if (ok) {
127
+ ctx.ui.notify(`Model error${errorDetail}. Switched to fallback: ${nextModelId} and resuming.`, "warning");
128
+ pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
129
+ return;
130
+ }
75
131
  }
76
132
  }
77
133
  }
78
134
  }
79
- }
80
135
 
81
- const sessionModel = getAutoModeStartModel();
82
- if (sessionModel) {
83
- if (ctx.model?.id !== sessionModel.id || ctx.model?.provider !== sessionModel.provider) {
84
- const startModel = ctx.modelRegistry.getAvailable().find((m) => m.provider === sessionModel.provider && m.id === sessionModel.id);
85
- if (startModel) {
86
- const ok = await pi.setModel(startModel, { persist: false });
87
- if (ok) {
88
- networkRetryCounters.clear();
89
- ctx.ui.notify(`Model error${errorDetail}. Restored session model: ${sessionModel.provider}/${sessionModel.id} and resuming.`, "warning");
90
- pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
91
- return;
136
+ // Try restoring session model
137
+ const sessionModel = getAutoModeStartModel();
138
+ if (sessionModel) {
139
+ if (ctx.model?.id !== sessionModel.id || ctx.model?.provider !== sessionModel.provider) {
140
+ const startModel = ctx.modelRegistry.getAvailable().find((m) => m.provider === sessionModel.provider && m.id === sessionModel.id);
141
+ if (startModel) {
142
+ const ok = await pi.setModel(startModel, { persist: false });
143
+ if (ok) {
144
+ retryState.networkRetryCount = 0;
145
+ retryState.currentRetryModelId = undefined;
146
+ ctx.ui.notify(`Model error${errorDetail}. Restored session model: ${sessionModel.provider}/${sessionModel.id} and resuming.`, "warning");
147
+ pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
148
+ return;
149
+ }
92
150
  }
93
151
  }
94
152
  }
95
153
  }
96
154
 
97
- const classification = classifyProviderError(errorMsg);
98
- const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
99
- if (classification.isTransient) {
100
- consecutiveTransientErrors += 1;
101
- } else {
102
- consecutiveTransientErrors = 0;
103
- }
104
- const baseRetryAfterMs = explicitRetryAfterMs ?? classification.suggestedDelayMs;
105
- const retryAfterMs = classification.isTransient
106
- ? baseRetryAfterMs * 2 ** Math.max(0, consecutiveTransientErrors - 1)
107
- : baseRetryAfterMs;
108
- const allowAutoResume = classification.isTransient && consecutiveTransientErrors <= MAX_TRANSIENT_AUTO_RESUMES;
109
- if (classification.isTransient && !allowAutoResume) {
110
- ctx.ui.notify(`Transient provider errors persisted after ${MAX_TRANSIENT_AUTO_RESUMES} auto-resume attempts. Pausing for manual review.`, "warning");
155
+ // --- Transient fallback: pause with auto-resume ---
156
+ if (isTransient(cls)) {
157
+ await pauseTransientWithBackoff(cls, pi, ctx, errorDetail, cls.kind === "rate-limit");
158
+ return;
111
159
  }
112
- await pauseAutoForProviderError(ctx.ui, errorDetail, () => pauseAuto(ctx, pi), {
113
- isRateLimit: classification.isRateLimit,
114
- isTransient: allowAutoResume,
115
- retryAfterMs,
116
- resume: allowAutoResume
117
- ? () => {
118
- pi.sendMessage(
119
- { customType: "gsd-auto-timeout-recovery", content: "Continue execution — provider error recovery delay elapsed.", display: false },
120
- { triggerTurn: true },
121
- );
122
- }
123
- : undefined,
160
+
161
+ // --- Permanent / unknown: pause indefinitely ---
162
+ await pauseAutoForProviderError(ctx.ui, errorDetail, () => pauseAuto(ctx, pi, {
163
+ message: `Provider error: ${errorDetail}`,
164
+ category: "provider",
165
+ isTransient: false,
166
+ }), {
167
+ isRateLimit: false,
168
+ isTransient: false,
169
+ retryAfterMs: 0,
124
170
  });
125
171
  return;
126
172
  }
127
173
 
174
+ // ── Success path ─────────────────────────────────────────────────────────
128
175
  try {
129
- consecutiveTransientErrors = 0;
130
- networkRetryCounters.clear();
176
+ resetRetryState(retryState);
131
177
  resolveAgentEnd(event);
132
178
  } catch (err) {
133
179
  const message = err instanceof Error ? err.message : String(err);
@@ -139,4 +185,3 @@ export async function handleAgentEnd(
139
185
  }
140
186
  }
141
187
  }
142
-
@@ -5,6 +5,7 @@ import type { ExtensionAPI } from "@gsd/pi-coding-agent";
5
5
  import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
6
6
 
7
7
  import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
8
+ import { setLogBasePath } from "../workflow-logger.js";
8
9
 
9
10
  /**
10
11
  * Resolve the correct DB path for the current working directory.
@@ -43,9 +44,14 @@ export async function ensureDbOpen(): Promise<boolean> {
43
44
  const dbPath = resolveProjectRootDbPath(basePath);
44
45
  const gsdDir = join(basePath, ".gsd");
45
46
 
47
+ // Derive the project root from the DB path (strip .gsd/gsd.db)
48
+ const projectRoot = join(dbPath, "..", "..");
49
+
46
50
  // Open existing DB file (may be at project root for worktrees)
47
51
  if (existsSync(dbPath)) {
48
- return db.openDatabase(dbPath);
52
+ const opened = db.openDatabase(dbPath);
53
+ if (opened) setLogBasePath(projectRoot);
54
+ return opened;
49
55
  }
50
56
 
51
57
  // No DB file — create + migrate from Markdown if .gsd/ has content
@@ -56,6 +62,7 @@ export async function ensureDbOpen(): Promise<boolean> {
56
62
  if (hasDecisions || hasRequirements || hasMilestones) {
57
63
  const opened = db.openDatabase(dbPath);
58
64
  if (opened) {
65
+ setLogBasePath(projectRoot);
59
66
  try {
60
67
  const { migrateFromMarkdown } = await import("../md-importer.js");
61
68
  migrateFromMarkdown(basePath);
@@ -69,7 +76,9 @@ export async function ensureDbOpen(): Promise<boolean> {
69
76
  }
70
77
 
71
78
  // .gsd/ exists but has no Markdown content (fresh project) — create empty DB
72
- return db.openDatabase(dbPath);
79
+ const opened = db.openDatabase(dbPath);
80
+ if (opened) setLogBasePath(projectRoot);
81
+ return opened;
73
82
  }
74
83
 
75
84
  return false;
@@ -245,7 +245,7 @@ export function registerHooks(pi: ExtensionAPI): void {
245
245
 
246
246
  pi.on("tool_execution_start", async (event) => {
247
247
  if (!isAutoActive()) return;
248
- markToolStart(event.toolCallId);
248
+ markToolStart(event.toolCallId, event.toolName);
249
249
  });
250
250
 
251
251
  pi.on("tool_execution_end", async (event) => {