gsd-pi 2.73.0 → 2.73.1-dev.088d28f

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 (284) hide show
  1. package/dist/cli.js +0 -47
  2. package/dist/help-text.js +1 -1
  3. package/dist/resource-loader.js +2 -2
  4. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +9 -3
  5. package/dist/resources/extensions/gsd/auto/phases.js +15 -9
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -6
  7. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
  8. package/dist/resources/extensions/gsd/auto-prompts.js +9 -6
  9. package/dist/resources/extensions/gsd/auto-start.js +23 -6
  10. package/dist/resources/extensions/gsd/auto.js +13 -1
  11. package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
  12. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
  13. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -1
  14. package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
  15. package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
  16. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  17. package/dist/resources/extensions/gsd/gsd-db.js +36 -2
  18. package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
  19. package/dist/resources/extensions/gsd/notification-widget.js +2 -2
  20. package/dist/resources/extensions/gsd/preferences-models.js +43 -0
  21. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  22. package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
  23. package/dist/resources/extensions/gsd/state.js +36 -0
  24. package/dist/startup-model-validation.js +8 -5
  25. package/dist/update-check.d.ts +1 -0
  26. package/dist/update-check.js +13 -5
  27. package/dist/update-cmd.js +4 -3
  28. package/dist/web/standalone/.next/BUILD_ID +1 -1
  29. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  30. package/dist/web/standalone/.next/build-manifest.json +3 -3
  31. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  32. package/dist/web/standalone/.next/required-server-files.json +3 -3
  33. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  34. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  44. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  60. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  72. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  92. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  102. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  108. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  122. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  124. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  126. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  128. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/index.html +1 -1
  138. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  139. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  140. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  141. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  143. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/page.js +2 -2
  145. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  147. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  148. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  149. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  151. package/dist/web/standalone/.next/server/middleware.js +2 -2
  152. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  154. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  155. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  156. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  157. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  158. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  159. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  160. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  161. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  162. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  163. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  164. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  165. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  166. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  167. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  168. package/dist/web/standalone/server.js +1 -1
  169. package/package.json +1 -1
  170. package/packages/pi-ai/dist/index.d.ts +1 -0
  171. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  172. package/packages/pi-ai/dist/index.js +1 -0
  173. package/packages/pi-ai/dist/index.js.map +1 -1
  174. package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  175. package/packages/pi-ai/dist/utils/overflow.js +12 -0
  176. package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
  177. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
  178. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
  179. package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
  180. package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
  181. package/packages/pi-ai/src/index.ts +4 -0
  182. package/packages/pi-ai/src/utils/overflow.ts +14 -1
  183. package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
  184. package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -1
  185. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
  187. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
  189. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
  191. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
  195. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/model-resolver.js +25 -68
  198. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
  200. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +51 -26
  202. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
  206. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
  211. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  212. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +116 -25
  214. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
  216. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +11 -3
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  222. package/packages/pi-coding-agent/package.json +1 -1
  223. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +38 -0
  224. package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -1
  225. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +355 -8
  226. package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
  227. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
  228. package/packages/pi-coding-agent/src/core/model-resolver.ts +26 -70
  229. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +62 -26
  230. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
  231. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
  232. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
  233. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +136 -30
  234. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
  235. package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
  236. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  237. package/packages/pi-tui/dist/tui.d.ts +8 -0
  238. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  239. package/packages/pi-tui/dist/tui.js +32 -3
  240. package/packages/pi-tui/dist/tui.js.map +1 -1
  241. package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
  242. package/packages/pi-tui/src/tui.ts +31 -3
  243. package/pkg/package.json +1 -1
  244. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -4
  245. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +23 -2
  246. package/src/resources/extensions/gsd/auto/phases.ts +22 -9
  247. package/src/resources/extensions/gsd/auto-dispatch.ts +15 -4
  248. package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
  249. package/src/resources/extensions/gsd/auto-prompts.ts +9 -3
  250. package/src/resources/extensions/gsd/auto-start.ts +30 -6
  251. package/src/resources/extensions/gsd/auto.ts +10 -0
  252. package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
  253. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
  254. package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -1
  255. package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
  256. package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
  257. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  258. package/src/resources/extensions/gsd/gsd-db.ts +52 -2
  259. package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
  260. package/src/resources/extensions/gsd/notification-widget.ts +2 -2
  261. package/src/resources/extensions/gsd/preferences-models.ts +41 -0
  262. package/src/resources/extensions/gsd/preferences-types.ts +12 -0
  263. package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
  264. package/src/resources/extensions/gsd/state.ts +46 -0
  265. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
  266. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
  267. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
  268. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
  269. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
  270. package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
  271. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
  272. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
  273. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  274. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
  275. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
  276. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
  277. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
  278. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +267 -0
  279. package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
  280. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  281. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  282. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  283. /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → nwYTvJZ1-hZIfw98d9Wfg}/_buildManifest.js +0 -0
  284. /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → nwYTvJZ1-hZIfw98d9Wfg}/_ssgManifest.js +0 -0
