gsd-pi 2.73.1 → 2.74.0-dev.b741afb

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 (378) hide show
  1. package/dist/cli-web-branch.d.ts +4 -3
  2. package/dist/cli-web-branch.js +10 -7
  3. package/dist/cli.js +184 -206
  4. package/dist/headless-query.js +4 -1
  5. package/dist/help-text.js +23 -0
  6. package/dist/logo.d.ts +1 -1
  7. package/dist/logo.js +1 -1
  8. package/dist/onboarding.js +59 -53
  9. package/dist/resource-loader.js +2 -2
  10. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +68 -4
  11. package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
  12. package/dist/resources/extensions/gsd/auto/phases.js +60 -10
  13. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +93 -57
  16. package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +23 -6
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
  19. package/dist/resources/extensions/gsd/auto-verification.js +88 -3
  20. package/dist/resources/extensions/gsd/auto.js +37 -10
  21. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
  22. package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
  23. package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
  24. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
  25. package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
  26. package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
  27. package/dist/resources/extensions/gsd/commands-do.js +79 -0
  28. package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
  29. package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
  30. package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
  31. package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
  32. package/dist/resources/extensions/gsd/commands-ship.js +187 -0
  33. package/dist/resources/extensions/gsd/db-writer.js +3 -5
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  35. package/dist/resources/extensions/gsd/graph-context.js +66 -0
  36. package/dist/resources/extensions/gsd/gsd-db.js +321 -0
  37. package/dist/resources/extensions/gsd/index.js +15 -2
  38. package/dist/resources/extensions/gsd/md-importer.js +3 -4
  39. package/dist/resources/extensions/gsd/memory-store.js +19 -51
  40. package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
  41. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
  42. package/dist/resources/extensions/gsd/notification-widget.js +2 -2
  43. package/dist/resources/extensions/gsd/preferences-models.js +43 -0
  44. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  45. package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
  46. package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
  47. package/dist/resources/extensions/gsd/state.js +66 -15
  48. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -0
  49. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
  50. package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
  51. package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
  52. package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
  53. package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
  54. package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
  55. package/dist/tsconfig.extensions.tsbuildinfo +1 -0
  56. package/dist/update-check.d.ts +1 -0
  57. package/dist/update-check.js +13 -5
  58. package/dist/update-cmd.js +4 -3
  59. package/dist/web/standalone/.next/BUILD_ID +1 -1
  60. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  61. package/dist/web/standalone/.next/build-manifest.json +3 -3
  62. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  63. package/dist/web/standalone/.next/required-server-files.json +3 -3
  64. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  65. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  75. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  91. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  103. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  133. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  139. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +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_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  153. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  155. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  157. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  159. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/index.html +1 -1
  169. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  170. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  171. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  172. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  173. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  174. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/page.js +2 -2
  176. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  178. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  179. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  180. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  182. package/dist/web/standalone/.next/server/middleware.js +2 -2
  183. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  185. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  186. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  187. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  188. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  189. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  190. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  191. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  192. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  193. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  194. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  195. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  196. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  197. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  198. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  199. package/dist/web/standalone/server.js +1 -1
  200. package/package.json +3 -3
  201. package/packages/daemon/package.json +2 -2
  202. package/packages/mcp-server/dist/index.d.ts +3 -0
  203. package/packages/mcp-server/dist/index.d.ts.map +1 -1
  204. package/packages/mcp-server/dist/index.js +3 -0
  205. package/packages/mcp-server/dist/index.js.map +1 -1
  206. package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
  207. package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
  208. package/packages/mcp-server/dist/readers/graph.js +548 -0
  209. package/packages/mcp-server/dist/readers/graph.js.map +1 -0
  210. package/packages/mcp-server/dist/readers/index.d.ts +2 -0
  211. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
  212. package/packages/mcp-server/dist/readers/index.js +1 -0
  213. package/packages/mcp-server/dist/readers/index.js.map +1 -1
  214. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  215. package/packages/mcp-server/dist/server.js +65 -0
  216. package/packages/mcp-server/dist/server.js.map +1 -1
  217. package/packages/mcp-server/package.json +2 -2
  218. package/packages/mcp-server/src/index.ts +15 -0
  219. package/packages/mcp-server/src/readers/graph.test.ts +426 -0
  220. package/packages/mcp-server/src/readers/graph.ts +708 -0
  221. package/packages/mcp-server/src/readers/index.ts +12 -0
  222. package/packages/mcp-server/src/server.ts +83 -0
  223. package/packages/mcp-server/tsconfig.json +1 -0
  224. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
  225. package/packages/native/package.json +2 -2
  226. package/packages/native/tsconfig.tsbuildinfo +1 -0
  227. package/packages/pi-agent-core/package.json +1 -1
  228. package/packages/pi-agent-core/tsconfig.json +1 -0
  229. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
  230. package/packages/pi-ai/dist/index.d.ts +1 -0
  231. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  232. package/packages/pi-ai/dist/index.js +1 -0
  233. package/packages/pi-ai/dist/index.js.map +1 -1
  234. package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  235. package/packages/pi-ai/dist/utils/overflow.js +12 -0
  236. package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
  237. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
  238. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
  239. package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
  240. package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
  241. package/packages/pi-ai/package.json +1 -1
  242. package/packages/pi-ai/src/index.ts +4 -0
  243. package/packages/pi-ai/src/utils/overflow.ts +14 -1
  244. package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
  245. package/packages/pi-ai/tsconfig.json +1 -0
  246. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
  247. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
  248. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  249. package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
  250. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  251. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
  252. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
  253. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
  254. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
  256. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  257. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +61 -28
  258. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  259. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  261. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
  262. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
  264. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
  265. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
  266. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
  267. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +94 -16
  269. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +11 -3
  272. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  273. package/packages/pi-coding-agent/package.json +1 -1
  274. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +355 -8
  275. package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
  276. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
  277. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +74 -32
  278. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
  279. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
  280. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +113 -21
  281. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
  282. package/packages/pi-coding-agent/tsconfig.json +1 -0
  283. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
  284. package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
  285. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  286. package/packages/pi-tui/dist/tui.d.ts +8 -0
  287. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  288. package/packages/pi-tui/dist/tui.js +32 -3
  289. package/packages/pi-tui/dist/tui.js.map +1 -1
  290. package/packages/pi-tui/package.json +1 -1
  291. package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
  292. package/packages/pi-tui/src/tui.ts +31 -3
  293. package/packages/pi-tui/tsconfig.json +1 -0
  294. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
  295. package/packages/rpc-client/package.json +1 -1
  296. package/packages/rpc-client/tsconfig.json +1 -0
  297. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
  298. package/pkg/package.json +1 -1
  299. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +107 -5
  300. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +111 -2
  301. package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
  302. package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
  303. package/src/resources/extensions/gsd/auto/phases.ts +90 -10
  304. package/src/resources/extensions/gsd/auto-dispatch.ts +10 -4
  305. package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
  306. package/src/resources/extensions/gsd/auto-post-unit.ts +107 -58
  307. package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
  308. package/src/resources/extensions/gsd/auto-start.ts +30 -6
  309. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
  310. package/src/resources/extensions/gsd/auto-verification.ts +98 -3
  311. package/src/resources/extensions/gsd/auto.ts +38 -14
  312. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
  313. package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
  314. package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
  315. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
  316. package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
  317. package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
  318. package/src/resources/extensions/gsd/commands-do.ts +109 -0
  319. package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
  320. package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
  321. package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
  322. package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
  323. package/src/resources/extensions/gsd/commands-ship.ts +219 -0
  324. package/src/resources/extensions/gsd/db-writer.ts +3 -5
  325. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  326. package/src/resources/extensions/gsd/graph-context.ts +85 -0
  327. package/src/resources/extensions/gsd/gsd-db.ts +467 -0
  328. package/src/resources/extensions/gsd/index.ts +18 -2
  329. package/src/resources/extensions/gsd/md-importer.ts +3 -5
  330. package/src/resources/extensions/gsd/memory-store.ts +31 -62
  331. package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
  332. package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
  333. package/src/resources/extensions/gsd/notification-widget.ts +2 -2
  334. package/src/resources/extensions/gsd/preferences-models.ts +41 -0
  335. package/src/resources/extensions/gsd/preferences-types.ts +12 -0
  336. package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
  337. package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
  338. package/src/resources/extensions/gsd/state.ts +80 -17
  339. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
  340. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
  341. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
  342. package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
  343. package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
  344. package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
  345. package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
  346. package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
  347. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
  348. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
  349. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
  350. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
  351. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
  352. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
  353. package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
  354. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
  355. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
  356. package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
  357. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  358. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
  359. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
  360. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
  361. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
  362. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
  363. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
  364. package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
  365. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
  366. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
  367. package/src/resources/extensions/gsd/tools/complete-slice.ts +19 -0
  368. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
  369. package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
  370. package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
  371. package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
  372. package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
  373. package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
  374. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  375. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  376. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  377. /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_buildManifest.js +0 -0
  378. /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_ssgManifest.js +0 -0
