gsd-pi 2.63.0 → 2.64.0-dev.1a85e85

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 (583) hide show
  1. package/README.md +46 -134
  2. package/dist/cli.js +48 -6
  3. package/dist/headless-query.js +11 -1
  4. package/dist/help-text.js +4 -1
  5. package/dist/onboarding.js +15 -8
  6. package/dist/resource-loader.js +18 -3
  7. package/dist/resources/extensions/cmux/index.js +21 -12
  8. package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
  9. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +4 -0
  11. package/dist/resources/extensions/gsd/auto/phases.js +157 -22
  12. package/dist/resources/extensions/gsd/auto/session.js +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
  16. package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
  18. package/dist/resources/extensions/gsd/auto-start.js +10 -21
  19. package/dist/resources/extensions/gsd/auto-timers.js +2 -1
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
  21. package/dist/resources/extensions/gsd/auto-verification.js +138 -1
  22. package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
  23. package/dist/resources/extensions/gsd/auto.js +24 -2
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
  25. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
  26. package/dist/resources/extensions/gsd/bootstrap/notify-interceptor.js +28 -0
  27. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
  28. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
  29. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +40 -1
  30. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +15 -0
  31. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
  32. package/dist/resources/extensions/gsd/bootstrap/system-context.js +50 -2
  33. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  34. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  35. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +103 -0
  36. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  37. package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
  38. package/dist/resources/extensions/gsd/constants.js +42 -0
  39. package/dist/resources/extensions/gsd/db-writer.js +72 -4
  40. package/dist/resources/extensions/gsd/forensics.js +20 -4
  41. package/dist/resources/extensions/gsd/gsd-db.js +64 -17
  42. package/dist/resources/extensions/gsd/guided-flow.js +19 -0
  43. package/dist/resources/extensions/gsd/metrics.js +27 -1
  44. package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
  45. package/dist/resources/extensions/gsd/notification-overlay.js +224 -0
  46. package/dist/resources/extensions/gsd/notification-store.js +268 -0
  47. package/dist/resources/extensions/gsd/notification-widget.js +56 -0
  48. package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
  49. package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
  50. package/dist/resources/extensions/gsd/preferences-types.js +6 -0
  51. package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
  52. package/dist/resources/extensions/gsd/preferences.js +11 -2
  53. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  54. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  55. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  56. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  57. package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
  58. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  59. package/dist/resources/extensions/gsd/prompts/system.md +4 -7
  60. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  61. package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
  62. package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
  63. package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
  64. package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
  65. package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
  66. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
  67. package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
  68. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
  69. package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
  70. package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
  71. package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
  72. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
  73. package/dist/resources/extensions/gsd/state.js +74 -14
  74. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  75. package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
  76. package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
  77. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
  78. package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
  79. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
  80. package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
  81. package/dist/resources/extensions/gsd/workflow-logger.js +8 -0
  82. package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
  83. package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
  84. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
  85. package/dist/resources/extensions/mcp-client/auth.js +101 -0
  86. package/dist/resources/extensions/mcp-client/index.js +10 -1
  87. package/dist/resources/extensions/ollama/index.js +28 -22
  88. package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
  89. package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
  90. package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
  91. package/dist/resources/extensions/ollama/ollama-client.js +23 -32
  92. package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
  93. package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
  94. package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
  95. package/dist/update-cmd.js +4 -2
  96. package/dist/web/standalone/.next/BUILD_ID +1 -1
  97. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -18
  98. package/dist/web/standalone/.next/build-manifest.json +3 -3
  99. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  100. package/dist/web/standalone/.next/required-server-files.json +4 -4
  101. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  102. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  103. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  105. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  113. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  124. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  148. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/notifications/route.js +3 -0
  177. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -0
  178. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -0
  179. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  181. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  189. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  191. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  194. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  197. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  199. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  203. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  205. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  206. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  211. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  214. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  217. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  218. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  219. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  221. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  223. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  224. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  225. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  226. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  227. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  228. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  229. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  230. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  231. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  232. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  233. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  234. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  235. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  236. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  237. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  238. package/dist/web/standalone/.next/server/app/index.html +1 -1
  239. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  240. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  241. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  242. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  243. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  244. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  245. package/dist/web/standalone/.next/server/app/page.js +2 -2
  246. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  247. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -18
  248. package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
  249. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  250. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  251. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  252. package/dist/web/standalone/.next/server/middleware.js +2 -2
  253. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  254. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  255. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  256. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  257. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  258. package/dist/web/standalone/.next/static/chunks/app/_global-error/page-8805a20e15762c3c.js +1 -0
  259. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  260. package/dist/web/standalone/.next/static/chunks/app/api/boot/route-8805a20e15762c3c.js +1 -0
  261. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-8805a20e15762c3c.js +1 -0
  262. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-8805a20e15762c3c.js +1 -0
  263. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-8805a20e15762c3c.js +1 -0
  264. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-8805a20e15762c3c.js +1 -0
  265. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-8805a20e15762c3c.js +1 -0
  266. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-8805a20e15762c3c.js +1 -0
  267. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-8805a20e15762c3c.js +1 -0
  268. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-8805a20e15762c3c.js +1 -0
  269. package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-8805a20e15762c3c.js +1 -0
  270. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-8805a20e15762c3c.js +1 -0
  271. package/dist/web/standalone/.next/static/chunks/app/api/files/route-8805a20e15762c3c.js +1 -0
  272. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-8805a20e15762c3c.js +1 -0
  273. package/dist/web/standalone/.next/static/chunks/app/api/git/route-8805a20e15762c3c.js +1 -0
  274. package/dist/web/standalone/.next/static/chunks/app/api/history/route-8805a20e15762c3c.js +1 -0
  275. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-8805a20e15762c3c.js +1 -0
  276. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-8805a20e15762c3c.js +1 -0
  277. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-8805a20e15762c3c.js +1 -0
  278. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-8805a20e15762c3c.js +1 -0
  279. package/dist/web/standalone/.next/static/chunks/app/api/notifications/route-8805a20e15762c3c.js +1 -0
  280. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-8805a20e15762c3c.js +1 -0
  281. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-8805a20e15762c3c.js +1 -0
  282. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-8805a20e15762c3c.js +1 -0
  283. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-8805a20e15762c3c.js +1 -0
  284. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-8805a20e15762c3c.js +1 -0
  285. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-8805a20e15762c3c.js +1 -0
  286. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-8805a20e15762c3c.js +1 -0
  287. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-8805a20e15762c3c.js +1 -0
  288. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-8805a20e15762c3c.js +1 -0
  289. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-8805a20e15762c3c.js +1 -0
  290. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-8805a20e15762c3c.js +1 -0
  291. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-8805a20e15762c3c.js +1 -0
  292. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-8805a20e15762c3c.js +1 -0
  293. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-8805a20e15762c3c.js +1 -0
  294. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-8805a20e15762c3c.js +1 -0
  295. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-8805a20e15762c3c.js +1 -0
  296. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-8805a20e15762c3c.js +1 -0
  297. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-8805a20e15762c3c.js +1 -0
  298. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-8805a20e15762c3c.js +1 -0
  299. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-8805a20e15762c3c.js +1 -0
  300. package/dist/web/standalone/.next/static/chunks/app/api/update/route-8805a20e15762c3c.js +1 -0
  301. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-8805a20e15762c3c.js +1 -0
  302. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  303. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  304. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  305. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-8805a20e15762c3c.js +1 -0
  306. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-8805a20e15762c3c.js +1 -0
  307. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  308. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-8805a20e15762c3c.js +1 -0
  309. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-8805a20e15762c3c.js +1 -0
  310. package/dist/web/standalone/.next/static/ffabZXz8JdN3EzX9EKt-R/_buildManifest.js +1 -0
  311. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  312. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  313. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  314. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  315. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  316. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  317. package/dist/web/standalone/server.js +1 -1
  318. package/dist/welcome-screen.js +1 -1
  319. package/package.json +1 -1
  320. package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
  321. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  322. package/packages/pi-agent-core/dist/agent-loop.js +70 -3
  323. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  324. package/packages/pi-agent-core/src/agent-loop.test.ts +317 -5
  325. package/packages/pi-agent-core/src/agent-loop.ts +90 -6
  326. package/packages/pi-ai/dist/types.d.ts +16 -1
  327. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  328. package/packages/pi-ai/dist/types.js.map +1 -1
  329. package/packages/pi-ai/src/types.ts +18 -1
  330. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts +2 -0
  331. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts.map +1 -0
  332. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +38 -0
  333. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -0
  334. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  335. package/packages/pi-coding-agent/dist/core/agent-session.js +11 -0
  336. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  337. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
  338. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  339. package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
  340. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  341. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
  342. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
  344. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  345. package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
  346. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
  348. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
  350. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
  351. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
  352. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
  353. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  354. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
  357. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
  359. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
  361. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  362. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts +2 -0
  363. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts.map +1 -0
  364. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +24 -0
  365. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -0
  366. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
  367. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  368. package/packages/pi-coding-agent/dist/core/resource-loader.js +84 -57
  369. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  370. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  371. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  372. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  373. package/packages/pi-coding-agent/package.json +1 -1
  374. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +64 -0
  375. package/packages/pi-coding-agent/src/core/agent-session.ts +10 -0
  376. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
  377. package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
  378. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
  379. package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
  380. package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
  381. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  382. package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
  383. package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
  384. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +42 -0
  385. package/packages/pi-coding-agent/src/core/resource-loader.ts +94 -57
  386. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  387. package/pkg/package.json +1 -1
  388. package/src/resources/extensions/cmux/index.ts +18 -12
  389. package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
  390. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
  391. package/src/resources/extensions/gsd/auto/loop.ts +5 -0
  392. package/src/resources/extensions/gsd/auto/phases.ts +194 -33
  393. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  394. package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
  395. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
  396. package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
  397. package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
  398. package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
  399. package/src/resources/extensions/gsd/auto-start.ts +11 -20
  400. package/src/resources/extensions/gsd/auto-timers.ts +2 -1
  401. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  402. package/src/resources/extensions/gsd/auto-verification.ts +190 -2
  403. package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
  404. package/src/resources/extensions/gsd/auto.ts +26 -1
  405. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
  406. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
  407. package/src/resources/extensions/gsd/bootstrap/notify-interceptor.ts +34 -0
  408. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
  409. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
  410. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +44 -1
  411. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +19 -0
  412. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
  413. package/src/resources/extensions/gsd/bootstrap/system-context.ts +59 -2
  414. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  415. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  416. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +139 -0
  417. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  418. package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
  419. package/src/resources/extensions/gsd/constants.ts +44 -0
  420. package/src/resources/extensions/gsd/db-writer.ts +78 -4
  421. package/src/resources/extensions/gsd/forensics.ts +21 -5
  422. package/src/resources/extensions/gsd/gsd-db.ts +64 -17
  423. package/src/resources/extensions/gsd/guided-flow.ts +22 -0
  424. package/src/resources/extensions/gsd/metrics.ts +28 -1
  425. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
  426. package/src/resources/extensions/gsd/notification-overlay.ts +267 -0
  427. package/src/resources/extensions/gsd/notification-store.ts +288 -0
  428. package/src/resources/extensions/gsd/notification-widget.ts +68 -0
  429. package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
  430. package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
  431. package/src/resources/extensions/gsd/preferences-types.ts +44 -0
  432. package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
  433. package/src/resources/extensions/gsd/preferences.ts +13 -2
  434. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  435. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  436. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  437. package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  438. package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
  439. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  440. package/src/resources/extensions/gsd/prompts/system.md +4 -7
  441. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  442. package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
  443. package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
  444. package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
  445. package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
  446. package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
  447. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
  448. package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
  449. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
  450. package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
  451. package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
  452. package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
  453. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
  454. package/src/resources/extensions/gsd/state.ts +67 -12
  455. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  456. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
  457. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
  458. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
  459. package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
  460. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
  461. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  462. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
  463. package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
  464. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
  465. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
  466. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
  467. package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
  468. package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
  469. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +76 -0
  470. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
  471. package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
  472. package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
  473. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
  474. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
  475. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
  476. package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
  477. package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
  478. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
  479. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
  480. package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
  481. package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
  482. package/src/resources/extensions/gsd/tests/notification-store.test.ts +249 -0
  483. package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
  484. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
  485. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
  486. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
  487. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
  488. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
  489. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
  490. package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
  491. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
  492. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
  493. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
  494. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
  495. package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
  496. package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
  497. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
  498. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
  499. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
  500. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
  501. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +163 -0
  502. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
  503. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
  504. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
  505. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
  506. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
  507. package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
  508. package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
  509. package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
  510. package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
  511. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
  512. package/src/resources/extensions/gsd/types.ts +44 -22
  513. package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
  514. package/src/resources/extensions/gsd/workflow-logger.ts +15 -1
  515. package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
  516. package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
  517. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
  518. package/src/resources/extensions/mcp-client/auth.ts +149 -0
  519. package/src/resources/extensions/mcp-client/index.ts +16 -1
  520. package/src/resources/extensions/ollama/index.ts +26 -25
  521. package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
  522. package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
  523. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
  524. package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
  525. package/src/resources/extensions/ollama/ollama-client.ts +30 -30
  526. package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
  527. package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
  528. package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
  529. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
  530. package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
  531. package/src/resources/extensions/ollama/types.ts +23 -0
  532. package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
  533. package/dist/web/standalone/.next/static/5FLUBNdqolRyyehCyChPd/_buildManifest.js +0 -1
  534. package/dist/web/standalone/.next/static/chunks/app/_global-error/page-c4cc189e7b117ea2.js +0 -1
  535. package/dist/web/standalone/.next/static/chunks/app/api/boot/route-c4cc189e7b117ea2.js +0 -1
  536. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-c4cc189e7b117ea2.js +0 -1
  537. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-c4cc189e7b117ea2.js +0 -1
  538. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-c4cc189e7b117ea2.js +0 -1
  539. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-c4cc189e7b117ea2.js +0 -1
  540. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-c4cc189e7b117ea2.js +0 -1
  541. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-c4cc189e7b117ea2.js +0 -1
  542. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-c4cc189e7b117ea2.js +0 -1
  543. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-c4cc189e7b117ea2.js +0 -1
  544. package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-c4cc189e7b117ea2.js +0 -1
  545. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-c4cc189e7b117ea2.js +0 -1
  546. package/dist/web/standalone/.next/static/chunks/app/api/files/route-c4cc189e7b117ea2.js +0 -1
  547. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-c4cc189e7b117ea2.js +0 -1
  548. package/dist/web/standalone/.next/static/chunks/app/api/git/route-c4cc189e7b117ea2.js +0 -1
  549. package/dist/web/standalone/.next/static/chunks/app/api/history/route-c4cc189e7b117ea2.js +0 -1
  550. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-c4cc189e7b117ea2.js +0 -1
  551. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-c4cc189e7b117ea2.js +0 -1
  552. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-c4cc189e7b117ea2.js +0 -1
  553. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-c4cc189e7b117ea2.js +0 -1
  554. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-c4cc189e7b117ea2.js +0 -1
  555. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-c4cc189e7b117ea2.js +0 -1
  556. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-c4cc189e7b117ea2.js +0 -1
  557. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-c4cc189e7b117ea2.js +0 -1
  558. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-c4cc189e7b117ea2.js +0 -1
  559. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-c4cc189e7b117ea2.js +0 -1
  560. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-c4cc189e7b117ea2.js +0 -1
  561. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-c4cc189e7b117ea2.js +0 -1
  562. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-c4cc189e7b117ea2.js +0 -1
  563. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-c4cc189e7b117ea2.js +0 -1
  564. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-c4cc189e7b117ea2.js +0 -1
  565. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-c4cc189e7b117ea2.js +0 -1
  566. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-c4cc189e7b117ea2.js +0 -1
  567. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-c4cc189e7b117ea2.js +0 -1
  568. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-c4cc189e7b117ea2.js +0 -1
  569. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-c4cc189e7b117ea2.js +0 -1
  570. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-c4cc189e7b117ea2.js +0 -1
  571. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-c4cc189e7b117ea2.js +0 -1
  572. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-c4cc189e7b117ea2.js +0 -1
  573. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-c4cc189e7b117ea2.js +0 -1
  574. package/dist/web/standalone/.next/static/chunks/app/api/update/route-c4cc189e7b117ea2.js +0 -1
  575. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-c4cc189e7b117ea2.js +0 -1
  576. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  577. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  578. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-c4cc189e7b117ea2.js +0 -1
  579. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-c4cc189e7b117ea2.js +0 -1
  580. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  581. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-c4cc189e7b117ea2.js +0 -1
  582. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c4cc189e7b117ea2.js +0 -1
  583. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → ffabZXz8JdN3EzX9EKt-R}/_ssgManifest.js +0 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Forensics detectStuckLoops tests — #1943