@@ -6,6 +6,7 @@ import { debugTime } from "../debug-logger.js";
6
6
  import { loadPrompt, getTemplatesDir } from "../prompt-loader.js";
7
7
  import { readForensicsMarker } from "../forensics.js";
8
8
  import { resolveAllSkillReferences, renderPreferencesForSystemPrompt, loadEffectiveGSDPreferences } from "../preferences.js";
9
+ import { resolveModelWithFallbacksForUnit } from "../preferences-models.js";
9
10
  import { resolveSkillReference } from "../preferences-skills.js";
10
11
  import { resolveGsdRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile } from "../paths.js";
11
12
  import { ensureCodebaseMapFresh, readCodebaseMap } from "../codebase-generator.js";
@@ -147,7 +148,11 @@ export async function buildBeforeAgentStartResult(event, ctx) {
147
148
  // Re-inject forensics context on follow-up turns (#2941)
148
149
  const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null;
149
150
  const worktreeBlock = buildWorktreeContextBlock();
150
- const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
151
+ const subagentModelConfig = resolveModelWithFallbacksForUnit("subagent");
152
+ const subagentModelBlock = subagentModelConfig
153
+ ? `\n\n## Subagent Model\n\nWhen spawning subagents via the \`subagent\` tool, always pass \`model: "${subagentModelConfig.primary}"\` in the tool call parameters. Never omit this — always specify it explicitly.`
154
+ : "";
155
+ const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}${subagentModelBlock}`;
151
156
  stopContextTimer({
152
157
  systemPromptSize: fullSystem.length,
153
158
  injectionSize: injection?.length ?? forensicsInjection?.length ?? 0,
@@ -17,6 +17,11 @@ import { projectRoot } from "./commands/context.js";
17
17
  import { loadPrompt } from "./prompt-loader.js";
18
18
  const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
19
19
  const UPDATE_FETCH_TIMEOUT_MS = 5000;
20
+ function resolveInstallCommand(pkg) {
21
+ if ('bun' in process.versions)
22
+ return `bun add -g ${pkg}`;
23
+ return `npm install -g ${pkg}`;
24
+ }
20
25
  async function fetchLatestVersionForCommand() {
21
26
  const controller = new AbortController();
22
27
  const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
@@ -344,13 +349,14 @@ export async function handleUpdate(ctx) {
344
349
  return;
345
350
  }
346
351
  ctx.ui.notify(`Updating: v${current} → v${latest}...`, "info");
352
+ const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`);
347
353
  try {
348
- execSync(`npm install -g ${NPM_PACKAGE}@latest`, {
354
+ execSync(installCmd, {
349
355
  stdio: ["ignore", "pipe", "ignore"],
350
356
  });
351
357
  ctx.ui.notify(`Updated to v${latest}. Restart your GSD session to use the new version.`, "info");
352
358
  }
353
359
  catch {
354
- ctx.ui.notify(`Update failed. Try manually: npm install -g ${NPM_PACKAGE}@latest`, "error");
360
+ ctx.ui.notify(`Update failed. Try manually: ${installCmd}`, "error");
355
361
  }
356
362
  }
@@ -14,6 +14,7 @@ import { join } from "node:path";
14
14
  import { gsdRoot } from "./paths.js";
15
15
  import { atomicWriteSync } from "./atomic-write.js";
16
16
  import { effectiveLockFile } from "./session-lock.js";
17
+ import { emitJournalEvent, queryJournal } from "./journal.js";
17
18
  function lockPath(basePath) {
18
19
  return join(gsdRoot(basePath), effectiveLockFile());
19
20
  }
@@ -110,3 +111,53 @@ export function formatCrashInfo(lock) {
110
111
  }
111
112
  return lines.join("\n");
112
113
  }
