@opengsd/gsd-pi 1.2.0-dev.5457a158 → 1.2.0-dev.822c9439

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 (489) hide show
  1. package/dist/headless-events.js +7 -5
  2. package/dist/mcp-server.js +2 -1
  3. package/dist/resource-loader.d.ts +9 -5
  4. package/dist/resource-loader.js +114 -6
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/GSD-WORKFLOW.md +5 -4
  7. package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
  8. package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
  9. package/dist/resources/extensions/async-jobs/index.js +65 -0
  10. package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
  11. package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
  12. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
  13. package/dist/resources/extensions/bg-shell/overlay.js +9 -6
  14. package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
  15. package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
  16. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
  17. package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
  18. package/dist/resources/extensions/browser-tools/index.js +69 -12
  19. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +3 -2
  20. package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +17 -2
  21. package/dist/resources/extensions/gsd/auto/detect-stuck.js +33 -13
  22. package/dist/resources/extensions/gsd/auto/dispatch-history.js +105 -0
  23. package/dist/resources/extensions/gsd/auto/dispatch-key.js +37 -0
  24. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  25. package/dist/resources/extensions/gsd/auto/orchestrator.js +89 -54
  26. package/dist/resources/extensions/gsd/auto/phases.js +49 -6
  27. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  28. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -34
  29. package/dist/resources/extensions/gsd/auto-dispatch.js +50 -58
  30. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -13
  31. package/dist/resources/extensions/gsd/auto-post-unit.js +30 -12
  32. package/dist/resources/extensions/gsd/auto-prompts.js +78 -19
  33. package/dist/resources/extensions/gsd/auto-start.js +12 -12
  34. package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
  35. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +5 -4
  36. package/dist/resources/extensions/gsd/auto-verification.js +23 -30
  37. package/dist/resources/extensions/gsd/auto-worktree.js +14 -1
  38. package/dist/resources/extensions/gsd/auto.js +37 -1
  39. package/dist/resources/extensions/gsd/blocked-models.js +28 -0
  40. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +26 -6
  41. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +23 -6
  42. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
  43. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
  44. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -59
  45. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +302 -80
  46. package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
  47. package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
  48. package/dist/resources/extensions/gsd/closeout-wizard.js +92 -0
  49. package/dist/resources/extensions/gsd/commands/context.js +16 -2
  50. package/dist/resources/extensions/gsd/commands-handlers.js +46 -3
  51. package/dist/resources/extensions/gsd/consent-question.js +353 -0
  52. package/dist/resources/extensions/gsd/consent-verdict.js +63 -0
  53. package/dist/resources/extensions/gsd/constants.js +0 -2
  54. package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
  55. package/dist/resources/extensions/gsd/db/queries.js +26 -0
  56. package/dist/resources/extensions/gsd/dispatch-guard.js +10 -35
  57. package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
  58. package/dist/resources/extensions/gsd/doctor-git-checks.js +2 -18
  59. package/dist/resources/extensions/gsd/engine-hook-contract.js +70 -0
  60. package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
  61. package/dist/resources/extensions/gsd/files.js +33 -19
  62. package/dist/resources/extensions/gsd/gsd-command-home.js +22 -12
  63. package/dist/resources/extensions/gsd/gsd-db.js +2 -1
  64. package/dist/resources/extensions/gsd/guidance.js +60 -0
  65. package/dist/resources/extensions/gsd/guided-flow.js +6 -3
  66. package/dist/resources/extensions/gsd/markdown-renderer.js +10 -0
  67. package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
  68. package/dist/resources/extensions/gsd/milestone-closeout.js +85 -24
  69. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
  70. package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
  71. package/dist/resources/extensions/gsd/parsers-legacy.js +16 -4
  72. package/dist/resources/extensions/gsd/preferences-models.js +2 -2
  73. package/dist/resources/extensions/gsd/projection-flush.js +7 -0
  74. package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  75. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  76. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  77. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  78. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
  79. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  80. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  81. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  82. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  83. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  84. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  85. package/dist/resources/extensions/gsd/prompts/run-uat.md +7 -5
  86. package/dist/resources/extensions/gsd/prompts/system.md +5 -2
  87. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  88. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  89. package/dist/resources/extensions/gsd/reactive-graph.js +8 -1
  90. package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
  91. package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
  92. package/dist/resources/extensions/gsd/session-lock.js +1 -1
  93. package/dist/resources/extensions/gsd/state.js +5 -0
  94. package/dist/resources/extensions/gsd/tool-contract.js +14 -3
  95. package/dist/resources/extensions/gsd/tool-presentation-plan.js +4 -4
  96. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  97. package/dist/resources/extensions/gsd/tools/complete-slice.js +22 -12
  98. package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
  99. package/dist/resources/extensions/gsd/tools/exec-tool.js +5 -0
  100. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
  101. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
  102. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
  103. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  104. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
  105. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
  106. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
  107. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +67 -2
  108. package/dist/resources/extensions/gsd/uat-policy.js +42 -16
  109. package/dist/resources/extensions/gsd/unit-context-composer.js +65 -0
  110. package/dist/resources/extensions/gsd/unit-registry.js +7 -20
  111. package/dist/resources/extensions/gsd/verdict-parser.js +1 -1
  112. package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
  113. package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
  114. package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
  115. package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
  116. package/dist/resources/extensions/gsd/workflow-events.js +6 -18
  117. package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
  118. package/dist/resources/extensions/gsd/worktree-lifecycle.js +3 -2
  119. package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
  120. package/dist/resources/extensions/gsd/worktree.js +8 -1
  121. package/dist/resources/extensions/search-the-web/native-search.js +5 -3
  122. package/dist/resources/extensions/shared/browser-contract.js +59 -0
  123. package/dist/resources/extensions/shared/gsd-browser-cli.js +116 -6
  124. package/dist/resources/shared/gsd-browser-path-sync.js +214 -0
  125. package/dist/resources/shared/package-manager-detection.js +1 -1
  126. package/dist/resources/shared/package.json +3 -0
  127. package/dist/resources/skills/create-skill/SKILL.md +3 -0
  128. package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
  129. package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
  130. package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
  131. package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
  132. package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
  133. package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  134. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  135. package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  136. package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
  137. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  138. package/dist/update-check.d.ts +2 -0
  139. package/dist/update-check.js +24 -1
  140. package/dist/update-cmd.js +20 -3
  141. package/dist/web/standalone/.next/BUILD_ID +1 -1
  142. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  143. package/dist/web/standalone/.next/build-manifest.json +3 -3
  144. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  145. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  146. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  147. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  155. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  156. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  157. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  158. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  159. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  160. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  161. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/index.html +1 -1
  165. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  168. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  169. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  170. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  171. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  172. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  173. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  176. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  177. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  178. package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
  179. package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
  180. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  181. package/dist/web/standalone/package.json +1 -1
  182. package/package.json +1 -1
  183. package/packages/cloud-mcp-gateway/package.json +2 -2
  184. package/packages/contracts/dist/rpc.d.ts +1 -0
  185. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  186. package/packages/contracts/dist/rpc.js.map +1 -1
  187. package/packages/contracts/package.json +1 -1
  188. package/packages/daemon/package.json +4 -4
  189. package/packages/gsd-agent-core/package.json +5 -5
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  194. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  198. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  201. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
  202. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  203. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  204. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
  205. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  206. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  207. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
  208. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  209. package/packages/gsd-agent-modes/package.json +7 -7
  210. package/packages/mcp-server/dist/cli.js +10 -5
  211. package/packages/mcp-server/dist/cli.js.map +1 -1
  212. package/packages/mcp-server/dist/moonshot-tool-schema.d.ts +29 -0
  213. package/packages/mcp-server/dist/moonshot-tool-schema.d.ts.map +1 -0
  214. package/packages/mcp-server/dist/moonshot-tool-schema.js +50 -0
  215. package/packages/mcp-server/dist/moonshot-tool-schema.js.map +1 -0
  216. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  217. package/packages/mcp-server/dist/server.js +4 -0
  218. package/packages/mcp-server/dist/server.js.map +1 -1
  219. package/packages/mcp-server/dist/workflow-tools.d.ts +18 -18
  220. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  221. package/packages/mcp-server/dist/workflow-tools.js +99 -38
  222. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  223. package/packages/mcp-server/package.json +5 -4
  224. package/packages/native/package.json +1 -1
  225. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
  226. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  227. package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
  228. package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  229. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  230. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  231. package/packages/pi-agent-core/dist/index.js +3 -0
  232. package/packages/pi-agent-core/dist/index.js.map +1 -1
  233. package/packages/pi-agent-core/package.json +1 -1
  234. package/packages/pi-ai/README.md +1 -0
  235. package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
  236. package/packages/pi-ai/dist/image-models.generated.js +6 -6
  237. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  238. package/packages/pi-ai/dist/index.d.ts +2 -0
  239. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  240. package/packages/pi-ai/dist/index.js +2 -0
  241. package/packages/pi-ai/dist/index.js.map +1 -1
  242. package/packages/pi-ai/dist/models.generated.d.ts +239 -153
  243. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  244. package/packages/pi-ai/dist/models.generated.js +256 -145
  245. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  246. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  247. package/packages/pi-ai/dist/providers/anthropic.js +12 -7
  248. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  249. package/packages/pi-ai/dist/providers/google-shared.d.ts +5 -0
  250. package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
  251. package/packages/pi-ai/dist/providers/google-shared.js +12 -3
  252. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
  253. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  254. package/packages/pi-ai/dist/providers/openai-completions.js +7 -3
  255. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  256. package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts +9 -0
  257. package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts.map +1 -0
  258. package/packages/pi-ai/dist/utils/moonshot-tool-schema.js +34 -0
  259. package/packages/pi-ai/dist/utils/moonshot-tool-schema.js.map +1 -0
  260. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  261. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +6 -2
  262. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  263. package/packages/pi-ai/package.json +3 -2
  264. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
  265. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
  267. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
  269. package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
  270. package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
  272. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
  274. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  275. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  276. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  277. package/packages/pi-coding-agent/dist/index.js +1 -1
  278. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
  280. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  281. package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
  282. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  283. package/packages/pi-coding-agent/package.json +7 -7
  284. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  285. package/packages/pi-tui/dist/tui.js +9 -0
  286. package/packages/pi-tui/dist/tui.js.map +1 -1
  287. package/packages/pi-tui/package.json +2 -2
  288. package/packages/rpc-client/package.json +2 -2
  289. package/pkg/package.json +1 -1
  290. package/src/resources/GSD-WORKFLOW.md +5 -4
  291. package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
  292. package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
  293. package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
  294. package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
  295. package/src/resources/extensions/async-jobs/index.ts +79 -0
  296. package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
  297. package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
  298. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
  299. package/src/resources/extensions/bg-shell/overlay.ts +9 -5
  300. package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
  301. package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
  302. package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
  303. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
  304. package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
  305. package/src/resources/extensions/browser-tools/index.ts +71 -13
  306. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
  307. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +40 -1
  308. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
  309. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +3 -2
  310. package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +21 -3
  311. package/src/resources/extensions/gsd/auto/detect-stuck.ts +32 -9
  312. package/src/resources/extensions/gsd/auto/dispatch-history.ts +152 -0
  313. package/src/resources/extensions/gsd/auto/dispatch-key.ts +39 -0
  314. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  315. package/src/resources/extensions/gsd/auto/orchestrator.ts +98 -56
  316. package/src/resources/extensions/gsd/auto/phases.ts +65 -26
  317. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  318. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +18 -48
  319. package/src/resources/extensions/gsd/auto-dispatch.ts +48 -61
  320. package/src/resources/extensions/gsd/auto-model-selection.ts +41 -12
  321. package/src/resources/extensions/gsd/auto-post-unit.ts +33 -12
  322. package/src/resources/extensions/gsd/auto-prompts.ts +115 -35
  323. package/src/resources/extensions/gsd/auto-start.ts +12 -14
  324. package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
  325. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +4 -4
  326. package/src/resources/extensions/gsd/auto-verification.ts +26 -28
  327. package/src/resources/extensions/gsd/auto-worktree.ts +14 -1
  328. package/src/resources/extensions/gsd/auto.ts +44 -1
  329. package/src/resources/extensions/gsd/blocked-models.ts +49 -0
  330. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -5
  331. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +23 -6
  332. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
  333. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
  334. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +211 -59
  335. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +350 -86
  336. package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
  337. package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
  338. package/src/resources/extensions/gsd/closeout-wizard.ts +102 -0
  339. package/src/resources/extensions/gsd/commands/context.ts +16 -2
  340. package/src/resources/extensions/gsd/commands-handlers.ts +46 -3
  341. package/src/resources/extensions/gsd/consent-question.ts +431 -0
  342. package/src/resources/extensions/gsd/consent-verdict.ts +86 -0
  343. package/src/resources/extensions/gsd/constants.ts +0 -3
  344. package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
  345. package/src/resources/extensions/gsd/db/queries.ts +37 -0
  346. package/src/resources/extensions/gsd/dispatch-guard.ts +8 -31
  347. package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
  348. package/src/resources/extensions/gsd/doctor-git-checks.ts +2 -19
  349. package/src/resources/extensions/gsd/engine-hook-contract.ts +79 -0
  350. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  351. package/src/resources/extensions/gsd/files.ts +33 -12
  352. package/src/resources/extensions/gsd/gsd-command-home.ts +13 -3
  353. package/src/resources/extensions/gsd/gsd-db.ts +4 -3
  354. package/src/resources/extensions/gsd/guidance.ts +78 -0
  355. package/src/resources/extensions/gsd/guided-flow.ts +21 -26
  356. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -0
  357. package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
  358. package/src/resources/extensions/gsd/milestone-closeout.ts +109 -24
  359. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
  360. package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
  361. package/src/resources/extensions/gsd/parsers-legacy.ts +16 -4
  362. package/src/resources/extensions/gsd/preferences-models.ts +2 -1
  363. package/src/resources/extensions/gsd/projection-flush.ts +20 -0
  364. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  365. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  366. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  367. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  368. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
  369. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  370. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  371. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  372. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  373. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  374. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  375. package/src/resources/extensions/gsd/prompts/run-uat.md +7 -5
  376. package/src/resources/extensions/gsd/prompts/system.md +5 -2
  377. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  378. package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  379. package/src/resources/extensions/gsd/reactive-graph.ts +11 -1
  380. package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
  381. package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
  382. package/src/resources/extensions/gsd/session-lock.ts +1 -1
  383. package/src/resources/extensions/gsd/state.ts +5 -0
  384. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +97 -1
  385. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +198 -26
  386. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
  387. package/src/resources/extensions/gsd/tests/blocked-models.test.ts +19 -0
  388. package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
  389. package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
  390. package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
  391. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  392. package/src/resources/extensions/gsd/tests/consent-question.test.ts +351 -0
  393. package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +67 -0
  394. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +10 -10
  395. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
  396. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
  397. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +273 -0
  398. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
  399. package/src/resources/extensions/gsd/tests/doctor-git-checks-terminal.test.ts +73 -0
  400. package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
  401. package/src/resources/extensions/gsd/tests/engine-hook-contract.test.ts +148 -0
  402. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
  403. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
  404. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
  405. package/src/resources/extensions/gsd/tests/gsd-command-home.test.ts +120 -0
  406. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +27 -0
  407. package/src/resources/extensions/gsd/tests/guidance.test.ts +23 -0
  408. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +2 -6
  409. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +7 -11
  410. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +20 -58
  411. package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
  412. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +199 -0
  413. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
  414. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +95 -4
  415. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
  416. package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
  417. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +18 -0
  418. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +138 -0
  419. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
  420. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
  421. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +124 -6
  422. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
  423. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +19 -1
  424. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
  425. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
  426. package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
  427. package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
  428. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +112 -29
  429. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +44 -0
  430. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
  431. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
  432. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
  433. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
  434. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
  435. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +273 -38
  436. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
  437. package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
  438. package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +358 -0
  439. package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -1
  440. package/src/resources/extensions/gsd/tool-contract.ts +38 -3
  441. package/src/resources/extensions/gsd/tool-presentation-plan.ts +4 -4
  442. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  443. package/src/resources/extensions/gsd/tools/complete-slice.ts +22 -12
  444. package/src/resources/extensions/gsd/tools/complete-task.ts +3 -2
  445. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -0
  446. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
  447. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
  448. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
  449. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  450. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
  451. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
  452. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
  453. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +81 -2
  454. package/src/resources/extensions/gsd/uat-policy.ts +62 -16
  455. package/src/resources/extensions/gsd/unit-context-composer.ts +99 -0
  456. package/src/resources/extensions/gsd/unit-registry.ts +7 -20
  457. package/src/resources/extensions/gsd/verdict-parser.ts +1 -1
  458. package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
  459. package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
  460. package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
  461. package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
  462. package/src/resources/extensions/gsd/workflow-events.ts +12 -20
  463. package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
  464. package/src/resources/extensions/gsd/worktree-lifecycle.ts +3 -8
  465. package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
  466. package/src/resources/extensions/gsd/worktree.ts +7 -1
  467. package/src/resources/extensions/search-the-web/native-search.ts +5 -3
  468. package/src/resources/extensions/shared/browser-contract.ts +66 -0
  469. package/src/resources/extensions/shared/gsd-browser-cli.ts +141 -6
  470. package/src/resources/shared/gsd-browser-path-sync.ts +273 -0
  471. package/src/resources/shared/package-manager-detection.ts +1 -1
  472. package/src/resources/shared/package.json +3 -0
  473. package/src/resources/skills/create-skill/SKILL.md +3 -0
  474. package/src/resources/skills/create-skill/references/executable-code.md +1 -1
  475. package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
  476. package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
  477. package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
  478. package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
  479. package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  480. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  481. package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  482. package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
  483. package/dist/resources/extensions/gsd/user-input-boundary.js +0 -218
  484. package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
  485. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +0 -173
  486. package/src/resources/extensions/gsd/user-input-boundary.ts +0 -216
  487. package/src/resources/skills/gsd-browser/SKILL.md +0 -41
  488. /package/dist/web/standalone/.next/static/{2p9Rv9pQflAxCBbGVI2vb → yWwBo-w09Y_W-nmeeWFRp}/_buildManifest.js +0 -0
  489. /package/dist/web/standalone/.next/static/{2p9Rv9pQflAxCBbGVI2vb → yWwBo-w09Y_W-nmeeWFRp}/_ssgManifest.js +0 -0
