@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
@@ -0,0 +1,134 @@
1
+ /**
2
+ * One-shot confirmation token for destructive bash commands.
3
+ *
4
+ * The destructive-command guard hard-blocks classified commands (force push,
5
+ * rm -rf, SQL drop, etc.) in all modes. The block instructs the model to
6
+ * confirm via ask_user_questions and re-issue the command. This module is the
7
+ * missing escape hatch: it records the user's confirmation and lets the exact
8
+ * confirmed command through exactly once.
9
+ *
10
+ * Design constraints:
11
+ * - In-memory only, never persisted. A confirmation token written to disk
12
+ * could silently auto-approve a destructive command in a later session —
13
+ * confirmation must be re-obtained every process lifetime.
14
+ * - One-shot. Consuming a token clears it, so a second destructive command
15
+ * (even an identical one) re-blocks and re-prompts.
16
+ * - Command-bound. The token only matches the exact (normalized) command
17
+ * string the user confirmed. A reworded command re-blocks, which is safe.
18
+ * - Per basePath, so concurrent workspaces in one process never share tokens.
19
+ *
20
+ * Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
21
+ */
22
+
23
+ import { resolve } from "node:path";
24
+
25
+ /**
26
+ * Question-id substring that marks an ask_user_questions call as a
27
+ * destructive-command confirmation. The tool_result handler promotes the
28
+ * pending command to a confirmed token when an affirmative answer arrives for
29
+ * a question whose id contains this marker.
30
+ */
31
+ export const DESTRUCTIVE_CONFIRM_GATE_MARKER = "destructive_confirm";
32
+
33
+ interface DestructiveConfirmationState {
34
+ /** Command awaiting confirmation, captured when the guard blocked it. */
35
+ pendingCommand: string | null;
36
+ /** Confirmed command cleared on first matching consume. */
37
+ confirmedCommand: string | null;
38
+ }
39
+
40
+ const statesByBasePath = new Map<string, DestructiveConfirmationState>();
41
+
42
+ function stateKey(basePath: string): string {
43
+ return resolve(basePath);
44
+ }
45
+
46
+ function getState(basePath: string): DestructiveConfirmationState {
47
+ const key = stateKey(basePath);
48
+ let state = statesByBasePath.get(key);
49
+ if (!state) {
50
+ state = { pendingCommand: null, confirmedCommand: null };
51
+ statesByBasePath.set(key, state);
52
+ }
53
+ return state;
54
+ }
55
+
56
+ /**
57
+ * Normalize a command for stable matching across block → confirm → retry.
58
+ * Trims surrounding whitespace and collapses internal runs of whitespace so
59
+ * cosmetic reformatting of the same command still matches the token.
60
+ */
61
+ export function normalizeDestructiveCommand(command: string): string {
62
+ return command.replace(/\s+/g, " ").trim();
63
+ }
64
+
65
+ /**
66
+ * Whether an ask_user_questions question id is a destructive-confirm gate.
67
+ */
68
+ export function isDestructiveConfirmGateId(questionId: unknown): boolean {
69
+ return typeof questionId === "string" && questionId.includes(DESTRUCTIVE_CONFIRM_GATE_MARKER);
70
+ }
71
+
72
+ /**
73
+ * Record that a destructive command was blocked and is awaiting confirmation.
74
+ * Called by the guard at block time. Overwrites any prior pending command —
75
+ * only the most recently blocked command can be confirmed.
76
+ */
77
+ export function requestDestructiveConfirmation(
78
+ command: string,
79
+ basePath: string = process.cwd(),
80
+ ): void {
81
+ const state = getState(basePath);
82
+ state.pendingCommand = normalizeDestructiveCommand(command);
83
+ // A fresh request invalidates any stale confirmed token for a different
84
+ // command so confirmation cannot leak across distinct destructive actions.
85
+ state.confirmedCommand = null;
86
+ }
87
+
88
+ /**
89
+ * Promote the pending command to a confirmed, one-shot token. Called by the
90
+ * tool_result handler when the user gives an affirmative answer to a
91
+ * destructive-confirm gate. Returns the confirmed command, or null if there
92
+ * was nothing pending (e.g. confirmation arrived without a preceding block).
93
+ */
94
+ export function confirmDestructiveCommand(
95
+ basePath: string = process.cwd(),
96
+ ): string | null {
97
+ const state = getState(basePath);
98
+ if (!state.pendingCommand) return null;
99
+ state.confirmedCommand = state.pendingCommand;
100
+ state.pendingCommand = null;
101
+ return state.confirmedCommand;
102
+ }
103
+
104
+ /**
105
+ * Check whether the given command has been confirmed, consuming the token if
106
+ * so. Returns true exactly once per confirmation; subsequent calls (or a
107
+ * non-matching command) return false. Called by the guard before blocking.
108
+ */
109
+ export function consumeDestructiveConfirmation(
110
+ command: string,
111
+ basePath: string = process.cwd(),
112
+ ): boolean {
113
+ const state = getState(basePath);
114
+ if (!state.confirmedCommand) return false;
115
+ if (state.confirmedCommand !== normalizeDestructiveCommand(command)) return false;
116
+ state.confirmedCommand = null;
117
+ return true;
118
+ }
119
+
120
+ /**
121
+ * Inspect the pending command without consuming it (diagnostics/tests).
122
+ */
123
+ export function peekPendingDestructiveCommand(
124
+ basePath: string = process.cwd(),
125
+ ): string | null {
126
+ return getState(basePath).pendingCommand;
127
+ }
128
+
129
+ /**
130
+ * Clear all destructive-confirmation state for a basePath (tests / flow reset).
131
+ */
132
+ export function resetDestructiveConfirmation(basePath: string = process.cwd()): void {
133
+ statesByBasePath.delete(stateKey(basePath));
134
+ }
@@ -387,7 +387,7 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
387
387
  // #3218: Provide actionable workaround when lock recovery fails
