@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
@@ -1,16 +1,18 @@
1
1
  // gsd-pi - Write gate runtime persistence and policy guards.
2
- import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { copyFileSync, existsSync, lstatSync, mkdirSync, readlinkSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
 
5
5
  import { minimatch } from "minimatch";
6
6
 
7
7
  import { GSD_PHASE_SCOPE_DISPLAY_REASON, shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
8
- import { stripMcpToolPrefix } from "../mcp-tool-name.js";
8
+ import { canonicalToolName } from "../engine-hook-contract.js";
9
+ import { loadJsonFileOrNull } from "../json-persistence.js";
9
10
  import { getIsolationMode } from "../preferences.js";
10
11
  import { compileSubagentPermissionContract, type ToolsPolicy } from "../unit-context-manifest.js";
11
12
  import { logWarning } from "../workflow-logger.js";
12
13
  import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
13
14
  import { worktreesDirs } from "../worktree-placement.js";
15
+ import { evaluateGateAnswer } from "../consent-verdict.js";
14
16
 
15
17
  /**
16
18
  * Regex matching milestone CONTEXT.md file names in both legacy M001
@@ -124,15 +126,20 @@ const GATE_SAFE_TOOLS = new Set([
124
126
  "ask_user_questions",
125
127
  ]);
126
128
 
127
- export function canonicalToolName(toolName: string): string {
128
- return stripMcpToolPrefix(toolName);
129
- }
129
+ /**
130
+ * Which process wrote a snapshot. Two processes share the snapshot file:
131
+ * the extension host ("host") and the workflow MCP child ("child"), which
132
+ * dynamically imports this same compiled module in its own process.
133
+ */
134
+ export type WriteGateWriter = "host" | "child";
130
135
 
131
136
  export interface WriteGateSnapshot {
132
137
  verifiedDepthMilestones: string[];
133
138
  verifiedApprovalGates?: string[];
134
139
  activeQueuePhase: boolean;
135
140
  pendingGateId: string | null;
141
+ /** Tag of the process that produced this snapshot (diagnostic only). */
142
+ writer?: WriteGateWriter;
136
143
  }
137
144
 
138
145
  /**
@@ -176,12 +183,11 @@ function currentWriteGateSnapshot(basePath: string = process.cwd()): WriteGateSn
176
183
  };
177
184
  }
178
185
 
179
- function persistWriteGateSnapshot(basePath: string): void {
180
- if (!shouldPersistWriteGateSnapshot()) return;
186
+ function writeSnapshotFileAtomic(basePath: string, snapshot: WriteGateSnapshot): void {
181
187
  const path = writeGateSnapshotPath(basePath);
182
188
  ensureWriteGateSnapshotDirectory(basePath);
183
189
  const tempPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
184
- writeFileSync(tempPath, JSON.stringify(currentWriteGateSnapshot(basePath), null, 2), "utf-8");
190
+ writeFileSync(tempPath, JSON.stringify(snapshot, null, 2), "utf-8");
185
191
  try {
186
192
  renameSync(tempPath, path);
187
193
  } catch (err: unknown) {
@@ -196,6 +202,18 @@ function persistWriteGateSnapshot(basePath: string): void {
196
202
  }
197
203
  }
198
204
 
205
+ /**
206
+ * Persist the current in-memory state for `basePath`, stamped with the
207
+ * writer provenance tag. Callers (mutateWriteGateState / childMutate) are
208
+ * responsible for first reconciling in-memory content against the disk
209
+ * snapshot — the read-merge-write sequence is synchronous, so a concurrent
210
+ * writer's update is folded in by that unconditional pre-persist read.
211
+ */
212
+ function persistWriteGateSnapshot(basePath: string, writer: WriteGateWriter): void {
213
+ if (!shouldPersistWriteGateSnapshot()) return;
214
+ writeSnapshotFileAtomic(basePath, { ...currentWriteGateSnapshot(basePath), writer });
215
+ }
216
+
199
217
  function clearPersistedWriteGateSnapshot(basePath: string): void {
200
218
  if (!shouldPersistWriteGateSnapshot()) return;
201
219
  const path = writeGateSnapshotPath(basePath);
@@ -219,6 +237,7 @@ function normalizeWriteGateSnapshot(value: unknown): WriteGateSnapshot {
219
237
  verifiedApprovalGates: [...new Set(verifiedGates)].sort(),
220
238
  activeQueuePhase: record.activeQueuePhase === true,
221
239
  pendingGateId: typeof record.pendingGateId === "string" ? record.pendingGateId : null,
240
+ ...(record.writer === "host" || record.writer === "child" ? { writer: record.writer } : {}),
222
241
  };
223
242
  }
224
243
 
@@ -229,6 +248,18 @@ const EMPTY_SNAPSHOT: WriteGateSnapshot = {
229
248
  pendingGateId: null,
230
249
  };
231
250
 
251
+ /**
252
+ * Read the snapshot file as it exists on disk. Returns null when the file
253
+ * is missing or unparseable (no in-memory fallback — callers decide).
254
+ */
255
+ function readDiskSnapshot(basePath: string): WriteGateSnapshot | null {
256
+ const raw = loadJsonFileOrNull(
257
+ writeGateSnapshotPath(basePath),
258
+ (data): data is Record<string, unknown> => typeof data === "object" && data !== null,
259
+ );
260
+ return raw === null ? null : normalizeWriteGateSnapshot(raw);
261
+ }
262
+
232
263
  export function loadWriteGateSnapshot(basePath: string): WriteGateSnapshot {
233
264
  const path = writeGateSnapshotPath(basePath);
234
265
  if (!existsSync(path)) {
@@ -238,32 +269,116 @@ export function loadWriteGateSnapshot(basePath: string): WriteGateSnapshot {
238
269
  if (shouldPersistWriteGateSnapshot()) return EMPTY_SNAPSHOT;
239
270
  return currentWriteGateSnapshot(basePath);
240
271
  }
241
- try {
242
- return normalizeWriteGateSnapshot(JSON.parse(readFileSync(path, "utf-8")));
243
- } catch {
244
- return currentWriteGateSnapshot(basePath);
272
+ const disk = readDiskSnapshot(basePath);
273
+ return disk ?? currentWriteGateSnapshot(basePath);
274
+ }
275
+
276
+ /**
277
+ * Reconcile a disk snapshot into the in-memory state. This is THE merge
278
+ * rule for the two-process seam:
279
+ *
280
+ * - verifications are a grow-only union: a milestone/gate verified by
281
+ * either process is never lost to a stale write from the other;
282
+ * - activeQueuePhase and pendingGateId take the disk value (last writer
283
+ * wins — matches the long-standing refresh semantics, and keeps
284
+ * "delete the snapshot file to clear the HARD BLOCK" working);
285
+ * - verified wins over pending: if the merged verified sets already cover
286
+ * the pending gate (gate id verified, or its depth milestone verified),
287
+ * the pending re-arm is dropped. This generalizes the ad-hoc re-arm
288
+ * guard that previously protected only the tool_execution_start window.
289
+ */
290
+ function mergeSnapshotIntoState(state: InMemoryWriteGateState, disk: WriteGateSnapshot): void {
291
+ for (const milestone of disk.verifiedDepthMilestones) state.verifiedDepthMilestones.add(milestone);
292
+ for (const gate of disk.verifiedApprovalGates ?? []) state.verifiedApprovalGates.add(gate);
293
+ state.activeQueuePhase = disk.activeQueuePhase;
294
+ state.pendingGateId = disk.pendingGateId;
295
+ dropVerifiedPendingGate(state);
296
+ }
297
+
298
+ /** Verified-on-disk wins over a pending re-arm (see mergeSnapshotIntoState). */
299
+ function dropVerifiedPendingGate(state: InMemoryWriteGateState): void {
300
+ const pending = state.pendingGateId;
301
+ if (!pending) return;
302
+ const milestoneId = extractDepthVerificationMilestoneId(pending);
303
+ if (
304
+ state.verifiedApprovalGates.has(pending) ||
305
+ (milestoneId !== null && state.verifiedDepthMilestones.has(milestoneId))
306
+ ) {
307
+ state.pendingGateId = null;
245
308
  }
246
309
  }
247
310
 
311
+ function replaceStateFromSnapshot(state: InMemoryWriteGateState, snapshot: WriteGateSnapshot): void {
312
+ state.pendingGateId = snapshot.pendingGateId;
313
+ state.activeQueuePhase = snapshot.activeQueuePhase;
314
+ state.verifiedDepthMilestones = new Set(snapshot.verifiedDepthMilestones);
315
+ state.verifiedApprovalGates = new Set(snapshot.verifiedApprovalGates ?? []);
316
+ }
317
+
248
318
  /**
249
- * Merge the persisted write-gate snapshot into the in-process Map entry.
319
+ * Reconcile the persisted write-gate snapshot into the in-process Map entry.
250
320
  * The workflow MCP server runs in a child process and records depth
251
321
  * verification there; without this refresh the extension host keeps stale
252
322
  * pending-gate memory and `activateDeferredApprovalGate` can re-arm a gate
253
323
  * that the subprocess already cleared on disk.
254
324
  *
255
- * Returns the snapshot used for the refresh so callers that need to inspect
256
- * it (e.g. re-arm guards) avoid a second disk read.
325
+ * Uses the union merge rule (mergeSnapshotIntoState) when a readable
326
+ * snapshot file exists; a missing or unparseable file is a full reset
327
+ * (replace with empty) so deleting the file still clears all gate state,
328
+ * and a corrupt file does not leave stale `pendingGateId` in memory for
329
+ * the next mutation to persist back.
330
+ *
331
+ * Returns the reconciled snapshot so callers that need to inspect it
332
+ * (e.g. re-arm guards) avoid a second disk read.
257
333
  */
258
334
  export function refreshWriteGateStateFromDisk(basePath: string): WriteGateSnapshot {
259
- const snapshot = loadWriteGateSnapshot(basePath);
260
- if (!shouldPersistWriteGateSnapshot()) return snapshot;
335
+ if (!shouldPersistWriteGateSnapshot()) return currentWriteGateSnapshot(basePath);
261
336
  const state = getWriteGateState(basePath);
262
- state.pendingGateId = snapshot.pendingGateId;
263
- state.activeQueuePhase = snapshot.activeQueuePhase;
264
- state.verifiedDepthMilestones = new Set(snapshot.verifiedDepthMilestones);
265
- state.verifiedApprovalGates = new Set(snapshot.verifiedApprovalGates ?? []);
266
- return snapshot;
337
+ const disk = readDiskSnapshot(basePath);
338
+ if (disk) {
339
+ mergeSnapshotIntoState(state, disk);
340
+ } else {
341
+ replaceStateFromSnapshot(state, EMPTY_SNAPSHOT);
342
+ }
343
+ return currentWriteGateSnapshot(basePath);
344
+ }
345
+
346
+ /**
347
+ * Read-modify-write primitive for gate mutations. Reconciles the disk
348
+ * snapshot into memory (union merge), applies the mutation on top, then
349
+ * persists. The whole sequence is synchronous, so the reconcile read
350
+ * doubles as the pre-persist merge of concurrent writes — there is no
351
+ * version field; the read-merge-write is simply unconditional.
352
+ *
353
+ * The mutate callback sees the already-reconciled state, so policy checks
354
+ * (e.g. host setPending's verified-on-disk-wins guard) can live inside it
355
+ * without a second disk read. Returning `false` from the callback aborts:
356
+ * nothing is persisted and this function returns false.
357
+ *
358
+ * `reconcile: false` skips the disk merge — used for intentional full
359
+ * resets where re-unioning disk verifications would defeat the reset.
360
+ */
361
+ function mutateWriteGateState(
362
+ basePath: string,
363
+ mutate: (state: InMemoryWriteGateState) => void | false,
364
+ opts?: { writer?: WriteGateWriter; reconcile?: boolean },
365
+ ): boolean {
366
+ const state = getWriteGateState(basePath);
367
+ if (shouldPersistWriteGateSnapshot() && (opts?.reconcile ?? true)) {
368
+ const disk = readDiskSnapshot(basePath);
369
+ if (disk) {
370
+ mergeSnapshotIntoState(state, disk);
371
+ } else {
372
+ // Missing OR unparseable on disk: treat as a full reset. Keeping
373
+ // stale in-memory state across a corrupt snapshot would persist it
374
+ // back on this very mutation and defeat the documented
375
+ // "delete the file to clear the HARD BLOCK gate" escape hatch.
376
+ replaceStateFromSnapshot(state, EMPTY_SNAPSHOT);
377
+ }
378
+ }
379
+ if (mutate(state) === false) return false;
380
+ persistWriteGateSnapshot(basePath, opts?.writer ?? defaultWriteGateWriter());
381
+ return true;
267
382
  }
268
383
 
269
384
  export function isDepthVerified(basePath: string = process.cwd()): boolean {
@@ -295,16 +410,17 @@ export function isQueuePhaseActive(basePath: string = process.cwd()): boolean {
295
410
  }
296
411
 
297
412
  export function setQueuePhaseActive(active: boolean, basePath: string): void {
298
- getWriteGateState(basePath).activeQueuePhase = active;
299
- persistWriteGateSnapshot(basePath);
413
+ mutateWriteGateState(basePath, (state) => {
414
+ state.activeQueuePhase = active;
415
+ });
300
416
  }
301
417
 
302
418
  export function resetWriteGateState(basePath: string): void {
303
- const state = getWriteGateState(basePath);
304
- state.verifiedDepthMilestones.clear();
305
- state.verifiedApprovalGates.clear();
306
- state.pendingGateId = null;
307
- persistWriteGateSnapshot(basePath);
419
+ mutateWriteGateState(basePath, (state) => {
420
+ state.verifiedDepthMilestones.clear();
421
+ state.verifiedApprovalGates.clear();
422
+ state.pendingGateId = null;
423
+ }, { reconcile: false });
308
424
  }
309
425
 
310
426
  export function clearDiscussionFlowState(basePath: string): void {
@@ -312,16 +428,20 @@ export function clearDiscussionFlowState(basePath: string): void {
312
428
  clearPersistedWriteGateSnapshot(basePath);
313
429
  }
314
430
 
431
+ /**
432
+ * Ambient (env-sniffed) export, reserved for the child's dynamic-import
433
+ * surface (packages/mcp-server) and module-internal use. Host-owned modules
434
+ * (register-hooks, auto-dispatch, …) must call
435
+ * hostWriteGateAdapter.markDepthVerified explicitly so a leaked
436
+ * GSD_WORKFLOW_* env variable cannot silently flip them to child semantics.
437
+ */
315
438
  export function markDepthVerified(milestoneId?: string | null, basePath: string = process.cwd()): void {
316
- if (!milestoneId) return;
317
- getWriteGateState(basePath).verifiedDepthMilestones.add(milestoneId);
318
- persistWriteGateSnapshot(basePath);
439
+ defaultWriteGateAdapter().markDepthVerified(milestoneId, basePath);
319
440
  }
320
441
 
442
+ /** Ambient export for the child's dynamic-import surface — see markDepthVerified. */
321
443
  export function markApprovalGateVerified(gateId?: string | null, basePath: string = process.cwd()): void {
322
- if (!gateId) return;
323
- getWriteGateState(basePath).verifiedApprovalGates.add(gateId);
324
- persistWriteGateSnapshot(basePath);
444
+ defaultWriteGateAdapter().markApprovalGateVerified(gateId, basePath);
325
445
  }
326
446
 
327
447
  export function isApprovalGateVerifiedInSnapshot(
@@ -357,31 +477,174 @@ function extractContextMilestoneId(inputPath: string): string | null {
357
477
  }
358
478
 
359
479
  /**
360
- * Mark a gate as pending (called when ask_user_questions is invoked with a gate ID).
480
+ * Mark a gate as pending (called when ask_user_questions is invoked with a
481
+ * gate ID). Delegates to the process's default adapter: in the workflow MCP
482
+ * child the arm is unconditional (a fresh question intentionally revokes
483
+ * prior verification); in the host the adapter's verified-on-disk-wins
484
+ * guard applies and a suppressed arm returns false.
485
+ *
486
+ * Ambient (env-sniffed) export, reserved for the child's dynamic-import
487
+ * surface (packages/mcp-server). Host-owned modules must call
488
+ * hostWriteGateAdapter.setPending explicitly.
361
489
  */
362
- export function setPendingGate(gateId: string, basePath: string): void {
363
- const state = getWriteGateState(basePath);
490
+ export function setPendingGate(gateId: string, basePath: string): boolean {
491
+ return defaultWriteGateAdapter().setPending(gateId, basePath);
492
+ }
493
+
494
+ /** Arm `gateId` on a reconciled state, revoking its prior verification. */
495
+ function armPendingGate(state: InMemoryWriteGateState, gateId: string): void {
364
496
  state.pendingGateId = gateId;
365
497
  state.verifiedApprovalGates.delete(gateId);
366
498
  const milestoneId = extractDepthVerificationMilestoneId(gateId);
367
499
  if (milestoneId) state.verifiedDepthMilestones.delete(milestoneId);
368
- persistWriteGateSnapshot(basePath);
369
500
  }
370
501
 
371
502
  /**
372
503
  * Clear the pending gate (called when the user confirms).
504
+ * Ambient export for the child's dynamic-import surface — host-owned
505
+ * modules must call hostWriteGateAdapter.clearPending explicitly.
373
506
  */
374
507
  export function clearPendingGate(basePath: string): void {
375
- getWriteGateState(basePath).pendingGateId = null;
376
- persistWriteGateSnapshot(basePath);
508
+ defaultWriteGateAdapter().clearPending(basePath);
377
509
  }
378
510
 
379
511
  /**
380
512
  * Get the currently pending gate, if any.
513
+ * Ambient export for the child's dynamic-import surface — host-owned
514
+ * modules should prefer hostWriteGateAdapter.readState.
381
515
  */
382
516
  export function getPendingGate(basePath: string = process.cwd()): string | null {
383
- refreshWriteGateStateFromDisk(basePath);
384
- return getWriteGateState(basePath).pendingGateId;
517
+ return defaultWriteGateAdapter().readState(basePath).pendingGateId;
518
+ }
519
+
520
+ // ─── Write-gate two-process seam ─────────────────────────────────────────────
521
+ //
522
+ // Two processes write .gsd/runtime/write-gate-state.json: the extension host
523
+ // and the workflow MCP child (which dynamically imports this same compiled
524
+ // module — see GSD_WORKFLOW_WRITE_GATE_MODULE in workflow-mcp.ts and
525
+ // packages/mcp-server/src/server.ts). The adapters below name that seam:
526
+ // every cross-process interleaving reduces to "host adapter op vs child
527
+ // adapter op", which is deterministic given the merge rule documented on
528
+ // mergeSnapshotIntoState plus the unconditional read-merge-write persist.
529
+
530
+ export interface WriteGateStateAdapter {
531
+ readonly writer: WriteGateWriter;
532
+ /** Fresh (child) or disk-reconciled (host) view of gate state. */
533
+ readState(basePath: string): WriteGateSnapshot;
534
+ markDepthVerified(milestoneId: string | null | undefined, basePath: string): void;
535
+ markApprovalGateVerified(gateId: string | null | undefined, basePath: string): void;
536
+ /**
537
+ * Arm a pending gate. Returns false when the adapter's policy suppressed
538
+ * the arm (host: gate already verified on disk — verified wins over a
539
+ * stale re-arm). Child adapter always arms (a fresh question intentionally
540
+ * revokes prior verification).
541
+ */
542
+ setPending(gateId: string, basePath: string): boolean;
543
+ clearPending(basePath: string): void;
544
+ }
545
+
546
+ /**
547
+ * HOST adapter: in-memory state + reconcile-on-read. Disk verifications are
548
+ * never lost (union merge), and a gate verified on disk wins over an
549
+ * in-memory pending re-arm — setPending refuses to clobber a verification
550
+ * the child already recorded. This generalizes the ad-hoc re-arm guard that
551
+ * previously protected only the tool_execution_start window in
552
+ * register-hooks.ts.
553
+ */
554
+ export const hostWriteGateAdapter: WriteGateStateAdapter = {
555
+ writer: "host",
556
+ readState(basePath: string): WriteGateSnapshot {
557
+ return refreshWriteGateStateFromDisk(basePath);
558
+ },
559
+ markDepthVerified(milestoneId, basePath): void {
560
+ if (!milestoneId) return;
561
+ mutateWriteGateState(basePath, (state) => {
562
+ state.verifiedDepthMilestones.add(milestoneId);
563
+ }, { writer: "host" });
564
+ },
565
+ markApprovalGateVerified(gateId, basePath): void {
566
+ if (!gateId) return;
567
+ mutateWriteGateState(basePath, (state) => {
568
+ state.verifiedApprovalGates.add(gateId);
569
+ }, { writer: "host" });
570
+ },
571
+ setPending(gateId: string, basePath: string): boolean {
572
+ // The verified-check runs inside the mutate callback so the single
573
+ // reconcile read in mutateWriteGateState serves both the guard and the
574
+ // pre-persist merge. A suppressed arm aborts without persisting.
575
+ return mutateWriteGateState(basePath, (state) => {
576
+ if (state.verifiedApprovalGates.has(gateId)) return false;
577
+ const milestoneId = extractDepthVerificationMilestoneId(gateId);
578
+ if (milestoneId && state.verifiedDepthMilestones.has(milestoneId)) return false;
579
+ armPendingGate(state, gateId);
580
+ }, { writer: "host" });
581
+ },
582
+ clearPending(basePath: string): void {
583
+ mutateWriteGateState(basePath, (state) => {
584
+ state.pendingGateId = null;
585
+ }, { writer: "host" });
586
+ },
587
+ };
588
+
589
+ /**
590
+ * CHILD adapter: write-through with always-fresh reads — load the disk
591
+ * snapshot, mutate, persist. No cross-turn in-memory state is trusted: the
592
+ * in-process Map entry is replaced from disk before every mutation so a
593
+ * long-lived MCP child never resurrects state the host has since changed.
594
+ */
595
+ export const childWriteGateAdapter: WriteGateStateAdapter = {
596
+ writer: "child",
597
+ readState(basePath: string): WriteGateSnapshot {
598
+ return loadWriteGateSnapshot(basePath);
599
+ },
600
+ markDepthVerified(milestoneId, basePath): void {
601
+ if (!milestoneId) return;
602
+ childMutate(basePath, (state) => {
603
+ state.verifiedDepthMilestones.add(milestoneId);
604
+ });
605
+ },
606
+ markApprovalGateVerified(gateId, basePath): void {
607
+ if (!gateId) return;
608
+ childMutate(basePath, (state) => {
609
+ state.verifiedApprovalGates.add(gateId);
610
+ });
611
+ },
612
+ setPending(gateId: string, basePath: string): boolean {
613
+ childMutate(basePath, (state) => armPendingGate(state, gateId));
614
+ return true;
615
+ },
616
+ clearPending(basePath: string): void {
617
+ childMutate(basePath, (state) => {
618
+ state.pendingGateId = null;
619
+ });
620
+ },
621
+ };
622
+
623
+ function childMutate(basePath: string, mutate: (state: InMemoryWriteGateState) => void): void {
624
+ const state = getWriteGateState(basePath);
625
+ if (shouldPersistWriteGateSnapshot()) {
626
+ // Always-fresh: disk is the only truth for the child; discard any
627
+ // cross-turn in-memory residue before mutating.
628
+ replaceStateFromSnapshot(state, readDiskSnapshot(basePath) ?? EMPTY_SNAPSHOT);
629
+ }
630
+ mutate(state);
631
+ persistWriteGateSnapshot(basePath, "child");
632
+ }
633
+
634
+ /**
635
+ * Which adapter the module-level convenience exports (markDepthVerified,
636
+ * setPendingGate, …) delegate to. The workflow MCP child is spawned with
637
+ * GSD_WORKFLOW_WRITE_GATE_MODULE / GSD_WORKFLOW_PROJECT_ROOT in its
638
+ * environment (workflow-mcp.ts), so when this module is dynamically imported
639
+ * inside that process the child adapter is selected automatically; the
640
+ * extension host process has neither variable and stays on the host adapter.
641
+ */
642
+ export function defaultWriteGateWriter(env: NodeJS.ProcessEnv = process.env): WriteGateWriter {
643
+ return env.GSD_WORKFLOW_WRITE_GATE_MODULE || env.GSD_WORKFLOW_PROJECT_ROOT ? "child" : "host";
644
+ }
645
+
646
+ function defaultWriteGateAdapter(): WriteGateStateAdapter {
647
+ return defaultWriteGateWriter() === "child" ? childWriteGateAdapter : hostWriteGateAdapter;
385
648
  }
386
649
 
387
650
  /**
@@ -456,34 +719,12 @@ export function shouldBlockPendingGateBashInSnapshot(
456
719
  };
457
720
  }
458
721
 
459
- /**
460
- * Check whether a depth_verification answer confirms the discussion is complete.
461
- * Uses structural validation: the selected answer must exactly match the first
462
- * option label from the question definition (the confirmation option by convention).
463
- * This rejects free-form "Other" text, decline options, and garbage input without
464
- * coupling to any specific label substring.
465
- *
466
- * @param selected The answer's selected value from details.response.answers[id].selected
467
- * @param options The question's options array from event.input.questions[n].options
468
- */
469
- export function isDepthConfirmationAnswer(
470
- selected: unknown,
471
- options?: Array<{ label?: string }>,
472
- ): boolean {
473
- const value = Array.isArray(selected) ? selected[0] : selected;
474
- if (typeof value !== "string" || !value) return false;
475
-
476
- // If options are available, structurally validate: selected must exactly match
477
- // the first option (confirmation) label. Rejects free-form "Other" and decline options.
478
- if (Array.isArray(options) && options.length > 0) {
479
- const confirmLabel = options[0]?.label;
480
- return typeof confirmLabel === "string" && value === confirmLabel;
481
- }
482
-
483
- // Fail-closed: no options means we cannot structurally validate the answer.
484
- // Returning false prevents any free-form string from unlocking the gate.
485
- return false;
486
- }
722
+ // The structural depth-confirmation validator lives in the consent-verdict
723
+ // leaf (../consent-verdict.ts) so the write gate and the Consent Question
724
+ // module share one verdict engine without an import cycle. Re-exported here
725
+ // because the workflow MCP child loads this module by dist path and validates
726
+ // the function is present (packages/mcp-server/src/server.ts).
727
+ export { isDepthConfirmationAnswer } from "../consent-verdict.js";
487
728
 
488
729
  export interface AskUserQuestionsGateQuestion {
489
730
  id?: unknown;
@@ -502,7 +743,7 @@ export type AskUserQuestionsGateResult =
502
743
  | { status: "not-gate" }
503
744
  | { status: "waiting"; pendingGateId: string; interrupted: boolean }
504
745
  | { status: "verified"; gateId: string; milestoneId: string | null }
505
- | { status: "answered"; gateId: string };
746
+ | { status: "declined"; gateId: string };
506
747
 
507
748
  function findGateQuestion(
508
749
  questions: AskUserQuestionsGateQuestion[],
@@ -511,13 +752,6 @@ function findGateQuestion(
511
752
  return questions.find((question) => question?.id === gateId);
512
753
  }
513
754
 
514
- function readSelectedGateAnswer(
515
- details: AskUserQuestionsGateDetails,
516
- questionId: string,
517
- ): unknown {
518
- return details.response?.answers?.[questionId]?.selected;
519
- }
520
-
521
755
  function verifyAnsweredGate(
522
756
  basePath: string,
523
757
  question: AskUserQuestionsGateQuestion,
@@ -531,6 +765,34 @@ function verifyAnsweredGate(
531
765
  return { status: "verified", gateId, milestoneId };
532
766
  }
533
767
 
768
+ /** Map an unresolved (non-verified) gate verdict to the caller-facing result. */
769
+ function unresolvedGateResult(
770
+ verdict: "declined" | "waiting" | "cancelled",
771
+ gateId: string,
772
+ details: AskUserQuestionsGateDetails,
773
+ ): AskUserQuestionsGateResult {
774
+ if (verdict === "declined") return { status: "declined", gateId };
775
+ // "waiting" (and the unreachable post-cancel case): an empty selection is
776
+ // not an answer — keep the gate pending and make the caller pause.
777
+ return {
778
+ status: "waiting",
779
+ pendingGateId: gateId,
780
+ interrupted: details.interrupted === true,
781
+ };
782
+ }
783
+
784
+ /**
785
+ * Apply an ask_user_questions round to durable gate state. The per-question
786
+ * VERDICT comes from the consent-verdict leaf (evaluateGateAnswer) — the same
787
+ * engine the Consent Question module uses — so write-gate only owns the
788
+ * persistence/arming side effects:
789
+ *
790
+ * - "verified" verdict → markApprovalGateVerified/markDepthVerified/clearPendingGate.
791
+ * - "declined" verdict → no state change; the gate (if armed) stays pending.
792
+ * - "waiting" verdict (empty/missing selection) → no state change; reported as
793
+ * "waiting" so callers pause instead of proceeding (fail-closed; an empty
794
+ * answer is never an answer).
795
+ */
534
796
  export function applyAskUserQuestionsGateResult(options: {
535
797
  basePath: string;
536
798
  questions: AskUserQuestionsGateQuestion[];
@@ -550,11 +812,11 @@ export function applyAskUserQuestionsGateResult(options: {
550
812
 
551
813
  const pendingQuestion = findGateQuestion(questions, currentPendingGate);
552
814
  if (pendingQuestion) {
553
- const selected = readSelectedGateAnswer(details, currentPendingGate);
554
- if (isDepthConfirmationAnswer(selected, pendingQuestion.options)) {
815
+ const verdict = evaluateGateAnswer(pendingQuestion, details);
816
+ if (verdict === "verified") {
555
817
  return verifyAnsweredGate(basePath, pendingQuestion, fallbackMilestoneId);
556
818
  }
557
- return { status: "answered", gateId: currentPendingGate };
819
+ return unresolvedGateResult(verdict, currentPendingGate, details);
558
820
  }
559
821
  }
560
822
 
@@ -562,12 +824,14 @@ export function applyAskUserQuestionsGateResult(options: {
562
824
 
563
825
  for (const question of questions) {
564
826
  if (typeof question.id !== "string" || !isGateQuestionId(question.id)) continue;
565
- const selected = readSelectedGateAnswer(details, question.id);
566
- if (!isDepthConfirmationAnswer(selected, question.options)) {
567
- return { status: "answered", gateId: question.id };
827
+ const verdict = evaluateGateAnswer(question, details);
828
+ if (verdict !== "verified") {
829
+ return unresolvedGateResult(verdict, question.id, details);
568
830
  }
569
831
  if (currentPendingGate && question.id !== currentPendingGate) {
570
- return { status: "answered", gateId: currentPendingGate };
832
+ // A different gate than the armed one was confirmed — the armed gate is
833
+ // still unresolved, so do not verify and let discussion continue.
834
+ return { status: "declined", gateId: currentPendingGate };
571
835
  }
572
836
  return verifyAnsweredGate(basePath, question, fallbackMilestoneId);
573
837
  }