@opengsd/gsd-pi 1.2.0-dev.e8563f58 → 1.2.0-dev.fbdca60b

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 (432) hide show
  1. package/dist/cli-model-override.d.ts +15 -0
  2. package/dist/cli-model-override.js +21 -0
  3. package/dist/cli.js +1 -18
  4. package/dist/loader.js +6 -4
  5. package/dist/register-agent-bundles.d.ts +11 -2
  6. package/dist/register-agent-bundles.js +18 -4
  7. package/dist/resource-loader.d.ts +10 -5
  8. package/dist/resource-loader.js +121 -6
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/ask-user-questions.js +3 -2
  11. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
  12. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
  13. package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
  14. package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
  15. package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
  16. package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  18. package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
  19. package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
  20. package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
  21. package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
  22. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
  23. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  24. package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
  25. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
  26. package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +3 -2
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -2
  29. package/dist/resources/extensions/gsd/auto-post-unit.js +18 -6
  30. package/dist/resources/extensions/gsd/auto-start.js +23 -3
  31. package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
  32. package/dist/resources/extensions/gsd/auto-verification.js +14 -2
  33. package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
  34. package/dist/resources/extensions/gsd/auto.js +45 -2
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
  36. package/dist/resources/extensions/gsd/commands/context.js +16 -2
  37. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
  38. package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
  39. package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
  40. package/dist/resources/extensions/gsd/db/engine.js +24 -6
  41. package/dist/resources/extensions/gsd/db/queries.js +30 -0
  42. package/dist/resources/extensions/gsd/db-migration-backup.js +51 -8
  43. package/dist/resources/extensions/gsd/db-transaction.js +27 -23
  44. package/dist/resources/extensions/gsd/db-writer.js +8 -17
  45. package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
  46. package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
  47. package/dist/resources/extensions/gsd/gsd-db.js +15 -20
  48. package/dist/resources/extensions/gsd/guided-flow.js +93 -4
  49. package/dist/resources/extensions/gsd/health-widget.js +87 -28
  50. package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
  51. package/dist/resources/extensions/gsd/memory-relations.js +1 -1
  52. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
  53. package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
  54. package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
  55. package/dist/resources/extensions/gsd/notifications.js +12 -7
  56. package/dist/resources/extensions/gsd/projection-flush.js +7 -0
  57. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  58. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  59. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
  62. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  68. package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -1
  69. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  71. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  72. package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
  73. package/dist/resources/extensions/gsd/session-lock.js +1 -1
  74. package/dist/resources/extensions/gsd/skill-activation.js +3 -6
  75. package/dist/resources/extensions/gsd/state.js +6 -2
  76. package/dist/resources/extensions/gsd/tool-contract.js +14 -3
  77. package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
  78. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  79. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  80. package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
  81. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
  82. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
  83. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
  84. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  85. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
  86. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
  87. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
  88. package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
  89. package/dist/resources/extensions/gsd/unit-registry.js +34 -4
  90. package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
  91. package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
  92. package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
  93. package/dist/resources/extensions/gsd/workflow-events.js +6 -18
  94. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
  95. package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
  96. package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
  97. package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
  98. package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
  99. package/dist/resources/extensions/gsd/worktree.js +8 -1
  100. package/dist/resources/extensions/mcp-client/manager.js +6 -1
  101. package/dist/resources/skills/create-skill/SKILL.md +3 -0
  102. package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
  103. package/dist/runtime-checks.d.ts +10 -0
  104. package/dist/runtime-checks.js +27 -0
  105. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  106. package/dist/web/standalone/.next/BUILD_ID +1 -1
  107. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  108. package/dist/web/standalone/.next/build-manifest.json +2 -2
  109. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  110. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  134. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  136. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/index.html +1 -1
  161. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  163. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  164. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  168. package/dist/web/standalone/.next/server/chunks/{5942.js → 1128.js} +1 -1
  169. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  170. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  172. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  173. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  174. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  175. package/package.json +3 -3
  176. package/packages/cloud-mcp-gateway/package.json +2 -2
  177. package/packages/contracts/package.json +1 -1
  178. package/packages/daemon/package.json +4 -4
  179. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  180. package/packages/gsd-agent-core/dist/sdk.js +6 -4
  181. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  182. package/packages/gsd-agent-core/package.json +5 -5
  183. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
  194. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +17 -0
  198. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  202. package/packages/gsd-agent-modes/package.json +7 -7
  203. package/packages/mcp-server/README.md +12 -3
  204. package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
  205. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
  206. package/packages/mcp-server/dist/cli-runner.js +137 -0
  207. package/packages/mcp-server/dist/cli-runner.js.map +1 -0
  208. package/packages/mcp-server/dist/cli.js +2 -53
  209. package/packages/mcp-server/dist/cli.js.map +1 -1
  210. package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
  211. package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
  212. package/packages/mcp-server/dist/pid-registry.js +459 -0
  213. package/packages/mcp-server/dist/pid-registry.js.map +1 -0
  214. package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
  215. package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
  216. package/packages/mcp-server/dist/probe-mode.js +10 -0
  217. package/packages/mcp-server/dist/probe-mode.js.map +1 -0
  218. package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
  219. package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
  220. package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
  221. package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
  222. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  223. package/packages/mcp-server/dist/workflow-tools.js +62 -43
  224. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  225. package/packages/mcp-server/package.json +5 -5
  226. package/packages/native/package.json +1 -1
  227. package/packages/pi-agent-core/dist/agent-loop.js +43 -2
  228. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  229. package/packages/pi-agent-core/package.json +1 -1
  230. package/packages/pi-ai/package.json +1 -1
  231. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  232. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  234. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
  237. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  238. package/packages/pi-coding-agent/package.json +7 -7
  239. package/packages/pi-tui/README.md +15 -0
  240. package/packages/pi-tui/dist/index.d.ts +2 -2
  241. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  242. package/packages/pi-tui/dist/index.js +2 -2
  243. package/packages/pi-tui/dist/index.js.map +1 -1
  244. package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
  245. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  246. package/packages/pi-tui/dist/terminal-image.js +54 -2
  247. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  248. package/packages/pi-tui/dist/terminal.d.ts +12 -0
  249. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  250. package/packages/pi-tui/dist/terminal.js +70 -25
  251. package/packages/pi-tui/dist/terminal.js.map +1 -1
  252. package/packages/pi-tui/dist/tui.d.ts +15 -0
  253. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  254. package/packages/pi-tui/dist/tui.js +106 -21
  255. package/packages/pi-tui/dist/tui.js.map +1 -1
  256. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  257. package/packages/pi-tui/dist/utils.js +110 -36
  258. package/packages/pi-tui/dist/utils.js.map +1 -1
  259. package/packages/pi-tui/package.json +2 -2
  260. package/packages/rpc-client/package.json +2 -2
  261. package/pkg/dist/theme/theme.d.ts.map +1 -1
  262. package/pkg/dist/theme/theme.js +45 -17
  263. package/pkg/dist/theme/theme.js.map +1 -1
  264. package/pkg/package.json +1 -1
  265. package/src/resources/extensions/ask-user-questions.ts +7 -2
  266. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
  267. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
  268. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
  269. package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
  270. package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
  271. package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
  272. package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
  273. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  274. package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
  275. package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
  276. package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
  277. package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
  278. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
  279. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  280. package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
  281. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
  282. package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +10 -16
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +11 -10
  285. package/src/resources/extensions/gsd/auto-post-unit.ts +21 -6
  286. package/src/resources/extensions/gsd/auto-start.ts +24 -4
  287. package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
  288. package/src/resources/extensions/gsd/auto-verification.ts +18 -2
  289. package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
  290. package/src/resources/extensions/gsd/auto.ts +56 -2
  291. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
  292. package/src/resources/extensions/gsd/commands/context.ts +16 -2
  293. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
  294. package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
  295. package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
  296. package/src/resources/extensions/gsd/db/engine.ts +26 -6
  297. package/src/resources/extensions/gsd/db/queries.ts +29 -0
  298. package/src/resources/extensions/gsd/db-migration-backup.ts +56 -7
  299. package/src/resources/extensions/gsd/db-transaction.ts +37 -20
  300. package/src/resources/extensions/gsd/db-writer.ts +11 -19
  301. package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
  302. package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
  303. package/src/resources/extensions/gsd/gsd-db.ts +15 -19
  304. package/src/resources/extensions/gsd/guided-flow.ts +145 -24
  305. package/src/resources/extensions/gsd/health-widget.ts +91 -27
  306. package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
  307. package/src/resources/extensions/gsd/memory-relations.ts +1 -1
  308. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
  309. package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
  310. package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
  311. package/src/resources/extensions/gsd/notifications.ts +13 -6
  312. package/src/resources/extensions/gsd/projection-flush.ts +20 -0
  313. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  314. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  315. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  316. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
  318. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  319. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  320. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  321. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  322. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  323. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  324. package/src/resources/extensions/gsd/prompts/run-uat.md +3 -1
  325. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  326. package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  327. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  328. package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
  329. package/src/resources/extensions/gsd/session-lock.ts +1 -1
  330. package/src/resources/extensions/gsd/skill-activation.ts +3 -6
  331. package/src/resources/extensions/gsd/state.ts +7 -1
  332. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
  334. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
  335. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +6 -1
  336. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
  337. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
  339. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
  340. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
  341. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
  342. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
  343. package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
  344. package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +68 -19
  345. package/src/resources/extensions/gsd/tests/db-transaction.test.ts +59 -0
  346. package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
  347. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
  348. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +62 -0
  349. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
  350. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
  351. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
  352. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
  353. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
  354. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +19 -0
  355. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
  356. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
  357. package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
  358. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
  359. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
  360. package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
  361. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
  362. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
  363. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
  365. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
  367. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
  368. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
  369. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
  370. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
  371. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
  372. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
  373. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
  374. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
  375. package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
  376. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
  377. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
  378. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
  379. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
  380. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
  381. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
  382. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
  383. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
  384. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
  385. package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
  386. package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
  387. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
  388. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  389. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
  390. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
  391. package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
  392. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
  393. package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
  394. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
  395. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
  396. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
  397. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
  398. package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
  399. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
  400. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
  401. package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
  402. package/src/resources/extensions/gsd/tool-contract.ts +38 -3
  403. package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
  404. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  405. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  406. package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
  407. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
  408. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
  409. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
  410. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  411. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
  412. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
  413. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
  414. package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
  415. package/src/resources/extensions/gsd/unit-registry.ts +34 -4
  416. package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
  417. package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
  418. package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
  419. package/src/resources/extensions/gsd/workflow-events.ts +12 -20
  420. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
  421. package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
  422. package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
  423. package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
  424. package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
  425. package/src/resources/extensions/gsd/worktree.ts +7 -1
  426. package/src/resources/extensions/mcp-client/manager.ts +7 -1
  427. package/src/resources/skills/create-skill/SKILL.md +3 -0
  428. package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
  429. package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
  430. package/src/resources/skills/gsd-browser/SKILL.md +0 -41
  431. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_buildManifest.js +0 -0
  432. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_ssgManifest.js +0 -0