388
388
  const lockDirPath = lockTarget + ".lock";
389
389
  const reason = existingPid
390
- ? `Another auto-mode session (PID ${existingPid}) appears to be running.\nStop it with \`kill ${existingPid}\` before starting a new session.`
390
+ ? `Another auto-mode session (PID ${existingPid}) appears to be running.\nRun \`/gsd stop\` for graceful shutdown, or choose "Force start" from \`/gsd auto\` to terminate it.`
391
391
  : `Another auto-mode session lock is stuck on this project.\nRun: rm -rf "${lockDirPath}" && rm -f "${lp}"`;
392
392
 
393
393
  return { acquired: false, reason, existingPid };
@@ -13,6 +13,11 @@ import type {
13
13
  MilestoneRegistryEntry,
14
14
  } from './types.js';
15
15
 
16
+ // Pre-migration fallback ONLY (ADR-017): deriveState must work on projects
17
+ // whose DB does not exist yet (before md-importer runs), so it parses markdown
18
+ // projections when `isDbAvailable()` is false or the DB has no rows. Once the
19
+ // DB is populated, decision reads go through gsd-db queries — these parsers
20
+ // must never be consulted when DB data is present.
16
21
  import {
17
22
  parseRoadmap,
18
23
  parsePlan,
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
 
10
10
  import { ModelPolicyDispatchBlockedError, resolvePreferredModelConfig, resolveModelId, selectAndApplyModel, floorThinkingLevelForUnit } from "../auto-model-selection.js";
11
+ import { blockModelUntil, clearTemporaryModelBlocksForTest } from "../blocked-models.ts";
11
12
 
12
13
  function makeTempDir(prefix: string): string {
13
14
  return mkdtempSync(join(tmpdir(), prefix));
@@ -223,6 +224,74 @@ test("selectAndApplyModel honors explicit phase models without downgrading (#361
223
224
  }
224
225
  });
225
226
 
