gsd-pi 2.71.0-dev.977c553 → 2.71.0-dev.d4d916a

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 (278) hide show
  1. package/dist/cli.js +12 -3
  2. package/dist/headless-events.d.ts +2 -0
  3. package/dist/headless-events.js +7 -0
  4. package/dist/headless.js +16 -3
  5. package/dist/mcp-server.js +6 -6
  6. package/dist/provider-migrations.d.ts +10 -0
  7. package/dist/provider-migrations.js +12 -0
  8. package/dist/resource-loader.js +139 -13
  9. package/dist/resources/GSD-WORKFLOW.md +1 -1
  10. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -4
  11. package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
  12. package/dist/resources/extensions/gsd/auto/loop.js +32 -1
  13. package/dist/resources/extensions/gsd/auto/phases.js +1 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
  17. package/dist/resources/extensions/gsd/auto-start.js +31 -7
  18. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  19. package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
  20. package/dist/resources/extensions/gsd/auto.js +52 -0
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +2 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
  23. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  24. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  25. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  26. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
  27. package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
  28. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
  29. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
  30. package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
  31. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  32. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  33. package/dist/resources/extensions/gsd/forensics.js +19 -6
  34. package/dist/resources/extensions/gsd/guided-flow.js +5 -10
  35. package/dist/resources/extensions/gsd/metrics.js +1 -0
  36. package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
  37. package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
  38. package/dist/resources/extensions/gsd/notification-store.js +56 -5
  39. package/dist/resources/extensions/gsd/notification-widget.js +5 -13
  40. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  42. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  43. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  44. package/dist/resources/extensions/gsd/prompts/execute-task.md +20 -19
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  48. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  49. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  50. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  51. package/dist/resources/extensions/gsd/session-model-override.js +25 -0
  52. package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
  53. package/dist/resources/extensions/ollama/index.js +13 -5
  54. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  55. package/dist/startup-model-validation.d.ts +0 -1
  56. package/dist/startup-model-validation.js +6 -2
  57. package/dist/web/standalone/.next/BUILD_ID +1 -1
  58. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  59. package/dist/web/standalone/.next/build-manifest.json +2 -2
  60. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  61. package/dist/web/standalone/.next/required-server-files.json +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.html +1 -1
  79. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  86. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  88. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  89. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  90. package/dist/web/standalone/server.js +1 -1
  91. package/package.json +1 -1
  92. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  93. package/packages/mcp-server/dist/workflow-tools.js +21 -11
  94. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  95. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  96. package/packages/mcp-server/src/workflow-tools.ts +31 -11
  97. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  98. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
  100. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
  101. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
  102. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
  103. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  104. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  106. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  107. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  108. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  109. package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
  110. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  111. package/packages/pi-ai/dist/providers/anthropic.js +7 -4
  112. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  113. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  114. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  115. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  116. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  117. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
  118. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  119. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  120. package/packages/pi-ai/src/providers/anthropic.ts +8 -4
  121. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  122. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
  123. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
  124. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
  125. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
  126. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
  128. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
  130. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
  132. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
  134. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
  136. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
  137. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
  138. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
  139. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
  141. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  143. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  144. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  145. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
  147. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
  149. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
  152. package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
  153. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
  154. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  155. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/index.js +1 -1
  157. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
  165. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  168. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  171. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  173. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +7 -2
  175. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -3
  181. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
  183. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  184. package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
  185. package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
  186. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
  187. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
  188. package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
  189. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  190. package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
  191. package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
  192. package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
  193. package/packages/pi-coding-agent/src/index.ts +1 -0
  194. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
  195. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
  196. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  197. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
  198. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -2
  199. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  200. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4 -3
  201. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
  202. package/src/resources/GSD-WORKFLOW.md +1 -1
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +13 -5
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +56 -4
  205. package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
  206. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  207. package/src/resources/extensions/gsd/auto/loop.ts +45 -1
  208. package/src/resources/extensions/gsd/auto/phases.ts +2 -0
  209. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  210. package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
  211. package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
  212. package/src/resources/extensions/gsd/auto-start.ts +38 -7
  213. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  214. package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
  215. package/src/resources/extensions/gsd/auto.ts +68 -0
  216. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +2 -0
  217. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
  218. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  219. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  220. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  221. package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
  222. package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
  223. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
  224. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
  225. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
  226. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  227. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  228. package/src/resources/extensions/gsd/forensics.ts +23 -7
  229. package/src/resources/extensions/gsd/guided-flow.ts +5 -10
  230. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  231. package/src/resources/extensions/gsd/metrics.ts +12 -1
  232. package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
  233. package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
  234. package/src/resources/extensions/gsd/notification-store.ts +54 -5
  235. package/src/resources/extensions/gsd/notification-widget.ts +5 -14
  236. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
  237. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  238. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  239. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  240. package/src/resources/extensions/gsd/prompts/execute-task.md +20 -19
  241. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  242. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  243. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  244. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  245. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  246. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  247. package/src/resources/extensions/gsd/session-model-override.ts +36 -0
  248. package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
  249. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
  250. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  251. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  252. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  253. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  254. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  255. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
  256. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
  257. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  258. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
  259. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
  260. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
  261. package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
  262. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
  263. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
  264. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
  265. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
  266. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  267. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  268. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  269. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
  270. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
  271. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
  272. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  273. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  274. package/src/resources/extensions/ollama/index.ts +13 -3
  275. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  276. package/src/resources/skills/create-skill/SKILL.md +2 -0
  277. /package/dist/web/standalone/.next/static/{4xyaXTn7-shVHaGMcl75o → IRnpNeY-_eO7SxKBIkTbL}/_buildManifest.js +0 -0
  278. /package/dist/web/standalone/.next/static/{4xyaXTn7-shVHaGMcl75o → IRnpNeY-_eO7SxKBIkTbL}/_ssgManifest.js +0 -0
