@opengsd/gsd-pi 1.1.1-dev.b2556262 → 1.2.0

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 (277) hide show
  1. package/dist/project-sessions.js +4 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -9
  4. package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +659 -57
  6. package/dist/resources/extensions/gsd/auto-prompts.js +110 -1
  7. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  8. package/dist/resources/extensions/gsd/auto-tool-tracking.js +5 -0
  9. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +29 -0
  10. package/dist/resources/extensions/gsd/auto.js +62 -464
  11. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -1
  12. package/dist/resources/extensions/gsd/debug-logger.js +10 -0
  13. package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
  14. package/dist/resources/extensions/gsd/guided-flow.js +2 -2
  15. package/dist/resources/extensions/gsd/markdown-renderer.js +31 -32
  16. package/dist/resources/extensions/gsd/mcp-filter.js +6 -0
  17. package/dist/resources/extensions/gsd/native-git-bridge.js +9 -0
  18. package/dist/resources/extensions/gsd/prompts/discuss.md +6 -7
  19. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  20. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -5
  21. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  22. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  23. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  24. package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  25. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  26. package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
  27. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
  28. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
  29. package/dist/resources/extensions/gsd/user-input-boundary.js +42 -4
  30. package/dist/web/standalone/.next/BUILD_ID +1 -1
  31. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  32. package/dist/web/standalone/.next/build-manifest.json +3 -3
  33. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  34. package/dist/web/standalone/.next/required-server-files.json +3 -3
  35. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  36. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  46. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  57. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  60. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  63. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  65. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  66. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  71. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  74. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  79. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  81. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  84. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  87. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  90. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  93. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  96. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  99. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  102. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  105. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  108. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  111. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  113. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  114. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  117. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  122. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  125. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  127. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  135. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  158. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  163. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/index.html +1 -1
  176. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  177. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  178. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  179. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  180. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  181. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  182. package/dist/web/standalone/.next/server/app/page.js +2 -2
  183. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  185. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  186. package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
  187. package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
  188. package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
  189. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/middleware.js +3 -3
  191. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  192. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  193. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  194. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +1 -0
  195. package/dist/web/standalone/.next/static/chunks/app/{layout-4ae2d68984392bbf.js → layout-b35cbfff38aaf4cf.js} +1 -1
  196. package/dist/web/standalone/.next/static/chunks/app/page-a48b7c48333b31c8.js +1 -0
  197. package/dist/web/standalone/.next/static/chunks/{main-app-90d1d8d5e5d2dc6b.js → main-app-590a74400e35f685.js} +1 -1
  198. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +1 -0
  199. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  200. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  201. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  202. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  203. package/dist/web/standalone/server.js +1 -1
  204. package/package.json +8 -6
  205. package/packages/cloud-mcp-gateway/package.json +2 -2
  206. package/packages/contracts/package.json +1 -1
  207. package/packages/daemon/package.json +4 -4
  208. package/packages/gsd-agent-core/package.json +5 -5
  209. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
  210. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  211. package/packages/gsd-agent-modes/package.json +7 -7
  212. package/packages/mcp-server/package.json +3 -3
  213. package/packages/native/package.json +1 -1
  214. package/packages/pi-agent-core/package.json +1 -1
  215. package/packages/pi-ai/dist/models.generated.d.ts +0 -34
  216. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  217. package/packages/pi-ai/dist/models.generated.js +0 -34
  218. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  219. package/packages/pi-ai/package.json +1 -1
  220. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
  222. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  223. package/packages/pi-coding-agent/package.json +7 -7
  224. package/packages/pi-tui/package.json +2 -2
  225. package/packages/rpc-client/package.json +2 -2
  226. package/pkg/package.json +1 -1
  227. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -8
  228. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +2 -2
  229. package/src/resources/extensions/gsd/auto/contracts.ts +8 -119
  230. package/src/resources/extensions/gsd/auto/orchestrator.ts +794 -58
  231. package/src/resources/extensions/gsd/auto-prompts.ts +114 -1
  232. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  233. package/src/resources/extensions/gsd/auto-tool-tracking.ts +5 -0
  234. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +33 -0
  235. package/src/resources/extensions/gsd/auto.ts +81 -500
  236. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
  237. package/src/resources/extensions/gsd/debug-logger.ts +11 -0
  238. package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
  239. package/src/resources/extensions/gsd/guided-flow.ts +2 -2
  240. package/src/resources/extensions/gsd/markdown-renderer.ts +38 -19
  241. package/src/resources/extensions/gsd/mcp-filter.ts +7 -0
  242. package/src/resources/extensions/gsd/native-git-bridge.ts +9 -0
  243. package/src/resources/extensions/gsd/prompts/discuss.md +6 -7
  244. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  245. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -5
  246. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  247. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  248. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  249. package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  250. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  251. package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
  252. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
  253. package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
  254. package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
  255. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +590 -855
  256. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
  257. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
  258. package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
  259. package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
  260. package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
  261. package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
  262. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +4 -2
  263. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +29 -2
  264. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +65 -0
  265. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +19 -5
  266. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +38 -0
  267. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +62 -0
  268. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
  269. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
  270. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
  271. package/src/resources/extensions/gsd/user-input-boundary.ts +37 -5
  272. package/dist/web/standalone/.next/server/chunks/678.js +0 -2
  273. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +0 -1
  274. package/dist/web/standalone/.next/static/chunks/app/page-6644fc6ee8ca1247.js +0 -1
  275. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +0 -1
  276. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → TA5o9SHAnCdK6Umm1MYxb}/_buildManifest.js +0 -0
  277. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → TA5o9SHAnCdK6Umm1MYxb}/_ssgManifest.js +0 -0