227
+ test("selectAndApplyModel skips a rate-limited primary until its reset time", async () => {
228
+ const originalCwd = process.cwd();
229
+ const originalGsdHome = process.env.GSD_HOME;
230
+ const tempProject = makeTempDir("gsd-rate-limit-fallback-project-");
231
+ const tempGsdHome = makeTempDir("gsd-rate-limit-fallback-home-");
232
+ const setModelCalls: string[] = [];
233
+
234
+ try {
235
+ clearTemporaryModelBlocksForTest();
236
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
237
+ writeFileSync(
238
+ join(tempProject, ".gsd", "PREFERENCES.md"),
239
+ [
240
+ "---",
241
+ "models:",
242
+ " execution:",
243
+ " model: gpt-5.5",
244
+ " provider: openai-codex",
245
+ " fallbacks:",
246
+ " - anthropic/claude-sonnet-4-6",
247
+ "---",
248
+ ].join("\n"),
249
+ "utf-8",
250
+ );
251
+ process.env.GSD_HOME = tempGsdHome;
252
+ process.chdir(tempProject);
253
+
254
+ const availableModels = [
255
+ { id: "gpt-5.5", provider: "openai-codex", api: "responses" },
256
+ { id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" },
257
+ ];
258
+ const ctx = {
259
+ modelRegistry: { getAvailable: () => availableModels },
260
+ sessionManager: { getSessionId: () => "test-session" },
261
+ ui: { notify: () => {} },
262
+ model: { provider: "openai-codex", id: "gpt-5.5", api: "responses" },
263
+ } as any;
264
+ const pi = {
265
+ setModel: async (model: { provider: string; id: string }) => {
266
+ setModelCalls.push(`${model.provider}/${model.id}`);
267
+ return true;
268
+ },
269
+ emitBeforeModelSelect: async () => undefined,
270
+ getActiveTools: () => [],
271
+ emitAdjustToolSet: async () => undefined,
272
+ setActiveTools: () => {},
273
+ } as any;
274
+
275
+ blockModelUntil(tempProject, "openai-codex", "gpt-5.5", Date.now() + 60_000, "session limit");
276
+ await selectAndApplyModel(ctx, pi, "execute-task", "M001/S01/T01", tempProject, undefined, false, { provider: "openai-codex", id: "gpt-5.5" }, undefined, true);
277
+
278
+ blockModelUntil(tempProject, "openai-codex", "gpt-5.5", Date.now() - 1, "expired");
279
+ await selectAndApplyModel(ctx, pi, "execute-task", "M001/S01/T02", tempProject, undefined, false, { provider: "openai-codex", id: "gpt-5.5" }, undefined, true);
280
+
281
+ assert.deepEqual(setModelCalls, [
282
+ "anthropic/claude-sonnet-4-6",
283
+ "openai-codex/gpt-5.5",
284
+ ]);
285
+ } finally {
286
+ clearTemporaryModelBlocksForTest();
287
+ process.chdir(originalCwd);
288
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
289
+ else process.env.GSD_HOME = originalGsdHome;
290
+ rmSync(tempProject, { recursive: true, force: true });
291
+ rmSync(tempGsdHome, { recursive: true, force: true });
292
+ }
293
+ });
294
+
226
295
  test("selectAndApplyModel lets explicit unit models bypass stale cross-provider lock (#116)", async () => {
227
296
  const originalCwd = process.cwd();
228
297
  const originalGsdHome = process.env.GSD_HOME;
@@ -803,7 +872,12 @@ test("selectAndApplyModel clamps explicit no-model thinking via ctx.model when r
803
872
 
804
873
  await selectAndApplyModel(
805
874
  {
806
- modelRegistry: { getAvailable: () => [] }, // registry lookup fails
875
+ // Empty registry results force the clamp source to fall back to ctx.model.
876
+ modelRegistry: {
877
+ getAvailable: () => [],
878
+ getAll: () => [],
879
+ isProviderRequestReady: () => true,
880
+ },
807
881
  sessionManager: { getSessionId: () => "test-session" },
808
882
  ui: { notify: () => {} },
809
883
  model: ctxModel,
@@ -857,3 +931,25 @@ test("resolveModelId: claude-code wins when session is claude-code regardless of
857
931
  assert.ok(result, "should resolve a model");
858
932
  assert.equal(result.provider, "claude-code", "claude-code must win when it is the session provider");
859
933
  });
934
+
935
+ test("resolveModelId: openai-codex wins over openai for bare GPT IDs when both are available", () => {
936
+ const availableModels = [
937
+ { id: "gpt-5.5", provider: "openai" },
938
+ { id: "gpt-5.5", provider: "openai-codex" },
939
+ ];
940
+
941
+ const result = resolveModelId("gpt-5.5", availableModels, undefined);
942
+ assert.ok(result, "should resolve a model");
943
+ assert.equal(result.provider, "openai-codex", "ChatGPT OAuth must win over platform API for bare IDs");
944
+ });
945
+
946
+ test("resolveModelId: github-copilot wins over openai when both offer the same GPT model", () => {
947
+ const availableModels = [
948
+ { id: "gpt-5.5", provider: "openai" },
949
+ { id: "gpt-5.5", provider: "github-copilot" },
950
+ ];
951
+
952
+ const result = resolveModelId("gpt-5.5", availableModels, undefined);
953
+ assert.ok(result);
954
+ assert.equal(result.provider, "github-copilot");
955
+ });
@@ -22,8 +22,8 @@ import {
22
22
  createAutoOrchestrator,
23
23
  decideOrchestratorDispatch,
24
24
  resolveLiveOrchestratorBasePath,
25
- STUCK_WINDOW_SIZE,
26
25
  } from "../auto/orchestrator.js";
26
+ import { STUCK_WINDOW_SIZE } from "../auto/dispatch-history.js";
27
27
  import type { OrchestratorContext } from "../auto/orchestrator.js";
28
28
  import type { AutoOrchestrationModule, AutoSessionContext } from "../auto/contracts.js";
29
29
  import type { GSDState } from "../types.js";
@@ -41,6 +41,10 @@ import {
41
41
  openDatabase,
42
42
  } from "../gsd-db.js";
43
43
  import { AutoSession } from "../auto/session.js";
44
+ import { registerAutoWorker } from "../db/auto-workers.js";
45
+ import { claimMilestoneLease } from "../db/milestone-leases.js";
46
+ import { recordDispatchClaim, markFailed } from "../db/unit-dispatches.js";
47
+ import { normalizeRealPath } from "../paths.js";
44
48
  import { acquireSessionLock, releaseSessionLock } from "../session-lock.js";
45
49
  import { queryJournal } from "../journal.js";
46
50
  import { invalidateAllCaches } from "../cache.js";
@@ -518,13 +522,12 @@ test("advance() is idempotent for the same active unit", async (t) => {
518
522
  if (first.kind === "advanced") {
519
523
  assert.deepEqual(first.unit, { unitType: "execute-task", unitId: "M001/S01/T01" });
520
524
  }
521
- assert.equal(second.kind, "blocked");
522
- if (second.kind !== "blocked") return;
525
+ assert.equal(second.kind, "skipped");
526
+ if (second.kind !== "skipped") return;
523
527
  assert.equal(second.reason, "idempotent advance: unit already active");
524
- assert.equal(second.action, "pause");
525
528
  });
526
529
 
527
- test("idempotency block fires with its own reason before saturation", async (t) => {
530
+ test("idempotency skip fires with its own reason before saturation", async (t) => {
528
531
  const f = makeFixture();
529
532
  t.after(() => f.cleanup());
530
533
 
@@ -532,10 +535,9 @@ test("idempotency block fires with its own reason before saturation", async (t)
532
535
  const second = await f.orchestrator.advance();
533
536
 
534
537
  assert.equal(first.kind, "advanced");
535
- assert.equal(second.kind, "blocked");
536
- if (second.kind !== "blocked") return;
538
+ assert.equal(second.kind, "skipped");
539
+ if (second.kind !== "skipped") return;
537
540
  assert.equal(second.reason, "idempotent advance: unit already active");
538
- assert.equal(second.action, "pause");
539
541
  });
540
542
 
541
543
  test("completeActiveUnit clears in-flight idempotency and stops stale same-unit advance", async (t) => {
@@ -671,12 +673,12 @@ test("resume() clears idempotent lock and allows re-advance", async (t) => {
671
673
  t.after(() => f.cleanup());
672
674
 
673
675
  const first = await f.orchestrator.advance();
674
- const blocked = await f.orchestrator.advance();
676
+ const idempotent = await f.orchestrator.advance();
675
677
  const resumed = await f.orchestrator.resume();
676
678
  const next = await f.orchestrator.advance();
677
679
 
678
680
  assert.equal(first.kind, "advanced");
679
- assert.equal(blocked.kind, "blocked");
681
+ assert.equal(idempotent.kind, "skipped");
680
682
  assert.equal(resumed.kind, "resumed");
681
683
  assert.equal(next.kind, "advanced");
682
684
  });
@@ -686,11 +688,11 @@ test("start() clears prior idempotent lock", async (t) => {
686
688
  t.after(() => f.cleanup());
687
689
 
688
690
  await f.orchestrator.advance();
689
- const blocked = await f.orchestrator.advance();
691
+ const idempotent = await f.orchestrator.advance();
690
692
  const restarted = await f.orchestrator.start(SESSION_CONTEXT);
691
693
  const next = await f.orchestrator.advance();
692
694
 
693
- assert.equal(blocked.kind, "blocked");
695
+ assert.equal(idempotent.kind, "skipped");
694
696
  assert.equal(restarted.kind, "started");
695
697
  assert.equal(next.kind, "advanced");
696
698
  });
@@ -700,32 +702,28 @@ test("stop() clears idempotent unit lock so advance can run again", async (t) =>
700
702
  t.after(() => f.cleanup());
701
703
 
702
704
  const first = await f.orchestrator.advance();
703
- const blocked = await f.orchestrator.advance();
705
+ const idempotent = await f.orchestrator.advance();
704
706
  const stopped = await f.orchestrator.stop("reset");
705
707
  const second = await f.orchestrator.advance();
706
708
 
707
709
  assert.equal(first.kind, "advanced");
708
- assert.equal(blocked.kind, "blocked");
710
+ assert.equal(idempotent.kind, "skipped");
709
711
  assert.equal(stopped.kind, "stopped");
710
712
  assert.equal(second.kind, "advanced");
711
713
  });
712
714
 
713
- test("blocked path journals advance-blocked and records a health snapshot", async (t) => {
715
+ test("idempotent path journals advance-skipped and records a health snapshot", async (t) => {
714
716
  const f = makeFixture();
715
717
  t.after(() => f.cleanup());
716
718
 
717
719
  await f.orchestrator.advance();
718
720
  await f.orchestrator.advance();
719
721
 
720
- assert.ok(f.journalNames().includes("advance-blocked"));
722
+ assert.ok(f.journalNames().includes("advance-skipped"));
721
723
  });
722
724
 
723
725
  // ─── Stuck-loop ring buffer (issue #5787) ──────────────────────────────────
724
726
 
725
- test("STUCK_WINDOW_SIZE matches the legacy auto/phases.ts constant", () => {
726
- assert.equal(STUCK_WINDOW_SIZE, 6);
727
- });
728
-
729
727
  test("stuck-loop: empty ring on a freshly constructed orchestrator advances normally", async (t) => {
730
728
  const f = makeFixture();
731
729
  t.after(() => f.cleanup());
@@ -761,21 +759,24 @@ test("stuck-loop: ring saturated with same unit blocks with action 'stop' and st
761
759
  // First call advances.
762
760
  assert.equal(results[0].kind, "advanced");
763
761
 
764
- // Intermediate calls are blocked by idempotency (not stuck-loop yet).
762
+ // Intermediate calls are skipped by idempotency (not stuck-loop yet).
765
763
  for (let i = 1; i < STUCK_WINDOW_SIZE - 1; i++) {
766
764
  const r = results[i];
767
- assert.equal(r.kind, "blocked", `round ${i} should be blocked`);
768
- if (r.kind !== "blocked") return;
765
+ assert.equal(r.kind, "skipped", `round ${i} should be skipped`);
766
+ if (r.kind !== "skipped") return;
769
767
  assert.equal(r.reason, "idempotent advance: unit already active");
770
- assert.equal(r.action, "pause");
771
768
  }
772
769
 
773
- // The final call (ring now holds STUCK_WINDOW_SIZE copies) returns stuck-loop.
770
+ // The final call (ring now holds STUCK_WINDOW_SIZE copies) returns stuck-loop
771
+ // with the detect-stuck rule verdict in the reason.
774
772
  const last = results[STUCK_WINDOW_SIZE - 1];
775
773
  assert.equal(last.kind, "blocked");
776
774
  if (last.kind !== "blocked") return;
777
775
  assert.equal(last.action, "stop");
778
- assert.equal(last.reason, `stuck-loop: execute-task:M001/S01/T01 picked ${STUCK_WINDOW_SIZE} times`);
776
+ assert.ok(
777
+ last.reason.startsWith("stuck-loop: execute-task:M001/S01/T01 derived"),
778
+ `expected detect-stuck verdict reason, got: ${last.reason}`,
779
+ );
779
780
  });
780
781
 
781
782
  test("stuck-loop: start() resets the ring so a fresh saturation cycle is required", async (t) => {
@@ -855,6 +856,80 @@ test("stuck-loop: stop('user-request') resets the ring (hard stop)", async (t) =
855
856
  assert.equal(next.kind, "advanced");
856
857
  });
857
858
 
859
+ test("stuck-loop #482 regression: start() rehydrates the window from the dispatch ledger so cross-session re-dispatch loops are detected", async (t) => {
860
+ const f = makeFixture();
861
+ t.after(() => f.cleanup());
862
+
863
+ // Simulate a PRIOR session: the dispatch ledger recorded the same unit
864
+ // being re-dispatched repeatedly without progress. The orchestrator under test is a
865
+ // fresh instance (as it would be after a session restart) — before the
866
+ // Dispatch History module, start() reset the window to [] and the loop
867
+ // would silently re-dispatch the unit forever (#482: 146 re-dispatches).
868
+ const worker = registerAutoWorker({ projectRootRealpath: normalizeRealPath(f.base) });
869
+ const lease = claimMilestoneLease(worker, "M001");
870
+ assert.equal(lease.ok, true);
871
+ if (!lease.ok) return;
872
+ for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
873
+ const claim = recordDispatchClaim({
874
+ traceId: `prior-session-${i}`,
875
+ workerId: worker,
876
+ milestoneLeaseToken: lease.token,
877
+ milestoneId: "M001",
878
+ unitType: "execute-task",
879
+ unitId: "M001/S01/T01",
880
+ });
881
+ assert.equal(claim.ok, true);
882
+ if (!claim.ok) return;
883
+ markFailed(claim.dispatchId, { errorSummary: "" });
884
+ }
885
+
886
+ const started = await f.orchestrator.start(SESSION_CONTEXT);
887
+ assert.equal(started.kind, "started");
888
+
889
+ // The very next decision for the same unit must trip the stuck verdict
890
+ // instead of advancing.
891
+ const result = await f.orchestrator.advance();
892
+ assert.equal(result.kind, "blocked");
893
+ if (result.kind !== "blocked") return;
894
+ assert.equal(result.action, "stop");
895
+ assert.ok(result.reason.startsWith("stuck-loop:"), `expected stuck-loop reason, got: ${result.reason}`);
896
+ });
897
+
898
+ test("stuck-loop #482: resume() with an empty window rehydrates from the dispatch ledger", async (t) => {
899
+ const f = makeFixture();
900
+ t.after(() => f.cleanup());
901
+
902
+ const worker = registerAutoWorker({ projectRootRealpath: normalizeRealPath(f.base) });
903
+ const lease = claimMilestoneLease(worker, "M001");
904
+ assert.equal(lease.ok, true);
905
+ if (!lease.ok) return;
906
+ for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
907
+ const claim = recordDispatchClaim({
908
+ traceId: `prior-session-resume-${i}`,
909
+ workerId: worker,
910
+ milestoneLeaseToken: lease.token,
911
+ milestoneId: "M001",
912
+ unitType: "execute-task",
913
+ unitId: "M001/S01/T01",
914
+ });
915
+ assert.equal(claim.ok, true);
916
+ if (!claim.ok) return;
917
+ markFailed(claim.dispatchId, { errorSummary: "" });
918
+ }
919
+
920
+ // Fresh orchestrator resuming a prior session: window starts empty, so
921
+ // resume() must rehydrate (while in-process resume keeps the live window —
922
+ // see the #572 preservation tests above).
923
+ const resumed = await f.orchestrator.resume();
924
+ assert.equal(resumed.kind, "resumed");
925
+
926
+ const result = await f.orchestrator.advance();
927
+ assert.equal(result.kind, "blocked");
928
+ if (result.kind !== "blocked") return;
929
+ assert.equal(result.action, "stop");
930
+ assert.ok(result.reason.startsWith("stuck-loop:"), `expected stuck-loop reason, got: ${result.reason}`);
931
+ });
932
+
858
933
  test("stuck-loop: journal records the stuck-loop reason on advance-blocked", async (t) => {
859
934
  const f = makeFixture();
860
935
  t.after(() => f.cleanup());
@@ -1148,6 +1223,103 @@ test("decideOrchestratorDispatch forwards constructor session when advance input
1148
1223
  }
1149
1224
  });
1150
1225
 
1226
+ test("decideOrchestratorDispatch evaluates deep pre-planning rules without an active milestone", async (t) => {
1227
+ const base = mkdtempSync(join(tmpdir(), "gsd-orchestrator-no-active-"));
1228
+ t.after(() => {
1229
+ resetRegistry();
1230
+ rmSync(base, { recursive: true, force: true });
1231
+ });
1232
+ resetRegistry();
1233
+ mkdirSync(join(base, ".gsd"), { recursive: true });
1234
+ writeFileSync(
1235
+ join(base, ".gsd", "PREFERENCES.md"),
1236
+ [
1237
+ "---",
1238
+ "planning_depth: deep",
1239
+ "workflow_prefs_captured: true",
1240
+ "---",
1241
+ "",
1242
+ ].join("\n"),
1243
+ );
1244
+
1245
+ const stateSnapshot: GSDState = {
1246
+ ...makeState(),
1247
+ activeMilestone: null,
1248
+ phase: "pre-planning",
1249
+ nextAction: "All remaining milestones are parked (M027). Run /gsd unpark M027 or create a new milestone.",
1250
+ registry: [{ id: "M027", title: "Parked", status: "parked" }],
1251
+ };
1252
+ const ctx = { model: {}, modelRegistry: { getAll: () => [] } } as never;
1253
+ const pi = { getActiveTools: () => [] } as never;
1254
+ const session = {
1255
+ basePath: base,
1256
+ originalBasePath: base,
1257
+ currentMilestoneId: "M027",
1258
+ } as never;
1259
+
1260
+ const result = await decideOrchestratorDispatch(ctx, pi, base, session, { stateSnapshot });
1261
+
1262
+ assert.ok(result && "unitType" in result, `expected project-level dispatch, got ${JSON.stringify(result)}`);
1263
+ assert.equal(result.unitType, "discuss-project");
1264
+ assert.equal(result.unitId, "PROJECT");
1265
+ });
1266
+
1267
+ test("decideOrchestratorDispatch does not replay milestone-scoped verification retry when no milestone is active", async (t) => {
1268
+ const base = mkdtempSync(join(tmpdir(), "gsd-orchestrator-no-active-retry-"));
1269
+ t.after(() => {
1270
+ resetRegistry();
1271
+ rmSync(base, { recursive: true, force: true });
1272
+ });
1273
+ resetRegistry();
1274
+ mkdirSync(join(base, ".gsd"), { recursive: true });
1275
+ writeFileSync(
1276
+ join(base, ".gsd", "PREFERENCES.md"),
1277
+ [
1278
+ "---",
1279
+ "planning_depth: deep",
1280
+ "workflow_prefs_captured: true",
1281
+ "---",
1282
+ "",
1283
+ ].join("\n"),
1284
+ );
1285
+
1286
+ const stateSnapshot: GSDState = {
1287
+ ...makeState(),
1288
+ activeMilestone: null,
1289
+ phase: "pre-planning",
1290
+ nextAction: "All remaining milestones are parked (M027). Run /gsd unpark M027 or create a new milestone.",
1291
+ registry: [{ id: "M027", title: "Parked", status: "parked" }],
1292
+ };
1293
+ const ctx = { model: {}, modelRegistry: { getAll: () => [] } } as never;
1294
+ const pi = { getActiveTools: () => [] } as never;
1295
+ const stalePendingRetry = {
1296
+ unitType: "execute-task",
1297
+ unitId: "M027.S1.T1",
1298
+ prompt: "stale retry prompt",
1299
+ pauseAfterUatDispatch: false,
1300
+ state: stateSnapshot,
1301
+ mid: "M027",
1302
+ midTitle: "Parked",
1303
+ };
1304
+ const session = {
1305
+ basePath: base,
1306
+ originalBasePath: base,
1307
+ currentMilestoneId: "M027",
1308
+ pendingVerificationRetryDispatch: stalePendingRetry,
1309
+ } as never;
1310
+
1311
+ const result = await decideOrchestratorDispatch(ctx, pi, base, session, { stateSnapshot });
1312
+
1313
+ assert.ok(result && "unitType" in result, `expected project-level dispatch, got ${JSON.stringify(result)}`);
1314
+ assert.equal(result.unitType, "discuss-project");
1315
+ assert.equal(result.unitId, "PROJECT");
1316
+ // The stale retry must be preserved for a future tick, not consumed by this
1317
+ // no-active-milestone path (mirrors pre-#712-fix behavior where !active
1318
+ // returned null before touching the retry).
1319
+ const sess = session as unknown as { pendingVerificationRetryDispatch: unknown };
1320
+ assert.equal(sess.pendingVerificationRetryDispatch, stalePendingRetry);
1321
+ });
1322
+
1151
1323
  test("decideOrchestratorDispatch adopts next active milestone after the session milestone is closed", async (t) => {
1152
1324
  const base = mkdtempSync(join(tmpdir(), "gsd-orchestrator-milestone-adopt-"));
1153
1325
  t.after(() => rmSync(base, { recursive: true, force: true }));