114
+ /**
115
+ * Emit a synthetic unit-end event for a unit that crashed without emitting its own.
116
+ *
117
+ * Queries the journal to find the most recent unit-start for the crashed unit.
118
+ * If a matching unit-end already exists (e.g. the hard timeout fired), this is a
119
+ * no-op. Called during crash recovery, before clearing the stale lock.
120
+ *
121
+ * Addresses the gap reported in #3348 where `unit-start` was emitted but no
122
+ * `unit-end` followed — side effects landed but the worker died before closeout.
123
+ */
124
+ export function emitCrashRecoveredUnitEnd(basePath, lock) {
125
+ // Skip bootstrap / starting pseudo-units — they have no meaningful unit-start event.
126
+ if (!lock.unitType || !lock.unitId || lock.unitType === "starting")
127
+ return;
128
+ try {
129
+ const all = queryJournal(basePath);
130
+ // Find the most recent unit-start for this unitId
131
+ const starts = all.filter((e) => e.eventType === "unit-start" && e.data?.unitId === lock.unitId);
132
+ if (starts.length === 0)
133
+ return;
134
+ const lastStart = starts[starts.length - 1];
135
+ // Check if a unit-end was already emitted (e.g. hard timeout fired after the crash)
136
+ const alreadyClosed = all.some((e) => e.eventType === "unit-end" &&
137
+ e.data?.unitId === lock.unitId &&
138
+ e.causedBy?.flowId === lastStart.flowId &&
139
+ e.causedBy?.seq === lastStart.seq);
140
+ if (alreadyClosed)
141
+ return;
142
+ // Find the highest seq in this flow for monotonic ordering
143
+ const maxSeq = all
144
+ .filter((e) => e.flowId === lastStart.flowId)
145
+ .reduce((max, e) => Math.max(max, e.seq), lastStart.seq);
146
+ emitJournalEvent(basePath, {
147
+ ts: new Date().toISOString(),
148
+ flowId: lastStart.flowId,
149
+ seq: maxSeq + 1,
150
+ eventType: "unit-end",
151
+ data: {
152
+ unitType: lock.unitType,
153
+ unitId: lock.unitId,
154
+ status: "crash-recovered",
155
+ artifactVerified: false,
156
+ },
157
+ causedBy: { flowId: lastStart.flowId, seq: lastStart.seq },
158
+ });
159
+ }
160
+ catch {
161
+ // Never throw from crash recovery path — journal failure must not block recovery
162
+ }
163
+ }
@@ -157,7 +157,7 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
157
157
 
158
158
  - `phases`: fine-grained control over which phases run. Usually set by `token_profile`, but can be overridden. Keys:
159
159
  - `skip_research`: boolean — skip milestone-level research. Default: `false`.
160
- - `reassess_after_slice`: boolean — run roadmap reassessment after each completed slice. Default: `false`.
160
+ - `reassess_after_slice`: boolean — run roadmap reassessment after each completed slice. Default: `true`.
161
161
  - `skip_reassess`: boolean — force-disable roadmap reassessment even if `reassess_after_slice` is enabled. Default: `false`.
162
162
  - `skip_slice_research`: boolean — skip per-slice research. Default: `false`.
163
163
 
@@ -1352,6 +1352,25 @@ export function setSliceSummaryMd(milestoneId, sliceId, summaryMd, uatMd) {
1352
1352
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1353
1353
  currentDb.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
1354
1354
  }
