gsd-pi 2.80.0-dev.3065f7bfa → 2.80.0-dev.488219eb6

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 (376) hide show
  1. package/dist/claude-cli-check.d.ts +30 -0
  2. package/dist/claude-cli-check.js +18 -7
  3. package/dist/cli.js +0 -19
  4. package/dist/headless-query.d.ts +10 -0
  5. package/dist/headless-query.js +6 -4
  6. package/dist/loader-entrypoint.d.ts +8 -0
  7. package/dist/loader-entrypoint.js +27 -0
  8. package/dist/loader.js +2 -11
  9. package/dist/mcp-server.d.ts +1 -0
  10. package/dist/mcp-server.js +6 -3
  11. package/dist/resources/.managed-resources-content-hash +1 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +18 -7
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +29 -0
  14. package/dist/resources/extensions/github-sync/sync.js +4 -1
  15. package/dist/resources/extensions/gsd/auto/loop.js +71 -8
  16. package/dist/resources/extensions/gsd/auto/phases.js +32 -8
  17. package/dist/resources/extensions/gsd/auto/resolve.js +12 -0
  18. package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
  19. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
  20. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +81 -76
  22. package/dist/resources/extensions/gsd/auto-start.js +41 -6
  23. package/dist/resources/extensions/gsd/auto-worktree.js +98 -80
  24. package/dist/resources/extensions/gsd/auto.js +84 -29
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +62 -13
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -26
  27. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +29 -12
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +6 -9
  29. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +4 -7
  30. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +6 -10
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +5 -9
  32. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +1 -1
  33. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +60 -26
  34. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +4 -0
  35. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  36. package/dist/resources/extensions/gsd/commands-config.js +1 -1
  37. package/dist/resources/extensions/gsd/commands-eval-review.js +2 -2
  38. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  39. package/dist/resources/extensions/gsd/db/unit-dispatches.js +53 -0
  40. package/dist/resources/extensions/gsd/git-service.js +37 -1
  41. package/dist/resources/extensions/gsd/guided-flow.js +14 -4
  42. package/dist/resources/extensions/gsd/init-wizard.js +4 -1
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
  44. package/dist/resources/extensions/gsd/slice-cadence.js +45 -2
  45. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +15 -9
  46. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -7
  47. package/dist/resources/extensions/gsd/tools/complete-task.js +1 -1
  48. package/dist/resources/extensions/gsd/worktree-resolver.js +22 -1
  49. package/dist/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  50. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  51. package/dist/web/standalone/.next/BUILD_ID +1 -1
  52. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  53. package/dist/web/standalone/.next/build-manifest.json +3 -3
  54. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  55. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  56. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.html +1 -1
  75. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  76. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  77. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  78. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  83. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/middleware.js +3 -3
  86. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  87. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  88. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  89. package/dist/web/standalone/.next/static/chunks/8359.e059d86b255fce1c.js +10 -0
  90. package/dist/web/standalone/.next/static/chunks/app/{page-fab3ebb85b006001.js → page-752f1e2ebdaa3e45.js} +1 -1
  91. package/dist/web/standalone/.next/static/chunks/{webpack-0481f1221120a7c6.js → webpack-de742b64187e13fe.js} +1 -1
  92. package/dist/welcome-screen.d.ts +2 -0
  93. package/dist/welcome-screen.js +9 -7
  94. package/package.json +1 -1
  95. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +35 -13
  96. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  97. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +21 -11
  98. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/anthropic.d.ts +7 -0
  100. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  101. package/packages/pi-ai/dist/providers/anthropic.js +9 -7
  102. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  103. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +23 -14
  104. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -1
  105. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +48 -21
  106. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
  107. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +22 -21
  108. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -1
  109. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +22 -21
  110. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -1
  111. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +39 -25
  112. package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -22
  113. package/packages/pi-ai/src/providers/anthropic.ts +22 -9
  114. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +34 -21
  115. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +56 -22
  116. package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +24 -28
  117. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +24 -28
  118. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  119. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +3 -1
  120. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -2
  122. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +22 -56
  125. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/tools/bash.js +3 -1
  129. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts +2 -0
  131. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/tools/edit.js +12 -1
  133. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/tools/find.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/tools/find.js +14 -6
  137. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts +2 -0
  139. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/tools/grep.js +12 -3
  141. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts +2 -0
  143. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js +3 -1
  145. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -1
  147. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
  149. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/tools/ls.js +10 -3
  153. package/packages/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -0
  155. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/tools/read.js +3 -1
  157. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js +7 -62
  159. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts +2 -0
  161. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js +115 -0
  163. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts +19 -0
  165. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/core/tools/tool-target.js +20 -0
  167. package/packages/pi-coding-agent/dist/core/tools/tool-target.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/tools/write.d.ts +4 -0
  169. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/tools/write.js +9 -1
  171. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +89 -2
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +25 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +153 -8
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +18 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +23 -5
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +19 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +23 -5
  190. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -1
  191. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +3 -1
  192. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  193. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +22 -66
  194. package/packages/pi-coding-agent/src/core/tools/bash.ts +4 -1
  195. package/packages/pi-coding-agent/src/core/tools/edit.ts +13 -1
  196. package/packages/pi-coding-agent/src/core/tools/find.ts +15 -6
  197. package/packages/pi-coding-agent/src/core/tools/grep.ts +13 -3
  198. package/packages/pi-coding-agent/src/core/tools/hashline-read.ts +4 -1
  199. package/packages/pi-coding-agent/src/core/tools/index.ts +8 -0
  200. package/packages/pi-coding-agent/src/core/tools/ls.ts +11 -3
  201. package/packages/pi-coding-agent/src/core/tools/read.ts +4 -1
  202. package/packages/pi-coding-agent/src/core/tools/spawn-shell-windows.test.ts +11 -72
  203. package/packages/pi-coding-agent/src/core/tools/tool-target-metadata.test.ts +127 -0
  204. package/packages/pi-coding-agent/src/core/tools/tool-target.ts +48 -0
  205. package/packages/pi-coding-agent/src/core/tools/write.ts +14 -2
  206. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +119 -2
  207. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +31 -1
  208. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +177 -8
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +24 -5
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +17 -1
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +21 -0
  212. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +25 -8
  213. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  214. package/src/resources/extensions/claude-code-cli/readiness.ts +25 -7
  215. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +30 -0
  216. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +26 -0
  217. package/src/resources/extensions/github-sync/sync.ts +8 -1
  218. package/src/resources/extensions/github-sync/tests/sync-source.test.ts +6 -18
  219. package/src/resources/extensions/gsd/auto/loop.ts +84 -8
  220. package/src/resources/extensions/gsd/auto/phases.ts +43 -8
  221. package/src/resources/extensions/gsd/auto/resolve.ts +19 -0
  222. package/src/resources/extensions/gsd/auto/run-unit.ts +3 -0
  223. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
  224. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
  225. package/src/resources/extensions/gsd/auto-post-unit.ts +93 -78
  226. package/src/resources/extensions/gsd/auto-start.ts +56 -6
  227. package/src/resources/extensions/gsd/auto-worktree.ts +118 -72
  228. package/src/resources/extensions/gsd/auto.ts +121 -29
  229. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +76 -12
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -26
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +27 -12
  232. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +7 -9
  233. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +4 -7
  234. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +6 -10
  235. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -9
  236. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +1 -1
  237. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +64 -26
  238. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +4 -0
  239. package/src/resources/extensions/gsd/commands/handlers/ops.ts +4 -2
  240. package/src/resources/extensions/gsd/commands-config.ts +1 -1
  241. package/src/resources/extensions/gsd/commands-eval-review.ts +2 -2
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  243. package/src/resources/extensions/gsd/db/unit-dispatches.ts +66 -0
  244. package/src/resources/extensions/gsd/git-service.ts +41 -2
  245. package/src/resources/extensions/gsd/guided-flow.ts +19 -4
  246. package/src/resources/extensions/gsd/init-wizard.ts +5 -1
  247. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
  248. package/src/resources/extensions/gsd/slice-cadence.ts +49 -2
  249. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +23 -9
  250. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +59 -89
  251. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +47 -172
  252. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +0 -35
  253. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +35 -9
  254. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  255. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +80 -59
  256. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +3 -47
  257. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +76 -18
  258. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +106 -0
  259. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +54 -95
  260. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +67 -26
  261. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +32 -30
  262. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +32 -128
  263. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +20 -54
  264. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +20 -30
  265. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +17 -29
  266. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +138 -0
  267. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +21 -39
  268. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +15 -24
  269. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +44 -29
  270. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +39 -51
  271. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +1 -217
  272. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +15 -32
  273. package/src/resources/extensions/gsd/tests/browser-teardown.test.ts +0 -41
  274. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +34 -27
  275. package/src/resources/extensions/gsd/tests/cmux.test.ts +51 -53
  276. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +39 -61
  277. package/src/resources/extensions/gsd/tests/commands-config.test.ts +26 -19
  278. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  279. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +29 -33
  280. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +45 -108
  281. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +35 -31
  282. package/src/resources/extensions/gsd/tests/cwd-fallback-hardening.test.ts +138 -0
  283. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +4 -68
  284. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +4 -9
  285. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +14 -65
  286. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +44 -37
  287. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +58 -40
  288. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +25 -15
  289. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +35 -17
  290. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +16 -21
  291. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +15 -82
  292. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +2 -20
  293. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +18 -26
  294. package/src/resources/extensions/gsd/tests/init-skip-git.test.ts +9 -12
  295. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +40 -0
  296. package/src/resources/extensions/gsd/tests/integration/commands-eval-review.integration.test.ts +4 -2
  297. package/src/resources/extensions/gsd/tests/integration/git-locale.test.ts +31 -20
  298. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +12 -0
  299. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +0 -47
  300. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +60 -202
  301. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +13 -59
  302. package/src/resources/extensions/gsd/tests/lazy-pi-tui-import.test.ts +44 -6
  303. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +21 -35
  304. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +79 -98
  305. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +70 -278
  306. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +37 -30
  307. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +32 -28
  308. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +24 -37
  309. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +9 -24
  310. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +113 -0
  311. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +36 -22
  312. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +36 -30
  313. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +74 -4
  314. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +20 -22
  315. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +130 -32
  316. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +18 -36
  317. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +35 -73
  318. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +76 -138
  319. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +70 -125
  320. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +59 -161
  321. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +33 -29
  322. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +22 -196
  323. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +23 -93
  324. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +50 -79
  325. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +27 -13
  326. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +151 -251
  327. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +41 -29
  328. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +58 -90
  329. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +32 -164
  330. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +57 -41
  331. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +14 -9
  332. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
  333. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +61 -1
  334. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +44 -42
  335. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +56 -24
  336. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +51 -11
  337. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +23 -0
  338. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +66 -50
  339. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +68 -107
  340. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +115 -42
  341. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +21 -97
  342. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +25 -116
  343. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +21 -57
  344. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +29 -76
  345. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +33 -24
  346. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +39 -30
  347. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +33 -47
  348. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +48 -46
  349. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +14 -102
  350. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +78 -232
  351. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +32 -35
  352. package/src/resources/extensions/gsd/tests/token-profile.test.ts +84 -309
  353. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +134 -341
  354. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +8 -25
  355. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +50 -1
  356. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +6 -101
  357. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +43 -36
  358. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -444
  359. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
  360. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +44 -189
  361. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -57
  362. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
  363. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +21 -44
  364. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +27 -26
  365. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +14 -13
  366. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -18
  367. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +36 -0
  368. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -13
  369. package/src/resources/extensions/gsd/tools/complete-milestone.ts +14 -15
  370. package/src/resources/extensions/gsd/tools/complete-task.ts +1 -1
  371. package/src/resources/extensions/gsd/worktree-resolver.ts +34 -1
  372. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +86 -40
  373. package/src/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  374. package/dist/web/standalone/.next/static/chunks/8336.631939fb583761fa.js +0 -10
  375. /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_buildManifest.js +0 -0
  376. /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_ssgManifest.js +0 -0