@@ -10,8 +10,10 @@ import {
10
10
  mergePendingToolCalls,
11
11
  resolveClaudePermissionMode,
12
12
  buildPromptFromContext,
13
+ buildSdkQueryPrompt,
13
14
  buildSdkOptions,
14
15
  createClaudeCodeElicitationHandler,
16
+ extractImageBlocksFromContext,
15
17
  extractToolResultsFromSdkUserMessage,
16
18
  getClaudeLookupCommand,
17
19
  parseAskUserQuestionsElicitation,
@@ -167,6 +169,92 @@ describe("stream-adapter — full context prompt (#2859)", () => {
167
169
  });
168
170
  });
169
171
 
172
+ describe("stream-adapter — image prompt forwarding (#4183)", () => {
173
+ test("extractImageBlocksFromContext maps user image parts to Anthropic base64 image blocks", () => {
174
+ const context: Context = {
175
+ messages: [
176
+ {
177
+ role: "user",
178
+ content: [
179
+ { type: "text", text: "look" },
180
+ {
181
+ type: "image",
182
+ data: "data:image/png;base64,abc123",
183
+ mimeType: "image/png",
184
+ },
185
+ ],
186
+ } as Message,
187
+ ],
188
+ };
189
+
190
+ const imageBlocks = extractImageBlocksFromContext(context);
191
+ assert.deepEqual(imageBlocks, [
192
+ {
193
+ type: "image",
194
+ source: {
195
+ type: "base64",
196
+ media_type: "image/png",
197
+ data: "abc123",
198
+ },
199
+ },
200
+ ]);
201
+ });
202
+
203
+ test("buildSdkQueryPrompt returns plain string when no images exist in context", () => {
204
+ const context: Context = {
205
+ messages: [{ role: "user", content: "hello" } as Message],
206
+ };
207
+ const textPrompt = buildPromptFromContext(context);
208
+
209
+ const prompt = buildSdkQueryPrompt(context, textPrompt);
210
+ assert.equal(typeof prompt, "string");
211
+ assert.equal(prompt, textPrompt);
212
+ });
213
+
214
+ test("buildSdkQueryPrompt wraps images and prompt text in an SDK user message iterable", async () => {
215
+ const context: Context = {
216
+ messages: [
217
+ {
218
+ role: "user",
219
+ content: [
220
+ { type: "image", data: "ZmFrZQ==", mimeType: "image/jpeg" },
221
+ { type: "text", text: "What is in this image?" },
222
+ ],
223
+ } as Message,
224
+ ],
225
+ };
226
+ const textPrompt = buildPromptFromContext(context);
227
+
228
+ const prompt = buildSdkQueryPrompt(context, textPrompt);
229
+ assert.notEqual(typeof prompt, "string");
230
+ assert.ok(prompt && typeof (prompt as any)[Symbol.asyncIterator] === "function");
231
+
232
+ const messages: any[] = [];
233
+ for await (const item of prompt as AsyncIterable<any>) {
234
+ messages.push(item);
235
+ }
236
+ assert.equal(messages.length, 1);
237
+ assert.deepEqual(messages[0], {
238
+ type: "user",
239
+ message: {
240
+ role: "user",
241
+ content: [
242
+ {
243
+ type: "image",
244
+ source: {
245
+ type: "base64",
246
+ media_type: "image/jpeg",
247
+ data: "ZmFrZQ==",
248
+ },
249
+ },
250
+ { type: "text", text: textPrompt },
251
+ ],
252
+ },
253
+ parent_tool_use_id: null,
254
+ });
255
+ });
256
+ });
257
+
170
258
  // ---------------------------------------------------------------------------