1355
+ function parseTaskArrayColumn(raw) {
1356
+ if (typeof raw !== "string" || raw.trim() === "")
1357
+ return [];
1358
+ try {
1359
+ const parsed = JSON.parse(raw);
1360
+ if (Array.isArray(parsed))
1361
+ return parsed.map((value) => String(value));
1362
+ if (parsed === null || parsed === undefined || parsed === "")
1363
+ return [];
1364
+ return [String(parsed)];
1365
+ }
1366
+ catch {
1367
+ // Older/corrupt rows may contain comma-separated strings instead of JSON.
1368
+ return raw
1369
+ .split(",")
1370
+ .map((value) => value.trim())
1371
+ .filter(Boolean);
1372
+ }
1373
+ }
1355
1374
  function rowToTask(row) {
1356
1375
  const parseTaskArray = (value) => {
1357
1376
  if (Array.isArray(value)) {
@@ -1390,8 +1409,8 @@ function rowToTask(row) {
1390
1409
  blocker_discovered: row["blocker_discovered"] === 1,
1391
1410
  deviations: row["deviations"],
1392
1411
  known_issues: row["known_issues"],
1393
- key_files: JSON.parse(row["key_files"] || "[]"),
1394
- key_decisions: JSON.parse(row["key_decisions"] || "[]"),
1412
+ key_files: parseTaskArrayColumn(row["key_files"]),
1413
+ key_decisions: parseTaskArrayColumn(row["key_decisions"]),
1395
1414
  full_summary_md: row["full_summary_md"],
1396
1415
  description: row["description"] ?? "",
1397
1416
  estimate: row["estimate"] ?? "",
@@ -1855,6 +1874,21 @@ export function deleteSlice(milestoneId, sliceId) {
1855
1874
  currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
1856
1875
  });
1857
1876
  }
1877
+ export function deleteMilestone(milestoneId) {
1878
+ if (!currentDb)
1879
+ throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1880
+ transaction(() => {
1881
+ currentDb.prepare(`DELETE FROM verification_evidence WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1882
+ currentDb.prepare(`DELETE FROM quality_gates WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1883
+ currentDb.prepare(`DELETE FROM tasks WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1884
+ currentDb.prepare(`DELETE FROM slice_dependencies WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1885
+ currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1886
+ currentDb.prepare(`DELETE FROM replan_history WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1887
+ currentDb.prepare(`DELETE FROM assessments WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1888
+ currentDb.prepare(`DELETE FROM artifacts WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1889
+ currentDb.prepare(`DELETE FROM milestones WHERE id = :mid`).run({ ":mid": milestoneId });
1890
+ });
1891
+ }
1858
1892
  export function updateSliceFields(milestoneId, sliceId, fields) {
1859
1893
  if (!currentDb)
1860
1894
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
@@ -15,7 +15,8 @@ import { join } from "node:path";
15
15
  import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } from "./paths.js";
16
16
  import { invalidateAllCaches } from "./cache.js";
17
17
  import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
18
- import { getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
18
+ import { deleteMilestone, getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
19
+ import { removeWorktree } from "./worktree-manager.js";
19
20
  import { logWarning } from "./workflow-logger.js";
20
21
  // ─── Park ──────────────────────────────────────────────────────────────────
21
22
  /**
@@ -99,12 +100,29 @@ export function discardMilestone(basePath, milestoneId) {
99
100
  const mDir = resolveMilestonePath(basePath, milestoneId);
100
101
  if (!mDir || !existsSync(mDir))
101
102
  return false;
103
+ try {
104
+ removeWorktree(basePath, milestoneId, {
105
+ branch: `milestone/${milestoneId}`,
106
+ deleteBranch: true,
107
+ });
108
+ }
109
+ catch (err) {
110
+ logWarning("engine", `discardMilestone worktree cleanup failed for ${milestoneId}: ${err.message}`);
111
+ }
102
112
  rmSync(mDir, { recursive: true, force: true });
103
113
  // Prune from queue order if present
104
114
  const order = loadQueueOrder(basePath);
105
115
  if (order && order.includes(milestoneId)) {
106
116
  saveQueueOrder(basePath, order.filter(id => id !== milestoneId));
107
117
  }
118
+ if (isDbAvailable()) {
119
+ try {
120
+ deleteMilestone(milestoneId);
121
+ }
122
+ catch (err) {
123
+ logWarning("engine", `discardMilestone DB cleanup failed for ${milestoneId}: ${err.message}`);
124
+ }
125
+ }
108
126
  invalidateAllCaches();
109
127
  return true;
110
128
  }
@@ -1,6 +1,6 @@
1
1
  // GSD Extension — Notification Widget
2
2
  // Always-on ambient widget rendered belowEditor showing unread count and
3
- // the most recent notification message. Refreshes every 5 seconds.
3
+ // the most recent notification message. Refreshes every 30 seconds.
4
4
  // Widget key: "gsd-notifications", placement: "belowEditor"
5
5
  import { getUnreadCount, onNotificationStoreChange } from "./notification-store.js";
6
6
  import { formattedShortcutPair } from "./shortcut-defs.js";
@@ -12,7 +12,7 @@ export function buildNotificationWidgetLines() {
12
12
  return [` 🔔 Notifications: ${unread} unread (${formattedShortcutPair("notifications")})`];
13
13
  }
14
14
  // ─── Widget init ────────────────────────────────────────────────────────
15
- const REFRESH_INTERVAL_MS = 5_000;
15
+ const REFRESH_INTERVAL_MS = 30_000;
16
16
  /**
17
17
  * Initialize the always-on notification widget (belowEditor).
18
18
  * Call once from session_start after the notification store is initialized.
@@ -6,6 +6,8 @@
6
6
  * and dynamic routing configuration.
7
7
  */
8
8
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
9
11
  import { defaultRoutingConfig } from "./model-router.js";
10
12
  import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
11
13
  /**
@@ -161,6 +163,47 @@ export function resolveDefaultSessionModel(sessionProvider) {
161
163
  }
162
164
  return undefined;
163
165
  }
166
+ /**
167
+ * Returns true if `provider` is defined as a custom provider in the user's
168
+ * `~/.gsd/agent/models.json` (Ollama, vLLM, LM Studio, OpenAI-compatible
169
+ * proxies, etc.).
170
+ *
171
+ * Used by auto-mode bootstrap to decide whether the session model
172
+ * (set via `/gsd model`) should override `PREFERENCES.md`. Custom providers
173
+ * are never reachable from `PREFERENCES.md` (which only knows built-in
174
+ * providers), so when the user has explicitly selected one, it must take
175
+ * priority — otherwise auto-mode tries to start the built-in provider from
176
+ * PREFERENCES.md and fails with "Not logged in · Please run /login" (#4122).
177
+ *
178
+ * Reads models.json directly with a lightweight JSON parse to avoid
179
+ * pulling in the full model-registry at this call site. Falls back to
180
+ * `~/.pi/agent/models.json` for parity with `resolveModelsJsonPath()`.
181
+ * Any read or parse error yields `false` (treat as not-custom) so a
182
+ * malformed models.json never breaks the session bootstrap.
183
+ */
184
+ export function isCustomProvider(provider) {
185
+ if (!provider)
186
+ return false;
187
+ const candidates = [
188
+ join(homedir(), ".gsd", "agent", "models.json"),
189
+ join(homedir(), ".pi", "agent", "models.json"),
190
+ ];
191
+ for (const path of candidates) {
192
+ if (!existsSync(path))
193
+ continue;
194
+ try {
195
+ const raw = readFileSync(path, "utf-8");
196
+ const parsed = JSON.parse(raw);
197
+ if (parsed?.providers && Object.prototype.hasOwnProperty.call(parsed.providers, provider)) {
198
+ return true;
199
+ }
200
+ }
201
+ catch {
202
+ // Ignore — malformed models.json must not break bootstrap.
203
+ }
204
+ }
205
+ return false;
206
+ }
164
207
  /**
165
208
  * Determines the next fallback model to try when the current model fails.
166
209
  * If the current model is not in the configured list, returns the primary model.
@@ -83,6 +83,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set([
83
83
  "discuss_preparation",
84
84
  "discuss_web_research",
85
85
  "discuss_depth",
86
+ "flat_rate_providers",
86
87
  ]);
87
88
  /** Canonical list of all dispatch unit types. */
88
89
  export const KNOWN_UNIT_TYPES = [
@@ -155,6 +155,28 @@ export function validatePreferences(preferences) {
155
155
  errors.push(`search_provider must be one of: brave, tavily, ollama, native, auto`);
156
156
  }
157
157
  }
158
+ // ─── Flat-rate Providers ────────────────────────────────────────────
159
+ // User-declared flat-rate providers for dynamic routing suppression.
160
+ // Built-in providers (github-copilot, copilot, claude-code) and any
161
+ // externalCli provider are already auto-detected; this list layers on
162
+ // top for private subscription proxies and custom CLI wrappers.
163
+ if (preferences.flat_rate_providers !== undefined) {
164
+ if (Array.isArray(preferences.flat_rate_providers)) {
165
+ const allStrings = preferences.flat_rate_providers.every((item) => typeof item === "string");
166
+ if (allStrings) {
167
+ // Strip empty/whitespace-only entries to avoid false matches.
168
+ validated.flat_rate_providers = preferences.flat_rate_providers
169
+ .map((s) => s.trim())
170
+ .filter((s) => s.length > 0);
171
+ }
172
+ else {
173
+ errors.push("flat_rate_providers must be an array of strings");
174
+ }
175
+ }
176
+ else {
177
+ errors.push("flat_rate_providers must be an array of strings");
178
+ }
179
+ }
158
180
  // ─── Phase Skip Preferences ─────────────────────────────────────────
159
181
  if (preferences.phases !== undefined) {
160
182
  if (typeof preferences.phases === "object" && preferences.phases !== null) {
@@ -516,6 +516,9 @@ function resolveSliceDependencies(activeMilestoneSlices) {
516
516
  return { activeSlice: null, activeSliceRow: null };
517
517
  }
518
518
  }
519
+ // First pass: find a slice with ALL dependencies satisfied (strict)
520
+ let bestFallback = null;
521
+ let bestFallbackSatisfied = -1;
519
522
  for (const s of activeMilestoneSlices) {
520
523
  if (isStatusDone(s.status))
521
524
  continue;
@@ -524,6 +527,23 @@ function resolveSliceDependencies(activeMilestoneSlices) {
524
527
  if (s.depends.every(dep => doneSliceIds.has(dep))) {
525
528
  return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
526
529
  }
530
+ // Track the slice with the most satisfied dependencies as fallback
531
+ const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
532
+ if (satisfied > bestFallbackSatisfied || (satisfied === bestFallbackSatisfied && !bestFallback)) {
533
+ bestFallback = s;
534
+ bestFallbackSatisfied = satisfied;
535
+ }
536
+ }
537
+ // Fallback: if no slice has all deps met but there ARE incomplete non-deferred
538
+ // slices, pick the one with the most deps satisfied. This prevents hard-blocking
539
+ // when dependency metadata is stale (e.g. after reassessment added/removed slices)
540
+ // or when deps reference slices from previous milestones.
541
+ if (bestFallback) {
542
+ const unmet = bestFallback.depends.filter(dep => !doneSliceIds.has(dep));
543
+ logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallback.id} ` +
544
+ `(${bestFallbackSatisfied}/${bestFallback.depends.length} deps met, ` +
545
+ `unmet: ${unmet.join(", ")})`, { mid: activeMilestoneSlices[0]?.milestone_id, sid: bestFallback.id });
546
+ return { activeSlice: { id: bestFallback.id, title: bestFallback.title }, activeSliceRow: bestFallback };
527
547
  }
528
548
  return { activeSlice: null, activeSliceRow: null };
529
549
  }
@@ -1269,6 +1289,8 @@ export async function _deriveStateImpl(basePath) {
1269
1289
  }
1270
1290
  }
1271
1291
  else {
1292
+ let bestFallbackLegacy = null;
1293
+ let bestFallbackLegacySatisfied = -1;
1272
1294
  for (const s of activeRoadmap.slices) {
1273
1295
  if (s.done)
1274
1296
  continue;
@@ -1276,6 +1298,20 @@ export async function _deriveStateImpl(basePath) {
1276
1298
  activeSlice = { id: s.id, title: s.title };
1277
1299
  break;
1278
1300
  }
1301
+ // Track best fallback
1302
+ const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
1303
+ if (satisfied > bestFallbackLegacySatisfied) {
1304
+ bestFallbackLegacy = s;
1305
+ bestFallbackLegacySatisfied = satisfied;
1306
+ }
1307
+ }
1308
+ // Fallback: if no slice has all deps met, pick the one with the most deps satisfied
1309
+ if (!activeSlice && bestFallbackLegacy) {
1310
+ const unmet = bestFallbackLegacy.depends.filter(dep => !doneSliceIds.has(dep));
1311
+ logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallbackLegacy.id} ` +
1312
+ `(${bestFallbackLegacySatisfied}/${bestFallbackLegacy.depends.length} deps met, ` +
1313
+ `unmet: ${unmet.join(", ")})`);
1314
+ activeSlice = { id: bestFallbackLegacy.id, title: bestFallbackLegacy.title };
1279
1315
  }
1280
1316
  }
1281
1317
  if (!activeSlice) {
@@ -34,15 +34,18 @@ export function validateConfiguredModel(modelRegistry, settingsManager) {
34
34
  if (!configuredModel || !configuredExists) {
35
35
  // Model not configured at all, or removed from registry — pick a fallback.
36
36
  // Only fires when the model is genuinely unknown (not just temporarily unavailable).
37
+ //
38
+ // Model-agnostic selection order:
39
+ // 1. Pi migration default (preserves migration from ~/.pi install)
40
+ // 2. Any model from the user's previously-chosen provider (provider stickiness)
41
+ // 3. First available model in registry order (user-controlled via models.json)
37
42
  const piDefault = getPiDefaultModelAndProvider();
38
43
  const preferred = (piDefault
39
44
  ? availableModels.find((m) => m.provider === piDefault.provider && m.id === piDefault.model)
40
45
  : undefined) ||
41
- availableModels.find((m) => m.provider === 'openai' && m.id === 'gpt-5.4') ||
42
- availableModels.find((m) => m.provider === 'openai') ||
43
- availableModels.find((m) => m.provider === 'anthropic' && m.id === 'claude-opus-4-6') ||
44
- availableModels.find((m) => m.provider === 'anthropic' && m.id.includes('opus')) ||
45
- availableModels.find((m) => m.provider === 'anthropic') ||
46
+ (configuredProvider
47
+ ? availableModels.find((m) => m.provider === configuredProvider)
48
+ : undefined) ||
46
49
  availableModels[0];
47
50
  if (preferred) {
48
51
  settingsManager.setDefaultModelAndProvider(preferred.provider, preferred.id);
@@ -9,6 +9,7 @@ export declare function compareSemver(a: string, b: string): number;
9
9
  export declare function readUpdateCache(cachePath?: string): UpdateCheckCache | null;
10
10
  export declare function writeUpdateCache(cache: UpdateCheckCache, cachePath?: string): void;
11
11
  export declare function fetchLatestVersionFromRegistry(registryUrl?: string, fetchTimeoutMs?: number): Promise<string | null>;
12
+ export declare function resolveInstallCommand(pkg: string): string;
12
13
  export interface UpdateCheckOptions {
13
14
  currentVersion?: string;
14
15
  cachePath?: string;
@@ -66,9 +66,16 @@ export async function fetchLatestVersionFromRegistry(registryUrl = DEFAULT_REGIS
66
66
  clearTimeout(timeout);
67
67
  }
68
68
  }
69
+ export function resolveInstallCommand(pkg) {
70
+ if ('bun' in process.versions) {
71
+ return `bun add -g ${pkg}`;
72
+ }
73
+ return `npm install -g ${pkg}`;
74
+ }
69
75
  function printUpdateBanner(current, latest) {
76
+ const installCmd = resolveInstallCommand('gsd-pi');
70
77
  process.stderr.write(` ${chalk.yellow('Update available:')} ${chalk.dim(`v${current}`)} → ${chalk.bold(`v${latest}`)}\n` +
71
- ` ${chalk.dim('Run')} npm update -g gsd-pi ${chalk.dim('or')} /gsd update ${chalk.dim('to upgrade')}\n\n`);
78
+ ` ${chalk.dim('Run')} ${installCmd} ${chalk.dim('or')} /gsd update ${chalk.dim('to upgrade')}\n\n`);
72
79
  }
73
80
  /**
74
81
  * Non-blocking update check. Queries npm registry at most once per 24h,
@@ -152,7 +159,7 @@ export async function checkAndPromptForUpdates(options = {}) {
152
159
  const readline = await import('node:readline');
153
160
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
154
161
  const choice = await new Promise((resolve) => {
155
- process.stderr.write(` ${chalk.bold('[1]')} Update now ${chalk.dim(`npm install -g ${NPM_PACKAGE_NAME}@latest`)}\n` +
162
+ process.stderr.write(` ${chalk.bold('[1]')} Update now ${chalk.dim(resolveInstallCommand(`${NPM_PACKAGE_NAME}@latest`))}\n` +
156
163
  ` ${chalk.bold('[2]')} Skip\n\n`);
157
164
  // Default to skip if the user doesn't respond within PROMPT_TIMEOUT_MS
158
165
  const timer = setTimeout(() => {
@@ -173,14 +180,15 @@ export async function checkAndPromptForUpdates(options = {}) {
173
180
  process.stdin.setRawMode(false);
174
181
  process.stdin.pause();
175
182
  if (choice === '1') {
176
- process.stderr.write(`\n ${chalk.dim('Running:')} npm install -g ${NPM_PACKAGE_NAME}@latest\n\n`);
183
+ const installCmd = resolveInstallCommand(`${NPM_PACKAGE_NAME}@latest`);
184
+ process.stderr.write(`\n ${chalk.dim('Running:')} ${installCmd}\n\n`);
177
185
  try {
178
- execSync(`npm install -g ${NPM_PACKAGE_NAME}@latest`, { stdio: 'inherit' });
186
+ execSync(installCmd, { stdio: 'inherit' });
179
187
  process.stderr.write(`\n ${chalk.green.bold(`✓ Updated to v${latestVersion}`)}\n\n`);
180
188
  return true;
181
189
  }
182
190
  catch {
183
- process.stderr.write(`\n ${chalk.yellow(`Update failed. You can run: npm install -g ${NPM_PACKAGE_NAME}@latest`)}\n\n`);
191
+ process.stderr.write(`\n ${chalk.yellow(`Update failed. You can run: ${installCmd}`)}\n\n`);
184
192
  }
185
193
  }
186
194
  else {
@@ -1,5 +1,5 @@
1
1
  import { execSync } from 'node:child_process';
2
- import { compareSemver, fetchLatestVersionFromRegistry } from './update-check.js';
2
+ import { compareSemver, fetchLatestVersionFromRegistry, resolveInstallCommand } from './update-check.js';
3
3
  const NPM_PACKAGE = 'gsd-pi';
4
4
  export async function runUpdate() {
5
5
  const current = process.env.GSD_VERSION || '0.0.0';
@@ -21,14 +21,15 @@ export async function runUpdate() {
21
21
  return;
22
22
  }
23
23
  process.stdout.write(`${dim}Updating:${reset} v${current} → ${bold}v${latest}${reset}\n`);
24
+ const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`);
24
25
  try {
25
- execSync(`npm install -g ${NPM_PACKAGE}@latest`, {
26
+ execSync(installCmd, {
26
27
  stdio: 'inherit',
27
28
  });
28
29
  process.stdout.write(`\n${green}${bold}Updated to v${latest}${reset}\n`);
29
30
  }
30
31
  catch {
31
- process.stderr.write(`\n${yellow}Update failed. Try manually: npm install -g ${NPM_PACKAGE}@latest${reset}\n`);
32
+ process.stderr.write(`\n${yellow}Update failed. Try manually: ${installCmd}${reset}\n`);
32
33
  process.exit(1);
33
34
  }
34
35
  }
@@ -1 +1 @@
1
- KSZ2dcC3p4z6lOmUpPpzr
1
+ nwYTvJZ1-hZIfw98d9Wfg
@@ -1,46 +1,46 @@
1
1
  {
2
2
  "/_not-found/page": "/_not-found",
3
3
  "/_global-error/page": "/_global-error",
4
- "/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
5
- "/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
6
4
  "/api/boot/route": "/api/boot",
5
+ "/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
6
+ "/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
7
7
  "/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
8
8
  "/api/dev-mode/route": "/api/dev-mode",
9
- "/api/cleanup/route": "/api/cleanup",
10
9
  "/api/doctor/route": "/api/doctor",
11
10
  "/api/captures/route": "/api/captures",
11
+ "/api/cleanup/route": "/api/cleanup",
12
12
  "/api/export-data/route": "/api/export-data",
13
13
  "/api/forensics/route": "/api/forensics",
14
14
  "/api/browse-directories/route": "/api/browse-directories",
15
15
  "/api/git/route": "/api/git",
16
- "/api/hooks/route": "/api/hooks",
17
16
  "/api/history/route": "/api/history",
17
+ "/api/hooks/route": "/api/hooks",
18
18
  "/api/inspect/route": "/api/inspect",
19
19
  "/api/knowledge/route": "/api/knowledge",
20
- "/api/notifications/route": "/api/notifications",
21
20
  "/api/live-state/route": "/api/live-state",
22
- "/api/preferences/route": "/api/preferences",
21
+ "/api/notifications/route": "/api/notifications",
23
22
  "/api/experimental/route": "/api/experimental",
24
- "/api/onboarding/route": "/api/onboarding",
23
+ "/api/preferences/route": "/api/preferences",
25
24
  "/api/recovery/route": "/api/recovery",
26
25
  "/api/projects/route": "/api/projects",
26
+ "/api/onboarding/route": "/api/onboarding",
27
27
  "/api/session/browser/route": "/api/session/browser",
28
28
  "/api/session/command/route": "/api/session/command",
29
- "/api/session/manage/route": "/api/session/manage",
30
- "/api/settings-data/route": "/api/settings-data",
31
29
  "/api/session/events/route": "/api/session/events",
30
+ "/api/settings-data/route": "/api/settings-data",
31
+ "/api/session/manage/route": "/api/session/manage",
32
32
  "/api/shutdown/route": "/api/shutdown",
33
33
  "/api/skill-health/route": "/api/skill-health",
34
34
  "/api/steer/route": "/api/steer",
35
- "/api/files/route": "/api/files",
36
35
  "/api/terminal/input/route": "/api/terminal/input",
37
36
  "/api/terminal/resize/route": "/api/terminal/resize",
38
- "/api/terminal/sessions/route": "/api/terminal/sessions",
39
37
  "/api/switch-root/route": "/api/switch-root",
38
+ "/api/terminal/sessions/route": "/api/terminal/sessions",
39
+ "/api/files/route": "/api/files",
40
40
  "/api/terminal/stream/route": "/api/terminal/stream",
41
+ "/api/undo/route": "/api/undo",
41
42
  "/api/terminal/upload/route": "/api/terminal/upload",
42
43
  "/api/visualizer/route": "/api/visualizer",
43
- "/api/undo/route": "/api/undo",
44
44
  "/api/update/route": "/api/update",
45
45
  "/api/remote-questions/route": "/api/remote-questions",
46
46
  "/page": "/"
@@ -4,14 +4,14 @@
4
4
  ],
5
5
  "devFiles": [],
6
6
  "lowPriorityFiles": [
7
- "static/KSZ2dcC3p4z6lOmUpPpzr/_buildManifest.js",
8
- "static/KSZ2dcC3p4z6lOmUpPpzr/_ssgManifest.js"
7
+ "static/nwYTvJZ1-hZIfw98d9Wfg/_buildManifest.js",
8
+ "static/nwYTvJZ1-hZIfw98d9Wfg/_ssgManifest.js"
9
9
  ],
10
10
  "rootMainFiles": [
11
11
  "static/chunks/webpack-b868033a5834586d.js",
12
12
  "static/chunks/4bd1b696-e356ca5ba0218e27.js",
13
13
  "static/chunks/3794-42fdce068d44fa4f.js",
14
- "static/chunks/main-app-d3d4c336195465f9.js"
14
+ "static/chunks/main-app-fdab67f7802d7832.js"
15
15
  ],
16
16
  "rootMainFilesTree": {},
17
17
  "pages": {
@@ -78,8 +78,8 @@
78
78
  "dynamicRoutes": {},
79
79
  "notFoundRoutes": [],
80
80
  "preview": {
81
- "previewModeId": "a5b55541bc3e0fa2f07c4087828bfdb9",
82
- "previewModeSigningKey": "1d263a5202d738e962bd891f42fec4583353ecdb77162ca3b09f7da49aa38e9c",
83
- "previewModeEncryptionKey": "82afb9b7e120d168495c89adfe9fcfe9feb930812ef95d0fe02a2f0d3c075948"
81
+ "previewModeId": "0d74a2aa6b006dd14811b9bbf32d42fa",
82
+ "previewModeSigningKey": "fa0cfda94f65f8fe26fd9b9bf19d6ae1cdbda72e6b17afe5827bf8861b070f06",
83
+ "previewModeEncryptionKey": "4458972e6433066063f0a25222104d24b8524438fd03f72cc9ea192774799ae5"
84
84
  }
85
85
  }
@@ -104,7 +104,7 @@
104
104
  "transform": "lodash/{{member}}"
105
105
  }
106
106
  },
107
- "outputFileTracingRoot": "/home/runner/_work/gsd-2/gsd-2",
107
+ "outputFileTracingRoot": "/__w/gsd-2/gsd-2",
108
108
  "cacheComponents": false,
109
109
  "cacheLife": {
110
110
  "default": {
@@ -308,11 +308,11 @@
308
308
  "node-pty"
309
309
  ],
310
310
  "turbopack": {
311
- "root": "/home/runner/_work/gsd-2/gsd-2"
311
+ "root": "/__w/gsd-2/gsd-2"
312
312
  },
313
313
  "distDirRoot": ".next"
314
314
  },
315
- "appDir": "/home/runner/_work/gsd-2/gsd-2/web",
315
+ "appDir": "/__w/gsd-2/gsd-2/web",
316
316
  "relativeAppDir": "web",
317
317
  "files": [
318
318
  ".next/routes-manifest.json",