@@ -24,7 +24,9 @@ test("buildMinimalGsdToolSet preserves non-GSD tools and replaces broad GSD surf
24
24
  "gsd_milestone_status",
25
25
  "gsd_checkpoint_db",
26
26
  "memory_query",
27
+ "gsd_memory_query",
27
28
  "capture_thought",
29
+ "gsd_capture_thought",
28
30
  "gsd_graph",
29
31
  ]);
30
32
 
@@ -575,6 +577,42 @@ test("scopeGsdWorkflowToolsForDispatch applies and restores per-unit skill visib
575
577
  assert.equal(calls.filter((call) => call.kind === "skills").length, 2);
576
578
  });
577
579
 
580
+ // ── Regression #534: auto-mode subprocess cannot call gsd_memory_query / gsd_capture_thought ──
581
+ // MCP-workflow subprocesses register the gsd_-prefixed variants, not the pi-native names.
582
+ // MINIMAL_GSD_TOOL_NAMES must include both so resolveScopedToolNames exposes them.
583
+
584
+ test("MINIMAL_GSD_TOOL_NAMES includes gsd_memory_query and gsd_capture_thought (regression #534)", () => {
585
+ assert.ok(
586
+ (MINIMAL_GSD_TOOL_NAMES as readonly string[]).includes("gsd_memory_query"),
587
+ "MINIMAL_GSD_TOOL_NAMES must include gsd_memory_query for MCP-workflow surface parity",
588
+ );
589
+ assert.ok(
590
+ (MINIMAL_GSD_TOOL_NAMES as readonly string[]).includes("gsd_capture_thought"),
591
+ "MINIMAL_GSD_TOOL_NAMES must include gsd_capture_thought for MCP-workflow surface parity",
592
+ );
593
+ });
594
+
595
+ test("buildMinimalAutoGsdToolSet resolves MCP-scoped gsd_memory_query and gsd_capture_thought when subprocess only registers gsd_-prefixed variants (regression #534)", () => {
596
+ // Simulate a subprocess that only exposes gsd_-prefixed MCP tool names,
597
+ // not the pi-native memory_query / capture_thought variants.
598
+ const result = buildMinimalAutoGsdToolSet([
599
+ "bash",
600
+ "read",
601
+ "mcp__gsd-workflow__gsd_exec",
602
+ "mcp__gsd-workflow__gsd_memory_query",
603
+ "mcp__gsd-workflow__gsd_capture_thought",
604
+ ], "execute-task");
605
+
606
+ assert.ok(
607
+ result.includes("mcp__gsd-workflow__gsd_memory_query"),
608
+ "mcp__gsd-workflow__gsd_memory_query must be included when only the gsd_-prefixed variant is available",
609
+ );
610
+ assert.ok(
611
+ result.includes("mcp__gsd-workflow__gsd_capture_thought"),
612
+ "mcp__gsd-workflow__gsd_capture_thought must be included when only the gsd_-prefixed variant is available",
613
+ );
614
+ });
615
+
578
616
  test("applyUnitSkillVisibility sets manifest or clears for wildcard", () => {
579
617
  const calls: Array<string[] | undefined> = [];
580
618
  applyUnitSkillVisibility({
@@ -2,6 +2,7 @@ import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
 
4
4
  import * as userInputBoundary from "../user-input-boundary.ts";
5
+ import { isAwaitingUserInput, shouldPauseForUserApprovalQuestion } from "../user-input-boundary.ts";
5
6
 
6
7
  test("lastAssistantText extracts the latest assistant text block content", () => {
7
8
  const lastAssistantText = (userInputBoundary as {
@@ -24,3 +25,64 @@ test("lastAssistantText extracts the latest assistant text block content", () =>
24
25
  );
25
26
  assert.equal(lastAssistantText?.(null), "");
26
27
  });
28
+
29
+ test("lastAssistantText includes thinking blocks so rate-limit notices are not dropped", () => {
30
+ const lastAssistantText = (userInputBoundary as {
31
+ lastAssistantText?: (messages: unknown[] | null | undefined) => string;
32
+ }).lastAssistantText;
33
+
34
+ assert.equal(typeof lastAssistantText, "function");
35
+ // Turn with only a thinking block (no text block) — must not return ""
36
+ const result = lastAssistantText?.([
37
+ {
38
+ role: "assistant",
39
+ content: [
40
+ { type: "thinking", thinking: "You've hit your limit · resets in 2h" },
41
+ ],
42
+ },
43
+ ]);
44
+ assert.ok(result?.includes("You've hit your limit"), `expected rate-limit text, got: ${JSON.stringify(result)}`);
45
+ });
46
+
47
+ test("isAwaitingUserInput does not trigger on thinking-block question marks", () => {
48
+ // A thinking block with a question mark must NOT pause auto-mode —
49
+ // it's internal reasoning, not a user-visible prompt.
50
+ const messages = [
51
+ {
52
+ role: "assistant",
53
+ content: [
54
+ { type: "thinking", thinking: "Should I skip research? Let me check the config." },
55
+ ],
56
+ },
57
+ ];
58
+ assert.equal(isAwaitingUserInput(messages), false);
59
+ assert.equal(shouldPauseForUserApprovalQuestion("discuss-project", messages), false);
60
+ });
61
+
62
+ test("isAwaitingUserInput does not trigger on thinking-block approval phrases", () => {
63
+ // A thinking block with approval phrases must NOT pause auto-mode.
64
+ const messages = [
65
+ {
66
+ role: "assistant",
67
+ content: [
68
+ { type: "thinking", thinking: "The user confirmed and approved the plan. Should I proceed?" },
69
+ ],
70
+ },
71
+ ];
72
+ assert.equal(isAwaitingUserInput(messages), false);
73
+ assert.equal(shouldPauseForUserApprovalQuestion("discuss-requirements", messages), false);
74
+ });
75
+
76
+ test("isAwaitingUserInput still triggers on text-block question marks when thinking is also present", () => {
77
+ // When thinking + text are both present and the text asks a question, it should still pause.
78
+ const messages = [
79
+ {
80
+ role: "assistant",
81
+ content: [
82
+ { type: "thinking", thinking: "Internal reasoning without questions." },
83
+ { type: "text", text: "Does this look correct?" },
84
+ ],
85
+ },
86
+ ];
87
+ assert.equal(isAwaitingUserInput(messages), true);
88
+ });
@@ -150,6 +150,30 @@ describe("Worktree Safety module", () => {
150
150
  assert.equal(result.kind, "safe");
151
151
  });
152
152
 
153
+ test("accepts project root for source-writing Unit when isolation mode is branch", () => {
154
+ const safety = createWorktreeSafetyModule({
155
+ existsSync: () => true,
156
+ lstatSync: () => ({ isFile: () => false }),
157
+ listRegisteredWorktrees: () => [{ path: projectRoot, branch: "milestone/M001" }],
158
+ getCurrentBranch: () => "milestone/M001",
159
+ });
160
+
161
+ const result = safety.validateUnitRoot({
162
+ unitType: "execute-task",
163
+ unitId: "M001/S01/T01",
164
+ writeScope: "source-writing",
165
+ projectRoot,
166
+ unitRoot: projectRoot,
167
+ milestoneId: "M001",
168
+ isolationMode: "branch",
169
+ expectedBranch: "milestone/M001",
170
+ });
171
+
172
+ assert.equal(result.ok, true);
173
+ assert.equal(result.kind, "safe");
174
+ assert.equal(result.branch, "milestone/M001");
175
+ });
176
+
153
177
  test("rejects non-project root for source-writing Unit when isolation mode is none", () => {
154
178
  const safety = createWorktreeSafetyModule({
155
179
  existsSync: () => true,
@@ -9,7 +9,7 @@ import test from 'node:test';
9
9
  import assert from 'node:assert/strict';
10
10
  import { join, sep } from 'node:path';
11
11
 
12
- import { GSD_PHASE_SCOPE_DISPLAY_REASON } from '../auto-unit-tool-scope.ts';
12
+ import { GSD_PHASE_SCOPE_DISPLAY_REASON, GSD_SECTION_CLOSE_GATE_DISPLAY_REASON } from '../auto-unit-tool-scope.ts';
13
13
  import { ALLOWED_PLANNING_DISPATCH_AGENTS, shouldBlockPlanningUnit } from '../bootstrap/write-gate.ts';
14
14
  import { extractSubagentAgentClasses } from '../bootstrap/subagent-input.ts';
15
15
  import { isDeterministicPolicyError } from '../auto-tool-tracking.ts';
@@ -377,14 +377,26 @@ test('auto-unit scope: execute-task allows only its task completion lifecycle to
377
377
  );
378
378
  assert.strictEqual(allowed.block, false);
379
379
 
380
+ // execute-task closes gates from summary sections, so gsd_save_gate_result gets
381
+ // the softer deterministic redirect instead of the normal HARD BLOCK wall.
380
382
  const blocked = shouldBlockPlanningUnit('gsd_save_gate_result', '', BASE, 'execute-task', ALL);
381
383
  assert.strictEqual(blocked.block, true);
382
- assert.match(blocked.reason!, /HARD BLOCK/);
384
+ assert.doesNotMatch(blocked.reason!, /HARD BLOCK/);
383
385
  assert.match(blocked.reason!, /gsd_save_gate_result/);
384
- assert.strictEqual(blocked.displayReason, GSD_PHASE_SCOPE_DISPLAY_REASON);
386
+ assert.match(blocked.reason!, /summary sections/);
387
+ assert.strictEqual(blocked.displayReason, GSD_SECTION_CLOSE_GATE_DISPLAY_REASON);
385
388
  assert.strictEqual(isDeterministicPolicyError(blocked.reason!), true);
386
389
  });
387
390
 
391
+ test('auto-unit scope: section-close gate units get the calm gsd_save_gate_result redirect', () => {
392
+ for (const unit of ['execute-task', 'complete-slice', 'validate-milestone']) {
393
+ const r = shouldBlockPlanningUnit('gsd_save_gate_result', '', BASE, unit, ALL);
394
+ assert.strictEqual(r.block, true, `${unit} should still block the call`);
395
+ assert.doesNotMatch(r.reason!, /HARD BLOCK/, `${unit} should use the calm redirect`);
396
+ assert.strictEqual(isDeterministicPolicyError(r.reason!), true, `${unit} redirect must stay deterministic`);
397
+ }
398
+ });
399
+
388
400
  test('auto-unit scope: execute-task blocks sibling task completion', () => {
389
401
  const r = shouldBlockPlanningUnit(
390
402
  'gsd_complete_task',
@@ -6,10 +6,11 @@ import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-
6
6
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
7
7
  import {
8
8
  getActiveRequirements,
9
- insertMilestone,
9
+ getAllMilestones,
10
10
  getMilestone,
11
11
  getSliceStatusSummary,
12
12
  getSliceTaskCounts,
13
+ insertMilestone,
13
14
  insertAssessment,
14
15
  insertGateRun,
15
16
  readTransaction,
@@ -126,6 +127,111 @@ function registerProjectMilestoneSequence(content: string): string[] {
126
127
  return registered;
127
128
  }
128
129
 
130
+ /** Minimal shape of a DB milestone row needed to re-render the sequence section. */
131
+ interface MilestoneSeqRow {
132
+ id: string;
133
+ title: string;
134
+ status: string;
135
+ vision: string;
136
+ }
137
+
138
+ /**
139
+ * Best-effort recovery of the human one-liner for each milestone id from a
140
+ * (possibly malformed) Milestone Sequence body. Deliberately lenient: tolerates
141
+ * any separator the canonical MILESTONE_LINE_RE rejects (en-dash, " : ", a
142
+ * missing checkbox, etc.) so a model formatting slip does not discard the prose.
143
+ */
144
+ function recoverMilestoneTails(sequenceBody: string): Map<string, string> {
145
+ const out = new Map<string, string>();
146
+ const lenient = /^\s*(?:-\s*)?(?:\[[ xX]\]\s*)?(M\d{3})\b\s*[:.\-–—]*\s*(.*)$/;
147
+ for (const rawLine of sequenceBody.split("\n")) {
148
+ const m = rawLine.match(lenient);
149
+ if (m) out.set(m[1], m[2].trim());
150
+ }
151
+ return out;
152
+ }
153
+
154
+ function firstSentence(text: string): string {
155
+ const trimmed = text.trim();
156
+ if (!trimmed) return "";
157
+ const idx = trimmed.search(/[.!?](\s|$)/);
158
+ return (idx >= 0 ? trimmed.slice(0, idx + 1) : trimmed).trim();
159
+ }
160
+
161
+ /** Render one canonical, parseable milestone line for the given DB row. */
162
+ function renderMilestoneLine(m: MilestoneSeqRow, recoveredTail: string): string {
163
+ const done = m.status === "complete";
164
+ let oneLiner = recoveredTail;
165
+ // The recovered tail often still carries the title (e.g. "Foo — bar" or
166
+ // "Foo : bar"). Strip a leading repetition of the title, then any separator.
167
+ if (oneLiner.toLowerCase().startsWith(m.title.toLowerCase())) {
168
+ oneLiner = oneLiner.slice(m.title.length).replace(/^\s*[:.\-–—]+\s*/, "").trim();
169
+ } else {
170
+ const sep = oneLiner.match(/\s+(?:—|–|--|-|:)\s+/);
171
+ if (sep && sep.index !== undefined) oneLiner = oneLiner.slice(sep.index + sep[0].length).trim();
172
+ }
173
+ // MILESTONE_LINE_RE requires non-empty prose after the separator.
174
+ if (!oneLiner) oneLiner = firstSentence(m.vision) || (done ? "Completed." : "Planned.");
175
+ return `- [${done ? "x" : " "}] ${m.id}: ${m.title} — ${oneLiner}`;
176
+ }
177
+
178
+ /**
179
+ * Rebuild the "## Milestone Sequence" section from authoritative DB rows when a
180
+ * model-authored PROJECT.md projection parsed to zero milestone lines but the DB
181
+ * already holds milestones. The DB is the source of truth (markdown is a
182
+ * projection), so this repairs the projection rather than failing the save.
183
+ * Preserves a leading HTML comment in the section and recovers one-liners
184
+ * best-effort. The returned content parses cleanly under MILESTONE_LINE_RE.
185
+ */
186
+ function rebuildMilestoneSequenceSection(content: string, milestones: MilestoneSeqRow[]): string {
187
+ const lines = content.split("\n");
188
+ const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence\s*$/.test(l));
189
+
190
+ const canonicalLines = (() => {
191
+ // Recover tails from the existing (malformed) body when the section exists.
192
+ let body = "";
193
+ if (headerIdx !== -1) {
194
+ let end = headerIdx + 1;
195
+ while (end < lines.length && !/^##\s+/.test(lines[end])) end++;
196
+ body = lines.slice(headerIdx + 1, end).join("\n");
197
+ }
198
+ const tails = recoverMilestoneTails(body);
199
+ return milestones.map(m => renderMilestoneLine(m, tails.get(m.id) ?? ""));
200
+ })();
201
+
202
+ if (headerIdx === -1) {
203
+ // No section at all — append a fresh, canonical one.
204
+ const sep = content.endsWith("\n") ? "" : "\n";
205
+ return `${content}${sep}\n## Milestone Sequence\n\n${canonicalLines.join("\n")}\n`;
206
+ }
207
+
208
+ let bodyEnd = headerIdx + 1;
209
+ while (bodyEnd < lines.length && !/^##\s+/.test(lines[bodyEnd])) bodyEnd++;
210
+ const existingBody = lines.slice(headerIdx + 1, bodyEnd);
211
+
212
+ // Preserve a contiguous leading HTML comment block (the "Check off…" hint).
213
+ let i = 0;
214
+ while (i < existingBody.length && existingBody[i].trim() === "") i++;
215
+ const preserved: string[] = [];
216
+ if (i < existingBody.length && existingBody[i].trim().startsWith("<!--")) {
217
+ while (i < existingBody.length) {
218
+ preserved.push(existingBody[i]);
219
+ const closed = existingBody[i].includes("-->");
220
+ i++;
221
+ if (closed) break;
222
+ }
223
+ }
224
+
225
+ return [
226
+ ...lines.slice(0, headerIdx + 1),
227
+ "",
228
+ ...(preserved.length ? [...preserved, ""] : []),
229
+ ...canonicalLines,
230
+ "",
231
+ ...lines.slice(bodyEnd),
232
+ ].join("\n");
233
+ }
234
+
129
235
  async function mirrorArtifactToActiveWorktreeProjection(
130
236
  basePath: string,
131
237
  relativePath: string,
@@ -260,6 +366,7 @@ export async function executeSummarySave(
260
366
  await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
261
367
 
262
368
  let registeredMilestones: string[] = [];
369
+ let milestoneSequenceSelfHealed = false;
263
370
  if (params.artifact_type === "PROJECT") {
264
371
  try {
265
372
  registeredMilestones = registerProjectMilestoneSequence(contentToSave);
@@ -294,29 +401,83 @@ export async function executeSummarySave(
294
401
  };
295
402
  }
296
403
  if (registeredMilestones.length === 0) {
297
- logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
404
+ const existingMilestones = getAllMilestones();
405
+ if (existingMilestones.length === 0) {
406
+ // Genuine first-save failure: no milestones parsed AND none in the DB.
407
+ // /gsd really would report "No Active Milestone" — hard-fail so the
408
+ // caller rewrites the sequence before proceeding.
409
+ logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
410
+ tool: "gsd_summary_save",
411
+ });
412
+ // PROJECT.md was persisted; invalidate so subsequent reads see the new
413
+ // artifacts row even though no milestones registered.
414
+ invalidateStateCache();
415
+ return {
416
+ content: [{
417
+ type: "text",
418
+ text:
419
+ `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
420
+ `so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
421
+ `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
422
+ `\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
423
+ }],
424
+ details: {
425
+ operation: "save_summary",
426
+ path: relativePath,
427
+ artifact_type: params.artifact_type,
428
+ error: "milestone_registration_empty_parse",
429
+ },
430
+ isError: true,
431
+ };
432
+ }
433
+
434
+ // Existing DB rows mean this is projection drift, not data loss. Rebuild
435
+ // the section from DB state and re-persist a parseable projection.
436
+ logWarning("tool", `gsd_summary_save: PROJECT.md parsed zero milestone lines but DB has ${existingMilestones.length} — rebuilding Milestone Sequence from DB (projection self-heal)`, {
298
437
  tool: "gsd_summary_save",
438
+ path: relativePath,
299
439
  });
300
- // PROJECT.md was persisted; invalidate so subsequent reads see the new
301
- // artifacts row even though no milestones registered.
302
- invalidateStateCache();
303
- return {
304
- content: [{
305
- type: "text",
306
- text:
307
- `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
308
- `so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
309
- `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
310
- `\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
311
- }],
312
- details: {
313
- operation: "save_summary",
440
+ try {
441
+ const healed = rebuildMilestoneSequenceSection(contentToSave, existingMilestones);
442
+ await saveArtifactToDb(
443
+ { path: relativePath, artifact_type: params.artifact_type, content: healed },
444
+ basePath,
445
+ );
446
+ await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, healed);
447
+ const healedRegisteredMilestones = registerProjectMilestoneSequence(healed);
448
+ if (healedRegisteredMilestones.length === 0) {
449
+ throw new Error("self-healed PROJECT.md still parsed zero milestone lines");
450
+ }
451
+ registeredMilestones = healedRegisteredMilestones;
452
+ milestoneSequenceSelfHealed = true;
453
+ } catch (healErr) {
454
+ const msg = healErr instanceof Error ? healErr.message : String(healErr);
455
+ logError("tool", `gsd_summary_save: Milestone Sequence self-heal failed: ${msg}`, {
456
+ tool: "gsd_summary_save",
314
457
  path: relativePath,
315
- artifact_type: params.artifact_type,
316
- error: "milestone_registration_empty_parse",
317
- },
318
- isError: true,
319
- };
458
+ error: msg,
459
+ });
460
+ invalidateStateCache();
461
+ return {
462
+ content: [{
463
+ type: "text",
464
+ text:
465
+ `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
466
+ `and automatic DB-backed Milestone Sequence repair failed: ${msg}. ` +
467
+ `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
468
+ `\`- [ ] M001: <Title> — <One-liner>\`, then re-call gsd_summary_save(PROJECT).`,
469
+ }],
470
+ details: {
471
+ operation: "save_summary",
472
+ path: relativePath,
473
+ artifact_type: params.artifact_type,
474
+ error: "milestone_sequence_self_heal_failed",
475
+ self_heal_error: msg,
476
+ },
477
+ isError: true,
478
+ };
479
+ }
480
+ invalidateStateCache();
320
481
  }
321
482
  }
322
483
 
@@ -339,6 +500,7 @@ export async function executeSummarySave(
339
500
  artifact_type: params.artifact_type,
340
501
  content_source: contentSource,
341
502
  ...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
503
+ ...(milestoneSequenceSelfHealed ? { milestoneSequenceSelfHealed: true } : {}),
342
504
  },
343
505
  };
344
506
  } catch (err) {
@@ -23,7 +23,7 @@ const APPROVAL_CHANGE_QUESTION_RE =
23
23
  const RESEARCH_DECISION_QUESTION_RE =
24
24
  /\b(?:research|skip)\b/i;
25
25
 
26
- function extractTextFromMessage(msg: unknown): string {
26
+ function extractVisibleTextFromMessage(msg: unknown): string {
27
27
  if (!msg || typeof msg !== "object") return "";
28
28
  const content = (msg as { content?: unknown }).content;
29
29
  if (typeof content === "string") return content;
@@ -35,6 +35,26 @@ function extractTextFromMessage(msg: unknown): string {
35
35
  if (typed.type === "text" && typeof typed.text === "string") {
36
36
  parts.push(typed.text);
37
37
  }
38
+ // thinking blocks intentionally excluded — they are internal reasoning, not user-visible
39
+ }
40
+ return parts.join("\n");
41
+ }
42
+
43
+ function extractTextFromMessage(msg: unknown): string {
44
+ if (!msg || typeof msg !== "object") return "";
45
+ const content = (msg as { content?: unknown }).content;
46
+ if (typeof content === "string") return content;
47
+ if (!Array.isArray(content)) return "";
48
+ const parts: string[] = [];
49
+ for (const block of content) {
50
+ if (!block || typeof block !== "object") continue;
51
+ const typed = block as { type?: unknown; text?: unknown; thinking?: unknown };
52
+ if (typed.type === "text" && typeof typed.text === "string") {
53
+ parts.push(typed.text);
54
+ }
55
+ if (typed.type === "thinking" && typeof typed.thinking === "string") {
56
+ parts.push(typed.thinking);
57
+ }
38
58
  }
39
59
  return parts.join("\n");
40
60
  }
@@ -51,12 +71,24 @@ export function lastAssistantText(messages: unknown[] | null | undefined): strin
51
71
  return "";
52
72
  }
53
73
 
74
+ function lastAssistantVisibleText(messages: unknown[] | null | undefined): string {
75
+ if (!Array.isArray(messages)) return "";
76
+ for (let i = messages.length - 1; i >= 0; i--) {
77
+ const msg = messages[i];
78
+ if (!msg || typeof msg !== "object") continue;
79
+ if ((msg as { role?: unknown }).role !== "assistant") continue;
80
+ const text = extractVisibleTextFromMessage(msg).trim();
81
+ if (text) return text;
82
+ }
83
+ return "";
84
+ }
85
+
54
86
  function anyMessageMatches(messages: unknown[] | undefined, pattern: RegExp): boolean {
55
87
  if (!Array.isArray(messages)) return false;
56
88
  return messages.some((msg) => {
57
89
  if (!msg || typeof msg !== "object") return false;
58
90
  if ((msg as { role?: unknown }).role === "user") return false;
59
- return pattern.test(extractTextFromMessage(msg));
91
+ return pattern.test(extractVisibleTextFromMessage(msg));
60
92
  });
61
93
  }
62
94
 
@@ -134,7 +166,7 @@ export function isExplicitApprovalResponse(
134
166
  export function isAwaitingUserInput(messages: unknown[] | undefined): boolean {
135
167
  if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
136
168
  if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
137
- const text = lastAssistantText(messages);
169
+ const text = lastAssistantVisibleText(messages);
138
170
  if (!text) return false;
139
171
  if (APPROVAL_WAIT_RE.test(text)) return true;
140
172
  const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
@@ -145,7 +177,7 @@ export function isAwaitingUserInput(messages: unknown[] | undefined): boolean {
145
177
  export function isAwaitingApprovalBoundary(messages: unknown[] | undefined): boolean {
146
178
  if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
147
179
  if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
148
- const text = lastAssistantText(messages);
180
+ const text = lastAssistantVisibleText(messages);
149
181
  if (!text) return false;
150
182
  if (APPROVAL_WAIT_RE.test(text)) return true;
151
183
  return hasApprovalQuestion(text);
@@ -158,7 +190,7 @@ export function shouldPauseForUserApprovalQuestion(
158
190
  if (!unitType || !USER_APPROVAL_UNIT_TYPES.has(unitType)) return false;
159
191
  if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
160
192
  if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
161
- const text = lastAssistantText(messages);
193
+ const text = lastAssistantVisibleText(messages);
162
194
  if (!text) return false;
163
195
  if (APPROVAL_WAIT_RE.test(text)) return true;
164
196
  if (unitType === "research-decision") return hasResearchDecisionQuestion(text);
@@ -1,2 +0,0 @@
1
- "use strict";exports.id=678,exports.ids=[678],exports.modules={10678:(a,b,c)=>{let d;c.d(b,{isOnboardingComplete:()=>r,readOnboardingRecord:()=>q});var e=c(89477),f=c(86697);c(76212),c(78335);var g=c(65521),h=c(62228);c(95398),process.env.GSD_ENABLE_NATIVE_GSD_PARSER,Symbol("native-unavailable"),new g.AsyncLocalStorage,c(97526),Object.values({Q3:{id:"Q3",scope:"slice",ownerTurn:"gate-evaluate",question:"How can this be exploited?",guidance:"Identify abuse scenarios: parameter tampering, replay attacks, privilege escalation.\nMap data exposure risks: PII, tokens, secrets accessible through this slice.\nDefine input trust boundaries: untrusted user input reaching DB, API, or filesystem.\nIf none apply, return verdict 'omitted' with rationale explaining why.",promptSection:"Abuse Surface"},Q4:{id:"Q4",scope:"slice",ownerTurn:"gate-evaluate",question:"Which existing requirements (R-IDs) does this slice touch, and which must be re-tested?",guidance:"List the R-IDs (e.g. R001, R003) touched by this slice; see the milestone requirements artifact at .gsd/milestones/<id>/REQUIREMENTS.md.\nIdentify what must be re-tested after shipping.\nFlag decisions that should be revisited given the new scope.\nIf no existing requirements are affected, return verdict 'omitted'.",promptSection:"Broken Promises"},Q5:{id:"Q5",scope:"task",ownerTurn:"execute-task",question:"What breaks when dependencies fail?",guidance:"Enumerate the task's external dependencies (APIs, filesystem, network, subprocesses).\nDescribe the failure path for each: timeout, malformed response, connection loss.\nVerify the implementation handles each failure or explicitly bubbles the error.\nReturn verdict 'omitted' only if the task has no external dependencies.",promptSection:"Failure Modes"},Q6:{id:"Q6",scope:"task",ownerTurn:"execute-task",question:"What is the 10x load breakpoint?",guidance:"Identify the resource that saturates first at 10x the expected load.\nDescribe the protection applied (pool sizing, rate limiting, pagination, caching).\nReturn verdict 'omitted' if the task has no runtime load dimension.",promptSection:"Load Profile"},Q7:{id:"Q7",scope:"task",ownerTurn:"execute-task",question:"What negative tests protect this task?",guidance:"List malformed inputs, error paths, and boundary conditions the tests cover.\nPoint to the specific test files or cases that assert each negative scenario.\nReturn verdict 'omitted' only if the task has no meaningful negative surface.",promptSection:"Negative Tests"},Q8:{id:"Q8",scope:"slice",ownerTurn:"complete-slice",question:"How will ops know this slice is healthy or broken?",guidance:"Describe the health signal (metric, log line, dashboard) that proves the slice works.\nDescribe the failure signal that triggers an alert or paging.\nDocument the recovery procedure and any monitoring gaps.\nReturn verdict 'omitted' only for slices with no runtime behavior at all.",promptSection:"Operational Readiness"},MV01:{id:"MV01",scope:"milestone",ownerTurn:"validate-milestone",question:"Is every success criterion in the milestone roadmap satisfied?",guidance:"Walk the success-criteria checklist from the milestone roadmap.\nFor each criterion, point to the slice / assessment / verification evidence that proves it.\nReturn verdict 'flag' if any criterion is unmet or unverifiable.",promptSection:"Success Criteria Checklist"},MV02:{id:"MV02",scope:"milestone",ownerTurn:"validate-milestone",question:"Does every slice have a SUMMARY.md and a passing assessment?",guidance:"Confirm every slice listed in the roadmap has a SUMMARY.md.\nConfirm each slice has an ASSESSMENT verdict of 'pass' (or justified 'omitted').\nFlag missing artifacts and slices with outstanding follow-ups or known limitations.",promptSection:"Slice Delivery Audit"},MV03:{id:"MV03",scope:"milestone",ownerTurn:"validate-milestone",question:"Do the slices integrate end-to-end?",guidance:"Trace at least one cross-slice flow proving the pieces compose.\nFlag gaps where two slices were built in isolation with no integration evidence.",promptSection:"Cross-Slice Integration"},MV04:{id:"MV04",scope:"milestone",ownerTurn:"validate-milestone",question:"Are all touched requirements covered and still coherent?",guidance:"For each requirement advanced, validated, surfaced, or invalidated across the milestone's slices, confirm the milestone-level evidence matches.\nFlag requirements that slices claim to advance but no artifact proves.",promptSection:"Requirement Coverage"}});class i{get(a){return this.entries.get(a)}set(a,b){this.entries.set(a,b)}has(a){return this.entries.has(a)}delete(a){return this.entries.delete(a)}asReadonlyMap(){return this.entries}closeNonActive(a,b){for(let[c,d]of this.entries)d.db!==a&&(this.entries.delete(c),b(d))}constructor(){this.entries=new Map}}class j{markAttempted(){this.attempted=!0}clearError(){this.lastError=null,this.lastPhase=null}recordError(a,b){this.lastPhase=a,this.lastError=b instanceof Error?b:Error(String(b))}reset(){this.attempted=!1,this.clearError()}snapshot(){return{attempted:this.attempted,lastError:this.lastError,lastPhase:this.lastPhase}}constructor(){this.attempted=!1,this.lastError=null,this.lastPhase=null}}class k{isInTransaction(){return this.depth>0}transaction(a,b){if(this.depth>0)return this.runNested(b);a.begin(),this.depth++;try{let c=b();return a.commit(),c}catch(b){throw a.rollback(),b}finally{this.depth--}}readTransaction(a,b,c){if(this.depth>0)return this.runNested(b);a.beginRead(),this.depth++;try{let c=b();return a.commit(),c}catch(b){try{a.rollback()}catch(a){c(a instanceof Error?a:Error(String(a)))}throw b}finally{this.depth--}}runNested(a){this.depth++;try{return a()}finally{this.depth--}}constructor(){this.depth=0}}class l{constructor(a){this.providerName=null,this.providerModule=null,this.loadAttempted=!1,this.deps=a}load(){if(this.loadAttempted)return;this.loadAttempted=!0;try{this.deps.suppressSqliteWarning();let a=this.deps.tryRequireNodeSqlite();if(a.DatabaseSync){this.providerModule=a,this.providerName="node:sqlite";return}}catch{}let a=this.loadBetterSqliteModule();if(a){this.providerModule=a,this.providerName="better-sqlite3";return}let b=22>parseInt(this.deps.nodeVersion.split(".")[0],10)?` GSD requires Node >= 22.0.0 (current: v${this.deps.nodeVersion}). Upgrade Node to fix this.`:"";this.deps.writeStderr(`gsd-db: No SQLite provider available (tried node:sqlite, better-sqlite3).${b}
2
- `)}getProviderName(){return this.providerName}openRaw(a){if(this.load(),!this.providerModule||!this.providerName)return null;if("node:sqlite"===this.providerName){let{DatabaseSync:b}=this.providerModule;return new b(a)}return new this.providerModule(a)}tryOpenBetterSqliteFallback(a){if("node:sqlite"!==this.providerName)return null;let b=this.loadBetterSqliteModule();return b?{providerName:"better-sqlite3",providerModule:b,rawDb:new b(a)}:null}commitFallback(a){this.providerName=a.providerName,this.providerModule=a.providerModule}reset(){this.loadAttempted=!1,this.providerModule=null,this.providerName=null}loadBetterSqliteModule(){try{let a=this.deps.tryRequireBetterSqlite3();if("function"==typeof a)return a;if(a&&"function"==typeof a.default)return a.default}catch{}return null}}function m(){if(void 0!==d)return d;try{d=createRequire(import.meta.url)}catch{d=null}return d}new l({tryRequireNodeSqlite:()=>{let a=m();if(!a)throw Error("unavailable");return a("node:sqlite")},tryRequireBetterSqlite3:()=>{let a=m();if(!a)throw Error("unavailable");return a("better-sqlite3")},suppressSqliteWarning:function(){let a=process.emit;process.emit=function(b,...c){return!("warning"===b&&c[0]&&"object"==typeof c[0]&&"name"in c[0]&&"ExperimentalWarning"===c[0].name&&"message"in c[0]&&"string"==typeof c[0].message&&c[0].message.includes("SQLite"))&&a.apply(process,[b,...c])}},nodeVersion:process.versions.node,writeStderr:a=>process.stderr.write(a)}),new j,new i,new k;let n=process.env.GSD_CODING_AGENT_DIR||(0,f.join)(process.env.GSD_HOME?(0,f.resolve)(process.env.GSD_HOME):(0,f.join)((0,h.homedir)(),".gsd"),"agent"),o=(0,f.join)(n,"onboarding.json"),p={version:1,flowVersion:1,completedAt:null,completedSteps:[],skippedSteps:[],lastResumePoint:null};function q(){if(!(0,e.existsSync)(o))return{...p};try{let a=JSON.parse((0,e.readFileSync)(o,"utf-8"));return{version:"number"==typeof a.version?a.version:1,flowVersion:"number"==typeof a.flowVersion?a.flowVersion:0,completedAt:"string"==typeof a.completedAt?a.completedAt:null,completedSteps:Array.isArray(a.completedSteps)?a.completedSteps.filter(a=>"string"==typeof a):[],skippedSteps:Array.isArray(a.skippedSteps)?a.skippedSteps.filter(a=>"string"==typeof a):[],lastResumePoint:"string"==typeof a.lastResumePoint?a.lastResumePoint:null}}catch{return{...p}}}function r(){let a=q();return null!==a.completedAt&&1===a.flowVersion}},97526:(a,b,c)=>{c.d(b,{DZ:()=>e,rs:()=>d});let d="GSD_GIT_ERROR";class e extends Error{constructor(a,b,c){super(b,c),this.name="GSDError",this.code=a}}}};
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9492],{14040:(e,t,l)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found/page",function(){return l(32910)}])},29999:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"styles",{enumerable:!0,get:function(){return l}});let l={error:{fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},desc:{display:"inline-block"},h1:{display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},h2:{fontSize:14,fontWeight:400,lineHeight:"49px",margin:0}};("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},32910:(e,t,l)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return o}});let r=l(18870),n=l(48664),o=function(){return(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)(n.HTTPAccessErrorFallback,{status:404,message:"This page could not be found."})})})};("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},48664:(e,t,l)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"HTTPAccessErrorFallback",{enumerable:!0,get:function(){return o}});let r=l(18870),n=l(29999);function o({status:e,message:t}){return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("title",{children:`${e}: ${t}`}),(0,r.jsx)("div",{style:n.styles.error,children:(0,r.jsxs)("div",{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,r.jsx)("h1",{className:"next-error-h1",style:n.styles.h1,children:e}),(0,r.jsx)("div",{style:n.styles.desc,children:(0,r.jsx)("h2",{style:n.styles.h2,children:t})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},e=>{e.O(0,[9990,686,7358],()=>e(e.s=14040)),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8974],{3267:(e,t,n)=>{Promise.resolve().then(n.bind(n,92703))},19013:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"workAsyncStorage",{enumerable:!0,get:function(){return r.workAsyncStorageInstance}});let r=n(34969)},23711:(e,t,n)=>{"use strict";n.d(t,{default:()=>u.a});var r=n(60276),u=n.n(r)},30039:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={bindSnapshot:function(){return s},createAsyncLocalStorage:function(){return a},createSnapshot:function(){return i}};for(var r in n)Object.defineProperty(t,r,{enumerable:!0,get:n[r]});let u=Object.defineProperty(Error("Invariant: AsyncLocalStorage accessed in runtime where it is not available"),"__NEXT_ERROR_CODE",{value:"E504",enumerable:!1,configurable:!0});class l{disable(){throw u}getStore(){}run(){throw u}exit(){throw u}enterWith(){throw u}static bind(e){return e}}let o="u">typeof globalThis&&globalThis.AsyncLocalStorage;function a(){return o?new o:new l}function s(e){return o?o.bind(e):l.bind(e)}function i(){return o?o.snapshot():function(e,...t){return e(...t)}}},34969:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"workAsyncStorageInstance",{enumerable:!0,get:function(){return r}});let r=(0,n(30039).createAsyncLocalStorage)()},47811:(e,t,n)=>{"use strict";function r({reason:e,children:t}){return t}Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"BailoutToCSR",{enumerable:!0,get:function(){return r}}),n(71495)},60276:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return u}});let r=n(51481)._(n(80260));function u(e,t){let n={};"function"==typeof e&&(n.loader=e);let u={...n,...t};return(0,r.default)({...u,modules:u.loadableGenerated?.modules})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},76670:(e,t,n)=>{"use strict";function r({moduleIds:e}){return null}Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"PreloadChunks",{enumerable:!0,get:function(){return r}}),n(18870),n(29411),n(19013),n(19132),n(63632)},80260:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}});let r=n(18870),u=n(45110),l=n(47811);function o(e){return{default:e&&"default"in e?e.default:e}}n(76670);let a={loader:()=>Promise.resolve(o(()=>null)),loading:null,ssr:!0},s=function(e){let t={...a,...e},n=(0,u.lazy)(()=>t.loader().then(o)),s=t.loading;function i(e){let o=s?(0,r.jsx)(s,{isLoading:!0,pastDelay:!0,error:null}):null,a=!t.ssr||!!t.loading,i=a?u.Suspense:u.Fragment,c=t.ssr?(0,r.jsxs)(r.Fragment,{children:[null,(0,r.jsx)(n,{...e})]}):(0,r.jsx)(l.BailoutToCSR,{reason:"next/dynamic",children:(0,r.jsx)(n,{...e})});return(0,r.jsx)(i,{...a?{fallback:o}:{},children:c})}return i.displayName="LoadableComponent",i}},92703:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(18870);let u=(0,n(23711).default)(()=>Promise.all([n.e(4825),n.e(5395),n.e(7882),n.e(796)]).then(n.bind(n,90796)).then(e=>e.GSDAppShell),{loadableGenerated:{webpack:()=>[90796]},ssr:!1,loading:()=>(0,r.jsx)("div",{className:"flex h-screen items-center justify-center bg-background text-sm text-muted-foreground",children:"Loading workspace…"})});function l(){return(0,r.jsx)(u,{})}}},e=>{e.O(0,[9990,686,7358],()=>e(e.s=3267)),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9337],{16951:(e,s,_)=>{Promise.resolve().then(_.t.bind(_,45336,23))}},e=>{e.O(0,[9990,686,7358],()=>e(e.s=16951)),_N_E=e.O()}]);