171
259
  // Bug #4102 — transcript fabrication regression tests
172
260
  // ---------------------------------------------------------------------------
@@ -343,6 +431,26 @@ describe("stream-adapter — session persistence (#2859)", () => {
343
431
  );
344
432
  });
345
433
 
434
+ test("buildSdkOptions maps reasoning to effort for adaptive Claude Code models (#3917)", () => {
435
+ const options = buildSdkOptions("claude-sonnet-4-6", "test", undefined, { reasoning: "high" });
436
+ assert.equal(options.effort, "high");
437
+ });
438
+
439
+ test("buildSdkOptions upgrades xhigh reasoning to max for opus 4.6 (#3917)", () => {
440
+ const options = buildSdkOptions("claude-opus-4-6", "test", undefined, { reasoning: "xhigh" });
441
+ assert.equal(options.effort, "max");
442
+ });
443
+
444
+ test("buildSdkOptions omits effort when reasoning is undefined (#3917)", () => {
445
+ const options = buildSdkOptions("claude-sonnet-4-6", "test");
446
+ assert.equal("effort" in options, false);
447
+ });
448
+
449
+ test("buildSdkOptions omits effort for non-adaptive Claude models (#3917)", () => {
450
+ const options = buildSdkOptions("claude-sonnet-4-20250514", "test", undefined, { reasoning: "high" });
451
+ assert.equal("effort" in options, false);
452
+ });
453
+
346
454
  test("buildSdkOptions includes workflow MCP server config when env is set", () => {
347
455
  const prev = {
348
456
  GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
@@ -774,11 +882,12 @@ describe("stream-adapter — MCP elicitation bridge", () => {
774
882
  },
775
883
  };
776
884
 
885
+ const secureValue = "ui-collected-value";
777
886
  const inputCalls: Array<{ opts?: { secure?: boolean } }> = [];
778
887
  const handler = createClaudeCodeElicitationHandler({
779
888
  input: async (_title: string, _placeholder?: string, opts?: { secure?: boolean }) => {
780
889
  inputCalls.push({ opts });
781
- return "example-secure-input";
890
+ return secureValue;
782
891
  },
783
892
  } as any);
784
893
  assert.ok(handler);
@@ -787,7 +896,7 @@ describe("stream-adapter — MCP elicitation bridge", () => {
787
896
  assert.deepEqual(result, {
788
897
  action: "accept",
789
898
  content: {
790
- TEST_SECURE_FIELD: "example-secure-input",
899
+ TEST_SECURE_FIELD: secureValue,
791
900
  },
792
901
  });
793
902
  assert.equal(inputCalls.length, 1);
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { WindowEntry } from "./types.js";
8
+ import { summarizeLogs } from "../workflow-logger.js";
8
9
 
9
10
  /**
10
11
  * Pattern matching ENOENT errors with a file path.
@@ -28,6 +29,13 @@ export function detectStuck(
28
29
  ): { stuck: true; reason: string } | null {
29
30
  if (window.length < 2) return null;
30
31
 
32
+ // Peek (not drain) the workflow-logger buffer so stuck reasons can surface
33
+ // the underlying diagnostic context (projection failures, DB degradations,
34
+ // reconcile warnings) that usually explains *why* the loop is stuck. The
35
+ // auto-loop's finalize step owns the buffer lifecycle — this is read-only.
36
+ const loggerSummary = summarizeLogs();
37
+ const suffix = loggerSummary ? ` — ${loggerSummary}` : "";
38
+
31
39
  const last = window[window.length - 1];
32
40
  const prev = window[window.length - 2];
33
41
 
@@ -35,7 +43,7 @@ export function detectStuck(
35
43
  if (last.error && prev.error && last.error === prev.error) {
36
44
  return {
37
45
  stuck: true,
38
- reason: `Same error repeated: ${last.error.slice(0, 200)}`,
46
+ reason: `Same error repeated: ${last.error.slice(0, 200)}${suffix}`,
39
47
  };
40
48
  }
41
49
 
@@ -45,7 +53,7 @@ export function detectStuck(
45
53
  if (lastThree.every((u) => u.key === last.key)) {
46
54
  return {
47
55
  stuck: true,
48
- reason: `${last.key} derived 3 consecutive times without progress`,
56
+ reason: `${last.key} derived 3 consecutive times without progress${suffix}`,
49
57
  };
50
58
  }
51
59
  }
@@ -60,7 +68,7 @@ export function detectStuck(
60
68
  ) {
61
69
  return {
62
70
  stuck: true,
63
- reason: `Oscillation detected: ${w[0].key} ↔ ${w[1].key}`,
71
+ reason: `Oscillation detected: ${w[0].key} ↔ ${w[1].key}${suffix}`,
64
72
  };
65
73
  }
66
74
  }
@@ -77,7 +85,7 @@ export function detectStuck(
77
85
  if (count >= 2) {
78
86
  return {
79
87
  stuck: true,
80
- reason: `Missing file referenced twice: ${filePath} (ENOENT)`,
88
+ reason: `Missing file referenced twice: ${filePath} (ENOENT)${suffix}`,
81
89
  };
82
90
  }
83
91
  enoentPaths.set(filePath, count);
@@ -180,6 +180,12 @@ export interface LoopDeps {
180
180
  startedAt: number,
181
181
  opts?: CloseoutOptions & Record<string, unknown>,
182
182
  ) => Promise<void>;
183
+ autoCommitUnit?: (
184
+ basePath: string,
185
+ unitType: string,
186
+ unitId: string,
187
+ ctx?: ExtensionContext,
188
+ ) => Promise<string | null>;
183
189
  recordOutcome: (unitType: string, tier: string, success: boolean) => void;
184
190
  writeLock: (
185
191
  lockBase: string,
@@ -30,7 +30,15 @@ import { MergeConflictError } from "../git-service.js";
30
30
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
31
31
  import { join, basename, dirname, parse as parsePath } from "node:path";
32
32
  import { existsSync, cpSync, readdirSync } from "node:fs";
33
- import { logWarning, logError } from "../workflow-logger.js";
33
+ import {
34
+ logWarning,
35
+ logError,
36
+ _resetLogs,
37
+ drainLogs,
38
+ drainAndSummarize,
39
+ formatForNotification,
40
+ hasAnyIssues,
41
+ } from "../workflow-logger.js";
34
42
  import { gsdRoot } from "../paths.js";
35
43
  import { atomicWriteSync } from "../atomic-write.js";
36
44
  import { verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.js";
@@ -159,6 +167,29 @@ async function closeoutAndStop(
159
167
  await deps.stopAuto(ctx, pi, reason);
160
168
  }
161
169
 
170
+ async function emitCancelledUnitEnd(
171
+ ic: IterationContext,
172
+ unitType: string,
173
+ unitId: string,
174
+ unitStartSeq: number,
175
+ errorContext?: { message: string; category: string; stopReason?: string; isTransient?: boolean; retryAfterMs?: number },
176
+ ): Promise<void> {
177
+ ic.deps.emitJournalEvent({
178
+ ts: new Date().toISOString(),
179
+ flowId: ic.flowId,
180
+ seq: ic.nextSeq(),
181
+ eventType: "unit-end",
182
+ data: {
183
+ unitType,
184
+ unitId,
185
+ status: "cancelled",
186
+ artifactVerified: false,
187
+ ...(errorContext ? { errorContext } : {}),
188
+ },
189
+ causedBy: { flowId: ic.flowId, seq: unitStartSeq },
190
+ });
191
+ }
192
+
162
193
  // ─── runPreDispatch ───────────────────────────────────────────────────────────
163
194
 
164
195
  /**
@@ -481,10 +512,13 @@ export async function runPreDispatch(
481
512
  );
482
513
  } else if (state.phase === "blocked") {
483
514
  const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
484
- await deps.stopAuto(ctx, pi, blockerMsg);
485
- ctx.ui.notify(`${blockerMsg}. Fix and run /gsd auto.`, "warning");
486
- deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention", basename(s.originalBasePath || s.basePath));
487
- deps.logCmuxEvent(prefs, blockerMsg, "error");
515
+ // Pause instead of hard-stop so the session is resumable with `/gsd auto`.
516
+ // Hard-stop here was causing premature termination when slice dependencies
517
+ // were temporarily unresolvable (e.g. after reassessment added new slices).
518
+ await deps.pauseAuto(ctx, pi);
519
+ ctx.ui.notify(`${blockerMsg}. Fix and run /gsd auto to resume.`, "warning");
520
+ deps.sendDesktopNotification("GSD", blockerMsg, "warning", "attention", basename(s.originalBasePath || s.basePath));
521
+ deps.logCmuxEvent(prefs, blockerMsg, "warning");
488
522
  } else {
489
523
  const ids = incomplete.map((m: { id: string }) => m.id).join(", ");
490
524
  const diag = `basePath=${s.basePath}, milestones=[${state.registry.map((m: { id: string; status: string }) => `${m.id}:${m.status}`).join(", ")}], phase=${state.phase}`;
@@ -583,13 +617,23 @@ export async function runPreDispatch(
583
617
  return { action: "break", reason: "milestone-complete" };
584
618
  }
585
619
 
586
- // Terminal: blocked
620
+ // Terminal: blocked — pause instead of hard-stop so the session is resumable.
587
621
  if (state.phase === "blocked") {
588
622
  const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
589
- await closeoutAndStop(ctx, pi, s, deps, blockerMsg);
590
- ctx.ui.notify(`${blockerMsg}. Fix and run /gsd auto.`, "warning");
591
- deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention", basename(s.originalBasePath || s.basePath));
592
- deps.logCmuxEvent(prefs, blockerMsg, "error");
623
+ if (s.currentUnit) {
624
+ await deps.closeoutUnit(
625
+ ctx,
626
+ s.basePath,
627
+ s.currentUnit.type,
628
+ s.currentUnit.id,
629
+ s.currentUnit.startedAt,
630
+ deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id),
631
+ );
632
+ }
633
+ await deps.pauseAuto(ctx, pi);
634
+ ctx.ui.notify(`${blockerMsg}. Fix and run /gsd auto to resume.`, "warning");
635
+ deps.sendDesktopNotification("GSD", blockerMsg, "warning", "attention", basename(s.originalBasePath || s.basePath));
636
+ deps.logCmuxEvent(prefs, blockerMsg, "warning");
593
637
  debugLog("autoLoop", { phase: "exit", reason: "blocked" });
594
638
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
595
639
  return { action: "break", reason: "blocked" };
@@ -1068,6 +1112,10 @@ export async function runUnitPhase(
1068
1112
  );
1069
1113
  const previousTier = s.currentUnitRouting?.tier;
1070
1114
 
1115
+ // Scope workflow-logger buffer to this unit so post-finalize drains are
1116
+ // per-unit. Without this, the module-level _buffer accumulates across every
1117
+ // unit in the same Node process (see workflow-logger.ts module header).
1118
+ _resetLogs();
1071
1119
  s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
1072
1120
  setCurrentPhase(unitType);
1073
1121
  s.lastToolInvocationError = null; // #2883: clear stale error from previous unit
@@ -1334,6 +1382,7 @@ export async function runUnitPhase(
1334
1382
  // Provider-error pause: pauseAuto already handled cleanup and scheduled
1335
1383
  // recovery. Don't hard-stop — just break out of the loop (#2762).
1336
1384
  if (unitResult.errorContext?.category === "provider") {
1385
+ await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1337
1386
  debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext.isTransient });
1338
1387
  return { action: "break", reason: "provider-pause" };
1339
1388
  }
@@ -1352,9 +1401,23 @@ export async function runUnitPhase(
1352
1401
  );
1353
1402
  debugLog("autoLoop", { phase: "session-timeout-pause", unitType, unitId });
1354
1403
  await deps.pauseAuto(ctx, pi);
1404
+ await deps.autoCommitUnit?.(s.basePath, unitType, unitId, ctx);
1405
+ await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1355
1406
  return { action: "break", reason: "session-timeout" };
1356
1407
  }
1357
1408
  // All other cancelled states (structural errors, non-transient failures): hard stop
1409
+ if (s.currentUnit) {
1410
+ await deps.closeoutUnit(
1411
+ ctx,
1412
+ s.basePath,
1413
+ unitType,
1414
+ unitId,
1415
+ s.currentUnit.startedAt,
1416
+ deps.buildSnapshotOpts(unitType, unitId),
1417
+ );
1418
+ }
1419
+ await deps.autoCommitUnit?.(s.basePath, unitType, unitId, ctx);
1420
+ await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1358
1421
  ctx.ui.notify(
1359
1422
  `Session creation failed for ${unitType} ${unitId}: ${unitResult.errorContext?.message ?? "unknown"}. Stopping auto-mode.`,
1360
1423
  "warning",
@@ -1532,6 +1595,9 @@ export async function runFinalize(
1532
1595
  // cannot mutate state for the next unit (#3757).
1533
1596
  s.currentUnit = null;
1534
1597
  clearCurrentPhase();
1598
+ // Drop any logger entries from the timed-out unit so they don't bleed
1599
+ // into the next iteration's drain.
1600
+ drainLogs();
1535
1601
  loopState.consecutiveFinalizeTimeouts++;
1536
1602
  debugLog("autoLoop", {
1537
1603
  phase: "pre-verification-timeout",
@@ -1630,6 +1696,9 @@ export async function runFinalize(
1630
1696
  // cannot mutate state for the next unit (#3757).
1631
1697
  s.currentUnit = null;
1632
1698
  clearCurrentPhase();
1699
+ // Drop any logger entries from the timed-out unit so they don't bleed
1700
+ // into the next iteration's drain.
1701
+ drainLogs();
1633
1702
  loopState.consecutiveFinalizeTimeouts++;
1634
1703
  debugLog("autoLoop", {
1635
1704
  phase: "post-verification-timeout",
@@ -1674,5 +1743,16 @@ export async function runFinalize(
1674
1743
  // Both pre and post verification completed without timeout — reset counter
1675
1744
  loopState.consecutiveFinalizeTimeouts = 0;
1676
1745
 
1746
+ // Surface accumulated workflow-logger issues for this unit to the user.
1747
+ // Warnings/errors logged during the unit are buffered in the logger and
1748
+ // drained here so the user sees a single consolidated post-unit alert.
1749
+ if (hasAnyIssues()) {
1750
+ const { logs } = drainAndSummarize();
1751
+ if (logs.length > 0) {
1752
+ const severity = logs.some((e) => e.severity === "error") ? "error" : "warning";
1753
+ ctx.ui.notify(formatForNotification(logs), severity);
1754
+ }
1755
+ }
1756
+
1677
1757
  return { action: "next", data: undefined as void };
1678
1758
  }
@@ -307,8 +307,11 @@ export const DISPATCH_RULES: DispatchRule[] = [
307
307
  {
308
308
  name: "reassess-roadmap (post-completion)",
309
309
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
310
- if (prefs?.phases?.skip_reassess || !prefs?.phases?.reassess_after_slice)
311
- return null;
310
+ if (prefs?.phases?.skip_reassess) return null;
311
+ // Default reassess_after_slice to true — reassessment after slice completion
312
+ // is essential for roadmap integrity. Opt-out via explicit `false`.
313
+ const reassessEnabled = prefs?.phases?.reassess_after_slice ?? true;
314
+ if (!reassessEnabled) return null;
312
315
  const needsReassess = await checkNeedsReassessment(basePath, mid, state);
313
316
  if (!needsReassess) return null;
314
317
  return {
@@ -877,11 +880,14 @@ export async function resolveDispatch(
877
880
  }
878
881
  }
879
882
 
880
- // No rule matched — unhandled phase
883
+ // No rule matched — unhandled phase.
884
+ // Use level "warning" so the loop pauses (resumable) instead of hard-stopping.
885
+ // Hard-stop here was causing premature termination for transient phase gaps
886
+ // (e.g. after reassessment modifies the roadmap and state needs re-derivation).
881
887
  return {
882
888
  action: "stop",
883
889
  reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
884
- level: "info",
890
+ level: "warning",
885
891
  matchedRule: "<no-match>",
886
892
  };
887
893
  }
@@ -15,6 +15,7 @@ import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabil
15
15
  import { getLedger, getProjectTotals } from "./metrics.js";
16
16
  import { unitPhaseLabel } from "./auto-dashboard.js";
17
17
  import { getSessionModelOverride } from "./session-model-override.js";
18
+ import { logWarning } from "./workflow-logger.js";
18
19
 
19
20
  export interface ModelSelectionResult {
20
21
  /** Routing metadata for metrics recording */
@@ -25,9 +26,7 @@ export interface ModelSelectionResult {
25
26
 
26
27
  export function resolvePreferredModelConfig(
27
28
  unitType: string,
28
- autoModeStartModel: { provider: string; id: string } | null,
29
- /** When false, only return explicit per-phase model configs — do not
30
- * synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */
29
+ autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null,
31
30
  isAutoMode = true,
32
31
  ) {
33
32
  const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
@@ -41,7 +40,7 @@ export function resolvePreferredModelConfig(
41
40
  if (!routingConfig.enabled || !routingConfig.tier_models) return undefined;
42
41
 
43
42
  // Don't synthesize a routing config for flat-rate providers (#3453).
44
- if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider)) return undefined;
43
+ if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx)) return undefined;
45
44
 
46
45
  const ceilingModel = routingConfig.tier_models.heavy
47
46
  ?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
@@ -68,7 +67,7 @@ export async function selectAndApplyModel(
68
67
  basePath: string,
69
68
  prefs: GSDPreferences | undefined,
70
69
  verbose: boolean,
71
- autoModeStartModel: { provider: string; id: string } | null,
70
+ autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null,
72
71
  retryContext?: { isRetry: boolean; previousTier?: string },
73
72
  /** When false (interactive/guided-flow), skip dynamic routing and use the session model.
74
73
  * Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
@@ -79,6 +78,17 @@ export async function selectAndApplyModel(
79
78
  const effectiveSessionModelOverride = sessionModelOverride === undefined
80
79
  ? getSessionModelOverride(ctx.sessionManager.getSessionId())
81
80
  : (sessionModelOverride ?? undefined);
81
+ // Enrich the start model with a flat-rate context up front so routing
82
+ // synthesis and the dispatch-time guard see the same signals (built-in
83
+ // list + user `flat_rate_providers` preference + externalCli auto-
84
+ // detection). The dispatch-time primary-model check below builds its
85
+ // own per-provider context when it has a resolved primary model.
86
+ if (autoModeStartModel) {
87
+ autoModeStartModel = {
88
+ ...autoModeStartModel,
89
+ flatRateCtx: buildFlatRateContext(autoModeStartModel.provider, ctx, prefs),
90
+ };
91
+ }
82
92
  const modelConfig = effectiveSessionModelOverride
83
93
  ? undefined
84
94
  : resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
@@ -107,12 +117,16 @@ export async function selectAndApplyModel(
107
117
  if (routingConfig.enabled) {
108
118
  const primaryModel = resolveModelId(modelConfig.primary, availableModels, ctx.model?.provider);
109
119
  if (primaryModel) {
110
- if (isFlatRateProvider(primaryModel.provider)) {
120
+ const primaryFlatRateCtx = buildFlatRateContext(primaryModel.provider, ctx, prefs);
121
+ if (isFlatRateProvider(primaryModel.provider, primaryFlatRateCtx)) {
111
122
  routingConfig.enabled = false;
112
123
  }
113
124
  } else if (
114
- (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider))
115
- || (ctx.model?.provider && isFlatRateProvider(ctx.model.provider))
125
+ (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx))
126
+ || (ctx.model?.provider && isFlatRateProvider(
127
+ ctx.model.provider,
128
+ buildFlatRateContext(ctx.model.provider, ctx, prefs),
129
+ ))
116
130
  ) {
117
131
  // Primary model unresolvable but provider signals indicate flat-rate —
118
132
  // disable routing to prevent quality degradation.
@@ -416,8 +430,68 @@ export function resolveModelId<T extends { id: string; provider: string }>(
416
430
  * Uses case-insensitive matching with alias support to prevent fail-open on
417
431
  * provider naming variations (e.g. "copilot" vs "github-copilot").
418
432
  */
419
- const FLAT_RATE_PROVIDERS = new Set(["github-copilot", "copilot", "claude-code"]);
433
+ const BUILTIN_FLAT_RATE = new Set(["github-copilot", "copilot", "claude-code"]);
434
+
435
+ /**
436
+ * Optional context that lets callers extend flat-rate detection beyond the
437
+ * hard-coded built-in list. Either signal on its own is enough to classify
438
+ * a provider as flat-rate.
439
+ */
440
+ export interface FlatRateContext {
441
+ /**
442
+ * Auth mode for the specific provider being checked, as returned by
443
+ * `ctx.modelRegistry.getProviderAuthMode(provider)`. Any provider that
444
+ * wraps a local CLI (externalCli) is, by definition, a flat-rate
445
+ * subscription wrapper — every request costs the same regardless of
446
+ * model, so dynamic routing only degrades quality.
447
+ */
448
+ authMode?: "apiKey" | "oauth" | "externalCli" | "none";
449
+ /**
450
+ * Case-insensitive list of extra provider IDs the user has declared as
451
+ * flat-rate via `preferences.flat_rate_providers`. Used for private
452
+ * subscription-backed proxies and enterprise-gated deployments that the
453
+ * built-in list doesn't know about.
454
+ */
455
+ userFlatRate?: readonly string[];
456
+ }
457
+
458
+ export function isFlatRateProvider(provider: string, opts?: FlatRateContext): boolean {
459
+ const p = provider.toLowerCase();
460
+ if (BUILTIN_FLAT_RATE.has(p)) return true;
461
+ if (opts?.userFlatRate?.some(id => id.toLowerCase() === p)) return true;
462
+ if (opts?.authMode === "externalCli") return true;
463
+ return false;
464
+ }
420
465
 
421
- export function isFlatRateProvider(provider: string): boolean {
422
- return FLAT_RATE_PROVIDERS.has(provider.toLowerCase());
466
+ /**
467
+ * Build a FlatRateContext for a given provider from live runtime state.
468
+ * Safe to call when ctx or prefs are undefined — missing pieces are
469
+ * treated as "no signal".
470
+ */
471
+ export function buildFlatRateContext(
472
+ provider: string,
473
+ ctx?: { modelRegistry?: { getProviderAuthMode?: (p: string) => string } },
474
+ prefs?: { flat_rate_providers?: readonly string[] },
475
+ ): FlatRateContext {
476
+ let authMode: FlatRateContext["authMode"];
477
+ const getAuthMode = ctx?.modelRegistry?.getProviderAuthMode;
478
+ if (typeof getAuthMode === "function") {
479
+ try {
480
+ const mode = getAuthMode(provider);
481
+ if (mode === "apiKey" || mode === "oauth" || mode === "externalCli" || mode === "none") {
482
+ authMode = mode;
483
+ }
484
+ } catch (err) {
485
+ // Registry lookup failure must never break flat-rate detection —
486
+ // fall through with authMode undefined and surface the cause.
487
+ logWarning(
488
+ "dispatch",
489
+ `flat-rate auth-mode lookup failed for ${provider}: ${err instanceof Error ? err.message : String(err)}`,
490
+ );
491
+ }
492
+ }
493
+ return {
494
+ authMode,
495
+ userFlatRate: prefs?.flat_rate_providers,
496
+ };
423
497
  }