3
+ *
4
+ * Verifies that detectStuckLoops counts distinct dispatches (unique startedAt
5
+ * values per type/id) instead of raw entry count, which produces false-positive
6
+ * stuck-loop anomalies when idle-watchdog duplicate metrics entries exist.
7
+ */
8
+
9
+ import test from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import type { UnitMetrics } from "../metrics.js";
12
+ import { detectStuckLoops, type ForensicAnomaly } from "../forensics.js";
13
+
14
+ // ── Helpers ──────────────────────────────────────────────────────────────────
15
+
16
+ function makeUnit(overrides: Partial<UnitMetrics> = {}): UnitMetrics {
17
+ return {
18
+ type: "execute-task",
19
+ id: "M001/S01/T01",
20
+ model: "claude-sonnet-4-20250514",
21
+ startedAt: 1000,
22
+ finishedAt: 2000,
23
+ tokens: { input: 1000, output: 500, cacheRead: 200, cacheWrite: 100, total: 1800 },
24
+ cost: 0.05,
25
+ toolCalls: 3,
26
+ assistantMessages: 2,
27
+ userMessages: 1,
28
+ ...overrides,
29
+ };
30
+ }
31
+
32
+
33
+ // ── Tests ────────────────────────────────────────────────────────────────────
34
+
35
+ test("#1943 detectStuckLoops does not flag idle-watchdog duplicates as stuck loops", () => {
36
+ const anomalies: ForensicAnomaly[] = [];
37
+ const startedAt = 1774011016218;
38
+
39
+ // 20 entries with the SAME startedAt — these are idle-watchdog duplicates,
40
+ // not real re-dispatches. They should count as 1 dispatch.
41
+ const units: UnitMetrics[] = [];
42
+ for (let i = 0; i < 20; i++) {
43
+ units.push(makeUnit({
44
+ type: "research-slice",
45
+ id: "M009/S02",
46
+ startedAt,
47
+ finishedAt: startedAt + (i + 1) * 15000,
48
+ cost: 1.50 + i * 0.05,
49
+ toolCalls: 0,
50
+ }));
51
+ }
52
+
53
+ detectStuckLoops(units, anomalies);
54
+
55
+ // A single dispatch (same startedAt) should NOT trigger a stuck-loop anomaly
56
+ assert.equal(
57
+ anomalies.length, 0,
58
+ `expected 0 anomalies for 20 watchdog snapshots of the same dispatch, got ${anomalies.length}: ${anomalies.map(a => a.summary).join(", ")}`,
59
+ );
60
+ });
61
+
62
+ test("#1943 detectStuckLoops correctly flags real re-dispatches", () => {
63
+ const anomalies: ForensicAnomaly[] = [];
64
+
65
+ // 3 entries with DIFFERENT startedAt values — these are real re-dispatches
66
+ const units: UnitMetrics[] = [
67
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 1000, finishedAt: 2000, cost: 0.05 }),
68
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 3000, finishedAt: 4000, cost: 0.06 }),
69
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 5000, finishedAt: 6000, cost: 0.07 }),
70
+ ];
71
+
72
+ detectStuckLoops(units, anomalies);
73
+
74
+ assert.equal(anomalies.length, 1, "3 distinct dispatches of the same unit should flag 1 anomaly");
75
+ assert.equal(anomalies[0].type, "stuck-loop");
76
+ assert.ok(anomalies[0].summary.includes("3 times"), `summary should mention 3 dispatches: ${anomalies[0].summary}`);
77
+ });
78
+
79
+ test("#1943 detectStuckLoops ignores watchdog duplicates but flags real re-dispatches in mixed data", () => {
80
+ const anomalies: ForensicAnomaly[] = [];
81
+
82
+ const units: UnitMetrics[] = [
83
+ // 5 watchdog duplicates for dispatch 1 (same startedAt = 1000)
84
+ ...Array.from({ length: 5 }, (_, i) =>
85
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 1000, finishedAt: 1000 + (i + 1) * 15000, cost: 0.05 + i * 0.01 }),
86
+ ),
87
+ // 3 watchdog duplicates for dispatch 2 (same startedAt = 100000)
88
+ ...Array.from({ length: 3 }, (_, i) =>
89
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 100000, finishedAt: 100000 + (i + 1) * 15000, cost: 0.08 + i * 0.01 }),
90
+ ),
91
+ // 1 entry for dispatch 3 (startedAt = 200000)
92
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 200000, finishedAt: 260000, cost: 0.10 }),
93
+ // Different unit — only 1 dispatch, should NOT be flagged
94
+ makeUnit({ type: "plan-slice", id: "M001/S01", startedAt: 500, finishedAt: 1500, cost: 0.02 }),
95
+ ];
96
+
97
+ detectStuckLoops(units, anomalies);
98
+
99
+ // M001/S01/T01 has 3 distinct dispatches (startedAt: 1000, 100000, 200000) — should be flagged
100
+ // M001/S01 has 1 dispatch — should NOT be flagged
101
+ assert.equal(anomalies.length, 1, `expected 1 anomaly (for the 3x dispatched task), got ${anomalies.length}`);
102
+ assert.ok(anomalies[0].summary.includes("3 times"));
103
+ });
@@ -0,0 +1,94 @@
1
+ // GSD2 — Regression tests for git-checkpoint rollback (#3576)
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+
4
+ import { describe, it } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { tmpdir } from "node:os";
9
+ import { execFileSync } from "node:child_process";
10
+ import { createCheckpoint, rollbackToCheckpoint, cleanupCheckpoint } from "../safety/git-checkpoint.js";
11
+
12
+ function git(args: string[], cwd: string): string {
13
+ return execFileSync("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
14
+ }
15
+
16
+ function createTempRepo(): string {
17
+ const dir = mkdtempSync(join(tmpdir(), "ckpt-test-"));
18
+ git(["init"], dir);
19
+ git(["config", "user.email", "test@test.com"], dir);
20
+ git(["config", "user.name", "Test"], dir);
21
+ writeFileSync(join(dir, "file.txt"), "initial\n");
22
+ git(["add", "."], dir);
23
+ git(["commit", "-m", "init"], dir);
24
+ git(["branch", "-M", "main"], dir);
25
+ return dir;
26
+ }
27
+
28
+ describe("git-checkpoint rollback", () => {
29
+ it("rolls back to checkpoint on checked-out branch", (t) => {
30
+ const repo = createTempRepo();
31
+ t.after(() => rmSync(repo, { recursive: true, force: true }));
32
+
33
+ // Create checkpoint at initial commit
34
+ const sha = createCheckpoint(repo, "unit-1");
35
+ assert.ok(sha, "checkpoint should return a SHA");
36
+
37
+ // Make a second commit
38
+ writeFileSync(join(repo, "file.txt"), "modified\n");
39
+ git(["add", "."], repo);
40
+ git(["commit", "-m", "second"], repo);
41
+
42
+ const headBefore = git(["rev-parse", "HEAD"], repo);
43
+ assert.notEqual(headBefore, sha, "HEAD should have advanced");
44
+
45
+ // Rollback — this must work on the checked-out branch
46
+ const result = rollbackToCheckpoint(repo, "unit-1", sha);
47
+ assert.equal(result, true, "rollback should succeed");
48
+
49
+ const headAfter = git(["rev-parse", "HEAD"], repo);
50
+ assert.equal(headAfter, sha, "HEAD should match checkpoint SHA after rollback");
51
+ });
52
+
53
+ it("returns false on detached HEAD", (t) => {
54
+ const repo = createTempRepo();
55
+ t.after(() => rmSync(repo, { recursive: true, force: true }));
56
+
57
+ const sha = git(["rev-parse", "HEAD"], repo);
58
+ git(["checkout", "--detach", sha], repo);
59
+
60
+ const result = rollbackToCheckpoint(repo, "unit-2", sha);
61
+ assert.equal(result, false, "rollback should fail on detached HEAD");
62
+ });
63
+
64
+ it("cleans up checkpoint ref after rollback", (t) => {
65
+ const repo = createTempRepo();
66
+ t.after(() => rmSync(repo, { recursive: true, force: true }));
67
+
68
+ const sha = createCheckpoint(repo, "unit-3");
69
+ assert.ok(sha);
70
+
71
+ // Ref should exist
72
+ const refBefore = git(["for-each-ref", "refs/gsd/checkpoints/unit-3", "--format=%(objectname)"], repo);
73
+ assert.equal(refBefore, sha);
74
+
75
+ rollbackToCheckpoint(repo, "unit-3", sha);
76
+
77
+ // Ref should be cleaned up
78
+ const refAfter = git(["for-each-ref", "refs/gsd/checkpoints/unit-3", "--format=%(objectname)"], repo);
79
+ assert.equal(refAfter, "", "checkpoint ref should be removed after rollback");
80
+ });
81
+
82
+ it("cleanupCheckpoint removes the ref without error", (t) => {
83
+ const repo = createTempRepo();
84
+ t.after(() => rmSync(repo, { recursive: true, force: true }));
85
+
86
+ const sha = createCheckpoint(repo, "unit-4");
87
+ assert.ok(sha);
88
+
89
+ cleanupCheckpoint(repo, "unit-4");
90
+
91
+ const ref = git(["for-each-ref", "refs/gsd/checkpoints/unit-4", "--format=%(objectname)"], repo);
92
+ assert.equal(ref, "", "ref should be gone");
93
+ });
94
+ });
@@ -0,0 +1,88 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+
4
+ import { openDatabase, closeDatabase, insertMilestone, insertSlice, getSlice } from '../gsd-db.ts';
5
+
6
+ test('insertSlice with minimal args does not wipe populated fields', (t) => {
7
+ t.after(() => { try { closeDatabase(); } catch { /* noop */ } });
8
+ openDatabase(":memory:");
9
+
10
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
11
+
12
+ // First insert: full data
13
+ insertSlice({
14
+ id: 'S01',
15
+ milestoneId: 'M001',
16
+ title: 'Auth flow',
17
+ status: 'in-progress',
18
+ risk: 'high',
19
+ demo: 'Login page renders.',
20
+ sequence: 3,
21
+ planning: {
22
+ goal: 'Secure authentication',
23
+ successCriteria: 'All tests pass',
24
+ proofLevel: 'integration',
25
+ integrationClosure: 'Fully integrated',
26
+ observabilityImpact: 'Metrics available',
27
+ },
28
+ });
29
+
30
+ const before = getSlice('M001', 'S01');
31
+ assert.ok(before, 'slice should exist after first insert');
32
+ assert.equal(before.title, 'Auth flow');
33
+ assert.equal(before.demo, 'Login page renders.');
34
+ assert.equal(before.risk, 'high');
35
+
36
+ // Second insert: minimal "ensure exists" call (mirrors complete-task.ts usage)
37
+ insertSlice({ id: 'S01', milestoneId: 'M001' });
38
+
39
+ const after = getSlice('M001', 'S01');
40
+ assert.ok(after, 'slice should still exist after second insert');
41
+
42
+ // These must NOT be wiped to empty strings
43
+ assert.equal(after.title, 'Auth flow', 'title must survive minimal re-insert');
44
+ assert.equal(after.demo, 'Login page renders.', 'demo must survive minimal re-insert');
45
+ assert.equal(after.risk, 'high', 'risk must survive minimal re-insert');
46
+ assert.equal(after.sequence, 3, 'sequence must survive minimal re-insert');
47
+
48
+ // Planning fields must also survive
49
+ assert.equal(after.goal, 'Secure authentication', 'goal must survive minimal re-insert');
50
+ assert.equal(after.success_criteria, 'All tests pass', 'success_criteria must survive');
51
+ assert.equal(after.proof_level, 'integration', 'proof_level must survive');
52
+ assert.equal(after.integration_closure, 'Fully integrated', 'integration_closure must survive');
53
+ assert.equal(after.observability_impact, 'Metrics available', 'observability_impact must survive');
54
+ });
55
+
56
+ test('insertSlice ON CONFLICT preserves completed status', (t) => {
57
+ t.after(() => { try { closeDatabase(); } catch { /* noop */ } });
58
+ openDatabase(":memory:");
59
+
60
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
61
+
62
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done slice', status: 'complete' });
63
+
64
+ // Re-insert with pending status (default) should NOT overwrite complete
65
+ insertSlice({ id: 'S01', milestoneId: 'M001' });
66
+
67
+ const after = getSlice('M001', 'S01');
68
+ assert.ok(after);
69
+ assert.equal(after.status, 'complete', 'completed status must not be overwritten');
70
+ });
71
+
72
+ test('insertSlice ON CONFLICT allows explicit updates to non-empty values', (t) => {
73
+ t.after(() => { try { closeDatabase(); } catch { /* noop */ } });
74
+ openDatabase(":memory:");
75
+
76
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
77
+
78
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Original', demo: 'Old demo', risk: 'low' });
79
+
80
+ // Explicit update with real values should overwrite
81
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Updated', demo: 'New demo', risk: 'high' });
82
+
83
+ const after = getSlice('M001', 'S01');
84
+ assert.ok(after);
85
+ assert.equal(after.title, 'Updated', 'explicit title update should apply');
86
+ assert.equal(after.demo, 'New demo', 'explicit demo update should apply');
87
+ assert.equal(after.risk, 'high', 'explicit risk update should apply');
88
+ });
@@ -1246,7 +1246,7 @@ describe('git-service', async () => {
1246
1246
  test('nativeAddAllWithExclusions: symlinked .gsd fallback', () => {
1247
1247
  // When .gsd is a symlink, git rejects `:!.gsd/...` pathspecs with
1248
1248
  // "fatal: pathspec '...' is beyond a symbolic link". The fix falls
1249
- // back to plain `git add -A`, which respects .gitignore.
1249
+ // back to `git add -u` (tracked files only), NOT `git add -A`.
1250
1250
  const repo = initTempRepo();
1251
1251
 
1252
1252
  // Create the real .gsd directory outside the repo, then symlink it
@@ -1258,11 +1258,18 @@ describe('git-service', async () => {
1258
1258
  // Symlink .gsd -> external directory
1259
1259
  symlinkSync(externalGsd, join(repo, ".gsd"));
1260
1260
 
1261
- // Add .gitignore so git add -A fallback skips .gsd/
1261
+ // Add .gitignore so .gsd/ is ignored
1262
1262
  writeFileSync(join(repo, ".gitignore"), ".gsd\n");
1263
1263
 
1264
- // Create a real file that should be staged
1264
+ // Create a tracked file and commit it, then modify it
1265
1265
  createFile(repo, "src/app.ts", "export const x = 1;");
1266
+ run("git add -A", repo);
1267
+ run('git commit -m "add app"', repo);
1268
+ writeFileSync(join(repo, "src/app.ts"), "export const x = 2;");
1269
+
1270
+ // Create an untracked file simulating large data (NOT in .gitignore)
1271
+ // This is the key scenario: large untracked dirs that git add -A would traverse
1272
+ createFile(repo, "data/large-model.bin", "pretend this is 10GB");
1266
1273
 
1267
1274
  // nativeAddAllWithExclusions should NOT throw despite .gsd being a symlink
1268
1275
  let threw = false;
@@ -1274,9 +1281,15 @@ describe('git-service', async () => {
1274
1281
  }
1275
1282
  assert.ok(!threw, "nativeAddAllWithExclusions does not throw with symlinked .gsd");
1276
1283
 
1277
- // Verify the real file was staged
1284
+ // Verify the tracked modified file was staged
1278
1285
  const staged = run("git diff --cached --name-only", repo);
1279
- assert.ok(staged.includes("src/app.ts"), "real file staged despite symlinked .gsd");
1286
+ assert.ok(staged.includes("src/app.ts"), "modified tracked file staged despite symlinked .gsd");
1287
+
1288
+ // CRITICAL: untracked files must NOT be staged — the symlink fallback
1289
+ // should use `git add -u` (tracked only), not `git add -A` (all files).
1290
+ // Using `git add -A` on a repo with large untracked data dirs hangs. (#1977)
1291
+ assert.ok(!staged.includes("data/large-model.bin"),
1292
+ "symlink fallback must not stage untracked files (would hang on large repos)");
1280
1293
  assert.ok(!staged.includes(".gsd"), ".gsd content not staged");
1281
1294
 
1282
1295
  rmSync(repo, { recursive: true, force: true });
@@ -1435,13 +1448,20 @@ describe('git-service', async () => {
1435
1448
  run('git add .gitignore', repo);
1436
1449
  run('git commit -m "add gitignore"', repo);
1437
1450
 
1451
+ // Pre-commit a tracked source file so git add -u can stage modifications.
1452
+ // The symlink fallback uses git add -u (tracked files only), so the file
1453
+ // must be tracked before the autoCommit scenario runs.
1454
+ createFile(repo, "src/feature.ts", "export const feature = true;");
1455
+ run('git add src/feature.ts', repo);
1456
+ run('git commit -m "add feature"', repo);
1457
+
1438
1458
  // Simulate new milestone artifacts created during execution
1439
1459
  writeFileSync(join(externalGsd, "milestones", "M009", "M009-SUMMARY.md"), "# M009 Summary");
1440
1460
  writeFileSync(join(externalGsd, "milestones", "M009", "S01-SUMMARY.md"), "# S01 Summary");
1441
1461
  writeFileSync(join(externalGsd, "milestones", "M009", "T01-VERIFY.json"), '{"passed":true}');
1442
1462
 
1443
- // Also create a normal source file change
1444
- createFile(repo, "src/feature.ts", "export const feature = true;");
1463
+ // Modify the tracked source file — git add -u will stage this change
1464
+ writeFileSync(join(repo, "src/feature.ts"), "export const feature = false; // updated");
1445
1465
 
1446
1466
  const svc = new GitServiceImpl(repo);
1447
1467
  const msg = svc.autoCommit("complete-milestone", "M009");
@@ -357,3 +357,37 @@ test('writeBlockerPlaceholder: does NOT update DB for non-execute-task types', a
357
357
  cleanup(base);
358
358
  }
359
359
  });
360
+
361
+ test('writeBlockerPlaceholder: updates DB slice status for complete-slice (#2653)', async () => {
362
+ const base = createFixtureBase();
363
+ try {
364
+ const { openDatabase, closeDatabase, insertMilestone, insertSlice, getSlice, isDbAvailable } =
365
+ await import("../../gsd-db.ts");
366
+
367
+ const dbPath = join(base, ".gsd", "gsd.db");
368
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
369
+
370
+ openDatabase(dbPath);
371
+ try {
372
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
373
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "active" });
374
+
375
+ // complete-slice blocker should update slice DB status to "complete"
376
+ writeBlockerPlaceholder("complete-slice", "M001/S01", base, "context exhaustion recovery");
377
+
378
+ const slice = getSlice("M001", "S01");
379
+ assert.equal(slice?.status, "complete",
380
+ "writeBlockerPlaceholder must update DB slice status to 'complete' for complete-slice so dispatch guard unblocks downstream (#2653)");
381
+
382
+ // Verify the full chain works: verifyExpectedArtifact should return true
383
+ // (requires both UAT file and DB status = complete)
384
+ // Note: the placeholder writes a SUMMARY file, but complete-slice also needs UAT.
385
+ // The placeholder itself doesn't write UAT, so artifact verification may still fail
386
+ // for complete-slice — but the DB status is now correct, breaking the circular dep.
387
+ } finally {
388
+ if (isDbAvailable()) closeDatabase();
389
+ }
390
+ } finally {
391
+ cleanup(base);
392
+ }
393
+ });
@@ -6,7 +6,7 @@
6
6
 
7
7
  import test from "node:test";
8
8
  import assert from "node:assert/strict";
9
- import { mkdtempSync, mkdirSync, readFileSync, rmSync } from "node:fs";
9
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
  import { tmpdir } from "node:os";
12
12
  import {
@@ -382,3 +382,118 @@ test("snapshotUnitMetrics counts toolCall blocks correctly (#1713)", () => {
382
382
  rmSync(tmpBase, { recursive: true, force: true });
383
383
  }
384
384
  });
385
+
386
+ // ── #1943 — Duplicate metrics entries from idle watchdog ──────────────────────
387
+
388
+ test("#1943 initMetrics deduplicates entries loaded from a corrupted disk ledger", () => {
389
+ const tmpBase = mkdtempSync(join(tmpdir(), "gsd-metrics-dedup-load-"));
390
+ mkdirSync(join(tmpBase, ".gsd"), { recursive: true });
391
+
392
+ try {
393
+ resetMetrics();
394
+
395
+ // Simulate a corrupted metrics.json with duplicate entries on disk
396
+ // (same type+id+startedAt but different finishedAt — idle watchdog pattern)
397
+ const corruptedLedger: MetricsLedger = {
398
+ version: 1,
399
+ projectStartedAt: 1700000000000,
400
+ units: [
401
+ makeUnit({ type: "research-slice", id: "M009/S02", startedAt: 1774011016218, finishedAt: 1774011031218, cost: 1.50, tokens: { input: 6600000, output: 100000, cacheRead: 0, cacheWrite: 0, total: 6700000 } }),
402
+ makeUnit({ type: "research-slice", id: "M009/S02", startedAt: 1774011016218, finishedAt: 1774011046218, cost: 1.55, tokens: { input: 6800000, output: 110000, cacheRead: 0, cacheWrite: 0, total: 6910000 } }),
403
+ makeUnit({ type: "research-slice", id: "M009/S02", startedAt: 1774011016218, finishedAt: 1774011061218, cost: 1.60, tokens: { input: 7000000, output: 120000, cacheRead: 0, cacheWrite: 0, total: 7120000 } }),
404
+ makeUnit({ type: "research-slice", id: "M009/S02", startedAt: 1774011016218, finishedAt: 1774011076218, cost: 1.65, tokens: { input: 7200000, output: 130000, cacheRead: 0, cacheWrite: 0, total: 7330000 } }),
405
+ // A different unit — should be preserved
406
+ makeUnit({ type: "execute-task", id: "M001/S01/T01", startedAt: 1774012000000, finishedAt: 1774012060000, cost: 0.50 }),
407
+ ],
408
+ };
409
+ writeFileSync(
410
+ join(tmpBase, ".gsd", "metrics.json"),
411
+ JSON.stringify(corruptedLedger, null, 2),
412
+ );
413
+
414
+ // Load the corrupted ledger — duplicates should be collapsed on load
415
+ initMetrics(tmpBase);
416
+ const ledger = getLedger();
417
+ assert.ok(ledger);
418
+
419
+ // The 4 entries with identical (type, id, startedAt) should collapse to 1,
420
+ // keeping the latest (highest finishedAt). Plus the 1 different unit = 2 total.
421
+ assert.equal(
422
+ ledger!.units.length, 2,
423
+ `expected 2 entries after dedup (1 collapsed group + 1 unique), got ${ledger!.units.length}`,
424
+ );
425
+
426
+ // The surviving duplicate should be the one with the latest finishedAt
427
+ const researchEntry = ledger!.units.find(u => u.type === "research-slice");
428
+ assert.ok(researchEntry);
429
+ assert.equal(researchEntry!.finishedAt, 1774011076218, "should keep the latest finishedAt");
430
+ assert.equal(researchEntry!.cost, 1.65, "should keep the latest cost");
431
+
432
+ // The on-disk file should also be deduplicated
433
+ const diskRaw = readFileSync(join(tmpBase, ".gsd", "metrics.json"), "utf-8");
434
+ const diskLedger: MetricsLedger = JSON.parse(diskRaw);
435
+ assert.equal(diskLedger.units.length, 2, "disk should also have deduplicated entries");
436
+ } finally {
437
+ resetMetrics();
438
+ rmSync(tmpBase, { recursive: true, force: true });
439
+ }
440
+ });
441
+
442
+ test("#1943 getProjectTotals reports correct cost after dedup (no 35% inflation)", () => {
443
+ // Simulate the exact scenario from the issue: 20 entries for a single dispatch
444
+ // with monotonically increasing token counts and 15s-apart finishedAt values
445
+ const startedAt = 1774011016218;
446
+ const baseCost = 1.50;
447
+ const duplicateUnits: UnitMetrics[] = [];
448
+
449
+ for (let i = 0; i < 20; i++) {
450
+ duplicateUnits.push(makeUnit({
451
+ type: "research-slice",
452
+ id: "M009/S02",
453
+ startedAt,
454
+ finishedAt: startedAt + (i + 1) * 15000,
455
+ cost: baseCost + i * 0.05,
456
+ toolCalls: 0,
457
+ tokens: {
458
+ input: 6600000 + i * 200000,
459
+ output: 100000 + i * 10000,
460
+ cacheRead: 0,
461
+ cacheWrite: 0,
462
+ total: 6700000 + i * 210000,
463
+ },
464
+ }));
465
+ }
466
+
467
+ // Without dedup, getProjectTotals would sum all 20 entries' costs
468
+ const rawTotals = getProjectTotals(duplicateUnits);
469
+ // With dedup (only last entry should count), cost should be the last entry's cost
470
+ const lastEntryCost = duplicateUnits[duplicateUnits.length - 1].cost;
471
+
472
+ // This test documents the bug: raw totals inflate cost by summing duplicates
473
+ assert.ok(
474
+ rawTotals.cost > lastEntryCost * 2,
475
+ "raw totals with duplicates inflate cost (bug demonstration)",
476
+ );
477
+
478
+ // After loading through initMetrics (which should dedup), totals should be correct
479
+ const tmpBase = mkdtempSync(join(tmpdir(), "gsd-metrics-cost-inflation-"));
480
+ mkdirSync(join(tmpBase, ".gsd"), { recursive: true });
481
+ try {
482
+ resetMetrics();
483
+ writeFileSync(
484
+ join(tmpBase, ".gsd", "metrics.json"),
485
+ JSON.stringify({ version: 1, projectStartedAt: 1700000000000, units: duplicateUnits }, null, 2),
486
+ );
487
+ initMetrics(tmpBase);
488
+ const ledger = getLedger()!;
489
+ const dedupedTotals = getProjectTotals(ledger.units);
490
+ assert.equal(ledger.units.length, 1, "20 duplicates should collapse to 1 entry");
491
+ assert.equal(
492
+ dedupedTotals.cost, lastEntryCost,
493
+ `deduped cost should be ${lastEntryCost}, not ${dedupedTotals.cost}`,
494
+ );
495
+ } finally {
496
+ resetMetrics();
497
+ rmSync(tmpBase, { recursive: true, force: true });
498
+ }
499
+ });