@opengsd/gsd-pi 1.2.0-dev.844675c9 → 1.2.0-dev.b1abb545

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 (375) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/ask-user-questions.js +78 -23
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +84 -228
  7. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  8. package/dist/resources/extensions/github-sync/templates.js +3 -3
  9. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +109 -11
  12. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  17. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  19. package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
  20. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
  22. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  23. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  24. package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
  25. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
  26. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  27. package/dist/resources/extensions/gsd/auto-worktree.js +21 -19
  28. package/dist/resources/extensions/gsd/auto.js +11 -7
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  30. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +100 -138
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  35. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  36. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  37. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  38. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  39. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  40. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  41. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  42. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  43. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  44. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  45. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  46. package/dist/resources/extensions/gsd/doctor.js +16 -9
  47. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  48. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  49. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  50. package/dist/resources/extensions/gsd/guided-flow.js +34 -468
  51. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
  53. package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
  54. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  55. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  56. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  57. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  58. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  59. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  60. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  61. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  62. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  63. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  64. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  65. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  66. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  67. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  68. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  69. package/dist/resources/extensions/gsd/preferences.js +147 -29
  70. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  71. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  73. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  75. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  77. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  78. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  79. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  80. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  81. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  83. package/dist/resources/extensions/gsd/state.js +13 -5
  84. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  85. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  86. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  87. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  88. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  89. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  90. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  91. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  92. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  93. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  94. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  95. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  96. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  97. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  98. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  99. package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
  100. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  101. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  102. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  103. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  104. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  105. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  106. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  107. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  108. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  109. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  110. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  111. package/dist/update-cmd.js +20 -0
  112. package/dist/web/standalone/.next/BUILD_ID +1 -1
  113. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  114. package/dist/web/standalone/.next/build-manifest.json +3 -3
  115. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  116. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  117. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/index.html +1 -1
  134. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  141. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  142. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  145. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  146. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  147. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  149. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  151. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  153. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  154. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  155. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  156. package/dist/web-mode.d.ts +2 -0
  157. package/dist/web-mode.js +20 -8
  158. package/package.json +2 -1
  159. package/packages/cloud-mcp-gateway/package.json +2 -2
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  163. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  165. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  166. package/packages/gsd-agent-core/package.json +5 -5
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  170. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  171. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  173. package/packages/gsd-agent-modes/package.json +7 -7
  174. package/packages/mcp-server/dist/server.d.ts +10 -0
  175. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  176. package/packages/mcp-server/dist/server.js +8 -0
  177. package/packages/mcp-server/dist/server.js.map +1 -1
  178. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  179. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  180. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  181. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  182. package/packages/mcp-server/package.json +3 -3
  183. package/packages/native/package.json +1 -1
  184. package/packages/pi-agent-core/package.json +1 -1
  185. package/packages/pi-ai/dist/models.generated.d.ts +8 -93
  186. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/models.generated.js +35 -120
  188. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  189. package/packages/pi-ai/package.json +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/dist/components/input.js +1 -1
  192. package/packages/pi-tui/dist/components/input.js.map +1 -1
  193. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  194. package/packages/pi-tui/dist/keys.js +39 -30
  195. package/packages/pi-tui/dist/keys.js.map +1 -1
  196. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  197. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  198. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  199. package/packages/pi-tui/package.json +2 -2
  200. package/packages/rpc-client/package.json +2 -2
  201. package/pkg/package.json +1 -1
  202. package/src/resources/extensions/ask-user-questions.ts +87 -24
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +108 -281
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +240 -0
  205. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  206. package/src/resources/extensions/github-sync/templates.ts +3 -3
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  208. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  209. package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
  210. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  211. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  212. package/src/resources/extensions/gsd/auto/orchestrator.ts +125 -12
  213. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  215. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  216. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  218. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  219. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
  221. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  222. package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
  223. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  224. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  225. package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
  226. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
  227. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  228. package/src/resources/extensions/gsd/auto-worktree.ts +33 -26
  229. package/src/resources/extensions/gsd/auto.ts +15 -8
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  232. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  233. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +116 -151
  234. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  235. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  236. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  237. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  239. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  240. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  241. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  242. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  243. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  244. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  245. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  246. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  247. package/src/resources/extensions/gsd/doctor.ts +15 -5
  248. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  249. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  250. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  251. package/src/resources/extensions/gsd/guided-flow.ts +47 -558
  252. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  253. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
  254. package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
  255. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  256. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  257. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  258. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  259. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  260. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  261. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  262. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  263. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  264. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  265. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  266. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  267. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  268. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  269. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  270. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  271. package/src/resources/extensions/gsd/preferences.ts +173 -28
  272. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  273. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  275. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  276. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  277. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  278. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  279. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  280. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  281. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  282. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  283. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  284. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  285. package/src/resources/extensions/gsd/state.ts +15 -5
  286. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  287. package/src/resources/extensions/gsd/templates/project.md +1 -0
  288. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  289. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  290. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  291. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  292. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  293. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  294. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  295. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
  296. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  297. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  298. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  299. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  300. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  301. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  302. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  303. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  304. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  305. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  306. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  307. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  308. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  309. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  310. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  311. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  312. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  313. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  314. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  315. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  316. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  317. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  318. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  319. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  320. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  321. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
  322. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  323. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  325. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  326. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  327. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  328. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  329. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  330. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  331. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
  332. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  333. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  334. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  335. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  336. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
  337. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  338. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  339. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  340. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  341. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  342. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  343. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  344. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  345. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  346. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  347. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  348. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  349. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  350. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  351. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  352. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  353. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  354. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  355. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  356. package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
  357. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  358. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  359. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  360. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  361. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  362. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  363. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  364. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  365. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  366. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  367. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  368. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  369. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  370. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  371. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  372. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  373. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  374. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_buildManifest.js +0 -0
  375. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_ssgManifest.js +0 -0
