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
@@ -3,45 +3,13 @@
3
3
  */
4
4
 
5
5
  import type { ThinkingLevel } from "@gsd/pi-agent-core";
6
- import { type Api, type KnownProvider, type Model, modelsAreEqual } from "@gsd/pi-ai";
6
+ import { type Api, type Model, modelsAreEqual } from "@gsd/pi-ai";
7
7
  import chalk from "chalk";
8
8
  import { minimatch } from "minimatch";
9
9
  import { isValidThinkingLevel } from "../cli/args.js";
10
10
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
11
11
  import type { ModelRegistry } from "./model-registry.js";
12
12
 
13
- /** Default model IDs for each known provider */
14
- const defaultModelPerProvider: Record<KnownProvider, string> = {
15
- "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
16
- anthropic: "claude-opus-4-6",
17
- "anthropic-vertex": "claude-sonnet-4-6",
18
- openai: "gpt-5.4",
19
- "azure-openai-responses": "gpt-5.2",
20
- "openai-codex": "gpt-5.4",
21
- google: "gemini-2.5-pro",
22
- "google-gemini-cli": "gemini-2.5-pro",
23
- "google-antigravity": "gemini-3.1-pro-high",
24
- "google-vertex": "gemini-3-pro-preview",
25
- "github-copilot": "gpt-4o",
26
- openrouter: "openai/gpt-5.1-codex",
27
- "vercel-ai-gateway": "anthropic/claude-opus-4-6",
28
- xai: "grok-4-fast-non-reasoning",
29
- groq: "openai/gpt-oss-120b",
30
- cerebras: "zai-glm-4.6",
31
- zai: "glm-4.6",
32
- mistral: "devstral-medium-latest",
33
- minimax: "MiniMax-M2.1",
34
- "minimax-cn": "MiniMax-M2.1",
35
- huggingface: "moonshotai/Kimi-K2.5",
36
- opencode: "claude-opus-4-6",
37
- "opencode-go": "kimi-k2.5",
38
- "kimi-coding": "kimi-k2-thinking",
39
- "alibaba-coding-plan": "qwen3.5-plus",
40
- "alibaba-dashscope": "qwen3.5-plus",
41
- ollama: "llama3.1:8b",
42
- "ollama-cloud": "qwen3:32b",
43
- };
44
-
45
13
  export interface ScopedModel {
46
14
  model: Model<Api>;
47
15
  /** Thinking level if explicitly specified in pattern (e.g., "model:high"), undefined otherwise */
@@ -123,10 +91,11 @@ function buildFallbackModel(provider: string, modelId: string, availableModels:
123
91
  const providerModels = availableModels.filter((m) => m.provider === provider);
124
92
  if (providerModels.length === 0) return undefined;
125
93
 
126
- const defaultId = defaultModelPerProvider[provider as KnownProvider];
127
- const baseModel = defaultId
128
- ? (providerModels.find((m) => m.id === defaultId) ?? providerModels[0])
129
- : providerModels[0];
94
+ // Use the first available model from this provider as a template for
95
+ // capabilities (context window, reasoning support, etc.). The user is
96
+ // explicitly providing a custom model id, so we just need any shape of
97
+ // model from the same provider to inherit from.
98
+ const baseModel = providerModels[0];
130
99
 
131
100
  return {
132
101
  ...baseModel,
@@ -503,33 +472,19 @@ export async function findInitialModel(options: {
503
472
  };
504
473
  }
505
474
 
506
- // 3. Try saved default from settings
507
- if (defaultProvider && defaultModelId) {
508
- // Guard against stale settings defaults: only use the saved provider/model
509
- // if the provider is actually request-ready (auth/OAuth/CLI ready).
510
- if (modelRegistry.isProviderRequestReady(defaultProvider)) {
511
- const found = modelRegistry.find(defaultProvider, defaultModelId);
512
- if (found) {
513
- // Check if the provider's recommended default is a higher-capability variant
514
- // of the saved model (e.g. saved "claude-opus-4-6" vs recommended "claude-opus-4-6-extended").
515
- // If so, prefer the recommended variant to avoid using a smaller context window (#1125).
516
- const recommendedId = defaultModelPerProvider[defaultProvider as KnownProvider];
517
- if (recommendedId && recommendedId !== defaultModelId && recommendedId.startsWith(defaultModelId)) {
518
- const recommended = modelRegistry.find(defaultProvider, recommendedId);
519
- if (recommended) {
520
- model = recommended;
521
- if (defaultThinkingLevel) {
522
- thinkingLevel = defaultThinkingLevel;
523
- }
524
- return { model, thinkingLevel, fallbackMessage: undefined };
525
- }
526
- }
527
- model = found;
528
- if (defaultThinkingLevel) {
529
- thinkingLevel = defaultThinkingLevel;
530
- }
531
- return { model, thinkingLevel, fallbackMessage: undefined };
475
+ // 3. Try saved default from settings — use it exactly as configured.
476
+ // Whatever the user chose is what gets used; no silent substitution.
477
+ // Skip the saved default if its provider is not request-ready (no auth
478
+ // available) so we fall through to an actually-usable model instead of
479
+ // returning a stale selection every selector surface would display.
480
+ if (defaultProvider && defaultModelId && modelRegistry.isProviderRequestReady(defaultProvider)) {
481
+ const found = modelRegistry.find(defaultProvider, defaultModelId);
482
+ if (found) {
483
+ model = found;
484
+ if (defaultThinkingLevel) {
485
+ thinkingLevel = defaultThinkingLevel;
532
486
  }
487
+ return { model, thinkingLevel, fallbackMessage: undefined };
533
488
  }
534
489
  }
535
490
 
@@ -537,16 +492,17 @@ export async function findInitialModel(options: {
537
492
  const availableModels = await modelRegistry.getAvailable();
538
493
 
539
494
  if (availableModels.length > 0) {
540
- // Try to find a default model from known providers
541
- for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {
542
- const defaultId = defaultModelPerProvider[provider];
543
- const match = availableModels.find((m) => m.provider === provider && m.id === defaultId);
544
- if (match) {
545
- return { model: match, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };
495
+ // Prefer a model from the user's saved provider if any is still available —
496
+ // provider stickiness, not a hard-coded Anthropic/OpenAI preference.
497
+ if (defaultProvider) {
498
+ const sameProvider = availableModels.find((m) => m.provider === defaultProvider);
499
+ if (sameProvider) {
500
+ return { model: sameProvider, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };
546
501
  }
547
502
  }
548
503
 
549
- // If no default found, use first available
504
+ // Otherwise use the first available registry order reflects models.json
505
+ // order, which the user controls.
550
506
  return { model: availableModels[0], thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };
551
507
  }
552
508
 
@@ -3,8 +3,15 @@ import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-t
3
3
  import { getMarkdownTheme, theme } from "../theme/theme.js";
4
4
  import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
5
5
 
6
+ export interface ContentRange {
7
+ startIndex: number;
8
+ endIndex: number;
9
+ }
10
+
6
11
  /**
7
- * Component that renders a complete assistant message
12
+ * Component that renders a complete assistant message, or a sub-range of its content[].
13
+ * When `range` is provided, only content[startIndex..endIndex] (inclusive) is rendered.
14
+ * Non-text/thinking blocks within the range are silently skipped.
8
15
  */
9
16
  export class AssistantMessageComponent extends Container {
10
17
  private contentContainer: Container;
@@ -12,18 +19,26 @@ export class AssistantMessageComponent extends Container {
12
19
  private markdownTheme: MarkdownTheme;
13
20
  private lastMessage?: AssistantMessage;
14
21
  private timestampFormat: TimestampFormat;
22
+ private range?: ContentRange;
23
+ private showMetadata: boolean;
15
24
 
16
25
  constructor(
17
26
  message?: AssistantMessage,
18
27
  hideThinkingBlock = false,
19
28
  markdownTheme: MarkdownTheme = getMarkdownTheme(),
20
29
  timestampFormat: TimestampFormat = "date-time-iso",
30
+ range?: ContentRange,
21
31
  ) {
22
32
  super();
23
33
 
24
34
  this.hideThinkingBlock = hideThinkingBlock;
25
35
  this.markdownTheme = markdownTheme;
26
36
  this.timestampFormat = timestampFormat;
37
+ this.range = range;
38
+ // No range = legacy full-message rendering; show metadata by default.
39
+ // Ranged (interleaved) instances start with metadata hidden; chat-controller
40
+ // calls setShowMetadata(true) on the last segment at message_end.
41
+ this.showMetadata = !range;
27
42
 
28
43
  // Container for text/thinking content
29
44
  this.contentContainer = new Container();
@@ -34,6 +49,20 @@ export class AssistantMessageComponent extends Container {
34
49
  }
35
50
  }
36
51
 
52
+ setRange(range: ContentRange | undefined): void {
53
+ this.range = range;
54
+ if (this.lastMessage) {
55
+ this.updateContent(this.lastMessage);
56
+ }
57
+ }
58
+
59
+ setShowMetadata(show: boolean): void {
60
+ this.showMetadata = show;
61
+ if (this.lastMessage) {
62
+ this.updateContent(this.lastMessage);
63
+ }
64
+ }
65
+
37
66
  override invalidate(): void {
38
67
  super.invalidate();
39
68
  if (this.lastMessage) {
@@ -51,7 +80,11 @@ export class AssistantMessageComponent extends Container {
51
80
  // Clear content container
52
81
  this.contentContainer.clear();
53
82
 
54
- const hasVisibleContent = message.content.some(
83
+ const start = this.range?.startIndex ?? 0;
84
+ const end = this.range?.endIndex ?? message.content.length - 1;
85
+ const slice = message.content.slice(start, end + 1);
86
+
87
+ const hasVisibleContent = slice.some(
55
88
  (c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()),
56
89
  );
57
90
 
@@ -59,9 +92,9 @@ export class AssistantMessageComponent extends Container {
59
92
  this.contentContainer.addChild(new Spacer(1));
60
93
  }
61
94
 
62
- // Render content in order
63
- for (let i = 0; i < message.content.length; i++) {
64
- const content = message.content[i];
95
+ // Render content in order; non-text/thinking blocks are silently skipped
96
+ for (let i = 0; i < slice.length; i++) {
97
+ const content = slice[i];
65
98
  if (content.type === "text" && content.text.trim()) {
66
99
  // Assistant text messages with no background - trim the text
67
100
  // Set paddingY=0 to avoid extra spacing before tool executions
@@ -69,7 +102,7 @@ export class AssistantMessageComponent extends Container {
69
102
  } else if (content.type === "thinking" && content.thinking.trim()) {
70
103
  // Add spacing only when another visible assistant content block follows.
71
104
  // This avoids a superfluous blank line before separately-rendered tool execution blocks.
72
- const hasVisibleContentAfter = message.content
105
+ const hasVisibleContentAfter = slice
73
106
  .slice(i + 1)
74
107
  .some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
75
108
 
@@ -94,30 +127,33 @@ export class AssistantMessageComponent extends Container {
94
127
  }
95
128
  }
96
129
 
97
- // Check if aborted - show after partial content
98
- // But only if there are no tool calls (tool execution components will show the error)
99
- const hasToolCalls = message.content.some((c) => c.type === "toolCall");
100
- if (!hasToolCalls) {
101
- if (message.stopReason === "aborted") {
102
- const abortMessage =
103
- message.errorMessage && message.errorMessage !== "Request was aborted"
104
- ? message.errorMessage
105
- : "Operation aborted";
106
- if (hasVisibleContent) {
130
+ // Metadata (errors, timestamp): gated on showMetadata so ranged instances stay clean
131
+ // until chat-controller explicitly enables it on the last segment at message_end.
132
+ if (this.showMetadata) {
133
+ // Check if aborted - show after partial content
134
+ // But only if there are no tool calls (tool execution components will show the error)
135
+ const hasToolCalls = message.content.some((c) => c.type === "toolCall");
136
+ if (!hasToolCalls) {
137
+ if (message.stopReason === "aborted") {
138
+ const abortMessage =
139
+ message.errorMessage && message.errorMessage !== "Request was aborted"
140
+ ? message.errorMessage
141
+ : "Operation aborted";
142
+ if (hasVisibleContent) {
143
+ this.contentContainer.addChild(new Spacer(1));
144
+ }
145
+ this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
146
+ } else if (message.stopReason === "error") {
147
+ const errorMsg = message.errorMessage || "Unknown error";
107
148
  this.contentContainer.addChild(new Spacer(1));
149
+ this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
108
150
  }
109
- this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
110
- } else if (message.stopReason === "error") {
111
- const errorMsg = message.errorMessage || "Unknown error";
112
- this.contentContainer.addChild(new Spacer(1));
113
- this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
114
151
  }
115
- }
116
152
 
117
- // Show timestamp when the message is complete (has a stop reason)
118
- if (message.stopReason && message.timestamp) {
119
- const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
120
- this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
153
+ if (message.stopReason && message.timestamp) {
154
+ const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
155
+ this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
156
+ }
121
157
  }
122
158
  }
123
159
  }
@@ -0,0 +1,73 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it, mock } from "node:test";
3
+
4
+ import { DynamicBorder } from "./dynamic-border.js";
5
+
6
+ function makeTUI() {
7
+ return {
8
+ renderCount: 0,
9
+ requestRender() {
10
+ this.renderCount++;
11
+ },
12
+ };
13
+ }
14
+
15
+ describe("DynamicBorder spinner", () => {
16
+ it("suppresses standalone render when an external render occurred recently", () => {
17
+ const border = new DynamicBorder((s) => s);
18
+ const tui = makeTUI();
19
+
20
+ border.startSpinner(tui as any, (s) => s);
21
+ // startSpinner calls requestRender once immediately
22
+ assert.equal(tui.renderCount, 1, "initial render on startSpinner");
23
+
24
+ // Simulate an externally-triggered render (e.g. from streaming)
25
+ border.render(80);
26
+
27
+ // Access the private interval callback by advancing the timer
28
+ // Instead, we directly test the render-batching logic:
29
+ // After render() sets lastExternalRender, a spinner tick within 200ms
30
+ // should NOT call requestRender.
31
+ const anyBorder = border as any;
32
+ assert.ok(
33
+ Date.now() - anyBorder.lastExternalRender < 200,
34
+ "lastExternalRender should be recent after render()",
35
+ );
36
+
37
+ border.stopSpinner();
38
+ });
39
+
40
+ it("triggers standalone render when no external render occurred recently", async () => {
41
+ const border = new DynamicBorder((s) => s);
42
+ const tui = makeTUI();
43
+
44
+ // Set lastExternalRender to a time well in the past
45
+ const anyBorder = border as any;
46
+ anyBorder.lastExternalRender = 0;
47
+
48
+ border.startSpinner(tui as any, (s) => s);
49
+ const initialCount = tui.renderCount;
50
+
51
+ // Wait for one spinner tick (200ms interval + buffer)
52
+ await new Promise((r) => setTimeout(r, 250));
53
+
54
+ assert.ok(
55
+ tui.renderCount > initialCount,
56
+ "spinner should trigger requestRender when no recent external render",
57
+ );
58
+
59
+ border.stopSpinner();
60
+ });
61
+
62
+ it("updates lastExternalRender on each render() call", () => {
63
+ const border = new DynamicBorder((s) => s);
64
+ const anyBorder = border as any;
65
+
66
+ const before = Date.now();
67
+ border.render(80);
68
+ const after = Date.now();
69
+
70
+ assert.ok(anyBorder.lastExternalRender >= before);
71
+ assert.ok(anyBorder.lastExternalRender <= after);
72
+ });
73
+ });
@@ -17,6 +17,7 @@ export class DynamicBorder implements Component {
17
17
  private spinnerIndex = 0;
18
18
  private spinnerInterval: NodeJS.Timeout | null = null;
19
19
  private spinnerColorFn?: (str: string) => string;
20
+ private lastExternalRender = 0;
20
21
 
21
22
  constructor(color: (str: string) => string = (str) => {
22
23
  try { return theme.fg("border", str); } catch { return str; }
@@ -31,7 +32,7 @@ export class DynamicBorder implements Component {
31
32
 
32
33
  /**
33
34
  * Start an animated spinner that prepends to the label.
34
- * The spinner rotates every 80ms and triggers a re-render via the TUI.
35
+ * The spinner rotates every 200ms and triggers a re-render via the TUI.
35
36
  */
36
37
  startSpinner(ui: TUI, colorFn: (str: string) => string): void {
37
38
  this.stopSpinner();
@@ -39,8 +40,12 @@ export class DynamicBorder implements Component {
39
40
  this.spinnerIndex = 0;
40
41
  this.spinnerInterval = setInterval(() => {
41
42
  this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
42
- ui.requestRender();
43
- }, 80);
43
+ // Only trigger standalone render if no other source rendered recently.
44
+ // During active streaming, message_update already calls requestRender().
45
+ if (Date.now() - this.lastExternalRender > 200) {
46
+ ui.requestRender();
47
+ }
48
+ }, 200);
44
49
  ui.requestRender();
45
50
  }
46
51
 
@@ -64,6 +69,7 @@ export class DynamicBorder implements Component {
64
69
  }
65
70
 
66
71
  render(width: number): string[] {
72
+ this.lastExternalRender = Date.now();
67
73
  const spinnerPrefix = this.spinnerInterval && this.spinnerColorFn
68
74
  ? this.spinnerColorFn(this.spinnerFrames[this.spinnerIndex]) + " "
69
75
  : "";
@@ -0,0 +1,71 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { findLatestPinnableText } from "./chat-controller.js";
5
+
6
+ test("findLatestPinnableText: empty content returns empty string", () => {
7
+ assert.equal(findLatestPinnableText([]), "");
8
+ });
9
+
10
+ test("findLatestPinnableText: no tool calls returns empty string", () => {
11
+ const blocks = [
12
+ { type: "text", text: "hello" },
13
+ { type: "text", text: "world" },
14
+ ];
15
+ assert.equal(findLatestPinnableText(blocks), "");
16
+ });
17
+
18
+ test("findLatestPinnableText: returns text preceding a tool call", () => {
19
+ const blocks = [
20
+ { type: "text", text: "doing the thing" },
21
+ { type: "toolCall", id: "1", name: "Read" },
22
+ ];
23
+ assert.equal(findLatestPinnableText(blocks), "doing the thing");
24
+ });
25
+
26
+ test("findLatestPinnableText: ignores trailing streaming text after the last tool call (regression: pinned mirror duplicated chat-container tokens)", () => {
27
+ const blocks = [
28
+ { type: "text", text: "first prose" },
29
+ { type: "toolCall", id: "1", name: "Read" },
30
+ { type: "text", text: "second prose still streaming" },
31
+ ];
32
+ assert.equal(findLatestPinnableText(blocks), "first prose");
33
+ });
34
+
35
+ test("findLatestPinnableText: with multiple tools, picks text before the most recent tool call", () => {
36
+ const blocks = [
37
+ { type: "text", text: "first" },
38
+ { type: "toolCall", id: "1", name: "Read" },
39
+ { type: "text", text: "second" },
40
+ { type: "toolCall", id: "2", name: "Grep" },
41
+ { type: "text", text: "third streaming" },
42
+ ];
43
+ assert.equal(findLatestPinnableText(blocks), "second");
44
+ });
45
+
46
+ test("findLatestPinnableText: treats serverToolUse the same as toolCall", () => {
47
+ const blocks = [
48
+ { type: "text", text: "before web search" },
49
+ { type: "serverToolUse", id: "ws1", name: "web_search" },
50
+ { type: "text", text: "answer streaming" },
51
+ ];
52
+ assert.equal(findLatestPinnableText(blocks), "before web search");
53
+ });
54
+
55
+ test("findLatestPinnableText: skips empty/whitespace-only text blocks", () => {
56
+ const blocks = [
57
+ { type: "text", text: "real prose" },
58
+ { type: "text", text: " " },
59
+ { type: "text", text: "" },
60
+ { type: "toolCall", id: "1", name: "Read" },
61
+ ];
62
+ assert.equal(findLatestPinnableText(blocks), "real prose");
63
+ });
64
+
65
+ test("findLatestPinnableText: thinking blocks are not pinnable", () => {
66
+ const blocks = [
67
+ { type: "thinking", thinking: "internal" },
68
+ { type: "toolCall", id: "1", name: "Read" },
69
+ ];
70
+ assert.equal(findLatestPinnableText(blocks), "");
71
+ });