@@ -10,7 +10,7 @@
10
10
  import { existsSync, cpSync, readFileSync, readdirSync, mkdirSync, realpathSync, rmSync, unlinkSync, statSync, lstatSync as lstatSyncFn, } from "node:fs";
11
11
  import { isAbsolute, join, relative, sep as pathSep } from "node:path";
12
12
  import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
13
- import { reconcileWorktreeDb, isDbAvailable, getMilestone, getMilestoneSlices, closeDatabase, openDatabase, getDbPath, } from "./gsd-db.js";
13
+ import { reconcileWorktreeDb, isDbAvailable, getMilestone, getMilestoneSlices, getSliceTasks, closeDatabase, openDatabase, getDbPath, } from "./gsd-db.js";
14
14
  import { execFileSync } from "node:child_process";
15
15
  import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
16
16
  import { gsdRoot, resolveGsdPathContract } from "./paths.js";
@@ -135,6 +135,35 @@ function isSamePath(a, b) {
135
135
  return false;
136
136
  }
137
137
  }
138
+ export function _isSamePathForTest(a, b) {
139
+ return isSamePath(a, b);
140
+ }
141
+ export function _resolveAutoWorktreeStartPointForTest(integrationBranch, gitMainBranch, branchExists) {
142
+ if (integrationBranch)
143
+ return integrationBranch;
144
+ return gitMainBranch &&
145
+ typeof gitMainBranch === "string" &&
146
+ gitMainBranch.length > 0 &&
147
+ branchExists(gitMainBranch)
148
+ ? gitMainBranch
149
+ : undefined;
150
+ }
151
+ export function _shouldReconcileWorktreeDbForTest(worktreeDbPath, mainDbPath, pathExists = existsSync, samePath = isSamePath) {
152
+ return pathExists(worktreeDbPath) && !samePath(worktreeDbPath, mainDbPath);
153
+ }
154
+ export function _isExpectedWorktreeUnlinkErrorForTest(code) {
155
+ return code === "ENOENT" || code === "EISDIR";
156
+ }
157
+ function stripGsdDisplayPrefix(value, id) {
158
+ const raw = String(value ?? "").trim();
159
+ if (!raw)
160
+ return undefined;
161
+ const lower = raw.toLowerCase();
162
+ const idLower = id.toLowerCase();
163
+ if (lower.startsWith(`${idLower}:`))
164
+ return raw.slice(id.length + 1).trim() || undefined;
165
+ return raw;
166
+ }
138
167
  // ─── ASSESSMENT Force-Sync Helper (#2821) ─────────────────────────────────