@@ -38,6 +38,8 @@ import {
38
38
  } from "../stream-adapter.ts";
39
39
  import type { AssistantMessage, Context, Message } from "@gsd/pi-ai";
40
40
  import type { SDKUserMessage } from "../sdk-types.ts";
41
+ import { _setAutoActiveForTest } from "../../gsd/auto.ts";
42
+ import { getInFlightToolCount, hasInteractiveToolInFlight, clearInFlightTools, isInteractiveElicitationInFlight } from "../../gsd/auto-tool-tracking.ts";
41
43
 
42
44
  // ---------------------------------------------------------------------------
43
45
  // Env helpers — `GSD_WORKFLOW_MCP_*` save/restore
@@ -58,6 +60,7 @@ const WORKFLOW_MCP_ENV_KEYS = [
58
60
  "GSD_WORKFLOW_MCP_ARGS",
59
61
  "GSD_WORKFLOW_MCP_ENV",
60
62
  "GSD_WORKFLOW_MCP_CWD",
63
+ "GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS",
61
64
  "GSD_PROJECT_ROOT",
62
65
  "GSD_WORKFLOW_PROJECT_ROOT",
63
66
  ] as const;
@@ -1043,6 +1046,31 @@ describe("stream-adapter — session persistence (#2859)", () => {
1043
1046
  }
1044
1047
  });
1045
1048
 