@@ -32,6 +32,7 @@ import {
32
32
  buildFinalAssistantContent,
33
33
  extractToolResultsFromSdkUserMessage,
34
34
  handleClaudeCodePartialStreamEvent,
35
+ shouldSuppressDuplicateToolUnavailableBlock,
35
36
  } from "./turn-assembler.js";
36
37
  import type {
37
38
  ExternalToolResultPayload,
@@ -59,7 +60,20 @@ import {
59
60
  computeMcpDisallowedTools,
60
61
  } from "../gsd/mcp-filter.js";
61
62
  import { RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES, resolveToolPresentationPlan } from "../gsd/tool-presentation-plan.js";
62
- import { getToolSurfaceReadinessError } from "../gsd/tool-surface-readiness.js";
63
+ import { getUnitToolSurfaceContract } from "../gsd/unit-tool-contracts.js";
64
+ import {
65
+ awaitWorkflowMcpToolRegistration,
66
+ getToolSurfaceReadinessError,
67
+ POST_PREFLIGHT_READINESS_RETRY_DELAYS_MS,
68
+ POST_PREFLIGHT_SDK_SETTLE_MS,
69
+ TOOL_SURFACE_NOT_READY,
70
+ type LiveToolSurfaceObservation,
71
+ } from "../gsd/tool-surface-readiness.js";
72
+ import {
73
+ beginWorkflowMcpSdkSession,
74
+ endWorkflowMcpSdkSession,
75
+ } from "../gsd/workflow-mcp-readiness-cache.js";
76
+ import { getGuidedUnitContext } from "../gsd/guided-unit-context.js";
63
77
  import { hasBrowserContractPrefix } from "../shared/browser-contract.js";
64
78
  import { showInterviewRound, type Question, type RoundResult } from "../shared/tui.js";
65
79
  import type {
@@ -75,6 +89,7 @@ export {
75
89
  extractToolResultsFromSdkUserMessage,
76
90
  handleClaudeCodePartialStreamEvent,
77
91
  mergePendingToolCalls,
92
+ shouldSuppressDuplicateToolUnavailableBlock,
78
93
  } from "./turn-assembler.js";
79
94
  export type {
80
95
  ExternalToolResultContentBlock,
@@ -92,6 +107,11 @@ interface ClaudeCodeStreamOptions extends SimpleStreamOptions {
92
107
  extensionUIContext?: ExtensionUIContext;
93
108
  onExternalToolCall?: (toolCall: ToolCall) => Promise<void> | void;
94
109
  onExternalToolResult?: (event: { toolCall: ToolCall; result: ExternalToolResultPayload }) => Promise<void> | void;
110
+ _sdkQueryForTest?: (args: {
111
+ prompt: string | AsyncIterable<unknown>;
112
+ options?: Record<string, unknown>;
113
+ }) => AsyncIterable<SDKMessage>;
114
+ _skipWorkflowMcpPreflightForTest?: boolean;
95
115
  }
96
116
 
97
117
  export function serverToolUseToToolCallLike(block: {
@@ -352,13 +372,18 @@ const GSD_PHASE_PATTERNS: Array<[string, RegExp]> = [
352
372
  ["reassess-roadmap", /\b(?:UNIT:\s*Reassess Roadmap|reassess-roadmap)\b/i],
353
373
  ["complete-slice", /\b(?:UNIT:\s*Complete Slice|complete-slice)\b/i],
354
374
  ["replan-slice", /\b(?:UNIT:\s*Replan Slice|replan-slice)\b/i],
375
+ ["refine-slice", /\b(?:UNIT:\s*Refine Slice|refine-slice)\b/i],
355
376
  ["plan-slice", /\b(?:UNIT:\s*Plan Slice|plan-slice|gsd_plan_slice)\b/i],
356
377
  ["plan-milestone", /\b(?:UNIT:\s*Plan Milestone|plan-milestone|gsd_plan_milestone)\b/i],
357
378
  ["execute-task", /\b(?:UNIT:\s*Execute Task|execute-task|execute-task-simple|reactive-execute)\b/i],
358
379
  ["gate-evaluate", /\b(?:UNIT:\s*Gate Evaluate|gate-evaluate|gsd_save_gate_result)\b/i],
359
380
  ["research-milestone", /\b(?:UNIT:\s*Research Milestone|research-milestone)\b/i],
360
381
  ["research-slice", /\b(?:UNIT:\s*Research Slice|research-slice)\b/i],
382
+ ["research-decision", /\b(?:research decision|research-decision)\b/i],
361
383
  ["discuss-milestone", /\b(?:Discuss milestone|discuss-milestone)\b/i],
384
+ ["discuss-slice", /\b(?:Discuss slice|discuss-slice)\b/i],
385
+ ["discuss-project", /\b(?:discuss-project|Discuss project)\b/i],
386
+ ["discuss-requirements", /\b(?:discuss-requirements|Discuss requirements)\b/i],
362
387
  ];
363
388
 
364
389
  export function inferGsdPhaseFromContext(context: Context): string | undefined {
@@ -372,6 +397,26 @@ export function inferGsdPhaseFromContext(context: Context): string | undefined {
372
397
  return undefined;
373
398
  }
374
399
 
400
+ /**
401
+ * Resolve the GSD unit phase for Claude Code SDK sessions. Guided-flow
402
+ * dispatch records the authoritative unit type before the turn is queued;
403
+ * prefer that over regex inference from prompt text.
404
+ */
405
+ export function resolveGsdPhaseForSdk(context: Context, projectRoot: string): string | undefined {
406
+ const resolvedRoot = resolveWorkflowMcpProjectRoot(projectRoot);
407
+ const guided =
408
+ getGuidedUnitContext(resolvedRoot)
409
+ ?? getGuidedUnitContext(projectRoot)
410
+ ?? getGuidedUnitContext();
411
+ if (guided?.unitType) {
412
+ const guidedRoot = resolveWorkflowMcpProjectRoot(guided.basePath);
413
+ if (guidedRoot === resolvedRoot) {
414
+ return guided.unitType;
415
+ }
416
+ }
417
+ return inferGsdPhaseFromContext(context);
418
+ }
419
+
375
420
  /**
376
421
  * Build a full conversational prompt from GSD's context messages.
377
422
  *
@@ -397,10 +442,15 @@ export function buildPromptFromContext(context: Context, toolContext: PromptTool
397
442
  const workflowToolLine = toolContext.workflowMcpServerName
398
443
  ? "- GSD workflow tools (gsd_exec, gsd_slice_complete, gsd_task_complete, gsd_plan_slice, gsd_save_gate_result, etc.) " +
399
444
  `are MCP tools — call them as mcp__${toolContext.workflowMcpServerName}__<tool_name> ` +
400
- `(e.g. mcp__${toolContext.workflowMcpServerName}__gsd_exec, mcp__${toolContext.workflowMcpServerName}__gsd_save_gate_result)\n`
445
+ `(e.g. mcp__${toolContext.workflowMcpServerName}__gsd_exec, mcp__${toolContext.workflowMcpServerName}__gsd_save_gate_result)\n` +
446
+ `- Structured user input: call mcp__${toolContext.workflowMcpServerName}__ask_user_questions. ` +
447
+ "Do not call bare ask_user_questions. Do not call native AskUserQuestion.\n"
401
448
  : "- GSD workflow MCP tools are unavailable in this Claude Code run.\n";
402
449
  const toolSearchLine = toolContext.workflowMcpServerName
403
- ? "- ToolSearch is NOT available never use it to discover tools; invoke the MCP tool directly\n"
450
+ ? "- ToolSearch is available only for Claude Code deferred workflow MCP hydration. " +
451
+ `If mcp__${toolContext.workflowMcpServerName}__<tool_name> is missing or Claude Code reports the server is still connecting, ` +
452
+ `use ToolSearch with select:mcp__${toolContext.workflowMcpServerName}__<tool_name> or the base tool name, then call the returned MCP tool directly. ` +
453
+ "Do not use ToolSearch for browser_* tools or general discovery.\n"
404
454
  : "- ToolSearch is NOT available — never use it to discover tools\n";
405
455
  const browserToolLine = toolContext.browserMcpServerName
406
456
  ? "- Browser verification uses gsd-browser MCP by default — call browser tools as " +
@@ -511,9 +561,11 @@ export function buildSdkQueryPrompt(
511
561
  parent_tool_use_id: null,
512
562
  };
513
563
 
514
- return (async function* () {
515
- yield sdkMessage;
516
- })();
564
+ return {
565
+ async *[Symbol.asyncIterator]() {
566
+ yield sdkMessage;
567
+ },
568
+ };
517
569
  }
518
570
 
519
571
  // ---------------------------------------------------------------------------
@@ -535,6 +587,59 @@ function makeErrorMessage(model: string, errorMsg: string): AssistantMessage {
535
587
  };
536
588
  }
537
589
 
590
+ export interface WorkflowMcpReadinessProgressState {
591
+ contentIndex?: number;
592
+ }
593
+
594
+ export function buildWorkflowMcpReadinessProgressMessage(input: {
595
+ unitType: string;
596
+ workflowServerName: string;
597
+ stage: "preflight" | "retry";
598
+ attempt?: number;
599
+ delayMs?: number;
600
+ }): string {
601
+ const { unitType, workflowServerName, stage } = input;
602
+ if (stage === "preflight") {
603
+ return `Starting ${workflowServerName} MCP for ${unitType}; waiting for workflow tools to register...`;
604
+ }
605
+
606
+ const attempt = input.attempt === undefined ? "" : ` attempt ${input.attempt}`;
607
+ const delay = input.delayMs === undefined ? "" : ` Retrying in ${formatReadinessDelay(input.delayMs)}.`;
608
+ return `Still waiting for ${workflowServerName} MCP tools for ${unitType}${attempt}.${delay}`;
609
+ }
610
+
611
+ function formatReadinessDelay(delayMs: number): string {
612
+ if (delayMs < 1_000) return `${delayMs}ms`;
613
+ const seconds = delayMs / 1_000;
614
+ return Number.isInteger(seconds) ? `${seconds}s` : `${seconds.toFixed(1)}s`;
615
+ }
616
+
617
+ export function pushWorkflowMcpReadinessProgressEvent(input: {
618
+ stream: Pick<AssistantMessageEventStream, "push">;
619
+ partial: AssistantMessage;
620
+ state: WorkflowMcpReadinessProgressState;
621
+ message: string;
622
+ }): void {
623
+ const { stream, partial, state, message } = input;
624
+ if (!message) return;
625
+
626
+ let contentIndex = state.contentIndex;
627
+ const existing = contentIndex === undefined ? undefined : partial.content[contentIndex];
628
+ if (contentIndex === undefined || existing?.type !== "text") {
629
+ contentIndex = partial.content.length;
630
+ state.contentIndex = contentIndex;
631
+ partial.content.push({ type: "text", text: "" });
632
+ stream.push({ type: "text_start", contentIndex, partial });
633
+ }
634
+
635
+ const block = partial.content[contentIndex];
636
+ if (block.type !== "text") return;
637
+
638
+ const delta = block.text.length === 0 ? message : `\n${message}`;
639
+ block.text += delta;
640
+ stream.push({ type: "text_delta", contentIndex, delta, partial });
641
+ }
642
+
538
643
  export function isClaudeCodeAbortErrorMessage(message: string | undefined | null): boolean {
539
644
  if (!message) return false;
540
645
  return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user|aborterror)\b/i.test(message);
@@ -1663,11 +1768,15 @@ function resolveExactWorkflowMcpToolsForPhase(
1663
1768
  workflowExplicitlyBlocked: boolean,
1664
1769
  ): string[] {
1665
1770
  if (!gsdPhase || !workflowServerName || workflowExplicitlyBlocked) return [];
1666
- const requiredTools = gsdPhase === "run-uat"
1771
+ const requestedTools = gsdPhase === "run-uat"
1667
1772
  ? [...RUN_UAT_WORKFLOW_TOOL_NAMES]
1668
- : getRequiredWorkflowToolsForAutoUnit(gsdPhase);
1669
- const supportTools = gsdPhase === "run-uat" ? [] : ["gsd_milestone_status"];
1670
- const requestedToolNames = [...new Set([...requiredTools, ...supportTools])];
1773
+ : [
1774
+ ...(getUnitToolSurfaceContract(gsdPhase)?.allowedGsdTools ?? []),
1775
+ ...getRequiredWorkflowToolsForAutoUnit(gsdPhase),
1776
+ ];
1777
+ const requestedToolNames = [...new Set(
1778
+ requestedTools.filter((toolName) => toolName.startsWith("gsd_") || toolName === "ask_user_questions"),
1779
+ )];
1671
1780
  if (requestedToolNames.length === 0) return [];
1672
1781
  return resolveToolPresentationPlan({
1673
1782
  phase: gsdPhase,
@@ -1677,6 +1786,25 @@ function resolveExactWorkflowMcpToolsForPhase(
1677
1786
  }).presentedToolNames;
1678
1787
  }
1679
1788
 
1789
+ function resolveClaudeNativeToolsForPhase(gsdPhase: string | undefined): string[] {
1790
+ const standardClaudeTools = [
1791
+ "Read",
1792
+ "Write",
1793
+ "Edit",
1794
+ "Glob",
1795
+ "Grep",
1796
+ "Bash",
1797
+ "Agent",
1798
+ "WebFetch",
1799
+ "WebSearch",
1800
+ ];
1801
+ if (!gsdPhase) return standardClaudeTools;
1802
+ if (gsdPhase === "run-uat" || gsdPhase === "complete-slice") {
1803
+ return [...RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES];
1804
+ }
1805
+ return standardClaudeTools;
1806
+ }
1807
+
1680
1808
  export function autoInitClaudeCodeWorkflowMcp(cwd: string): void {
1681
1809
  const projectRoot = resolveWorkflowMcpProjectRoot(cwd);
1682
1810
  try {
@@ -1690,6 +1818,70 @@ export function autoInitClaudeCodeWorkflowMcp(cwd: string): void {
1690
1818
  }
1691
1819
  }
1692
1820
 
1821
+ export function resolveClaudeCodeToolSurfaceReadinessError(input: {
1822
+ unitType: string | undefined;
1823
+ workflowServerName: string | undefined;
1824
+ observation: LiveToolSurfaceObservation;
1825
+ projectRoot?: string | undefined;
1826
+ allowPendingToolSearchHydration?: boolean | undefined;
1827
+ }): Promise<string | null> {
1828
+ const error = getToolSurfaceReadinessError(input);
1829
+ if (
1830
+ error
1831
+ && input.allowPendingToolSearchHydration
1832
+ && input.workflowServerName
1833
+ && input.observation.mcpServers.some(
1834
+ (server) => server.name === input.workflowServerName && server.status === "pending",
1835
+ )
1836
+ ) {
1837
+ return Promise.resolve(null);
1838
+ }
1839
+ return Promise.resolve(error);
1840
+ }
1841
+
1842
+ export { awaitWorkflowMcpToolRegistration } from "../gsd/tool-surface-readiness.js";
1843
+
1844
+ const TOOL_SURFACE_READINESS_RETRY_DELAYS_MS = [500, 1_000, 2_000, 4_000, 8_000, 15_000, 15_000, 15_000] as const;
1845
+
1846
+ export function shouldRetryClaudeCodeToolSurfaceReadiness(error: string | null): boolean {
1847
+ if (!error) return false;
1848
+ return error.includes(TOOL_SURFACE_NOT_READY) && !/\bterminal\b/i.test(error);
1849
+ }
1850
+
1851
+ export function resolveClaudeCodeToolSurfaceReadinessRetryDelayMs(
1852
+ error: string | null,
1853
+ attempt: number,
1854
+ preflightVerified = false,
1855
+ ): number | null {
1856
+ if (!shouldRetryClaudeCodeToolSurfaceReadiness(error)) return null;
1857
+ const delays = preflightVerified
1858
+ ? POST_PREFLIGHT_READINESS_RETRY_DELAYS_MS
1859
+ : TOOL_SURFACE_READINESS_RETRY_DELAYS_MS;
1860
+ return delays[attempt] ?? null;
1861
+ }
1862
+
1863
+ function makeAbortError(): Error {
1864
+ const error = new Error("AbortError: The operation was aborted");
1865
+ error.name = "AbortError";
1866
+ return error;
1867
+ }
1868
+
1869
+ function delay(ms: number, signal?: AbortSignal): Promise<void> {
1870
+ if (signal?.aborted) return Promise.reject(makeAbortError());
1871
+
1872
+ return new Promise((resolve, reject) => {
1873
+ const timeout = setTimeout(() => {
1874
+ signal?.removeEventListener("abort", onAbort);
1875
+ resolve();
1876
+ }, ms);
1877
+ const onAbort = (): void => {
1878
+ clearTimeout(timeout);
1879
+ reject(makeAbortError());
1880
+ };
1881
+ signal?.addEventListener("abort", onAbort, { once: true });
1882
+ });
1883
+ }
1884
+
1693
1885
  /**
1694
1886
  * Build the options object passed to the Claude Agent SDK's `query()` call.
1695
1887
  *
@@ -1840,34 +2032,27 @@ export function buildSdkOptions(
1840
2032
  `mcp__${workflowServerName}__gsd_save_gate_result`,
1841
2033
  ]
1842
2034
  : [];
2035
+ const allowToolSearchForWorkflowMcp = workflowMcpTools.length > 0 || exactWorkflowMcpTools.length > 0;
1843
2036
  const disallowedTools: string[] = [...new Set([
1844
- "ToolSearch",
2037
+ ...(allowToolSearchForWorkflowMcp ? [] : ["ToolSearch"]),
2038
+ ...(gsdPhase ? ["Skill"] : []),
1845
2039
  ...(workflowMcpTools.length > 0 || exactWorkflowMcpTools.length > 0 ? ["AskUserQuestion"] : []),
1846
2040
  ...questionToolSurface.disallowedTools,
1847
2041
  ...runUatDisallowedTools,
1848
2042
  ...extraDisallowedTools,
1849
2043
  ])];
1850
- const standardClaudeTools = [
1851
- "Read",
1852
- "Write",
1853
- "Edit",
1854
- "Glob",
1855
- "Grep",
1856
- "Bash",
1857
- "Agent",
1858
- "WebFetch",
1859
- "WebSearch",
1860
- ];
2044
+ const nativeTools = resolveClaudeNativeToolsForPhase(gsdPhase);
1861
2045
  const allowedTools = gsdPhase === "run-uat"
1862
2046
  ? [
1863
- ...RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES,
2047
+ ...nativeTools,
1864
2048
  ...(exactWorkflowMcpTools.length > 0 ? exactWorkflowMcpTools : []),
1865
2049
  ...allowedBrowserMcpTools,
1866
2050
  ]
1867
2051
  : [
1868
- ...standardClaudeTools,
2052
+ ...nativeTools,
1869
2053
  ...exactWorkflowMcpTools,
1870
- ...(workflowMcpTools.length > 0 ? workflowMcpTools : ["AskUserQuestion"]),
2054
+ ...(!gsdPhase && workflowMcpTools.length > 0 ? workflowMcpTools : []),
2055
+ ...(workflowMcpTools.length === 0 && exactWorkflowMcpTools.length === 0 ? ["AskUserQuestion"] : []),
1871
2056
  ...allowedBrowserMcpTools,
1872
2057
  ];
1873
2058
  const supportsAdaptive = modelSupportsAdaptiveThinking(modelId);
@@ -1938,6 +2123,20 @@ export function streamViaClaudeCode(
1938
2123
  return stream;
1939
2124
  }
1940
2125
 
2126
+ interface SdkAttemptMessageState {
2127
+ builder: PartialMessageBuilder | null;
2128
+ intermediateToolBlocks: AssistantMessage["content"];
2129
+ toolResultsById: Map<string, ExternalToolResultPayload>;
2130
+ }
2131
+
2132
+ function createSdkAttemptMessageState(): SdkAttemptMessageState {
2133
+ return {
2134
+ builder: null,
2135
+ intermediateToolBlocks: [],
2136
+ toolResultsById: new Map<string, ExternalToolResultPayload>(),
2137
+ };
2138
+ }
2139
+
1941
2140
  /** Async pump that drives the Claude Agent SDK's async-iterable message stream and pushes events into `stream`. */
1942
2141
  async function pumpSdkMessages(
1943
2142
  model: Model<any>,
@@ -1946,38 +2145,30 @@ async function pumpSdkMessages(
1946
2145
  stream: AssistantMessageEventStream,
1947
2146
  ): Promise<void> {
1948
2147
  const modelId = model.id;
1949
- let builder: PartialMessageBuilder | null = null;
1950
2148
  /** Track the last text content seen across all assistant turns for the final message. */
1951
2149
  let lastTextContent = "";
1952
2150
  let lastThinkingContent = "";
1953
- /** Collect tool blocks from intermediate SDK turns for tool execution rendering. */
1954
- const intermediateToolBlocks: AssistantMessage["content"] = [];
1955
- /** Preserve real external tool results from Claude Code's synthetic user messages. */
1956
- const toolResultsById = new Map<string, ExternalToolResultPayload>();
1957
2151
 
1958
2152
  try {
1959
- // Dynamic import — the SDK is an optional dependency.
1960
- const sdkModule = "@anthropic-ai/claude-agent-sdk";
1961
- const sdk = (await import(/* webpackIgnore: true */ sdkModule)) as {
1962
- query: (args: {
1963
- prompt: string | AsyncIterable<unknown>;
1964
- options?: Record<string, unknown>;
1965
- }) => AsyncIterable<SDKMessage>;
1966
- };
1967
-
1968
- // Bridge GSD's AbortSignal to SDK's AbortController
1969
- const controller = new AbortController();
1970
- if (options?.signal) {
1971
- options.signal.addEventListener("abort", () => controller.abort(), { once: true });
1972
- }
1973
-
1974
2153
  const permissionMode = await resolveClaudePermissionMode();
1975
- const uiContext = (options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext;
1976
- const onExternalToolCall = (options as ClaudeCodeStreamOptions | undefined)?.onExternalToolCall;
1977
- const onExternalToolResult = (options as ClaudeCodeStreamOptions | undefined)?.onExternalToolResult;
2154
+ const claudeOptions = options as ClaudeCodeStreamOptions | undefined;
2155
+ const uiContext = claudeOptions?.extensionUIContext;
2156
+ const onExternalToolCall = claudeOptions?.onExternalToolCall;
2157
+ const onExternalToolResult = claudeOptions?.onExternalToolResult;
2158
+ const sdkQueryForTest = claudeOptions?._sdkQueryForTest;
2159
+ const query = sdkQueryForTest ?? (
2160
+ // Dynamic import — the SDK is an optional dependency.
2161
+ (await import(/* webpackIgnore: true */ "@anthropic-ai/claude-agent-sdk")) as {
2162
+ query: (args: {
2163
+ prompt: string | AsyncIterable<unknown>;
2164
+ options?: Record<string, unknown>;
2165
+ }) => AsyncIterable<SDKMessage>;
2166
+ }
2167
+ ).query;
1978
2168
  const cwd = resolveClaudeCodeCwd(options);
2169
+ const projectRoot = resolveWorkflowMcpProjectRoot(cwd);
1979
2170
  autoInitClaudeCodeWorkflowMcp(cwd);
1980
- const gsdPhase = inferGsdPhaseFromContext(context);
2171
+ const gsdPhase = resolveGsdPhaseForSdk(context, projectRoot);
1981
2172
  const canUseToolHandler = createClaudeCodeCanUseToolHandler(uiContext);
1982
2173
  // When no UI is available (headless / auto-mode), auto-approve all
1983
2174
  // tool requests. This replaces the old bypassPermissions workaround.
@@ -2001,20 +2192,15 @@ async function pumpSdkMessages(
2001
2192
  },
2002
2193
  );
2003
2194
  const workflowMcpServerName = workflowMcpServerNameFromAllowedTools(sdkOpts.allowedTools);
2195
+ const allowPendingToolSearchHydration =
2196
+ Boolean(workflowMcpServerName && gsdPhase)
2197
+ && !(sdkOpts.disallowedTools as string[] | undefined)?.includes("ToolSearch");
2004
2198
  const prompt = buildPromptFromContext(context, {
2005
2199
  workflowMcpServerName,
2006
2200
  browserMcpServerName: browserMcpServerNameFromAllowedTools(sdkOpts.allowedTools),
2007
2201
  });
2008
2202
  const queryPrompt = buildSdkQueryPrompt(context, prompt);
2009
2203
 
2010
- const queryResult = sdk.query({
2011
- prompt: queryPrompt,
2012
- options: {
2013
- ...sdkOpts,
2014
- abortController: controller,
2015
- },
2016
- });
2017
-
2018
2204
  // Emit start with an empty partial
2019
2205
  const initialPartial: AssistantMessage = {
2020
2206
  role: "assistant",
@@ -2027,201 +2213,320 @@ async function pumpSdkMessages(
2027
2213
  timestamp: Date.now(),
2028
2214
  };
2029
2215
  stream.push({ type: "start", partial: initialPartial });
2216
+ const readinessProgressState: WorkflowMcpReadinessProgressState = {};
2030
2217
 
2031
- for await (const msg of queryResult as AsyncIterable<SDKMessage>) {
2032
- if (options?.signal?.aborted) {
2033
- // User-initiated cancel — emit an aborted error so the agent
2034
- // loop classifies this as a deliberate stop, not a transient
2035
- // provider failure that should be retried.
2036
- stream.push({
2037
- type: "error",
2038
- reason: "aborted",
2039
- error: makeAbortedMessage(modelId, lastTextContent),
2040
- });
2041
- return;
2218
+ const trackWorkflowMcpSdk = Boolean(workflowMcpServerName && gsdPhase);
2219
+ if (trackWorkflowMcpSdk) beginWorkflowMcpSdkSession();
2220
+ try {
2221
+ let workflowMcpPreflightVerified = false;
2222
+ const shouldRunWorkflowMcpPreflight =
2223
+ workflowMcpServerName && gsdPhase && !claudeOptions?._skipWorkflowMcpPreflightForTest;
2224
+ if (shouldRunWorkflowMcpPreflight) {
2225
+ try {
2226
+ const progressMessage = buildWorkflowMcpReadinessProgressMessage({
2227
+ unitType: gsdPhase,
2228
+ workflowServerName: workflowMcpServerName,
2229
+ stage: "preflight",
2230
+ });
2231
+ pushWorkflowMcpReadinessProgressEvent({
2232
+ stream,
2233
+ partial: initialPartial,
2234
+ state: readinessProgressState,
2235
+ message: progressMessage,
2236
+ });
2237
+ uiContext?.setStatus?.("gsd-step", progressMessage);
2238
+ const preflightError = await awaitWorkflowMcpToolRegistration({
2239
+ unitType: gsdPhase,
2240
+ workflowServerName: workflowMcpServerName,
2241
+ projectRoot,
2242
+ signal: options?.signal,
2243
+ });
2244
+ if (preflightError) {
2245
+ stream.push({
2246
+ type: "error",
2247
+ reason: "error",
2248
+ error: makeErrorMessage(modelId, preflightError),
2249
+ });
2250
+ return;
2251
+ }
2252
+ workflowMcpPreflightVerified = true;
2253
+ } finally {
2254
+ uiContext?.setStatus?.("gsd-step", "");
2255
+ }
2256
+ if (workflowMcpPreflightVerified) {
2257
+ await delay(POST_PREFLIGHT_SDK_SETTLE_MS, options?.signal);
2258
+ }
2042
2259
  }
2043
2260
 
2044
- switch (msg.type) {
2045
- // -- Init --
2046
- case "system": {
2047
- // Tool Surface Readiness gate: the init message is the first (and
2048
- // only) point where the session reports its live tool surface and
2049
- // MCP server statuses. If the workflow server failed or has not
2050
- // registered this Unit's required tools, abort before the first
2051
- // model turn with a transient, recovery-classifiable error
2052
- // (tool-unavailable → retry) instead of letting the model hit
2053
- // "No such tool available" mid-Unit and improvise around it.
2054
- const init = msg as unknown as {
2055
- subtype?: string;
2056
- tools?: string[];
2057
- mcp_servers?: { name: string; status: string }[];
2058
- };
2059
- if (init.subtype === "init") {
2060
- const readinessError = getToolSurfaceReadinessError({
2061
- unitType: gsdPhase,
2062
- workflowServerName: workflowMcpServerName,
2063
- observation: { tools: init.tools ?? [], mcpServers: init.mcp_servers ?? [] },
2261
+ sdkAttemptLoop:
2262
+ for (let readinessAttempt = 0; ; readinessAttempt++) {
2263
+ let { builder, intermediateToolBlocks, toolResultsById } = createSdkAttemptMessageState();
2264
+ const controller = new AbortController();
2265
+ const forwardAbort = (): void => controller.abort();
2266
+ if (options?.signal) {
2267
+ options.signal.addEventListener("abort", forwardAbort, { once: true });
2268
+ }
2269
+
2270
+ const queryResult = query({
2271
+ prompt: queryPrompt,
2272
+ options: {
2273
+ ...sdkOpts,
2274
+ abortController: controller,
2275
+ },
2276
+ });
2277
+
2278
+ try {
2279
+ for await (const msg of queryResult as AsyncIterable<SDKMessage>) {
2280
+ if (options?.signal?.aborted) {
2281
+ // User-initiated cancel — emit an aborted error so the agent
2282
+ // loop classifies this as a deliberate stop, not a transient
2283
+ // provider failure that should be retried.
2284
+ stream.push({
2285
+ type: "error",
2286
+ reason: "aborted",
2287
+ error: makeAbortedMessage(modelId, lastTextContent),
2064
2288
  });
2065
- if (readinessError) {
2066
- controller.abort();
2067
- stream.push({
2068
- type: "error",
2069
- reason: "error",
2070
- error: makeErrorMessage(modelId, readinessError),
2071
- });
2072
- return;
2073
- }
2289
+ return;
2074
2290
  }
2075
- break;
2076
- }
2077
2291
 
2078
- // -- Streaming partial messages --
2079
- case "stream_event": {
2080
- const partial = msg as SDKPartialAssistantMessage;
2081
-
2082
- const event = partial.event;
2083
-
2084
- const result = handleClaudeCodePartialStreamEvent(builder, event, modelId);
2085
- builder = result.builder;
2086
- const assistantEvent = result.assistantEvent;
2087
- if (assistantEvent) {
2088
- stream.push(assistantEvent);
2089
- if (assistantEvent.type === "toolcall_start") {
2090
- const toolBlock = assistantEvent.partial.content[assistantEvent.contentIndex];
2091
- if (toolBlock?.type === "toolCall") {
2092
- try {
2093
- await onExternalToolCall?.(toolBlock);
2094
- } catch (error) {
2095
- console.warn("[claude-code] onExternalToolCall callback failed:", error);
2292
+ switch (msg.type) {
2293
+ // -- Init --
2294
+ case "system": {
2295
+ // Tool Surface Readiness gate: the init message is the first (and
2296
+ // only) point where the session reports its live tool surface and
2297
+ // MCP server statuses. If the workflow server failed or has not
2298
+ // registered this Unit's required tools, abort before the first
2299
+ // model turn with a transient, recovery-classifiable error
2300
+ // (tool-unavailable retry) instead of letting the model hit
2301
+ // "No such tool available" mid-Unit and improvise around it.
2302
+ const init = msg as unknown as {
2303
+ subtype?: string;
2304
+ tools?: string[];
2305
+ mcp_servers?: { name: string; status: string }[];
2306
+ };
2307
+ if (init.subtype === "init") {
2308
+ const readinessError = await resolveClaudeCodeToolSurfaceReadinessError({
2309
+ unitType: gsdPhase,
2310
+ workflowServerName: workflowMcpServerName,
2311
+ projectRoot,
2312
+ observation: { tools: init.tools ?? [], mcpServers: init.mcp_servers ?? [] },
2313
+ allowPendingToolSearchHydration,
2314
+ });
2315
+ if (readinessError) {
2316
+ const retryDelayMs = resolveClaudeCodeToolSurfaceReadinessRetryDelayMs(
2317
+ readinessError,
2318
+ readinessAttempt,
2319
+ workflowMcpPreflightVerified,
2320
+ );
2321
+ if (retryDelayMs !== null && !options?.signal?.aborted) {
2322
+ controller.abort();
2323
+ const progressMessage = buildWorkflowMcpReadinessProgressMessage({
2324
+ unitType: gsdPhase ?? "workflow unit",
2325
+ workflowServerName: workflowMcpServerName ?? "workflow",
2326
+ stage: "retry",
2327
+ attempt: readinessAttempt + 1,
2328
+ delayMs: retryDelayMs,
2329
+ });
2330
+ pushWorkflowMcpReadinessProgressEvent({
2331
+ stream,
2332
+ partial: initialPartial,
2333
+ state: readinessProgressState,
2334
+ message: progressMessage,
2335
+ });
2336
+ uiContext?.setStatus?.("gsd-step", progressMessage);
2337
+ await delay(retryDelayMs, options?.signal);
2338
+ uiContext?.setStatus?.("gsd-step", "");
2339
+ continue sdkAttemptLoop;
2340
+ }
2341
+ controller.abort();
2342
+ stream.push({
2343
+ type: "error",
2344
+ reason: "error",
2345
+ error: makeErrorMessage(modelId, readinessError),
2346
+ });
2347
+ return;
2096
2348
  }
2097
2349
  }
2350
+ break;
2098
2351
  }
2099
- }
2100
- break;
2101
- }
2102
2352
 
2103
- // -- Complete assistant message (non-streaming fallback) --
2104
- case "assistant": {
2105
- const sdkAssistant = msg as SDKAssistantMessage;
2106
-
2107
- // Capture text content from complete messages
2108
- for (const block of sdkAssistant.message.content) {
2109
- if (block.type === "text") {
2110
- lastTextContent = block.text;
2111
- } else if (block.type === "thinking") {
2112
- lastThinkingContent = block.thinking;
2353
+ // -- Streaming partial messages --
2354
+ case "stream_event": {
2355
+ const partial = msg as SDKPartialAssistantMessage;
2356
+
2357
+ const event = partial.event;
2358
+
2359
+ const result = handleClaudeCodePartialStreamEvent(builder, event, modelId);
2360
+ builder = result.builder;
2361
+ const assistantEvent = result.assistantEvent;
2362
+ if (assistantEvent) {
2363
+ stream.push(assistantEvent);
2364
+ if (assistantEvent.type === "toolcall_start") {
2365
+ const toolBlock = assistantEvent.partial.content[assistantEvent.contentIndex];
2366
+ if (toolBlock?.type === "toolCall") {
2367
+ try {
2368
+ await onExternalToolCall?.(toolBlock);
2369
+ } catch (error) {
2370
+ console.warn("[claude-code] onExternalToolCall callback failed:", error);
2371
+ }
2372
+ }
2373
+ }
2374
+ }
2375
+ break;
2113
2376
  }
2114
- }
2115
- break;
2116
- }
2117
2377
 
2118
- // -- User message (synthetic tool result — signals turn boundary) --
2119
- case "user": {
2120
- // Capture content from the completed turn before resetting
2121
- if (builder) {
2122
- for (const block of builder.message.content) {
2123
- if (block.type === "text" && block.text) {
2124
- lastTextContent = block.text;
2125
- } else if (block.type === "thinking" && block.thinking) {
2126
- lastThinkingContent = block.thinking;
2127
- } else if (block.type === "toolCall" || block.type === "serverToolUse") {
2128
- // Collect tool blocks for externalToolExecution rendering
2129
- intermediateToolBlocks.push(block);
2378
+ // -- Complete assistant message (non-streaming fallback) --
2379
+ case "assistant": {
2380
+ const sdkAssistant = msg as SDKAssistantMessage;
2381
+
2382
+ // Capture text content from complete messages
2383
+ for (const block of sdkAssistant.message.content) {
2384
+ if (block.type === "text") {
2385
+ lastTextContent = block.text;
2386
+ } else if (block.type === "thinking") {
2387
+ lastThinkingContent = block.thinking;
2388
+ }
2130
2389
  }
2390
+ break;
2131
2391
  }
2132
- }
2133
2392
 
2134
- // Extract tool results from the SDK's synthetic user message
2135
- // and attach to corresponding tool call blocks immediately.
2136
- for (const { toolUseId, result } of extractToolResultsFromSdkUserMessage(msg as SDKUserMessage)) {
2137
- toolResultsById.set(toolUseId, result);
2138
- }
2139
- attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
2140
-
2141
- // Push a synthetic toolcall_end for each tool call from this turn
2142
- // so the TUI can render tool results in real-time during the SDK
2143
- // session instead of waiting until the entire session completes.
2144
- if (builder) {
2145
- for (const block of builder.message.content) {
2146
- const extResult = (block as ToolCallWithExternalResult).externalResult;
2147
- if (!extResult) continue;
2148
- const contentIndex = builder.message.content.indexOf(block);
2149
- if (contentIndex < 0) continue;
2150
- // Push synthetic completion events with result attached so the
2151
- // chat-controller can update pending ToolExecutionComponents.
2152
- if (block.type === "toolCall") {
2153
- try {
2154
- await onExternalToolResult?.({
2155
- toolCall: block,
2156
- result: extResult,
2157
- });
2158
- } catch (error) {
2159
- console.warn("[claude-code] onExternalToolResult callback failed:", error);
2393
+ // -- User message (synthetic tool result signals turn boundary) --
2394
+ case "user": {
2395
+ // Capture content from the completed turn before resetting
2396
+ if (builder) {
2397
+ for (const block of builder.message.content) {
2398
+ if (block.type === "text" && block.text) {
2399
+ lastTextContent = block.text;
2400
+ } else if (block.type === "thinking" && block.thinking) {
2401
+ lastThinkingContent = block.thinking;
2402
+ } else if (block.type === "toolCall" || block.type === "serverToolUse") {
2403
+ // Collect tool blocks for externalToolExecution rendering
2404
+ intermediateToolBlocks.push(block);
2405
+ }
2160
2406
  }
2161
- stream.push({
2162
- type: "toolcall_end",
2163
- contentIndex,
2164
- toolCall: block,
2165
- partial: builder.message,
2166
- });
2167
- } else if (block.type === "serverToolUse") {
2168
- try {
2169
- await onExternalToolResult?.({
2170
- toolCall: serverToolUseToToolCallLike(block),
2171
- result: extResult,
2172
- });
2173
- } catch (error) {
2174
- console.warn("[claude-code] onExternalToolResult callback failed:", error);
2407
+ }
2408
+
2409
+ // Extract tool results from the SDK's synthetic user message
2410
+ // and attach to corresponding tool call blocks immediately.
2411
+ for (const { toolUseId, result } of extractToolResultsFromSdkUserMessage(msg as SDKUserMessage)) {
2412
+ toolResultsById.set(toolUseId, result);
2413
+ }
2414
+ attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
2415
+
2416
+ // Push a synthetic toolcall_end for each tool call from this turn
2417
+ // so the TUI can render tool results in real-time during the SDK
2418
+ // session instead of waiting until the entire session completes.
2419
+ if (builder) {
2420
+ for (const block of builder.message.content) {
2421
+ const extResult = (block as ToolCallWithExternalResult).externalResult;
2422
+ if (!extResult) continue;
2423
+ const contentIndex = builder.message.content.indexOf(block);
2424
+ if (contentIndex < 0) continue;
2425
+ const suppressDuplicateUnavailable = shouldSuppressDuplicateToolUnavailableBlock(
2426
+ block,
2427
+ builder.message.content,
2428
+ );
2429
+ // Push synthetic completion events with result attached so the
2430
+ // chat-controller can update pending ToolExecutionComponents.
2431
+ if (block.type === "toolCall") {
2432
+ if (suppressDuplicateUnavailable) {
2433
+ delete (block as ToolCallWithExternalResult).externalResult;
2434
+ stream.push({
2435
+ type: "toolcall_end",
2436
+ contentIndex,
2437
+ toolCall: block,
2438
+ partial: builder.message,
2439
+ });
2440
+ (block as ToolCallWithExternalResult).externalResult = extResult;
2441
+ continue;
2442
+ }
2443
+ try {
2444
+ await onExternalToolResult?.({
2445
+ toolCall: block,
2446
+ result: extResult,
2447
+ });
2448
+ } catch (error) {
2449
+ console.warn("[claude-code] onExternalToolResult callback failed:", error);
2450
+ }
2451
+ stream.push({
2452
+ type: "toolcall_end",
2453
+ contentIndex,
2454
+ toolCall: block,
2455
+ partial: builder.message,
2456
+ });
2457
+ } else if (block.type === "serverToolUse") {
2458
+ try {
2459
+ await onExternalToolResult?.({
2460
+ toolCall: serverToolUseToToolCallLike(block),
2461
+ result: extResult,
2462
+ });
2463
+ } catch (error) {
2464
+ console.warn("[claude-code] onExternalToolResult callback failed:", error);
2465
+ }
2466
+ stream.push({
2467
+ type: "server_tool_use",
2468
+ contentIndex,
2469
+ partial: builder.message,
2470
+ });
2471
+ }
2175
2472
  }
2176
- stream.push({
2177
- type: "server_tool_use",
2178
- contentIndex,
2179
- partial: builder.message,
2180
- });
2181
2473
  }
2474
+
2475
+ builder = null;
2476
+ break;
2182
2477
  }
2183
- }
2184
2478
 
2185
- builder = null;
2186
- break;
2187
- }
2479
+ // -- Result (terminal) --
2480
+ case "result": {
2481
+ const result = msg as SDKResultMessage;
2482
+ const finalContent = buildFinalAssistantContent({
2483
+ intermediateToolBlocks,
2484
+ pendingContent: builder?.message.content,
2485
+ toolResultsById,
2486
+ lastThinkingContent,
2487
+ lastTextContent,
2488
+ fallbackResultText:
2489
+ result.subtype === "success" && result.result ? result.result : undefined,
2490
+ });
2188
2491
 
2189
- // -- Result (terminal) --
2190
- case "result": {
2191
- const result = msg as SDKResultMessage;
2192
- const finalContent = buildFinalAssistantContent({
2193
- intermediateToolBlocks,
2194
- pendingContent: builder?.message.content,
2195
- toolResultsById,
2196
- lastThinkingContent,
2197
- lastTextContent,
2198
- fallbackResultText:
2199
- result.subtype === "success" && result.result ? result.result : undefined,
2200
- });
2492
+ const finalMessage: AssistantMessage = {
2493
+ role: "assistant",
2494
+ content: finalContent,
2495
+ api: "anthropic-messages",
2496
+ provider: "claude-code",
2497
+ model: modelId,
2498
+ usage: mapUsage(result.usage, result.total_cost_usd),
2499
+ stopReason: result.is_error ? "error" : "stop",
2500
+ timestamp: Date.now(),
2501
+ };
2201
2502
 
2202
- const finalMessage: AssistantMessage = {
2203
- role: "assistant",
2204
- content: finalContent,
2205
- api: "anthropic-messages",
2206
- provider: "claude-code",
2207
- model: modelId,
2208
- usage: mapUsage(result.usage, result.total_cost_usd),
2209
- stopReason: result.is_error ? "error" : "stop",
2210
- timestamp: Date.now(),
2211
- };
2212
-
2213
- if (result.is_error) {
2214
- finalMessage.errorMessage = getResultErrorMessage(result);
2215
- stream.push({ type: "error", reason: "error", error: finalMessage });
2216
- } else {
2217
- stream.push({ type: "done", reason: "stop", message: finalMessage });
2503
+ if (result.is_error) {
2504
+ finalMessage.errorMessage = getResultErrorMessage(result);
2505
+ stream.push({ type: "error", reason: "error", error: finalMessage });
2506
+ } else {
2507
+ stream.push({ type: "done", reason: "stop", message: finalMessage });
2508
+ }
2509
+ return;
2510
+ }
2511
+
2512
+ default:
2513
+ break;
2218
2514
  }
2219
- return;
2515
+ }
2516
+ } finally {
2517
+ options?.signal?.removeEventListener("abort", forwardAbort);
2220
2518
  }
2221
2519
 
2222
- default:
2223
- break;
2520
+ // The SDK stream ended without a terminal `result` message and
2521
+ // without a readiness retry (the only restart path, via
2522
+ // `continue sdkAttemptLoop`). Break out so the post-loop
2523
+ // exhaustion handler emits a transient stream-exhausted error,
2524
+ // instead of silently starting another SDK session and looping
2525
+ // forever.
2526
+ break sdkAttemptLoop;
2224
2527
  }
2528
+ } finally {
2529
+ if (trackWorkflowMcpSdk) endWorkflowMcpSdkSession();
2225
2530
  }
2226
2531
 
2227
2532
  // Generator exhaustion without a terminal result is a stream interruption,