139
168
  /** Regex matching YAML frontmatter `verdict:` field. */
140
169
  const VERDICT_RE = /verdict:\s*[\w-]+/i;
@@ -288,7 +317,7 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
288
317
  catch (err) {
289
318
  // ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
290
319
  const code = err.code;
291
- if (code !== "ENOENT" && code !== "EISDIR") {
320
+ if (!_isExpectedWorktreeUnlinkErrorForTest(code)) {
292
321
  logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
293
322
  }
294
323
  }
@@ -317,6 +346,44 @@ export const SAFE_AUTO_RESOLVE_PATTERNS = [
317
346
  * Covers `.gsd/` state files and common build artifacts. */
318
347
  export const isSafeToAutoResolve = (filePath) => filePath.startsWith(".gsd/") ||
319
348
  SAFE_AUTO_RESOLVE_PATTERNS.some((re) => re.test(filePath));
349
+ function removeMergeStateFiles(basePath, contextLabel) {
350
+ try {
351
+ const gitDir_ = resolveGitDir(basePath);
352
+ for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
353
+ const p = join(gitDir_, f);
354
+ if (existsSync(p))
355
+ unlinkSync(p);
356
+ }
357
+ }
358
+ catch (err) {
359
+ logError("worktree", `${contextLabel} merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
360
+ }
361
+ }
362
+ function cleanupSquashConflictState(basePath) {
363
+ // `git merge --squash` conflicts can leave unmerged index entries without
364
+ // MERGE_HEAD, so merge-abort alone is not enough. Reset the merge index, then
365
+ // remove merge message files that native/libgit2 paths may have created.
366
+ try {
367
+ nativeMergeAbort(basePath);
368
+ }
369
+ catch (err) {
370
+ // Expected for squash conflicts when MERGE_HEAD was never written.
371
+ debugLog("squash-conflict-cleanup:merge-abort-skipped", {
372
+ error: err instanceof Error ? err.message : String(err),
373
+ });
374
+ }
375
+ try {
376
+ execFileSync("git", ["reset", "--merge"], {
377
+ cwd: basePath,
378
+ stdio: ["ignore", "pipe", "pipe"],
379
+ encoding: "utf-8",
380
+ });
381
+ }
382
+ catch (err) {
383
+ logError("worktree", `git reset --merge failed after squash conflict: ${err instanceof Error ? err.message : String(err)}`);
384
+ }
385
+ removeMergeStateFiles(basePath, "squash conflict");
386
+ }
320
387
  // ─── Dispatch-Level Sync (project root ↔ worktree) ──────────────────────────
321
388
  /**
322
389
  * Sync milestone artifacts from project root INTO worktree before deriveState.
@@ -945,17 +1012,7 @@ export function enterBranchModeForMilestone(basePath, milestoneId) {
945
1012
  // Create the milestone branch from the integration branch start-point.
946
1013
  const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
947
1014
  const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
948
- // Validate main_branch preference exists in the repo before using it —
949
- // a stale preference (e.g. "master" when repo uses "main") would cause
950
- // nativeBranchForceReset to fail with a bad start-point reference.
951
- const validatedPrefBranch = gitPrefs?.main_branch &&
952
- typeof gitPrefs.main_branch === "string" &&
953
- gitPrefs.main_branch.length > 0 &&
954
- nativeBranchExists(basePath, gitPrefs.main_branch)
955
- ? gitPrefs.main_branch
956
- : undefined;
957
- const startPoint = integrationBranch ??
958
- validatedPrefBranch ??
1015
+ const startPoint = _resolveAutoWorktreeStartPointForTest(integrationBranch, gitPrefs?.main_branch, (branchName) => nativeBranchExists(basePath, branchName)) ??
959
1016
  nativeDetectMainBranch(basePath);
960
1017
  // TOCTOU ancestry guard (Issue #4980 HIGH-3).
961
1018
  //
@@ -1161,13 +1218,7 @@ export function createAutoWorktree(basePath, milestoneId) {
1161
1218
  // would fork worktrees from the wrong (stale) branch.
1162
1219
  const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
1163
1220
  const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
1164
- const validatedPrefBranch = gitPrefs?.main_branch &&
1165
- typeof gitPrefs.main_branch === "string" &&
1166
- gitPrefs.main_branch.length > 0 &&
1167
- nativeBranchExists(basePath, gitPrefs.main_branch)
1168
- ? gitPrefs.main_branch
1169
- : undefined;
1170
- const startPoint = integrationBranch ?? validatedPrefBranch ?? undefined;
1221
+ const startPoint = _resolveAutoWorktreeStartPointForTest(integrationBranch, gitPrefs?.main_branch, (branchName) => nativeBranchExists(basePath, branchName));
1171
1222
  info = createWorktree(basePath, milestoneId, {
1172
1223
  branch,
1173
1224
  startPoint,
@@ -1244,7 +1295,7 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
1244
1295
  const contract = resolveGsdPathContract(previousCwd, originalBasePath);
1245
1296
  const worktreeDbPath = join(contract.worktreeGsd ?? join(previousCwd, ".gsd"), "gsd.db");
1246
1297
  const mainDbPath = contract.projectDb;
1247
- if (existsSync(worktreeDbPath) && !isSamePath(worktreeDbPath, mainDbPath)) {
1298
+ if (_shouldReconcileWorktreeDbForTest(worktreeDbPath, mainDbPath)) {
1248
1299
  reconcileWorktreeDb(mainDbPath, worktreeDbPath);
1249
1300
  }
1250
1301
  }
@@ -1494,7 +1545,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1494
1545
  const contract = resolveGsdPathContract(worktreeCwd, originalBasePath_);
1495
1546
  const worktreeDbPath = join(contract.worktreeGsd ?? join(worktreeCwd, ".gsd"), "gsd.db");
1496
1547
  const mainDbPath = contract.projectDb;
1497
- if (existsSync(worktreeDbPath) && !isSamePath(worktreeDbPath, mainDbPath)) {
1548
+ if (_shouldReconcileWorktreeDbForTest(worktreeDbPath, mainDbPath)) {
1498
1549
  reconcileWorktreeDb(mainDbPath, worktreeDbPath);
1499
1550
  }
1500
1551
  }
@@ -1508,14 +1559,23 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1508
1559
  if (isDbAvailable()) {
1509
1560
  completedSlices = getMilestoneSlices(milestoneId)
1510
1561
  .filter(s => s.status === "complete")
1511
- .map(s => ({ id: s.id, title: s.title }));
1562
+ .map(s => ({
1563
+ id: s.id,
1564
+ title: stripGsdDisplayPrefix(s.title, s.id) ?? s.id,
1565
+ tasks: getSliceTasks(milestoneId, s.id)
1566
+ .filter((task) => task.status === "complete")
1567
+ .map((task) => ({
1568
+ id: task.id,
1569
+ title: stripGsdDisplayPrefix(task.title, task.id) ?? task.id,
1570
+ })),
1571
+ }));
1512
1572
  }
1513
1573
  // Fallback: parse roadmap content when DB is unavailable
1514
1574
  if (completedSlices.length === 0 && roadmapContent) {
1515
1575
  const sliceRe = /- \[x\] \*\*(\w+):\s*(.+?)\*\*/gi;
1516
1576
  let m;
1517
1577
  while ((m = sliceRe.exec(roadmapContent)) !== null) {
1518
- completedSlices.push({ id: m[1], title: m[2] });
1578
+ completedSlices.push({ id: m[1], title: m[2], tasks: [] });
1519
1579
  }
1520
1580
  }
1521
1581
  // 3. chdir to original base
@@ -1572,7 +1632,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1572
1632
  }
1573
1633
  // 6. Build rich commit message
1574
1634
  const dbMilestone = getMilestone(milestoneId);
1575
- let milestoneTitle = (dbMilestone?.title ?? "").replace(/^M\d+:\s*/, "").trim();
1635
+ let milestoneTitle = stripGsdDisplayPrefix(dbMilestone?.title, milestoneId) ?? "";
1576
1636
  // Fallback: parse title from roadmap content header (e.g. "# M020: Backend foundation")
1577
1637
  if (!milestoneTitle && roadmapContent) {
1578
1638
  const titleMatch = roadmapContent.match(new RegExp(`^#\\s+${milestoneId}:\\s*(.+)`, "m"));
@@ -1581,15 +1641,22 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1581
1641
  }
1582
1642
  milestoneTitle = milestoneTitle || milestoneId;
1583
1643
  const subject = `feat: ${milestoneTitle}`;
1644
+ const milestoneContext = milestoneTitle === milestoneId
1645
+ ? `Milestone: ${milestoneId}`
1646
+ : `Milestone: ${milestoneId} - ${milestoneTitle}`;
1584
1647
  let body = "";
1585
1648
  if (completedSlices.length > 0) {
1586
1649
  const sliceLines = completedSlices
1587
1650
  .map((s) => `- ${s.id}: ${s.title}`)
1588
1651
  .join("\n");
1589
- body = `\n\nCompleted slices:\n${sliceLines}\n\nGSD-Milestone: ${milestoneId}\nBranch: ${milestoneBranch}`;
1652
+ const taskLines = completedSlices
1653
+ .flatMap((s) => s.tasks.map((task) => `- ${s.id}/${task.id}: ${task.title}`))
1654
+ .join("\n");
1655
+ const taskBlock = taskLines ? `\n\nCompleted tasks:\n${taskLines}` : "";
1656
+ body = `\n\nCompleted slices:\n${sliceLines}${taskBlock}\n\n${milestoneContext}\nGSD-Milestone: ${milestoneId}\nBranch: ${milestoneBranch}`;
1590
1657
  }
1591
1658
  else {
1592
- body = `\n\nGSD-Milestone: ${milestoneId}\nBranch: ${milestoneBranch}`;
1659
+ body = `\n\n${milestoneContext}\nGSD-Milestone: ${milestoneId}\nBranch: ${milestoneBranch}`;
1593
1660
  }
1594
1661
  const commitMessage = subject + body;
1595
1662
  // 6b. Reconcile worktree HEAD with milestone branch ref (#1846).
@@ -1790,17 +1857,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1790
1857
  // or interrupted operation) causes `git merge --squash` to refuse with
1791
1858
  // "fatal: You have not concluded your merge (MERGE_HEAD exists)".
1792
1859
  // Defensively remove merge artifacts before starting.
1793
- try {
1794
- const gitDir_ = resolveGitDir(originalBasePath_);
1795
- for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
1796
- const p = join(gitDir_, f);
1797
- if (existsSync(p))
1798
- unlinkSync(p);
1799
- }
1800
- }
1801
- catch (err) { /* best-effort */
1802
- logError("worktree", `merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1803
- }
1860
+ removeMergeStateFiles(originalBasePath_, "pre-merge");
1804
1861
  // 8. Squash merge — auto-resolve .gsd/ state file conflicts (#530)
1805
1862
  const mergeResult = nativeMergeSquash(originalBasePath_, milestoneBranch);
1806
1863
  if (!mergeResult.success) {
@@ -1810,17 +1867,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1810
1867
  if (mergeResult.conflicts.includes("__dirty_working_tree__")) {
1811
1868
  // Defensively clean merge state — the native path may leave MERGE_HEAD
1812
1869
  // even when the merge is rejected (#2912).
1813
- try {
1814
- const gitDir_ = resolveGitDir(originalBasePath_);
1815
- for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
1816
- const p = join(gitDir_, f);
1817
- if (existsSync(p))
1818
- unlinkSync(p);
1819
- }
1820
- }
1821
- catch (err) { /* best-effort */
1822
- logError("worktree", `merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1823
- }
1870
+ removeMergeStateFiles(originalBasePath_, "dirty-tree rejection");
1824
1871
  // Pop stash before throwing so local work is not lost.
1825
1872
  if (stashed) {
1826
1873
  try {
@@ -1869,26 +1916,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1869
1916
  }
1870
1917
  // If there are still real code conflicts, escalate
1871
1918
  if (codeConflicts.length > 0) {
1872
- // Abort merge state so MERGE_HEAD is not left on disk (#2912).
1873
- // libgit2's merge creates MERGE_HEAD even for squash merges; if left
1874
- // dangling, subsequent merges fail and doctor reports corrupt state.
1875
- try {
1876
- nativeMergeAbort(originalBasePath_);
1877
- }
1878
- catch (err) { /* best-effort */
1879
- logError("worktree", `git merge-abort failed: ${err instanceof Error ? err.message : String(err)}`);
1880
- }
1881
- try {
1882
- const gitDir_ = resolveGitDir(originalBasePath_);
1883
- for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
1884
- const p = join(gitDir_, f);
1885
- if (existsSync(p))
1886
- unlinkSync(p);
1887
- }
1888
- }
1889
- catch (err) { /* best-effort */
1890
- logError("worktree", `merge state file cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1891
- }
1919
+ cleanupSquashConflictState(originalBasePath_);
1892
1920
  // Pop stash before throwing so local work is not lost (#2151).
1893
1921
  if (stashed) {
1894
1922
  try {
@@ -1919,17 +1947,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1919
1947
  // of which trigger git's SQUASH_MSG cleanup. MERGE_HEAD is created by
1920
1948
  // libgit2's merge even in squash mode and is not removed by nativeCommit.
1921
1949
  // If left on disk, doctor reports `corrupt_merge_state` on every subsequent run.
1922
- try {
1923
- const gitDir_ = resolveGitDir(originalBasePath_);
1924
- for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
1925
- const p = join(gitDir_, f);
1926
- if (existsSync(p))
1927
- unlinkSync(p);
1928
- }
1929
- }
1930
- catch (err) { /* best-effort */
1931
- logError("worktree", `post-commit merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1932
- }
1950
+ removeMergeStateFiles(originalBasePath_, "post-commit");
1933
1951
  // 9a-ii. Restore stashed files now that the merge+commit is complete (#2151).
1934
1952
  // Pop after commit so stashed changes do not interfere with the squash merge
1935
1953
  // or the commit content. Conflict on pop is non-fatal — the stash entry is
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Auto-mode orchestration, session lifecycle, and stop handling.
1
3
  /**
2
4
  * GSD Auto Mode — Fresh Session Per Unit
3
5
  *
@@ -162,6 +164,12 @@ function restoreProjectRootEnv() {
162
164
  s.hadProjectRootEnv = false;
163
165
  s.projectRootEnvCaptured = false;
164
166
  }
167
+ export function _captureProjectRootEnvForTest(projectRoot) {
168
+ captureProjectRootEnv(projectRoot);
169
+ }
170
+ export function _restoreProjectRootEnvForTest() {
171
+ restoreProjectRootEnv();
172
+ }
165
173
  function captureMilestoneLockEnv(milestoneId) {
166
174
  if (!s.milestoneLockEnvCaptured) {
167
175
  s.hadMilestoneLockEnv = Object.prototype.hasOwnProperty.call(process.env, "GSD_MILESTONE_LOCK");
@@ -239,6 +247,11 @@ function synthesizePausedSessionRecovery(basePath, unitType, unitId, sessionFile
239
247
  export function _synthesizePausedSessionRecoveryForTest(basePath, unitType, unitId, sessionFile) {
240
248
  return synthesizePausedSessionRecovery(basePath, unitType, unitId, sessionFile);
241
249
  }
250
+ export function _resolvePausedResumeBasePathForTest(basePath, pausedWorktreePath, pathExists = existsSync) {
251
+ return pausedWorktreePath && pathExists(pausedWorktreePath)
252
+ ? pausedWorktreePath
253
+ : basePath;
254
+ }
242
255
  const DETACHED_AUTO_KEEPALIVE_INTERVAL_MS = 30_000;
243
256
  function withDetachedAutoKeepalive(run) {
244
257
  const keepAlive = setInterval(() => { }, DETACHED_AUTO_KEEPALIVE_INTERVAL_MS);
@@ -415,6 +428,20 @@ export function _warnIfWorktreeMissingForTest(worktreePath, milestoneId) {
415
428
  export function isAutoPaused() {
416
429
  return s.paused;
417
430
  }
431
+ export async function refreshResumeResourcesAndDb(basePath, deps = {}) {
432
+ const env = deps.env ?? process.env;
433
+ const importModule = deps.importModule ?? ((specifier) => import(specifier));
434
+ const agentDir = env.GSD_CODING_AGENT_DIR || join(gsdHome(), "agent");
435
+ const pkgRoot = env.GSD_PKG_ROOT;
436
+ const resourceLoaderPath = pkgRoot
437
+ ? pathToFileURL(join(pkgRoot, "dist", "resource-loader.js")).href
438
+ : new URL("../../../resource-loader.js", import.meta.url).href;
439
+ const { initResources } = await importModule(resourceLoaderPath);
440
+ initResources(agentDir);
441
+ const { primeCache } = await importModule("./prompt-loader.js");
442
+ primeCache();
443
+ await (deps.openProjectDb ?? openProjectDbIfPresent)(basePath);
444
+ }
418
445
  export function setActiveEngineId(id) {
419
446
  s.activeEngineId = id;
420
447
  }
@@ -618,7 +645,21 @@ function handleLostSessionLock(ctx, lockStatus) {
618
645
  * the stale unit state, progress widget, status badge, and restores CWD so
619
646
  * the dashboard does not show an orphaned timer and the shell is usable.
620
647
  */
621
- function cleanupAfterLoopExit(ctx) {
648
+ export async function rerootCommandSession(cmdCtx, workspaceRoot) {
649
+ if (!cmdCtx || !workspaceRoot)
650
+ return { status: "skipped" };
651
+ try {
652
+ const result = await cmdCtx.newSession({ workspaceRoot });
653
+ return result.cancelled ? { status: "cancelled" } : { status: "ok" };
654
+ }
655
+ catch (err) {
656
+ return {
657
+ status: "failed",
658
+ error: err instanceof Error ? err.message : String(err),
659
+ };
660
+ }
661
+ }
662
+ export async function cleanupAfterLoopExit(ctx) {
622
663
  s.currentUnit = null;
623
664
  s.active = false;
624
665
  deactivateGSD();
@@ -657,6 +698,26 @@ function cleanupAfterLoopExit(ctx) {
657
698
  logWarning("engine", `chdir failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
658
699
  }
659
700
  }
701
+ if (s.originalBasePath && s.cmdCtx) {
702
+ const result = await rerootCommandSession(s.cmdCtx, s.basePath);
703
+ if (result.status === "cancelled") {
704
+ logWarning("engine", "post-loop session re-root was cancelled", { file: "auto.ts", basePath: s.basePath });
705
+ }
706
+ else if (result.status === "failed") {
707
+ logWarning("engine", `post-loop session re-root failed: ${result.error ?? "unknown"}`, { file: "auto.ts", basePath: s.basePath });
708
+ }
709
+ }
710
+ }
711
+ export function _cleanupAfterLoopExitForTest(ctx) {
712
+ return cleanupAfterLoopExit(ctx);
713
+ }
714
+ export function _resolveAutoWorktreeExitActionForTest(currentMilestoneId, milestoneMergedInPhases, milestoneComplete) {
715
+ const action = _selectStopAutoWorktreeExit({
716
+ currentMilestoneId: currentMilestoneId ?? null,
717
+ milestoneComplete,
718
+ milestoneMergedInPhases,
719
+ });
720
+ return action === "none" ? "skip" : action;
660
721
  }
661
722
  export async function stopAuto(ctx, pi, reason) {
662
723
  if (!s.active && !s.paused)
@@ -797,11 +858,16 @@ export async function stopAuto(ctx, pi, reason) {
797
858
  // Non-fatal — fall through to preserveBranch path
798
859
  logWarning("engine", `milestone summary check failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
799
860
  }
800
- if (milestoneComplete) {
861
+ const exitAction = _selectStopAutoWorktreeExit({
862
+ currentMilestoneId: s.currentMilestoneId,
863
+ milestoneComplete,
864
+ milestoneMergedInPhases: s.milestoneMergedInPhases,
865
+ });
866
+ if (exitAction === "merge") {
801
867
  // Milestone is complete — merge worktree branch back to main
802
868
  resolver.mergeAndExit(s.currentMilestoneId, notifyCtx);
803
869
  }
804
- else {
870
+ else if (exitAction === "preserve") {
805
871
  // Milestone still in progress — preserve branch for later resumption
806
872
  resolver.exitMilestone(s.currentMilestoneId, notifyCtx, {
807
873
  preserveBranch: true,
@@ -810,6 +876,7 @@ export async function stopAuto(ctx, pi, reason) {
810
876
  }
811
877
  }
812
878
  catch (e) {
879
+ ctx?.ui.notify(`Worktree cleanup failed for ${s.currentMilestoneId ?? "current milestone"}: ${e instanceof Error ? e.message : String(e)}. Resolve the preserved branch/worktree and run /gsd auto to resume.`, "warning");
813
880
  debugLog("stop-cleanup-worktree", { error: e instanceof Error ? e.message : String(e) });
814
881
  }
815
882
  // ── Step 5: Rebuild state while DB is still open (#3599) ──
@@ -860,14 +927,12 @@ export async function stopAuto(ctx, pi, reason) {
860
927
  // its own cwd for tools and system prompt; refresh it before returning to the
861
928
  // user so follow-up commands do not target a removed milestone worktree.
862
929
  if (s.originalBasePath && ctx && s.cmdCtx) {
863
- try {
864
- const result = await s.cmdCtx.newSession({ workspaceRoot: s.basePath });
865
- if (result.cancelled) {
866
- logWarning("engine", "post-stop session re-root was cancelled", { file: "auto.ts", basePath: s.basePath });
867
- }
930
+ const result = await rerootCommandSession(s.cmdCtx, s.basePath);
931
+ if (result.status === "cancelled") {
932
+ logWarning("engine", "post-stop session re-root was cancelled", { file: "auto.ts", basePath: s.basePath });
868
933
  }
869
- catch (err) {
870
- logWarning("engine", `post-stop session re-root failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts", basePath: s.basePath });
934
+ else if (result.status === "failed") {
935
+ logWarning("engine", `post-stop session re-root failed: ${result.error ?? "unknown"}`, { file: "auto.ts", basePath: s.basePath });
871
936
  }
872
937
  }
873
938
  // ── Step 8: Ledger notification ──
@@ -995,6 +1060,11 @@ export async function stopAuto(ctx, pi, reason) {
995
1060
  s.reset();
996
1061
  }
997
1062
  }
1063
+ export function _selectStopAutoWorktreeExit(args) {
1064
+ if (!args.currentMilestoneId || args.milestoneMergedInPhases)
1065
+ return "none";
1066
+ return args.milestoneComplete ? "merge" : "preserve";
1067
+ }
998
1068
  /**
999
1069
  * Pause auto-mode without destroying state. Context is preserved.
1000
1070
  * The user can interact with the agent, then `/gsd auto` resumes
@@ -1585,9 +1655,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1585
1655
  if (resumeWorktreePath && !existsSync(resumeWorktreePath)) {
1586
1656
  logWarning("session", `Worktree was expected at ${resumeWorktreePath} but is missing. Continuing in project-root mode. To restart with a fresh worktree, run /gsd-debug or recreate the milestone.`, { file: "auto.ts", milestoneId: s.currentMilestoneId ?? "" });
1587
1657
  }
1588
- if (resumeWorktreePath && existsSync(resumeWorktreePath)) {
1589
- s.basePath = resumeWorktreePath;
1590
- }
1658
+ s.basePath = _resolvePausedResumeBasePathForTest(base, resumeWorktreePath);
1591
1659
  // Rebuild scope now that s.basePath reflects the actual worktree (or project root).
1592
1660
  rebuildScope(s.basePath, s.currentMilestoneId);
1593
1661
  // Ensure the workflow-logger audit log is pinned to the project root
@@ -1632,20 +1700,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1632
1700
  // tree; deployed extensions live at ~/.gsd/agent/extensions/gsd/ where the
1633
1701
  // relative path resolves to ~/.gsd/agent/resource-loader.js which doesn't exist.
1634
1702
  // Using GSD_PKG_ROOT constructs a correct absolute path in both contexts (#3949).
1635
- const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome(), "agent");
1636
- const pkgRoot = process.env.GSD_PKG_ROOT;
1637
- const resourceLoaderPath = pkgRoot
1638
- ? pathToFileURL(join(pkgRoot, "dist", "resource-loader.js")).href
1639
- : new URL("../../../resource-loader.js", import.meta.url).href;
1640
- const { initResources } = await import(resourceLoaderPath);
1641
- initResources(agentDir);
1642
- // initResources() uses synchronous fs APIs, so the prompt-template cache
1643
- // can be primed immediately — no need for the legacy 1s setTimeout deferral.
1644
- const { primeCache } = await import("./prompt-loader.js");
1645
- primeCache();
1646
- // Open the project DB before rebuild/derive so resume uses DB-backed
1647
- // state instead of falling back to stale markdown parsing (#2940).
1648
- await openProjectDbIfPresent(s.basePath);
1703
+ await refreshResumeResourcesAndDb(s.basePath);
1649
1704
  try {
1650
1705
  await rebuildState(s.basePath);
1651
1706
  pi.events.emit(CMUX_CHANNELS.SIDEBAR, { action: "sync", preferences: loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, state: await deriveState(s.basePath) });
@@ -1698,7 +1753,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1698
1753
  runKernelLoop: runUokKernelLoop,
1699
1754
  runLegacyLoop: runLegacyAutoLoop,
1700
1755
  });
1701
- cleanupAfterLoopExit(ctx);
1756
+ await cleanupAfterLoopExit(ctx);
1702
1757
  return;
1703
1758
  }
1704
1759
  // ── Fresh start path — delegated to auto-start.ts ──
@@ -1745,7 +1800,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1745
1800
  runKernelLoop: runUokKernelLoop,
1746
1801
  runLegacyLoop: runLegacyAutoLoop,
1747
1802
  });
1748
- cleanupAfterLoopExit(ctx);
1803
+ await cleanupAfterLoopExit(ctx);
1749
1804
  }
1750
1805
  // describeNextUnit is imported from auto-dashboard.ts and re-exported
1751
1806
  export { describeNextUnit } from "./auto-dashboard.js";
@@ -5,7 +5,7 @@ import { clearPathCache } from "../paths.js";
5
5
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
6
6
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
7
7
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
8
- import { isSessionSwitchInFlight, resolveAgentEnd, resolveAgentEndCancelled } from "../auto/resolve.js";
8
+ import { isSessionSwitchAbortGraceActive, isSessionSwitchInFlight, resolveAgentEnd, resolveAgentEndCancelled, } from "../auto/resolve.js";
9
9
  import { resolveModelId } from "../auto-model-selection.js";
10
10
  import { resolveProjectRoot } from "../worktree.js";
11
11
  import { clearDiscussionFlowState } from "./write-gate.js";
@@ -53,6 +53,41 @@ export function isUserInitiatedAbortMessage(message) {
53
53
  return false;
54
54
  return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
55
55
  }
56
+ function isBareClaudeCodeSessionSwitchAbortMarker(message) {
57
+ if (!message)
58
+ return false;
59
+ const normalized = message.trim().replace(/\s+/g, " ").toLowerCase();
60
+ return normalized === "claude code process aborted by user"
61
+ || normalized === "request aborted by user"
62
+ || normalized === "process aborted by user"
63
+ || normalized === "claude code stream aborted by caller";
64
+ }
65
+ function readAssistantTextContent(content) {
66
+ if (!Array.isArray(content))
67
+ return "";
68
+ return content
69
+ .map((block) => {
70
+ if (!block || typeof block !== "object")
71
+ return "";
72
+ const text = block.text;
73
+ return typeof text === "string" ? text : "";
74
+ })
75
+ .filter(Boolean)
76
+ .join("\n");
77
+ }
78
+ export function isClaudeCodeSessionSwitchAbortMessage(lastMsg) {
79
+ if (!lastMsg || typeof lastMsg !== "object")
80
+ return false;
81
+ const m = lastMsg;
82
+ const carriers = [
83
+ m.errorMessage ? String(m.errorMessage) : "",
84
+ readAssistantTextContent(m.content),
85
+ ].filter((value) => value.trim().length > 0);
86
+ if ((m.stopReason === "error" || m.stopReason === "aborted") && carriers.length > 0) {
87
+ return carriers.every(isBareClaudeCodeSessionSwitchAbortMarker);
88
+ }
89
+ return false;
90
+ }
56
91
  /**
57
92
  * Resolve an agent_end event observed while a session switch is in flight.
58
93
  *
@@ -65,18 +100,23 @@ export function isUserInitiatedAbortMessage(message) {
65
100
  * "Auto-mode stopped — Unit aborted: Claude Code process aborted by user"
66
101
  * even though no user input occurred.
67
102
  *
68
- * The user-abort branch is intentionally ignored when the abort fires while
69
- * the session-switch is in flight: the abort is the expected side-effect of
70
- * the transition, not a user signal. Other branches (genuine `stopReason ===
71
- * "aborted"` with content/errorMessage) preserve the prior behavior.
103
+ * Claude Code abort markers are intentionally ignored when the abort fires
104
+ * while the session-switch is in flight: the abort is the expected side-effect
105
+ * of the transition, not a user signal. Other branches (genuine `stopReason
106
+ * === "aborted"` with diagnostic content/errorMessage) preserve the prior
107
+ * behavior.
72
108
  */
73
109
  export function _handleSessionSwitchAgentEnd(lastMsg, resolveCancelled) {
74
110
  if (!lastMsg || typeof lastMsg !== "object")
75
111
  return;
76
112
  const m = lastMsg;
113
+ if (isClaudeCodeSessionSwitchAbortMessage(m)) {
114
+ // Internal abort from in-flight session transition — drop on the floor.
115
+ return;
116
+ }
77
117
  if (m.stopReason === "error") {
78
118
  const rawErrorMsg = m.errorMessage ? String(m.errorMessage) : "";
79
- if (isUserInitiatedAbortMessage(rawErrorMsg)) {
119
+ if (isBareClaudeCodeSessionSwitchAbortMarker(rawErrorMsg)) {
80
120
  // Internal abort from in-flight session transition — drop on the floor.
81
121
  return;
82
122
  }
@@ -91,6 +131,15 @@ export function _handleSessionSwitchAgentEnd(lastMsg, resolveCancelled) {
91
131
  }
92
132
  }
93
133
  }
134
+ export function resolveAgentEndErrorDisplay(rawErrorMsg, content) {
135
+ const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
136
+ if (isUseless && Array.isArray(content)) {
137
+ const textBlock = content.find((b) => b.type === "text" && b.text);
138
+ if (textBlock)
139
+ return textBlock.text.slice(0, 300);
140
+ }
141
+ return rawErrorMsg;
142
+ }
94
143
  async function pauseTransientWithBackoff(cls, pi, ctx, errorDetail, isRateLimit) {
95
144
  retryState.consecutiveTransientCount += 1;
96
145
  const baseRetryAfterMs = "retryAfterMs" in cls ? cls.retryAfterMs : 15_000;
@@ -161,6 +210,12 @@ export async function handleAgentEnd(pi, event, ctx) {
161
210
  _handleSessionSwitchAgentEnd(lastMsg, resolveAgentEndCancelled);
162
211
  return;
163
212
  }
213
+ if (isSessionSwitchAbortGraceActive() && isClaudeCodeSessionSwitchAbortMessage(lastMsg)) {
214
+ // Claude Code can report the abort from `newSession()` a few hundred ms
215
+ // after the guard drops. That event belongs to the old turn; do not let it
216
+ // cancel the freshly-dispatched unit.
217
+ return;
218
+ }
164
219
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "aborted") {
165
220
  // Empty content with aborted stopReason is a non-fatal agent stop (the LLM
166
221
  // chose to end without producing output). Only pause on genuine fatal aborts
@@ -204,16 +259,10 @@ export async function handleAgentEnd(pi, event, ctx) {
204
259
  });
205
260
  return;
206
261
  }
207
- const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
208
262
  // #3588: When errorMessage is uninformative, extract the real error from
209
263
  // the assistant message text content for display purposes only.
210
264
  // Classification still uses rawErrorMsg to avoid false positives from prose.
211
- let displayMsg = rawErrorMsg;
212
- if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
213
- const textBlock = lastMsg.content.find((b) => b.type === "text" && b.text);
214
- if (textBlock)
215
- displayMsg = textBlock.text.slice(0, 300);
216
- }
265
+ const displayMsg = resolveAgentEndErrorDisplay(rawErrorMsg, "content" in lastMsg ? lastMsg.content : undefined);
217
266
  const errorDetail = displayMsg ? `: ${displayMsg}` : "";
218
267
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
219
268
  // ── 1. Classify using rawErrorMsg to avoid prose false-positives ────