1049
+ test("buildSdkOptions can disable workflow MCP ask_user_questions explicitly", () => {
1050
+ const restore = setWorkflowMcpEnv({
1051
+ GSD_WORKFLOW_MCP_COMMAND: "node",
1052
+ GSD_WORKFLOW_MCP_NAME: "gsd-workflow",
1053
+ GSD_WORKFLOW_MCP_ARGS: JSON.stringify(["packages/mcp-server/dist/cli.js"]),
1054
+ GSD_WORKFLOW_MCP_CWD: "/tmp/project",
1055
+ GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS: "0",
1056
+ });
1057
+ const originalCwd = process.cwd();
1058
+ const emptyDir = mkdtempSync(join(tmpdir(), "claude-mcp-ask-disabled-"));
1059
+ try {
1060
+ process.chdir(emptyDir);
1061
+ const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
1062
+ assert.ok(
1063
+ (options.disallowedTools as string[]).includes("mcp__gsd-workflow__ask_user_questions"),
1064
+ "explicit opt-out must block the MCP question tool even when workflow wildcard is allowed",
1065
+ );
1066
+ assert.ok((options.disallowedTools as string[]).includes("AskUserQuestion"));
1067
+ } finally {
1068
+ process.chdir(originalCwd);
1069
+ rmSync(emptyDir, { recursive: true, force: true });
1070
+ restore();
1071
+ }
1072
+ });
1073
+
1046
1074
  test("buildSdkOptions scopes run-uat to exact workflow MCP tools", () => {
1047
1075
  const restore = setWorkflowMcpEnv({
1048
1076
  GSD_WORKFLOW_MCP_COMMAND: "node",
@@ -1140,6 +1168,38 @@ describe("stream-adapter — session persistence (#2859)", () => {
1140
1168
  assert.equal(inferGsdPhaseFromContext(context), "plan-milestone");
1141
1169
  });
1142
1170
 
1171
+ test("buildSdkOptions presents ask_user_questions for discuss phases", () => {
1172
+ const restore = setWorkflowMcpEnv({
1173
+ GSD_WORKFLOW_MCP_COMMAND: "node",
1174
+ GSD_WORKFLOW_MCP_NAME: "gsd-workflow",
1175
+ GSD_WORKFLOW_MCP_ARGS: JSON.stringify(["packages/mcp-server/dist/cli.js"]),
1176
+ GSD_WORKFLOW_MCP_ENV: JSON.stringify({ GSD_CLI_PATH: "/tmp/gsd" }),
1177
+ GSD_WORKFLOW_MCP_CWD: "/tmp/project",
1178
+ });
1179
+ const originalCwd = process.cwd();
1180
+ const emptyDir = mkdtempSync(join(tmpdir(), "claude-mcp-discuss-"));
1181
+ try {
1182
+ process.chdir(emptyDir);
1183
+ const options = buildSdkOptions("claude-sonnet-4-20250514", "test", undefined, { gsdPhase: "discuss-milestone" });
1184
+ const allowedTools = options.allowedTools as string[];
1185
+ const disallowedTools = options.disallowedTools as string[];
1186
+
1187
+ assert.ok(
1188
+ allowedTools.includes("mcp__gsd-workflow__ask_user_questions"),
1189
+ "discuss phases must expose the exact workflow MCP question tool",
1190
+ );
1191
+ assert.ok(disallowedTools.includes("AskUserQuestion"));
1192
+ assert.ok(
1193
+ !disallowedTools.includes("mcp__gsd-workflow__ask_user_questions"),
1194
+ "workflow MCP ask_user_questions should remain enabled by default",
1195
+ );
1196
+ } finally {
1197
+ process.chdir(originalCwd);
1198
+ rmSync(emptyDir, { recursive: true, force: true });
1199
+ restore();
1200
+ }
1201
+ });
1202
+
1143
1203
  test("buildSdkOptions prefers custom workflow MCP question tools over native AskUserQuestion", () => {
1144
1204
  const restore = setWorkflowMcpEnv({
1145
1205
  GSD_WORKFLOW_MCP_COMMAND: "node",
@@ -1662,6 +1722,26 @@ describe("stream-adapter — MCP elicitation bridge", () => {
1662
1722
  });
1663
1723
  });
1664
1724
 
1725
+ test("createClaudeCodeElicitationHandler returns cancel when custom UI is dismissed", async () => {
1726
+ let selectCalls = 0;
1727
+ const handler = createClaudeCodeElicitationHandler({
1728
+ custom: async () => ({
1729
+ endInterview: false,
1730
+ answers: {},
1731
+ }),
1732
+ select: async () => {
1733
+ selectCalls++;
1734
+ return "Cloud-synced";
1735
+ },
1736
+ } as any);
1737
+ assert.ok(handler);
1738
+
1739
+ const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1740
+
1741
+ assert.deepEqual(result, { action: "cancel" });
1742
+ assert.equal(selectCalls, 0, "dismissed custom question must not re-open dialog fallback");
1743
+ });
1744
+
1665
1745
  test("parseTextInputElicitation recognizes secure free-text MCP forms", () => {
1666
1746
  const request = {
1667
1747
  serverName: "gsd-workflow",
@@ -1773,6 +1853,166 @@ describe("stream-adapter — MCP elicitation bridge", () => {
1773
1853
  assert.equal(inputCalls.length, 1);
1774
1854
  assert.equal(inputCalls[0]?.opts?.secure, true, "secure_env_collect fields should request secure input");
1775
1855
  });
1856
+
1857
+ // -- self-cancel loop fix (#2676 / claude-code-cli) ----------------------
1858
+ //
1859
+ // Under claude-code-cli, ask_user_questions arrives as an SDK elicitation,
1860
+ // not an MCP tool dispatch, so the auto-mode watchdogs never saw an in-flight
1861
+ // tool during the human wait and re-dispatched/aborted the turn hosting the
1862
+ // question (the "self-cancel loop"). The fix brackets the human wait with the
1863
+ // interactive-tool guard and disambiguates a system-teardown abort (decline)
1864
+ // from a deliberate user dismissal (cancel).
1865
+
1866
+ test("makes the SDK elicitation visible to the interactive-tool guard during the human wait", async () => {
1867
+ _setAutoActiveForTest(true);
1868
+ clearInFlightTools();
1869
+ try {
1870
+ let countDuringWait = -1;
1871
+ let interactiveDuringWait = false;
1872
+ const handler = createClaudeCodeElicitationHandler({
1873
+ custom: async () => {
1874
+ // Observe the in-flight guard state WHILE the question is open —
1875
+ // this is the window where the watchdogs previously saw 0 tools.
1876
+ countDuringWait = getInFlightToolCount();
1877
+ interactiveDuringWait = hasInteractiveToolInFlight();
1878
+ return {
1879
+ endInterview: false,
1880
+ answers: { storage_scope: { selected: "Cloud-synced", notes: "" }, platform: { selected: ["Web"], notes: "" } },
1881
+ };
1882
+ },
1883
+ } as any);
1884
+ assert.ok(handler);
1885
+
1886
+ const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1887
+
1888
+ assert.equal(countDuringWait, 1, "elicitation must register as an in-flight tool during the human wait");
1889
+ assert.equal(interactiveDuringWait, true, "elicitation must be recognized as an interactive tool during the wait");
1890
+ assert.equal(result.action, "accept");
1891
+ assert.equal(getInFlightToolCount(), 0, "in-flight tool must be cleared after the elicitation resolves");
1892
+ } finally {
1893
+ _setAutoActiveForTest(false);
1894
+ clearInFlightTools();
1895
+ }
1896
+ });
1897
+
1898
+ test("clears the in-flight tool even when the interview UI throws (finally)", async () => {
1899
+ _setAutoActiveForTest(true);
1900
+ clearInFlightTools();
1901
+ try {
1902
+ const handler = createClaudeCodeElicitationHandler({
1903
+ // custom throws -> showInterviewRound rejects -> handler falls back
1904
+ // to dialogs; the in-flight entry must still be cleared via finally.
1905
+ custom: async () => {
1906
+ throw new Error("simulated UI failure");
1907
+ },
1908
+ select: async (_title: string, options: string[], opts?: { allowMultiple?: boolean }) => {
1909
+ if (opts?.allowMultiple) return ["Web"];
1910
+ return options[0];
1911
+ },
1912
+ input: async () => "note",
1913
+ } as any);
1914
+ assert.ok(handler);
1915
+
1916
+ await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1917
+ assert.equal(getInFlightToolCount(), 0, "in-flight tool must be cleared even when the UI throws");
1918
+ } finally {
1919
+ _setAutoActiveForTest(false);
1920
+ clearInFlightTools();
1921
+ }
1922
+ });
1923
+
1924
+ test("returns decline (not cancel) when an interrupt empties the answers", async () => {
1925
+ // A system/host teardown that aborts the signal mid-wait surfaces as
1926
+ // interrupted:true -> the handler must return decline so the model does
1927
+ // not re-ask against a clean user-declined cancel (the re-ask amplifier).
1928
+ const handler = createClaudeCodeElicitationHandler({
1929
+ custom: async () => ({ endInterview: false, answers: {}, interrupted: true }),
1930
+ select: async () => {
1931
+ throw new Error("interrupted elicitation must not re-open dialogs");
1932
+ },
1933
+ } as any);
1934
+ assert.ok(handler);
1935
+
1936
+ const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1937
+ assert.deepEqual(result, { action: "decline" });
1938
+ });
1939
+
1940
+ test("returns cancel (today's semantics) when the user genuinely dismisses", async () => {
1941
+ const handler = createClaudeCodeElicitationHandler({
1942
+ custom: async () => ({ endInterview: false, answers: {} }),
1943
+ } as any);
1944
+ assert.ok(handler);
1945
+
1946
+ const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1947
+ assert.deepEqual(result, { action: "cancel" });
1948
+ });
1949
+
1950
+ test("does not register an in-flight tool outside auto-mode (wrapper self-gates)", async () => {
1951
+ _setAutoActiveForTest(false);
1952
+ clearInFlightTools();
1953
+ let countDuringWait = -1;
1954
+ const handler = createClaudeCodeElicitationHandler({
1955
+ custom: async () => {
1956
+ countDuringWait = getInFlightToolCount();
1957
+ return { endInterview: false, answers: { storage_scope: { selected: "Cloud-synced", notes: "" }, platform: { selected: ["Web"], notes: "" } } };
1958
+ },
1959
+ } as any);
1960
+ assert.ok(handler);
1961
+
1962
+ await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1963
+ assert.equal(countDuringWait, 0, "foreground/non-auto elicitation must not touch the in-flight guard");
1964
+ });
1965
+
1966
+ // The FOREGROUND self-cancel regression guard: the s.active-gated in-flight
1967
+ // guard above is a no-op in foreground, so the foreground approval-gate pause
1968
+ // path needs a SEPARATE, ungated signal to see the elicitation as the active
1969
+ // human boundary. isInteractiveElicitationInFlight() must be true DURING the
1970
+ // wait and false after, even with auto inactive (#cc-elicitation-self-cancel).
1971
+ test("sets the ungated interactive-elicitation marker in FOREGROUND (auto inactive)", async () => {
1972
+ _setAutoActiveForTest(false);
1973
+ clearInFlightTools();
1974
+ try {
1975
+ let markerDuringWait = false;
1976
+ let countDuringWait = -1;
1977
+ const handler = createClaudeCodeElicitationHandler({
1978
+ custom: async () => {
1979
+ markerDuringWait = isInteractiveElicitationInFlight();
1980
+ countDuringWait = getInFlightToolCount();
1981
+ return {
1982
+ endInterview: false,
1983
+ answers: { storage_scope: { selected: "Cloud-synced", notes: "" }, platform: { selected: ["Web"], notes: "" } },
1984
+ };
1985
+ },
1986
+ } as any);
1987
+ assert.ok(handler);
1988
+
1989
+ const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
1990
+
1991
+ assert.equal(markerDuringWait, true, "ungated marker must be true during the foreground human wait");
1992
+ assert.equal(countDuringWait, 0, "the separate marker must NOT touch the in-flight tool count in foreground");
1993
+ assert.equal(result.action, "accept");
1994
+ assert.equal(isInteractiveElicitationInFlight(), false, "marker must clear after the elicitation resolves");
1995
+ } finally {
1996
+ clearInFlightTools();
1997
+ }
1998
+ });
1999
+
2000
+ test("clears the ungated marker even when the interview UI throws in FOREGROUND (finally)", async () => {
2001
+ _setAutoActiveForTest(false);
2002
+ clearInFlightTools();
2003
+ try {
2004
+ const handler = createClaudeCodeElicitationHandler({
2005
+ custom: async () => {
2006
+ throw new Error("ui exploded");
2007
+ },
2008
+ } as any);
2009
+ assert.ok(handler);
2010
+ await handler!(askUserQuestionsRequest, { signal: new AbortController().signal }).catch(() => undefined);
2011
+ assert.equal(isInteractiveElicitationInFlight(), false, "marker must clear via finally on throw");
2012
+ } finally {
2013
+ clearInFlightTools();
2014
+ }
2015
+ });
1776
2016
  });
1777
2017
 
1778
2018
  // ---------------------------------------------------------------------------
@@ -0,0 +1,287 @@
1
+ // gsd-pi - Claude Code logical turn assembly
2
+
3
+ import type {
4
+ AssistantMessage,
5
+ AssistantMessageEvent,
6
+ ToolCall,
7
+ } from "@gsd/pi-ai";
8
+ import { PartialMessageBuilder } from "./partial-builder.js";
9
+ import type {
10
+ BetaRawMessageStreamEvent,
11
+ SDKUserMessage,
12
+ } from "./sdk-types.js";
13
+
14
+ /** A single content block returned by an external (SDK-executed) tool call. */
15
+ export interface ExternalToolResultContentBlock {
16
+ type: string;
17
+ text?: string;
18
+ data?: string;
19
+ mimeType?: string;
20
+ }
21
+
22
+ /** The full result payload returned by an external tool, including content blocks and error status. */
23
+ export interface ExternalToolResultPayload {
24
+ content: ExternalToolResultContentBlock[];
25
+ details?: Record<string, unknown>;
26
+ isError: boolean;
27
+ }
28
+
29
+ /** A `ToolCall` block augmented with the external result attached by the SDK synthetic user message. */
30
+ export type ToolCallWithExternalResult = ToolCall & {
31
+ externalResult?: ExternalToolResultPayload;
32
+ };
33
+
34
+ /** Normalise heterogeneous SDK tool-result content (string, array, or object) into a uniform `ExternalToolResultContentBlock[]`. */
35
+ function normalizeToolResultContent(content: unknown): ExternalToolResultContentBlock[] {
36
+ if (typeof content === "string") {
37
+ return [{ type: "text", text: content }];
38
+ }
39
+
40
+ if (!Array.isArray(content)) {
41
+ if (content == null) return [{ type: "text", text: "" }];
42
+ return [{ type: "text", text: JSON.stringify(content) }];
43
+ }
44
+
45
+ const blocks: ExternalToolResultContentBlock[] = [];
46
+
47
+ for (const item of content) {
48
+ if (typeof item === "string") {
49
+ blocks.push({ type: "text", text: item });
50
+ continue;
51
+ }
52
+ if (!item || typeof item !== "object") {
53
+ blocks.push({ type: "text", text: String(item) });
54
+ continue;
55
+ }
56
+
57
+ const block = item as Record<string, unknown>;
58
+ if (block.type === "text") {
59
+ blocks.push({ type: "text", text: typeof block.text === "string" ? block.text : "" });
60
+ continue;
61
+ }
62
+ if (
63
+ block.type === "image"
64
+ && typeof block.data === "string"
65
+ && typeof block.mimeType === "string"
66
+ ) {
67
+ blocks.push({ type: "image", data: block.data, mimeType: block.mimeType });
68
+ continue;
69
+ }
70
+
71
+ blocks.push({ type: "text", text: JSON.stringify(block) });
72
+ }
73
+
74
+ return blocks.length > 0 ? blocks : [{ type: "text", text: "" }];
75
+ }
76
+
77
+ /**
78
+ * Extract a `details` payload from an MCP tool-result block.
79
+ *
80
+ * MCP's `CallToolResult` carries structured data in `structuredContent` — the
81
+ * protocol's supported channel for non-text payloads. Claude Code's synthetic
82
+ * user message may surface that field in one of two shapes depending on SDK
83
+ * version: as a sibling on the `mcp_tool_result` block itself, or as a
84
+ * dedicated content sub-block with `type: "structuredContent"`. Snake-case
85
+ * (`structured_content`) is accepted defensively in case a transport hop
86
+ * rewrites casing. All other shapes return undefined, matching the nullable
87
+ * `details` contract consumed by tool result renderers.
88
+ */
89
+ function extractStructuredDetailsFromBlock(block: Record<string, unknown>): Record<string, unknown> | undefined {
90
+ const sibling = block.structuredContent ?? (block as Record<string, unknown>).structured_content;
91
+ if (sibling && typeof sibling === "object" && !Array.isArray(sibling)) {
92
+ return sibling as Record<string, unknown>;
93
+ }
94
+
95
+ if (Array.isArray(block.content)) {
96
+ for (const item of block.content) {
97
+ if (!item || typeof item !== "object") continue;
98
+ const sub = item as Record<string, unknown>;
99
+ if (sub.type !== "structuredContent" && sub.type !== "structured_content") continue;
100
+ const payload = sub.structuredContent ?? sub.structured_content ?? sub.data ?? sub.value;
101
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
102
+ return payload as Record<string, unknown>;
103
+ }
104
+ }
105
+ }
106
+
107
+ // Return undefined (not {}) when no structured payload is present, matching
108
+ // the pre-#4477 contract where `details` was nullable. An empty-object
109
+ // sentinel is truthy and breaks downstream consumers that gate on
110
+ // `if (details)`. `undefined` matches the type of the field these results
111
+ // flow into (`Record<string, unknown> | undefined`).
112
+ return undefined;
113
+ }
114
+
115
+ /**
116
+ * True for items that are MCP `structuredContent` pseudo-blocks living inside
117
+ * a tool-result `content[]` array. These blocks carry the structured payload
118
+ * (extracted separately by `extractStructuredDetailsFromBlock`) and must NOT
119
+ * leak into the visible content rendered to the user — otherwise the renderer
120
+ * stringifies the JSON pseudo-block and shows it next to the actual tool
121
+ * output. See PR #4477 review (post-fix-round).
122
+ */
123
+ function isStructuredContentPseudoBlock(item: unknown): boolean {
124
+ if (!item || typeof item !== "object") return false;
125
+ const type = (item as Record<string, unknown>).type;
126
+ return type === "structuredContent" || type === "structured_content";
127
+ }
128
+
129
+ /**
130
+ * Strip `structuredContent` pseudo-blocks from a tool-result content array
131
+ * before normalization. The structured payload is extracted via the sibling
132
+ * `structuredContent` field (or a dedicated extractor pass on the raw block);
133
+ * the visible content path must not include the pseudo-block itself.
134
+ */
135
+ function stripStructuredContentPseudoBlocks(content: unknown): unknown {
136
+ if (!Array.isArray(content)) return content;
137
+ return content.filter((item) => !isStructuredContentPseudoBlock(item));
138
+ }
139
+
140
+ /** Extract tool result payloads from an SDK synthetic user message, keyed by tool-use ID. */
141
+ export function extractToolResultsFromSdkUserMessage(message: SDKUserMessage): Array<{
142
+ toolUseId: string;
143
+ result: ExternalToolResultPayload;
144
+ }> {
145
+ const extracted: Array<{ toolUseId: string; result: ExternalToolResultPayload }> = [];
146
+ const seen = new Set<string>();
147
+ const rawMessage = message.message as Record<string, unknown> | null | undefined;
148
+ const content = Array.isArray(rawMessage?.content) ? rawMessage.content : [];
149
+
150
+ for (const item of content) {
151
+ if (!item || typeof item !== "object") continue;
152
+ const block = item as Record<string, unknown>;
153
+ const type = typeof block.type === "string" ? block.type : "";
154
+ if (type !== "tool_result" && type !== "mcp_tool_result") continue;
155
+
156
+ const toolUseId = typeof block.tool_use_id === "string" ? block.tool_use_id : "";
157
+ if (!toolUseId || seen.has(toolUseId)) continue;
158
+ seen.add(toolUseId);
159
+
160
+ extracted.push({
161
+ toolUseId,
162
+ result: {
163
+ content: normalizeToolResultContent(stripStructuredContentPseudoBlocks(block.content)),
164
+ details: extractStructuredDetailsFromBlock(block),
165
+ isError: block.is_error === true,
166
+ },
167
+ });
168
+ }
169
+
170
+ if (extracted.length === 0) {
171
+ const fallback = message.tool_use_result;
172
+ if (fallback && typeof fallback === "object") {
173
+ const toolResult = fallback as Record<string, unknown>;
174
+ const toolUseId = typeof toolResult.tool_use_id === "string" ? toolResult.tool_use_id : "";
175
+ if (toolUseId) {
176
+ extracted.push({
177
+ toolUseId,
178
+ result: {
179
+ content: normalizeToolResultContent(stripStructuredContentPseudoBlocks(toolResult.content)),
180
+ details: extractStructuredDetailsFromBlock(toolResult),
181
+ isError: toolResult.is_error === true,
182
+ },
183
+ });
184
+ }
185
+ }
186
+ }
187
+
188
+ return extracted;
189
+ }
190
+
191
+ /** Attach external tool results from the SDK synthetic user message to their corresponding tool-call blocks by ID. */
192
+ export function attachExternalResultsToToolBlocks(
193
+ toolBlocks: AssistantMessage["content"],
194
+ toolResultsById: ReadonlyMap<string, ExternalToolResultPayload>,
195
+ ): void {
196
+ for (const block of toolBlocks) {
197
+ if (block.type !== "toolCall" && block.type !== "serverToolUse") continue;
198
+ const externalResult = toolResultsById.get(block.id);
199
+ if (!externalResult) continue;
200
+ (block as ToolCallWithExternalResult & { id: string }).externalResult = externalResult;
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Build the final assistant content that Agent Core consumes in
206
+ * `externalToolExecution` mode. This preserves tool-call blocks, attaches any
207
+ * SDK-produced external results by tool-call id, and then appends the final
208
+ * text/thinking blocks for the completed turn.
209
+ */
210
+ export function buildFinalAssistantContent(params: {
211
+ intermediateToolBlocks: AssistantMessage["content"];
212
+ pendingContent?: AssistantMessage["content"];
213
+ toolResultsById: ReadonlyMap<string, ExternalToolResultPayload>;
214
+ lastThinkingContent?: string;
215
+ lastTextContent?: string;
216
+ fallbackResultText?: string;
217
+ }): AssistantMessage["content"] {
218
+ const mergedToolBlocks = [...params.intermediateToolBlocks];
219
+ if (params.pendingContent) {
220
+ mergePendingToolCalls(mergedToolBlocks, params.pendingContent);
221
+ }
222
+ attachExternalResultsToToolBlocks(mergedToolBlocks, params.toolResultsById);
223
+
224
+ const finalContent: AssistantMessage["content"] = [...mergedToolBlocks];
225
+ if (params.pendingContent && params.pendingContent.length > 0) {
226
+ for (const block of params.pendingContent) {
227
+ if (block.type === "text" || block.type === "thinking") {
228
+ finalContent.push(block);
229
+ }
230
+ }
231
+ } else {
232
+ if (params.lastThinkingContent) {
233
+ finalContent.push({ type: "thinking", thinking: params.lastThinkingContent });
234
+ }
235
+ if (params.lastTextContent) {
236
+ finalContent.push({ type: "text", text: params.lastTextContent });
237
+ }
238
+ }
239
+
240
+ if (finalContent.length === 0 && params.fallbackResultText) {
241
+ finalContent.push({ type: "text", text: params.fallbackResultText });
242
+ }
243
+
244
+ return finalContent;
245
+ }
246
+
247
+ /**
248
+ * Merge tool-call blocks from the active partial-message builder into the
249
+ * running list of intermediate tool calls, preserving order and de-duping
250
+ * by tool-call id. Exposed for testing the F3 fix (final-turn tool calls
251
+ * dropped when `result` arrives without a preceding synthetic `user`).
252
+ */
253
+ export function mergePendingToolCalls(
254
+ intermediate: AssistantMessage["content"],
255
+ pending: AssistantMessage["content"],
256
+ ): AssistantMessage["content"] {
257
+ const alreadyIncluded = new Set<string>();
258
+ for (const block of intermediate) {
259
+ if (block.type === "toolCall") alreadyIncluded.add(block.id);
260
+ }
261
+ for (const block of pending) {
262
+ if (block.type !== "toolCall") continue;
263
+ if (alreadyIncluded.has(block.id)) continue;
264
+ alreadyIncluded.add(block.id);
265
+ intermediate.push(block);
266
+ }
267
+ return intermediate;
268
+ }
269
+
270
+ export function handleClaudeCodePartialStreamEvent(
271
+ builder: PartialMessageBuilder | null,
272
+ event: BetaRawMessageStreamEvent,
273
+ modelId: string,
274
+ ): { builder: PartialMessageBuilder | null; assistantEvent: AssistantMessageEvent | null } {
275
+ if (event.type === "message_start") {
276
+ // Claude Code can emit repeated SDK message_start events inside one
277
+ // logical assistant response. Keep appending until a synthetic user
278
+ // tool-result boundary explicitly clears the builder.
279
+ return {
280
+ builder: builder ?? new PartialMessageBuilder((event as any).message?.model ?? modelId),
281
+ assistantEvent: null,
282
+ };
283
+ }
284
+
285
+ if (!builder) return { builder, assistantEvent: null };
286
+ return { builder, assistantEvent: builder.handleEvent(event) };
287
+ }
@@ -9,7 +9,7 @@
9
9
  * for the `gh` CLI body parameters.
10
10
  */
11
11
 
12
- import { buildPrEvidence } from "../gsd/pr-evidence.js";
12
+ import { buildPullRequestEvidence } from "../gsd/pull-request-process.js";
13
13
 
14
14
  // ─── Helpers ────────────────────────────────────────────────────────────────
15
15
 
@@ -116,7 +116,7 @@ export function formatSlicePRBody(data: SliceData): string {
116
116
  .filter((issueNumber): issueNumber is number => typeof issueNumber === "number")
117
117
  .map((issueNumber) => `#${issueNumber}`);
118
118
 
119
- return buildPrEvidence({
119
+ return buildPullRequestEvidence({
120
120
  milestoneId: data.id,
121
121
  subjectId: data.id,
122
122
  subjectKind: "slice",
@@ -267,7 +267,7 @@ export function formatSwarmLanePRBody(data: SwarmLanePRData): string {
267
267
  `### Transition risks\n${checkedList(data.transitionRisks, "No transition risks identified").join("\n")}`,
268
268
  ].filter(Boolean);
269
269
 
270
- return buildPrEvidence({
270
+ return buildPullRequestEvidence({
271
271
  milestoneId: laneLabel,
272
272
  subjectId: laneLabel,
273
273
  subjectKind: "workflow",
@@ -62,7 +62,7 @@ describe("templates", () => {
62
62
  assert.ok(body.includes("- Session type"));
63
63
  assert.ok(body.includes("## Linked Issue"));
64
64
  assert.ok(body.includes("## Tests Run"));
65
- assert.ok(body.includes("## AI Assistance Disclosure"));
65
+ assert.ok(!body.includes("## AI Assistance Disclosure"));
66
66
  });
67
67
 
68
68
  it("renders task checklist with issue links", () => {
@@ -173,7 +173,7 @@ describe("templates", () => {
173
173
  assert.ok(body.includes("- Disable writer sequence enrichment"));
174
174
  assert.ok(body.includes("- npm run typecheck:extensions"));
175
175
  assert.ok(body.includes("Closes #123"));
176
- assert.ok(body.includes("## AI Assistance Disclosure"));
176
+ assert.ok(!body.includes("## AI Assistance Disclosure"));
177
177
  });
178
178
 
179
179
  it("formats release checklist bodies from lane state", () => {
@@ -0,0 +1,31 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Resolves closeout artifact projection roots across project and milestone worktrees.
3
+
4
+ import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
5
+ import { resolveWorktreeProjectRoot } from "./worktree-root.js";
6
+
7
+ export interface CloseoutArtifactProjectionInput {
8
+ milestoneId: string;
9
+ basePath: string;
10
+ originalBasePath?: string;
11
+ }
12
+
13
+ export interface CloseoutArtifactProjection {
14
+ projectRoot: string;
15
+ canonicalMilestoneRoot: string;
16
+ summaryArtifactBasePath: string;
17
+ gateEvidenceBasePath: string;
18
+ }
19
+
20
+ export function resolveCloseoutArtifactProjection(
21
+ input: CloseoutArtifactProjectionInput,
22
+ ): CloseoutArtifactProjection {
23
+ const projectRoot = resolveWorktreeProjectRoot(input.basePath, input.originalBasePath);
24
+ const canonicalMilestoneRoot = resolveCanonicalMilestoneRoot(projectRoot, input.milestoneId);
25
+ return {
26
+ projectRoot,
27
+ canonicalMilestoneRoot,
28
+ summaryArtifactBasePath: canonicalMilestoneRoot,
29
+ gateEvidenceBasePath: canonicalMilestoneRoot,
30
+ };
31
+ }
@@ -27,13 +27,43 @@ export interface AutoStatus {
27
27
  transitionCount: number;
28
28
  }
29
29
 
30
+ export type AutoTerminalOutcome =
31
+ | {
32
+ code: "all-complete";
33
+ displayReason: string;
34
+ allMilestonesComplete: true;
35
+ }
36
+ | {
37
+ code: "no-remaining-units";
38
+ displayReason: string;
39
+ allMilestonesComplete: false;
40
+ }
41
+ | {
42
+ code: "settlement-blocked";
43
+ displayReason: string;
44
+ nextAction: string;
45
+ milestoneId: string;
46
+ allMilestonesComplete: false;
47
+ };
48
+
30
49
  export type AutoAdvanceResult =
31
50
  | { kind: "started" }
32
51
  | { kind: "resumed" }
33
52
  | { kind: "advanced"; unit: UnitRef; stateSnapshot: GSDState }
34
53
  | { kind: "skipped"; reason: string; stateSnapshot?: GSDState }
35
- | { kind: "blocked"; reason: string; action: "pause" | "stop"; stateSnapshot?: GSDState }
36
- | { kind: "stopped"; reason: string; stateSnapshot?: GSDState }
54
+ | {
55
+ kind: "blocked";
56
+ reason: string;
57
+ action: "pause" | "stop";
58
+ stateSnapshot?: GSDState;
59
+ terminalOutcome?: AutoTerminalOutcome;
60
+ }
61
+ | {
62
+ kind: "stopped";
63
+ reason: string;
64
+ stateSnapshot?: GSDState;
65
+ terminalOutcome?: AutoTerminalOutcome;
66
+ }
37
67
  | { kind: "paused"; reason: string }
38
68
  | { kind: "error"; reason: string };
39
69