@@ -18,6 +18,7 @@ import { ensureGsdSymlink, isInheritedRepo, validateProjectId } from "./repo-ide
18
18
  import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
19
19
  import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
20
20
  import { gsdRoot, resolveMilestoneFile } from "./paths.js";
21
+ import { milestoneEntryBlockedGuidance } from "./guidance.js";
21
22
  import { invalidateAllCaches } from "./cache.js";
22
23
  import { writeLock, clearLock, readCrashLock, isLockProcessAlive } from "./crash-recovery.js";
23
24
  import { acquireSessionLock, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
@@ -741,12 +742,14 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
741
742
  // phase-specific planning model for a discuss turn (#2829).
742
743
  //
743
744
  // Precedence:
744
- // 1) Explicit session override via /gsd model (this session)
745
- // 2) Current session model from settings/session restore (if provider ready)
746
- // 3) GSD model preferences from PREFERENCES.md (validated against live auth)
745
+ // 1) Explicit session override via /gsd model or /gsd auto --model (this session)
746
+ // 2) GSD model preferences from PREFERENCES.md (validated against live auth)
747
+ // 3) Current session model from settings/session restore (if provider ready)
747
748
  //
748
- // This preserves #3517 defaults while honoring explicit runtime model
749
- // selection for subsequent /gsd runs in the same session.
749
+ // PREFERENCES.md wins over the ambient session default (#3517) so /gsd auto
750
+ // does not stick on claude-code/claude-sonnet-4-6 when the user configured
751
+ // models via /gsd workflow-preferences or PREFERENCES.md. Custom providers
752
+ // still skip PREFERENCES.md entirely (#4122).
750
753
  //
751
754
  // Exception (#4122): when the session provider is a custom provider declared
752
755
  // in ~/.gsd/agent/models.json (Ollama, vLLM, OpenAI-compatible proxy, etc.),
@@ -758,7 +761,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
758
761
  const sessionProviderIsCustom = isCustomProvider(ctx.model?.provider);
759
762
  const preferredModel = sessionProviderIsCustom
760
763
  ? null
761
- : resolveDefaultSessionModel(ctx.model?.provider);
764
+ : resolveDefaultSessionModel(ctx.model?.provider, base);
762
765
  // Validate the preferred model against the live registry + provider auth so
763
766
  // an unconfigured PREFERENCES.md entry (no API key / OAuth) can't become the
764
767
  // start-model snapshot. Without this, every subsequent unit would try to
@@ -781,8 +784,8 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
781
784
  : null;
782
785
  const startThinkingSnapshot = pi.getThinkingLevel();
783
786
  const startModelSnapshot = manualSessionOverride
784
- ?? currentSessionModel
785
787
  ?? validatedPreferredModel
788
+ ?? currentSessionModel
786
789
  ?? null;
787
790
  try {
788
791
  // Validate GSD_PROJECT_ID early so the user gets immediate feedback
@@ -1270,11 +1273,8 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
1270
1273
  if (enterResult.reason === "lease-conflict") {
1271
1274
  ctx.ui.notify(`Cannot enter milestone ${s.currentMilestoneId}: lease is held by another worker.`, "error");
1272
1275
  }
1273
- else if (enterResult.reason === "creation-failed") {
1274
- ctx.ui.notify(`Cannot enter milestone ${s.currentMilestoneId}: worktree/branch creation failed. Isolation is degraded.`, "error");
1275
- }
1276
- else if (enterResult.reason === "isolation-degraded") {
1277
- ctx.ui.notify(`Cannot enter milestone ${s.currentMilestoneId}: isolation is degraded from a prior worktree failure. Close processes locking the worktree and retry, or run /gsd doctor fix.`, "error");
1276
+ else if (enterResult.reason === "creation-failed" || enterResult.reason === "isolation-degraded") {
1277
+ ctx.ui.notify(milestoneEntryBlockedGuidance(s.currentMilestoneId, enterResult.reason), "error");
1278
1278
  }
1279
1279
  else if (enterResult.reason === "invalid-milestone-id") {
1280
1280
  ctx.ui.notify(`Cannot enter milestone ${s.currentMilestoneId}: milestone id is invalid.`, "error");
@@ -36,17 +36,17 @@ export function isSuspiciousGhostCompletion(ctx, startedAt, maxElapsedMs = GHOST
36
36
  activity.assistantMessages === 0);
37
37
  }
38
38
  /**
39
- * Snapshot metrics, save activity log, and fire-and-forget memory extraction
40
- * for a completed unit. Returns the activity log file path (if any).
39
+ * Snapshot metrics, save activity log, extract memories, and record the git
40
+ * transaction for a completed auto-mode unit.
41
41
  */
42
- export async function closeoutUnit(ctx, basePath, unitType, unitId, startedAt, opts) {
43
- const modelId = ctx.model?.id ?? "unknown";
44
- snapshotUnitMetrics(ctx, unitType, unitId, startedAt, modelId, opts);
45
- const activityFile = saveActivityLog(ctx, basePath, unitType, unitId);
42
+ export async function closeoutAutoUnit(request) {
43
+ const modelId = request.ctx.model?.id ?? "unknown";
44
+ snapshotUnitMetrics(request.ctx, request.unitType, request.unitId, request.startedAt, modelId, request.opts);
45
+ const activityFile = saveActivityLog(request.ctx, request.basePath, request.unitType, request.unitId);
46
46
  if (activityFile) {
47
47
  try {
48
- const { buildMemoryLLMCall, extractMemoriesFromUnit } = await import('./memory-extractor.js');
49
- const llmCallFn = buildMemoryLLMCall(ctx);
48
+ const { buildMemoryLLMCall, extractMemoriesFromUnit } = await import("./memory-extractor.js");
49
+ const llmCallFn = buildMemoryLLMCall(request.ctx);
50
50
  if (llmCallFn) {
51
51
  // Awaited: a fire-and-forget here lets memory-extractor writes land in
52
52
  // .gsd/ after closeoutUnit returns but before the milestone merge
@@ -55,10 +55,10 @@ export async function closeoutUnit(ctx, basePath, unitType, unitId, startedAt, o
55
55
  // bounded by the extractor's LLM call, which is the acceptable price
56
56
  // for not racing the merge boundary.
57
57
  try {
58
- await extractMemoriesFromUnit(activityFile, unitType, unitId, llmCallFn);
58
+ await extractMemoriesFromUnit(activityFile, request.unitType, request.unitId, llmCallFn);
59
59
  }
60
60
  catch (err) {
61
- logWarning("engine", `memory extraction failed for ${unitType}/${unitId}: ${err.message}`);
61
+ logWarning("engine", `memory extraction failed for ${request.unitType}/${request.unitId}: ${err.message}`);
62
62
  }
63
63
  }
64
64
  }
@@ -66,22 +66,46 @@ export async function closeoutUnit(ctx, basePath, unitType, unitId, startedAt, o
66
66
  logWarning("engine", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
67
67
  }
68
68
  }
69
- if (opts?.traceId && opts.turnId && opts.gitAction && opts.gitStatus) {
69
+ const gitTransaction = resolveGitTransactionOptions(request.opts);
70
+ if (gitTransaction) {
70
71
  writeTurnGitTransaction({
71
- basePath,
72
- traceId: opts.traceId,
73
- turnId: opts.turnId,
74
- unitType,
75
- unitId,
72
+ basePath: request.basePath,
73
+ traceId: gitTransaction.traceId,
74
+ turnId: gitTransaction.turnId,
75
+ unitType: request.unitType,
76
+ unitId: request.unitId,
76
77
  stage: "record",
77
- action: opts.gitAction,
78
- push: opts.gitPush === true,
79
- status: opts.gitStatus,
80
- error: opts.gitError,
78
+ action: gitTransaction.gitAction,
79
+ push: gitTransaction.gitPush === true,
80
+ status: gitTransaction.gitStatus,
81
+ error: gitTransaction.gitError,
81
82
  metadata: {
82
83
  activityFile,
83
84
  },
84
85
  });
85
86
  }
86
- return activityFile ?? undefined;
87
+ return {
88
+ ...(activityFile ? { activityFile } : {}),
89
+ gitTransactionRecorded: Boolean(gitTransaction),
90
+ };
91
+ }
92
+ function resolveGitTransactionOptions(opts) {
93
+ if (!opts?.traceId || !opts.turnId || !opts.gitAction || !opts.gitStatus)
94
+ return null;
95
+ return {
96
+ traceId: opts.traceId,
97
+ turnId: opts.turnId,
98
+ gitAction: opts.gitAction,
99
+ gitStatus: opts.gitStatus,
100
+ gitPush: opts.gitPush,
101
+ gitError: opts.gitError,
102
+ };
103
+ }
104
+ /**
105
+ * Compatibility wrapper for existing auto-loop callers. New code should prefer
106
+ * closeoutAutoUnit so the closeout request and result stay explicit.
107
+ */
108
+ export async function closeoutUnit(ctx, basePath, unitType, unitId, startedAt, opts) {
109
+ const result = await closeoutAutoUnit({ ctx, basePath, unitType, unitId, startedAt, opts });
110
+ return result.activityFile;
87
111
  }
@@ -1,6 +1,7 @@
1
1
  import { parseUnitId } from "./unit-id.js";
2
2
  import { AUTO_UNIT_SCOPED_TOOLS, getForbiddenGsdToolReason, } from "./unit-tool-contracts.js";
3
- import { WORKFLOW_TOOL_ALIAS_PAIRS, canonicalWorkflowSurfaceToolName, isWorkflowSurfaceAliasTool, stripMcpToolPrefix, } from "./workflow-tool-surface.js";
3
+ import { WORKFLOW_TOOL_ALIAS_PAIRS, isWorkflowSurfaceAliasTool, stripMcpToolPrefix, } from "./workflow-tool-surface.js";
4
+ import { canonicalWorkflowToolName } from "./engine-hook-contract.js";
4
5
  export { AUTO_UNIT_SCOPED_TOOLS, RUN_UAT_BROWSER_TOOL_NAMES, } from "./unit-tool-contracts.js";
5
6
  // Scope-class membership is declared per unit in the Unit Registry (ADR-033).
6
7
  // EXECUTE_TASK_UNIT_TYPES = scopeClass "execute-task"; the section-close gate
@@ -22,9 +23,9 @@ const SCOPED_GSD_LIFECYCLE_TOOLS = new Set([
22
23
  .map(canonicalWorkflowToolName));
23
24
  export const GSD_PHASE_SCOPE_DISPLAY_REASON = "This GSD phase only allows its scoped workflow tools.";
24
25
  export const GSD_SECTION_CLOSE_GATE_DISPLAY_REASON = "Gates here close by writing summary sections — gsd_save_gate_result isn't needed.";
25
- export function canonicalWorkflowToolName(toolName) {
26
- return canonicalWorkflowSurfaceToolName(toolName);
27
- }
26
+ // Normalizer seam lives in engine-hook-contract.ts; re-exported here for
27
+ // existing scope importers.
28
+ export { canonicalWorkflowToolName };
28
29
  export function isWorkflowAliasTool(toolName) {
29
30
  return isWorkflowSurfaceAliasTool(toolName);
30
31
  }
@@ -1,7 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Post-unit verification gate for GSD auto-mode units.
3
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
4
- import { gsdProjectionRoot, resolveSlicePath, resolveMilestoneFile } from "./paths.js";
4
+ import { gsdProjectionRoot, resolveSlicePath } from "./paths.js";
5
5
  import { resolveMilestoneValidationVerdict } from "./milestone-validation-verdict.js";
6
6
  import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
7
7
  import { parseUnitId } from "./unit-id.js";
@@ -9,8 +9,6 @@ import { isDbAvailable, getTask, getSliceTasks, getMilestoneSlices } from "./gsd
9
9
  import { loadEffectiveGSDPreferences } from "./preferences.js";
10
10
  import { isClosedStatus } from "./status-guards.js";
11
11
  import { loadFile } from "./files.js";
12
- import { parseRoadmap } from "./parsers-legacy.js";
13
- import { isMilestoneComplete } from "./state.js";
14
12
  import { runVerificationGate, runVerificationGateForTargets, formatFailureContext, captureRuntimeErrors, runDependencyAudit, } from "./verification-gate.js";
15
13
  import { writeVerificationJSON } from "./verification-evidence.js";
16
14
  import { logWarning } from "./workflow-logger.js";
@@ -25,6 +23,7 @@ import { getSlice } from "./gsd-db.js";
25
23
  import { getLedger } from "./metrics.js";
26
24
  import { getUnitCostSpikeAction, resolveUnitCostSpikeMultiplier } from "./auto-budget.js";
27
25
  import { formatPostUnitStatusCard } from "./auto-status-message.js";
26
+ import { detectWebApp } from "./web-app-uat.js";
28
27
  function getCurrentUnitCostStats(unitId) {
29
28
  const ledger = getLedger();
30
29
  if (!ledger || !Array.isArray(ledger.units) || ledger.units.length === 0) {
@@ -274,32 +273,15 @@ async function runValidateMilestonePostCheck(vctx, pauseAuto) {
274
273
  * DB-backed projects are authoritative (#4094 peer review); falls back to
275
274
  * roadmap parsing only when the DB is unavailable.
276
275
  */
277
- async function countIncompleteSlices(basePath, milestoneId) {
278
- if (isDbAvailable()) {
279
- const slices = getMilestoneSlices(milestoneId);
280
- if (slices.length === 0) {
281
- // No DB rows — treat as "unknown", do not pause.
282
- return 1;
283
- }
284
- return slices.filter((slice) => !isClosedStatus(slice.status)).length;
285
- }
286
- // Filesystem fallback: parse the roadmap markdown.
287
- try {
288
- const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
289
- if (!roadmapFile)
290
- return 1;
291
- const roadmapContent = await loadFile(roadmapFile);
292
- if (!roadmapContent)
293
- return 1;
294
- const roadmap = parseRoadmap(roadmapContent);
295
- if (roadmap.slices.length === 0)
296
- return 1;
297
- return isMilestoneComplete(roadmap) ? 0 : 1;
298
- }
299
- catch {
300
- // Parsing failures should not cause false-positive pauses.
276
+ async function countIncompleteSlices(_basePath, milestoneId) {
277
+ // DB-authoritative (ADR-017): no markdown fallback. DB unavailable or no
278
+ // rows means "unknown" — do not pause.
279
+ if (!isDbAvailable())
301
280
  return 1;
302
- }
281
+ const slices = getMilestoneSlices(milestoneId);
282
+ if (slices.length === 0)
283
+ return 1;
284
+ return slices.filter((slice) => !isClosedStatus(slice.status)).length;
303
285
  }
304
286
  /**
305
287
  * Run the verification gate for the current execute-task unit.
@@ -636,14 +618,25 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
636
618
  s.pendingVerificationRetry = null;
637
619
  return "continue";
638
620
  }
621
+ else if (verdict.reason === "no-host-checks" &&
622
+ taskAlreadyComplete &&
623
+ detectWebApp(s.basePath) &&
624
+ !result.runtimeErrors?.some((e) => e.blocking)) {
625
+ s.verificationRetryCount.delete(retryKey);
626
+ s.verificationRetryFailureHashes.delete(retryKey);
627
+ s.pendingVerificationRetry = null;
628
+ ctx.ui.notify("No task-level host verification command was found for a completed browser-facing task; continuing so slice UAT can verify the UI with browser tools.", "warning");
629
+ return "continue";
630
+ }
639
631
  else if (verdict.reason === "no-host-checks") {
640
632
  s.verificationRetryCount.delete(retryKey);
641
633
  s.verificationRetryFailureHashes.delete(retryKey);
642
634
  s.pendingVerificationRetry = null;
643
- ctx.ui.notify("Verification gate FAILED no runnable host-owned verification checks were discovered. Pausing for human review.", "error");
635
+ const pauseMessage = `Verification failed: ${verdict.failureContext}`;
636
+ ctx.ui.notify(`Verification gate FAILED — ${verdict.failureContext}`, "error");
644
637
  process.stderr.write(`verification-gate: ${verdict.failureContext}\n`);
645
638
  await pauseAuto(ctx, pi, {
646
- message: "Verification failed: no runnable host-owned verification checks were discovered.",
639
+ message: pauseMessage,
647
640
  category: "unknown",
648
641
  });
649
642
  return "pause";
@@ -800,6 +800,14 @@ function _resolveIntegrationBranchForReuse(basePath, milestoneId) {
800
800
  return null;
801
801
  }
802
802
  }
803
+ function safeCwd(fallback) {
804
+ try {
805
+ return process.cwd();
806
+ }
807
+ catch {
808
+ return fallback;
809
+ }
810
+ }
803
811
  /**
804
812
  * When reusing an existing milestone branch, fast-forward it onto the
805
813
  * integration branch when that's safe (branch is a strict ancestor of
@@ -949,7 +957,7 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
949
957
  originalBasePath = resolveWorktreeProjectRoot(originalBasePath);
950
958
  const branch = autoWorktreeBranch(milestoneId);
951
959
  const { preserveBranch = false, preserveWorktree = false } = opts;
952
- const previousCwd = process.cwd();
960
+ const previousCwd = safeCwd(originalBasePath);
953
961
  // Wrap the entire teardown body in a single try/finally so activeWorkspace
954
962
  // is ALWAYS cleared — even if process.chdir throws (e.g. originalBasePath
955
963
  // was deleted before teardown ran). Previously the finally only covered
@@ -1901,6 +1909,11 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1901
1909
  }
1902
1910
  catch (err) {
1903
1911
  logWarning("worktree", `chdir to project root after merge failed: ${err instanceof Error ? err.message : String(err)}`);
1912
+ debugLog("mergeMilestoneToMain", {
1913
+ phase: "post-merge-chdir-failed",
1914
+ target: originalBasePath_,
1915
+ error: err instanceof Error ? err.message : String(err),
1916
+ });
1904
1917
  }
1905
1918
  };
1906
1919
  let shouldCleanup = false;
@@ -557,6 +557,13 @@ export function getAutoModeStartModel() {
557
557
  export function setCurrentDispatchedModelId(model) {
558
558
  s.currentDispatchedModelId = model ? `${model.provider}/${model.id}` : null;
559
559
  }
560
+ /**
561
+ * Update the active unit model after runtime recovery switches models mid-unit.
562
+ * The next session restore path reads this field before dispatching again.
563
+ */
564
+ export function setCurrentUnitModelForRecovery(model) {
565
+ s.currentUnitModel = model;
566
+ }
560
567
  // Tool tracking — delegates to auto-tool-tracking.ts
561
568
  export function markToolStart(toolCallId, toolName) {
562
569
  _markToolStart(toolCallId, s.active, toolName);
@@ -623,6 +630,35 @@ export function stopAutoRemote(projectRoot) {
623
630
  return { found: false, error: err.message };
624
631
  }
625
632
  }
633
+ /**
634
+ * Force-stop a remote auto-mode session before stealing its lock.
635
+ * The normal stop path stays SIGTERM-only so cooperative sessions can clean up;
636
+ * this path is only for the explicit "Force start" action.
637
+ */
638
+ export function forceStopAutoRemote(projectRoot) {
639
+ const lock = readCrashLock(projectRoot);
640
+ if (!lock)
641
+ return { found: false };
642
+ if (lock.pid === process.pid) {
643
+ clearLock(projectRoot);
644
+ return { found: false };
645
+ }
646
+ if (!isLockProcessAlive(lock)) {
647
+ clearLock(projectRoot);
648
+ return { found: false };
649
+ }
650
+ try {
651
+ process.kill(lock.pid, "SIGTERM");
652
+ if (isLockProcessAlive(lock)) {
653
+ process.kill(lock.pid, "SIGKILL");
654
+ }
655
+ clearLock(projectRoot);
656
+ return { found: true, pid: lock.pid };
657
+ }
658
+ catch (err) {
659
+ return { found: false, error: err.message };
660
+ }
661
+ }
626
662
  /**
627
663
  * Check if a remote auto-mode session is running (from a different process).
628
664
  * Reads the crash lock, checks PID liveness, and returns session details.
@@ -1824,7 +1860,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1824
1860
  if (freshStartAssessment.classification === "running") {
1825
1861
  const pid = freshStartAssessment.lock?.pid;
1826
1862
  ctx.ui.notify(pid
1827
- ? `Another auto-mode session (PID ${pid}) appears to be running.\nStop it with \`kill ${pid}\` before starting a new session.`
1863
+ ? `Another auto-mode session (PID ${pid}) appears to be running.\nRun \`/gsd stop\` for graceful shutdown, or choose "Force start" from \`/gsd auto\` to terminate it.`
1828
1864
  : "Another auto-mode session appears to be running.", "error");
1829
1865
  return;
1830
1866
  }
@@ -9,12 +9,16 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
9
  import { dirname, join } from "node:path";
10
10
  import { gsdRoot } from "./paths.js";
11
11
  import { withFileLockSync } from "./file-lock.js";
12
+ const temporaryBlockedModels = new Map();
12
13
  function blockedModelsPath(basePath) {
13
14
  return join(gsdRoot(basePath), "runtime", "blocked-models.json");
14
15
  }
15
16
  function modelKey(provider, id) {
16
17
  return `${provider.toLowerCase()}/${id.toLowerCase()}`;
17
18
  }
19
+ function temporaryModelKey(basePath, provider, id) {
20
+ return `${basePath}:${modelKey(provider, id)}`;
21
+ }
18
22
  function readFileSafe(path) {
19
23
  if (!existsSync(path))
20
24
  return { version: 1, blocked: [] };
@@ -41,6 +45,30 @@ export function isModelBlocked(basePath, provider, id) {
41
45
  const target = modelKey(provider, id);
42
46
  return loadBlockedModels(basePath).some((e) => modelKey(e.provider, e.id) === target);
43
47
  }
48
+ export function blockModelUntil(basePath, provider, id, blockedUntil, reason) {
49
+ const key = temporaryModelKey(basePath, provider, id);
50
+ if (blockedUntil <= Date.now()) {
51
+ temporaryBlockedModels.delete(key);
52
+ return;
53
+ }
54
+ temporaryBlockedModels.set(key, { provider, id, reason, blockedUntil });
55
+ }
56
+ export function isModelTemporarilyUnavailable(basePath, provider, id, now = Date.now()) {
57
+ if (!provider || !id)
58
+ return false;
59
+ const key = temporaryModelKey(basePath, provider, id);
60
+ const entry = temporaryBlockedModels.get(key);
61
+ if (!entry)
62
+ return false;
63
+ if (entry.blockedUntil <= now) {
64
+ temporaryBlockedModels.delete(key);
65
+ return false;
66
+ }
67
+ return true;
68
+ }
69
+ export function clearTemporaryModelBlocksForTest() {
70
+ temporaryBlockedModels.clear();
71
+ }
44
72
  export function blockModel(basePath, provider, id, reason) {
45
73
  const path = blockedModelsPath(basePath);
46
74
  mkdirSync(dirname(path), { recursive: true });
@@ -2,7 +2,7 @@
2
2
  import { logWarning } from "../workflow-logger.js";
3
3
  import { checkDeepProjectSetupAfterTurn, checkAutoStartAfterDiscuss, maybeHandleReadyPhraseWithoutFiles, maybeHandleEmptyIntentTurn, resetEmptyTurnCounter, } from "../guided-flow.js";
4
4
  import { clearPathCache } from "../paths.js";
5
- import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, isAutoCompletionStopInProgress, pauseAuto, setCurrentDispatchedModelId, } from "../auto.js";
5
+ import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, isAutoCompletionStopInProgress, pauseAuto, setCurrentDispatchedModelId, setCurrentUnitModelForRecovery, } from "../auto.js";
6
6
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
7
7
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
8
8
  import { isSessionSwitchAbortGraceActive, isSessionSwitchInFlight, resolveAgentEnd, resolveAgentEndCancelled, } from "../auto/resolve.js";
@@ -13,7 +13,7 @@ import { clearDiscussionFlowState } from "./write-gate.js";
13
13
  import { clearGuidedUnitContext } from "../guided-unit-context.js";
14
14
  import { resumeAutoAfterProviderDelay } from "./provider-error-resume.js";
15
15
  import { classifyError, createRetryState, resetRetryState, isTransient, } from "../error-classifier.js";
16
- import { blockModel, isModelBlocked } from "../blocked-models.js";
16
+ import { blockModel, blockModelUntil, isModelBlocked, isModelTemporarilyUnavailable } from "../blocked-models.js";
17
17
  import { getProjectGSDPreferencesPath } from "../preferences.js";
18
18
  import { resolveProviderErrorGuidance } from "../provider-error-guidance.js";
19
19
  import { formatGuidance } from "../guidance.js";
@@ -96,9 +96,12 @@ async function tryProviderModelFallback(params) {
96
96
  if (!nextModelId)
97
97
  break;
98
98
  const candidate = resolveModelId(nextModelId, availableModels, rejectedProvider);
99
- if (candidate && !isModelBlocked(basePath, candidate.provider, candidate.id)) {
99
+ if (candidate &&
100
+ !isModelBlocked(basePath, candidate.provider, candidate.id) &&
101
+ !isModelTemporarilyUnavailable(basePath, candidate.provider, candidate.id)) {
100
102
  const ok = await pi.setModel(candidate, { persist: false });
101
103
  if (ok) {
104
+ setCurrentUnitModelForRecovery(candidate);
102
105
  setCurrentDispatchedModelId({ provider: candidate.provider, id: candidate.id });
103
106
  switchedNotify(`${candidate.provider}/${candidate.id}`);
104
107
  pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
@@ -111,11 +114,13 @@ async function tryProviderModelFallback(params) {
111
114
  const sessionModel = getAutoModeStartModel();
112
115
  if (sessionModel &&
113
116
  !(sessionModel.provider === rejectedProvider && sessionModel.id === rejectedId) &&
114
- !isModelBlocked(basePath, sessionModel.provider, sessionModel.id)) {
117
+ !isModelBlocked(basePath, sessionModel.provider, sessionModel.id) &&
118
+ !isModelTemporarilyUnavailable(basePath, sessionModel.provider, sessionModel.id)) {
115
119
  const startModel = availableModels.find((m) => m.provider === sessionModel.provider && m.id === sessionModel.id);
116
120
  if (startModel) {
117
121
  const ok = await pi.setModel(startModel, { persist: false });
118
122
  if (ok) {
123
+ setCurrentUnitModelForRecovery(startModel);
119
124
  setCurrentDispatchedModelId({ provider: startModel.provider, id: startModel.id });
120
125
  switchedNotify(`${startModel.provider}/${startModel.id}`);
121
126
  pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
@@ -533,6 +538,10 @@ export async function handleAgentEnd(pi, event, ctx) {
533
538
  if (currentProvider === "openai-codex" || currentProvider === "google-gemini-cli") {
534
539
  cls.retryAfterMs = Math.min(cls.retryAfterMs, 30_000);
535
540
  }
541
+ const dash = getAutoDashboardData();
542
+ if (dash.basePath && ctx.model?.provider && ctx.model?.id) {
543
+ blockModelUntil(dash.basePath, ctx.model.provider, ctx.model.id, Date.now() + cls.retryAfterMs, rawErrorMsg || displayMsg || "rate limit");
544
+ }
536
545
  }
537
546
  // ── 2. Decide & Act ──────────────────────────────────────────────────
538
547
  // --- Network errors: same-model retry with backoff ---
@@ -572,9 +581,14 @@ export async function handleAgentEnd(pi, event, ctx) {
572
581
  retryState.networkRetryCount = 0;
573
582
  retryState.currentRetryModelId = undefined;
574
583
  const modelToSet = resolveModelId(nextModelId, availableModels, ctx.model?.provider);
575
- if (modelToSet) {
584
+ const modelUnavailable = dash.basePath && modelToSet
585
+ ? isModelBlocked(dash.basePath, modelToSet.provider, modelToSet.id) ||
586
+ isModelTemporarilyUnavailable(dash.basePath, modelToSet.provider, modelToSet.id)
587
+ : false;
588
+ if (modelToSet && !modelUnavailable) {
576
589
  const ok = await pi.setModel(modelToSet, { persist: false });
577
590
  if (ok) {
591
+ setCurrentUnitModelForRecovery(modelToSet);
578
592
  setCurrentDispatchedModelId({ provider: modelToSet.provider, id: modelToSet.id });
579
593
  ctx.ui.notify(`Model error${errorDetail}. Switched to fallback: ${nextModelId} and resuming.`, "warning");
580
594
  pi.sendMessage({ customType: "gsd-auto-timeout-recovery", content: "Continue execution.", display: false }, { triggerTurn: true });
@@ -587,11 +601,17 @@ export async function handleAgentEnd(pi, event, ctx) {
587
601
  // Try restoring session model
588
602
  const sessionModel = getAutoModeStartModel();
589
603
  if (sessionModel) {
590
- if (ctx.model?.id !== sessionModel.id || ctx.model?.provider !== sessionModel.provider) {
604
+ const dash = getAutoDashboardData();
605
+ const sessionModelUnavailable = dash.basePath
606
+ ? isModelBlocked(dash.basePath, sessionModel.provider, sessionModel.id) ||
607
+ isModelTemporarilyUnavailable(dash.basePath, sessionModel.provider, sessionModel.id)
608
+ : false;
609
+ if (!sessionModelUnavailable && (ctx.model?.id !== sessionModel.id || ctx.model?.provider !== sessionModel.provider)) {
591
610
  const startModel = ctx.modelRegistry.getAvailable().find((m) => m.provider === sessionModel.provider && m.id === sessionModel.id);
592
611
  if (startModel) {
593
612
  const ok = await pi.setModel(startModel, { persist: false });
594
613
  if (ok) {
614
+ setCurrentUnitModelForRecovery(startModel);
595
615
  setCurrentDispatchedModelId({ provider: startModel.provider, id: startModel.id });
596
616
  retryState.networkRetryCount = 0;
597
617
  retryState.currentRetryModelId = undefined;
@@ -4,7 +4,6 @@ import { existsSync, readdirSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
7
- import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
8
7
  import { logWarning } from "../workflow-logger.js";
9
8
  import { openWorkflowDatabase } from "../db-workspace.js";
10
9
  import { getAutoWorktreePath } from "../auto-worktree.js";
@@ -79,18 +78,36 @@ export function registerDynamicTools(pi) {
79
78
  const baseBash = createBashTool(fallbackRoot, {
80
79
  spawnHook: (ctx) => ctx,
81
80
  });
81
+ // The auto-mode stalled-tool watchdog only exists in GSD/auto-mode, so the
82
+ // watchdog verbiage is injected here (the GSD-registered tool) rather than in
83
+ // core bash.ts, which is reused by non-GSD embeddings that have no watchdog.
84
+ const WATCHDOG_DETAIL = "Genuine hangs are caught by the auto-mode stalled-tool watchdog (stalled: 5m / idle: 10m / soft: 20m / hard: 30m).";
85
+ const gsdBashDescription = `${baseBash.description} ${WATCHDOG_DETAIL}`;
86
+ const gsdBashParameters = (() => {
87
+ const params = baseBash.parameters;
88
+ if (!params?.properties?.timeout)
89
+ return params;
90
+ return {
91
+ ...params,
92
+ properties: {
93
+ ...params.properties,
94
+ timeout: {
95
+ ...params.properties.timeout,
96
+ description: `${params.properties.timeout.description} ${WATCHDOG_DETAIL}`,
97
+ },
98
+ },
99
+ };
100
+ })();
82
101
  const dynamicBash = {
83
102
  ...baseBash,
103
+ description: gsdBashDescription,
104
+ parameters: gsdBashParameters,
84
105
  execute: async (toolCallId, params, signal, onUpdate, ctx) => {
85
106
  const basePath = resolveCtxCwd(ctx);
86
107
  const fresh = createBashTool(basePath, {
87
108
  spawnHook: (spawnCtx) => ({ ...spawnCtx, cwd: basePath }),
88
109
  });
89
- const paramsWithTimeout = {
90
- ...params,
91
- timeout: params.timeout ?? DEFAULT_BASH_TIMEOUT_SECS,
92
- };
93
- return fresh.execute(toolCallId, paramsWithTimeout, signal, onUpdate, ctx);
110
+ return fresh.execute(toolCallId, params, signal, onUpdate, ctx);
94
111
  },
95
112
  };
96
113
  pi.registerTool(dynamicBash);
@@ -114,8 +114,8 @@ export function registerExecTools(pi) {
114
114
  ],
115
115
  parameters: Type.Object({
116
116
  query: Type.Optional(Type.String({ description: "Substring matched against id and purpose (case-insensitive)." })),
117
- runtime: Type.Optional(Type.Union([Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")], {
118
- description: "Restrict to one runtime.",
117
+ runtime: Type.Optional(Type.String({
118
+ description: "Restrict to one runtime: bash, node, or python.",
119
119
  })),
120
120
  failing_only: Type.Optional(Type.Boolean({ description: "Only non-zero exit codes and timeouts." })),
121
121
  limit: Type.Optional(Type.Number({ description: "Max results (default 20, cap 200)", minimum: 1, maximum: 200 })),
@@ -13,6 +13,7 @@ import { registerHooks } from "./register-hooks.js";
13
13
  import { registerShortcuts } from "./register-shortcuts.js";
14
14
  import { writeCrashLog } from "./crash-log.js";
15
15
  import { logWarning } from "../workflow-logger.js";
16
+ import { UNIT_TOOL_CONTRACTS } from "../unit-tool-contracts.js";
16
17
  // Static import so cmux event listeners are registered synchronously during
17
18
  // extension bootstrap. Prior implementation used `void import().then()` which
18
19
  // queued listener registration as a microtask — any CMUX_CHANNELS emit fired
@@ -30,6 +31,9 @@ const EPIPE_STORM_THRESHOLD = 100;
30
31
  const EPIPE_STORM_WINDOW_MS = 10_000;
31
32
  let epipeCount = 0;
32
33
  let epipeWindowStart = 0;
34
+ export const CRITICAL_GSD_WORKFLOW_TOOL_NAMES = [...new Set(Object.values(UNIT_TOOL_CONTRACTS)
35
+ .flatMap((contract) => contract.requiredWorkflowTools)
36
+ .filter((toolName) => toolName.startsWith("gsd_")))].sort();
33
37
  /** Write to stderr without ever re-throwing — stderr can EPIPE too, which would
34
38
  * re-enter this handler and re-loop. */
35
39
  function safeStderr(msg) {
@@ -121,6 +125,20 @@ export function installEpipeGuard() {
121
125
  process.on("unhandledRejection", _gsdRejectionGuard);
122
126
  }
123
127
  }
128
+ function assertCriticalGsdWorkflowToolsRegistered(pi) {
129
+ if (typeof pi.getAllTools !== "function")
130
+ return;
131
+ const registered = new Set(pi.getAllTools().map((tool) => tool.name));
132
+ const missing = CRITICAL_GSD_WORKFLOW_TOOL_NAMES.filter((toolName) => !registered.has(toolName));
133
+ if (missing.length === 0)
134
+ return;
135
+ const message = [
136
+ `Critical GSD workflow tool registration failed; missing required tool(s): ${missing.join(", ")}.`,
137
+ "Check earlier bootstrap warnings for the registration slot that failed.",
138
+ ].join(" ");
139
+ logWarning("bootstrap", message);
140
+ throw new Error(message);
141
+ }
124
142
  export function registerGsdExtension(pi) {
125
143
  // Note: registerGSDCommand is called by index.ts before this function,
126
144
  // so we intentionally skip it here to avoid double-registration.
@@ -186,4 +204,5 @@ export function registerGsdExtension(pi) {
186
204
  logWarning("bootstrap", `Failed to register ${name}: ${err instanceof Error ? err.message : String(err)}`);
187
205
  }
188
206
  }
207
+ assertCriticalGsdWorkflowToolsRegistered(pi);
189
208
  }