@@ -46,3 +46,41 @@ export function isInfrastructureError(err: unknown): string | null {
46
46
  if (msg.includes("database disk image is malformed")) return "SQLITE_CORRUPT";
47
47
  return null;
48
48
  }
49
+
50
+ /**
51
+ * Default wait duration when a cooldown error is detected but no specific
52
+ * expiry is available from AuthStorage (e.g., error propagated across
53
+ * process boundary without structured backoff data).
54
+ */
55
+ export const COOLDOWN_FALLBACK_WAIT_MS = 35_000; // 35s — slightly longer than the 30s rate-limit backoff
56
+
57
+ /** Maximum consecutive cooldown retries before the auto-loop gives up. */
58
+ export const MAX_COOLDOWN_RETRIES = 5;
59
+
60
+ /**
61
+ * Detect whether an error is a transient credential cooldown that should
62
+ * be waited out rather than counted as a consecutive failure.
63
+ *
64
+ * Prefers the structured `CredentialCooldownError` (code: AUTH_COOLDOWN)
65
+ * thrown by sdk.ts. Falls back to message matching for errors that
66
+ * propagated across process boundaries without the typed class.
67
+ */
68
+ export function isTransientCooldownError(err: unknown): boolean {
69
+ if (err && typeof err === "object" && (err as Record<string, unknown>).code === "AUTH_COOLDOWN") {
70
+ return true;
71
+ }
72
+ // Fallback: message match for cross-process error propagation
73
+ const msg = err instanceof Error ? err.message : String(err);
74
+ return /in a cooldown window/i.test(msg);
75
+ }
76
+
77
+ /**
78
+ * Extract retryAfterMs from a CredentialCooldownError, if available.
79
+ * Returns undefined for unstructured errors or when no retry hint exists.
80
+ */
81
+ export function getCooldownRetryAfterMs(err: unknown): number | undefined {
82
+ if (err && typeof err === "object" && (err as Record<string, unknown>).code === "AUTH_COOLDOWN") {
83
+ return (err as Record<string, unknown>).retryAfterMs as number | undefined;
84
+ }
85
+ return undefined;
86
+ }
@@ -211,6 +211,8 @@ export interface LoopDeps {
211
211
  verbose: boolean,
212
212
  startModel: { provider: string; id: string } | null,
213
213
  retryContext?: { isRetry: boolean; previousTier?: string },
214
+ isAutoMode?: boolean,
215
+ sessionModelOverride?: { provider: string; id: string } | null,
214
216
  ) => Promise<{
215
217
  routing: { tier: string; modelDowngraded: boolean } | null;
216
218
  appliedModel: { provider: string; id: string } | null;
@@ -27,7 +27,7 @@ import {
27
27
  runFinalize,
28
28
  } from "./phases.js";
29
29
  import { debugLog } from "../debug-logger.js";
30
- import { isInfrastructureError } from "./infra-errors.js";
30
+ import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
31
31
  import { resolveEngine } from "../engine-resolver.js";
32
32
 
33
33
  /**
@@ -48,6 +48,7 @@ export async function autoLoop(
48
48
  let iteration = 0;
49
49
  const loopState: LoopState = { recentUnits: [], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 };
50
50
  let consecutiveErrors = 0;
51
+ let consecutiveCooldowns = 0;
51
52
  const recentErrorMessages: string[] = [];
52
53
 
53
54
  while (s.active) {
@@ -203,6 +204,7 @@ export async function autoLoop(
203
204
 
204
205
  deps.clearUnitTimeout();
205
206
  consecutiveErrors = 0;
207
+ consecutiveCooldowns = 0;
206
208
  recentErrorMessages.length = 0;
207
209
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
208
210
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
@@ -265,6 +267,7 @@ export async function autoLoop(
265
267
  if (finalizeResult.action === "continue") continue;
266
268
 
267
269
  consecutiveErrors = 0; // Iteration completed successfully
270
+ consecutiveCooldowns = 0;
268
271
  recentErrorMessages.length = 0;
269
272
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
270
273
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
@@ -300,6 +303,47 @@ export async function autoLoop(
300
303
  break;
301
304
  }
302
305
 
306
+ // ── Credential cooldown: wait and retry with bounded budget ──
307
+ // A 429 triggers a 30s credential backoff in AuthStorage. If the SDK's
308
+ // getApiKey() retries couldn't outlast the window, the error surfaces
309
+ // here. Wait for the cooldown to clear rather than counting it as a
310
+ // consecutive failure — but cap retries so we don't spin for hours
311
+ // on persistent quota exhaustion.
312
+ if (isTransientCooldownError(loopErr)) {
313
+ consecutiveCooldowns++;
314
+ const retryAfterMs = getCooldownRetryAfterMs(loopErr);
315
+ debugLog("autoLoop", {
316
+ phase: "cooldown-wait",
317
+ iteration,
318
+ consecutiveCooldowns,
319
+ retryAfterMs,
320
+ error: msg,
321
+ });
322
+
323
+ if (consecutiveCooldowns > MAX_COOLDOWN_RETRIES) {
324
+ ctx.ui.notify(
325
+ `Auto-mode stopped: ${consecutiveCooldowns} consecutive credential cooldowns — rate limit or quota may be persistently exhausted.`,
326
+ "error",
327
+ );
328
+ await deps.stopAuto(
329
+ ctx,
330
+ pi,
331
+ `${consecutiveCooldowns} consecutive credential cooldowns exceeded retry budget`,
332
+ );
333
+ break;
334
+ }
335
+
336
+ const waitMs = (retryAfterMs !== undefined && retryAfterMs > 0 && retryAfterMs <= 60_000)
337
+ ? retryAfterMs + 500 // Use structured hint + small buffer
338
+ : COOLDOWN_FALLBACK_WAIT_MS;
339
+ ctx.ui.notify(
340
+ `Credentials in cooldown (${consecutiveCooldowns}/${MAX_COOLDOWN_RETRIES}) — waiting ${Math.round(waitMs / 1000)}s before retrying.`,
341
+ "warning",
342
+ );
343
+ await new Promise(resolve => setTimeout(resolve, waitMs));
344
+ continue; // Retry iteration without incrementing consecutiveErrors
345
+ }
346
+
303
347
  consecutiveErrors++;
304
348
  recentErrorMessages.push(msg.length > 120 ? msg.slice(0, 120) + "..." : msg);
305
349
  debugLog("autoLoop", {
@@ -1183,6 +1183,8 @@ export async function runUnitPhase(
1183
1183
  s.verbose,
1184
1184
  s.autoModeStartModel,
1185
1185
  sidecarItem ? undefined : { isRetry, previousTier },
1186
+ undefined,
1187
+ s.manualSessionModelOverride,
1186
1188
  );
1187
1189
  s.currentUnitRouting =
1188
1190
  modelResult.routing as AutoSession["currentUnitRouting"];
@@ -87,6 +87,10 @@ export class AutoSession {
87
87
  previousProjectRootEnv: string | null = null;
88
88
  hadProjectRootEnv = false;
89
89
  projectRootEnvCaptured = false;
90
+ previousMilestoneLockEnv: string | null = null;
91
+ hadMilestoneLockEnv = false;
92
+ milestoneLockEnvCaptured = false;
93
+ sessionMilestoneLock: string | null = null;
90
94
  gitService: GitServiceImpl | null = null;
91
95
 
92
96
  // ── Dispatch counters ────────────────────────────────────────────────────
@@ -107,6 +111,8 @@ export class AutoSession {
107
111
 
108
112
  // ── Model state ──────────────────────────────────────────────────────────
109
113
  autoModeStartModel: StartModel | null = null;
114
+ /** Explicit /gsd model pin captured at bootstrap (session-scoped policy override). */
115
+ manualSessionModelOverride: StartModel | null = null;
110
116
  currentUnitModel: Model<Api> | null = null;
111
117
  /** Fully-qualified model ID (provider/id) set after selectAndApplyModel + hook overrides (#2899). */
112
118
  currentDispatchedModelId: string | null = null;
@@ -200,6 +206,10 @@ export class AutoSession {
200
206
  this.previousProjectRootEnv = null;
201
207
  this.hadProjectRootEnv = false;
202
208
  this.projectRootEnvCaptured = false;
209
+ this.previousMilestoneLockEnv = null;
210
+ this.hadMilestoneLockEnv = false;
211
+ this.milestoneLockEnvCaptured = false;
212
+ this.sessionMilestoneLock = null;
203
213
  this.gitService = null;
204
214
 
205
215
  // Dispatch
@@ -214,6 +224,7 @@ export class AutoSession {
214
224
 
215
225
  // Model
216
226
  this.autoModeStartModel = null;
227
+ this.manualSessionModelOverride = null;
217
228
  this.currentUnitModel = null;
218
229
  this.currentDispatchedModelId = null;
219
230
  this.originalModelId = null;
@@ -6,7 +6,13 @@
6
6
  * or AutoContext dependency. State accessors are passed as callbacks.
7
7
  */
8
8
 
9
- import type { ExtensionContext, ExtensionCommandContext, SessionMessageEntry } from "@gsd/pi-coding-agent";
9
+ import type {
10
+ ExtensionContext,
11
+ ExtensionCommandContext,
12
+ SessionMessageEntry,
13
+ ReadonlyFooterDataProvider,
14
+ Theme,
15
+ } from "@gsd/pi-coding-agent";
10
16
  import type { GSDState } from "./types.js";
11
17
  import { getCurrentBranch } from "./worktree.js";
12
18
  import { getActiveHook } from "./post-unit-hooks.js";
@@ -17,7 +23,6 @@ import {
17
23
  resolveSliceFile,
18
24
  } from "./paths.js";
19
25
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
20
- import { formatShortcut } from "./files.js";
21
26
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
22
27
  import { execFileSync } from "node:child_process";
23
28
  import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
@@ -38,6 +43,7 @@ import {
38
43
  type RtkSessionSavings,
39
44
  } from "../shared/rtk-session-stats.js";
40
45
  import { logWarning } from "./workflow-logger.js";
46
+ import { formattedShortcutPair } from "./shortcut-defs.js";
41
47
 
42
48
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
43
49
 
@@ -358,12 +364,23 @@ function getLastCommit(basePath: string): { timeAgo: string; message: string } |
358
364
  // ─── Footer Factory ───────────────────────────────────────────────────────────
359
365
 
360
366
  /**
361
- * Footer factory that renders zero lines — hides the built-in footer entirely.
362
- * All footer info (pwd, branch, tokens, cost, model) is shown inside the
363
- * progress widget instead, so there's no gap or redundancy.
367
+ * Footer factory used by auto-mode.
368
+ * Keep footer minimal but preserve extension status context from setStatus().
364
369
  */
365
- export const hideFooter = () => ({
366
- render(_width: number): string[] { return []; },
370
+ function sanitizeFooterStatus(text: string): string {
371
+ return text.replace(/\s+/g, " ").trim();
372
+ }
373
+
374
+ export const hideFooter = (_tui: unknown, theme: Theme, footerData: ReadonlyFooterDataProvider) => ({
375
+ render(width: number): string[] {
376
+ const extensionStatuses = footerData.getExtensionStatuses();
377
+ if (extensionStatuses.size === 0) return [];
378
+ const statusLine = Array.from(extensionStatuses.entries())
379
+ .sort(([a], [b]) => a.localeCompare(b))
380
+ .map(([, text]) => sanitizeFooterStatus(text))
381
+ .join(" ");
382
+ return [truncateToWidth(theme.fg("dim", statusLine), width, theme.fg("dim", "..."))];
383
+ },
367
384
  invalidate() {},
368
385
  dispose() {},
369
386
  });
@@ -646,14 +663,6 @@ export function updateProgressWidget(
646
663
  : "";
647
664
  lines.push(rightAlign(headerLeft, headerRight, width));
648
665
 
649
- // Worktree/branch right-aligned below header
650
- const branchLabel = worktreeName && cachedBranch
651
- ? `${worktreeName} (${cachedBranch})`
652
- : cachedBranch ?? "";
653
- if (branchLabel) {
654
- lines.push(rightAlign("", theme.fg("dim", branchLabel), width));
655
- }
656
-
657
666
  // Show health signal details when degraded (yellow/red)
658
667
  if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
659
668
  // Show up to 3 most relevant signals in compact form
@@ -917,15 +926,17 @@ export function updateProgressWidget(
917
926
  // Hints line
918
927
  const hintParts: string[] = [];
919
928
  hintParts.push("esc pause");
920
- hintParts.push(`${formatShortcut("Ctrl+Alt+G")} dashboard`);
929
+ hintParts.push(`${formattedShortcutPair("dashboard")} dashboard`);
930
+ hintParts.push(`${formattedShortcutPair("parallel")} parallel`);
921
931
  const hintStr = theme.fg("dim", hintParts.join(" | "));
922
932
  const commitStr = lastCommit
923
933
  ? theme.fg("dim", `${lastCommit.timeAgo} ago: ${commitMsg}`)
924
934
  : "";
935
+ const locationStr = theme.fg("dim", widgetPwd);
925
936
  if (commitStr) {
926
- lines.push(rightAlign(`${pad}${commitStr}`, hintStr, width));
937
+ lines.push(rightAlign(`${pad}${locationStr} · ${commitStr}`, hintStr, width));
927
938
  } else {
928
- lines.push(rightAlign("", hintStr, width));
939
+ lines.push(rightAlign(`${pad}${locationStr}`, hintStr, width));
929
940
  }
930
941
 
931
942
  lines.push(...ui.bar());
@@ -14,6 +14,7 @@ import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
14
14
  import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides, adjustToolSet, filterToolsForProvider } from "./model-router.js";
15
15
  import { getLedger, getProjectTotals } from "./metrics.js";
16
16
  import { unitPhaseLabel } from "./auto-dashboard.js";
17
+ import { getSessionModelOverride } from "./session-model-override.js";
17
18
 
18
19
  export interface ModelSelectionResult {
19
20
  /** Routing metadata for metrics recording */
@@ -72,8 +73,15 @@ export async function selectAndApplyModel(
72
73
  /** When false (interactive/guided-flow), skip dynamic routing and use the session model.
73
74
  * Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
74
75
  isAutoMode = true,
76
+ /** Explicit /gsd model pin captured at bootstrap for long-running auto loops. */
77
+ sessionModelOverride?: { provider: string; id: string } | null,
75
78
  ): Promise<ModelSelectionResult> {
76
- const modelConfig = resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
79
+ const effectiveSessionModelOverride = sessionModelOverride === undefined
80
+ ? getSessionModelOverride(ctx.sessionManager.getSessionId())
81
+ : (sessionModelOverride ?? undefined);
82
+ const modelConfig = effectiveSessionModelOverride
83
+ ? undefined
84
+ : resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
77
85
  let routing: { tier: string; modelDowngraded: boolean } | null = null;
78
86
  let appliedModel: Model<Api> | null = null;
79
87
 
@@ -85,6 +85,7 @@ import { sep as pathSep } from "node:path";
85
85
  import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
86
86
  import { resolveDefaultSessionModel, resolveDynamicRoutingConfig } from "./preferences-models.js";
87
87
  import type { WorktreeResolver } from "./worktree-resolver.js";
88
+ import { getSessionModelOverride } from "./session-model-override.js";
88
89
 
89
90
  export interface BootstrapDeps {
90
91
  shouldUseWorktreeIsolation: () => boolean;
@@ -266,13 +267,42 @@ export async function bootstrapAutoSession(
266
267
  // Capture the user's session model before guided-flow dispatch can apply a
267
268
  // phase-specific planning model for a discuss turn (#2829).
268
269
  //
269
- // GSD PREFERENCES.md takes priority over the session model from settings.json
270
- // (#3517). The session model (ctx.model) comes from findInitialModel() which
271
- // reads defaultProvider/defaultModel from ~/.gsd/agent/settings.json. When
272
- // the user has explicit model preferences in PREFERENCES.md, those should win.
270
+ // Precedence:
271
+ // 1) Explicit session override via /gsd model (this session)
272
+ // 2) GSD model preferences from PREFERENCES.md (validated against live auth)
273
+ // 3) Current session model from settings/session restore (if provider ready)
274
+ //
275
+ // This preserves #3517 defaults while honoring explicit runtime model
276
+ // selection for subsequent /gsd runs in the same session.
277
+ const manualSessionOverride = getSessionModelOverride(ctx.sessionManager.getSessionId());
273
278
  const preferredModel = resolveDefaultSessionModel(ctx.model?.provider);
274
- const startModelSnapshot = preferredModel
275
- ?? (ctx.model
279
+ // Validate the preferred model against the live registry + provider auth so
280
+ // an unconfigured PREFERENCES.md entry (no API key / OAuth) can't become the
281
+ // start-model snapshot. Without this, every subsequent unit would try to
282
+ // fall back to an unusable model.
283
+ let validatedPreferredModel: { provider: string; id: string } | undefined;
284
+ if (preferredModel) {
285
+ const { resolveModelId } = await import("./auto-model-selection.js");
286
+ const available = ctx.modelRegistry.getAvailable();
287
+ const match = resolveModelId(
288
+ `${preferredModel.provider}/${preferredModel.id}`,
289
+ available,
290
+ ctx.model?.provider,
291
+ );
292
+ if (match) {
293
+ validatedPreferredModel = { provider: match.provider, id: match.id };
294
+ } else {
295
+ ctx.ui.notify(
296
+ `Preferred model ${preferredModel.provider}/${preferredModel.id} from PREFERENCES.md is not configured; falling back to session default.`,
297
+ "warning",
298
+ );
299
+ }
300
+ }
301
+ const sessionModelReady =
302
+ ctx.model && ctx.modelRegistry.isProviderRequestReady(ctx.model.provider);
303
+ const startModelSnapshot = manualSessionOverride
304
+ ?? validatedPreferredModel
305
+ ?? (sessionModelReady && ctx.model
276
306
  ? { provider: ctx.model.provider, id: ctx.model.id }
277
307
  : null);
278
308
 
@@ -678,7 +708,7 @@ export async function bootstrapAutoSession(
678
708
  }
679
709
 
680
710
  // ── DB lifecycle ──
681
- const gsdDbPath = join(s.basePath, ".gsd", "gsd.db");
711
+ const gsdDbPath = resolveProjectRootDbPath(s.basePath);
682
712
  const gsdDirPath = join(s.basePath, ".gsd");
683
713
  if (existsSync(gsdDirPath) && !existsSync(gsdDbPath)) {
684
714
  const hasDecisions = existsSync(join(gsdDirPath, "DECISIONS.md"));
@@ -731,6 +761,7 @@ export async function bootstrapAutoSession(
731
761
  id: startModelSnapshot.id,
732
762
  };
733
763
  }
764
+ s.manualSessionModelOverride = manualSessionOverride ?? null;
734
765
 
735
766
  // Apply worker model override from parallel orchestrator (#worker-model).
736
767
  // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
@@ -92,7 +92,7 @@ export function clearInFlightTools(): void {
92
92
  * handler. When these errors occur, retrying the same unit will produce the same
93
93
  * failure, so the retry loop must be broken.
94
94
  */
95
- const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
95
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON/i;
96
96
 
97
97
  /**
98
98
  * Returns true if the error message indicates a tool invocation failure due to
@@ -2043,7 +2043,7 @@ export function mergeMilestoneToMain(
2043
2043
  // 12. Remove worktree directory first (must happen before branch deletion)
2044
2044
  try {
2045
2045
  removeWorktree(originalBasePath_, milestoneId, {
2046
- branch: null as unknown as string,
2046
+ branch: milestoneBranch,
2047
2047
  deleteBranch: false,
2048
2048
  });
2049
2049
  } catch (err) {
@@ -165,6 +165,7 @@ import {
165
165
  reconcileMergeState,
166
166
  } from "./auto-recovery.js";
167
167
  import { resolveDispatch, DISPATCH_RULES } from "./auto-dispatch.js";
168
+ import { getErrorMessage } from "./error-utils.js";
168
169
  import { initRegistry, convertDispatchRules } from "./rule-registry.js";
169
170
  import { emitJournalEvent as _emitJournalEvent, type JournalEntry } from "./journal.js";
170
171
  import {
@@ -272,6 +273,53 @@ function restoreProjectRootEnv(): void {
272
273
  s.projectRootEnvCaptured = false;
273
274
  }
274
275
 
276
+ function captureMilestoneLockEnv(milestoneId: string | null): void {
277
+ if (!s.milestoneLockEnvCaptured) {
278
+ s.hadMilestoneLockEnv = Object.prototype.hasOwnProperty.call(process.env, "GSD_MILESTONE_LOCK");
279
+ s.previousMilestoneLockEnv = process.env.GSD_MILESTONE_LOCK ?? null;
280
+ s.milestoneLockEnvCaptured = true;
281
+ }
282
+
283
+ if (milestoneId) {
284
+ process.env.GSD_MILESTONE_LOCK = milestoneId;
285
+ } else {
286
+ delete process.env.GSD_MILESTONE_LOCK;
287
+ }
288
+ }
289
+
290
+ function restoreMilestoneLockEnv(): void {
291
+ if (!s.milestoneLockEnvCaptured) return;
292
+
293
+ if (s.hadMilestoneLockEnv && s.previousMilestoneLockEnv !== null) {
294
+ process.env.GSD_MILESTONE_LOCK = s.previousMilestoneLockEnv;
295
+ } else {
296
+ delete process.env.GSD_MILESTONE_LOCK;
297
+ }
298
+
299
+ s.previousMilestoneLockEnv = null;
300
+ s.hadMilestoneLockEnv = false;
301
+ s.milestoneLockEnvCaptured = false;
302
+ }
303
+
304
+ export function startAutoDetached(
305
+ ctx: ExtensionCommandContext,
306
+ pi: ExtensionAPI,
307
+ base: string,
308
+ verboseMode: boolean,
309
+ options?: {
310
+ step?: boolean;
311
+ interrupted?: InterruptedSessionAssessment;
312
+ milestoneLock?: string | null;
313
+ },
314
+ ): void {
315
+ void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
316
+ const message = getErrorMessage(err);
317
+ ctx.ui.notify(`Auto-start failed: ${message}`, "error");
318
+ logWarning("engine", `auto start error: ${message}`, { file: "auto.ts" });
319
+ debugLog("auto-start-failed", { error: message });
320
+ });
321
+ }
322
+
275
323
  export function shouldUseWorktreeIsolation(): boolean {
276
324
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
277
325
  if (prefs?.isolation === "worktree") return true;
@@ -549,11 +597,13 @@ function buildSnapshotOpts(
549
597
  _unitType: string,
550
598
  _unitId: string,
551
599
  ): {
600
+ autoSessionKey?: string;
552
601
  continueHereFired?: boolean;
553
602
  promptCharCount?: number;
554
603
  baselineCharCount?: number;
555
604
  } & Record<string, unknown> {
556
605
  return {
606
+ ...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
557
607
  promptCharCount: s.lastPromptCharCount,
558
608
  baselineCharCount: s.lastBaselineCharCount,
559
609
  ...(s.currentUnitRouting ?? {}),
@@ -574,6 +624,7 @@ function handleLostSessionLock(
574
624
  s.paused = false;
575
625
  clearUnitTimeout();
576
626
  restoreProjectRootEnv();
627
+ restoreMilestoneLockEnv();
577
628
  deregisterSigtermHandler();
578
629
  clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
579
630
  const base = lockBase();
@@ -610,6 +661,7 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
610
661
  s.active = false;
611
662
  clearUnitTimeout();
612
663
  restoreProjectRootEnv();
664
+ restoreMilestoneLockEnv();
613
665
 
614
666
  // Clear crash lock and release session lock so the next `/gsd next` does
615
667
  // not see a stale lock with the current PID and treat it as a "remote"
@@ -880,6 +932,7 @@ export async function stopAuto(
880
932
  ctx?.ui.setWidget("gsd-progress", undefined);
881
933
  ctx?.ui.setFooter(undefined);
882
934
  restoreProjectRootEnv();
935
+ restoreMilestoneLockEnv();
883
936
 
884
937
  // Reset all session state in one call
885
938
  s.reset();
@@ -933,6 +986,7 @@ export async function pauseAuto(
933
986
  activeEngineId: s.activeEngineId,
934
987
  activeRunDir: s.activeRunDir,
935
988
  autoStartTime: s.autoStartTime,
989
+ milestoneLock: s.sessionMilestoneLock ?? undefined,
936
990
  };
937
991
  const runtimeDir = join(gsdRoot(s.originalBasePath || s.basePath), "runtime");
938
992
  mkdirSync(runtimeDir, { recursive: true });
@@ -971,6 +1025,7 @@ export async function pauseAuto(
971
1025
  s.active = false;
972
1026
  s.paused = true;
973
1027
  restoreProjectRootEnv();
1028
+ restoreMilestoneLockEnv();
974
1029
  s.pendingVerificationRetry = null;
975
1030
  s.verificationRetryCount.clear();
976
1031
  ctx?.ui.setStatus("gsd-auto", "paused");
@@ -1154,6 +1209,7 @@ export async function startAuto(
1154
1209
  options?: {
1155
1210
  step?: boolean;
1156
1211
  interrupted?: InterruptedSessionAssessment;
1212
+ milestoneLock?: string | null;
1157
1213
  },
1158
1214
  ): Promise<void> {
1159
1215
  if (s.active) {
@@ -1163,6 +1219,12 @@ export async function startAuto(
1163
1219
 
1164
1220
  const requestedStepMode = options?.step ?? false;
1165
1221
  const interruptedAssessment = options?.interrupted ?? null;
1222
+ if (options?.milestoneLock !== undefined) {
1223
+ s.sessionMilestoneLock = options.milestoneLock ?? null;
1224
+ }
1225
+ if (s.sessionMilestoneLock) {
1226
+ captureMilestoneLockEnv(s.sessionMilestoneLock);
1227
+ }
1166
1228
 
1167
1229
  // Escape stale worktree cwd from a previous milestone (#608).
1168
1230
  base = escapeStaleWorktree(base);
@@ -1194,6 +1256,7 @@ export async function startAuto(
1194
1256
  s.originalBasePath = meta.originalBasePath || base;
1195
1257
  s.stepMode = meta.stepMode ?? requestedStepMode;
1196
1258
  s.autoStartTime = meta.autoStartTime || Date.now();
1259
+ s.sessionMilestoneLock = meta.milestoneLock ?? null;
1197
1260
  s.paused = true;
1198
1261
  try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); }
1199
1262
  ctx.ui.notify(
@@ -1228,6 +1291,7 @@ export async function startAuto(
1228
1291
  s.pausedUnitType = meta.unitType ?? null;
1229
1292
  s.pausedUnitId = meta.unitId ?? null;
1230
1293
  s.autoStartTime = meta.autoStartTime || Date.now();
1294
+ s.sessionMilestoneLock = meta.milestoneLock ?? null;
1231
1295
  s.paused = true;
1232
1296
  try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); }
1233
1297
  ctx.ui.notify(
@@ -1247,6 +1311,10 @@ export async function startAuto(
1247
1311
  if (!s.autoStartTime || s.autoStartTime <= 0) s.autoStartTime = Date.now();
1248
1312
  }
1249
1313
 
1314
+ if (s.sessionMilestoneLock) {
1315
+ captureMilestoneLockEnv(s.sessionMilestoneLock);
1316
+ }
1317
+
1250
1318
  if (!s.paused) {
1251
1319
  s.stepMode = requestedStepMode;
1252
1320
  }
@@ -121,6 +121,8 @@ export function registerHooks(pi: ExtensionAPI): void {
121
121
  return { cancel: true };
122
122
  }
123
123
  const basePath = process.cwd();
124
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
125
+ await ensureDbOpen();
124
126
  const state = await deriveState(basePath);
125
127
  if (!state.activeMilestone || !state.activeSlice || !state.activeTask) return;
126
128
  if (state.phase !== "executing") return;