gsd-pi 2.76.0 → 2.77.0

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 (536) hide show
  1. package/README.md +45 -25
  2. package/dist/claude-cli-check.js +32 -3
  3. package/dist/mcp-server.d.ts +7 -0
  4. package/dist/mcp-server.js +35 -1
  5. package/dist/onboarding.js +45 -0
  6. package/dist/resource-loader.d.ts +1 -1
  7. package/dist/resource-loader.js +2 -8
  8. package/dist/resources/agents/researcher.md +1 -1
  9. package/dist/resources/extensions/claude-code-cli/readiness.js +31 -8
  10. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  11. package/dist/resources/extensions/gsd/auto/loop.js +9 -0
  12. package/dist/resources/extensions/gsd/auto/phases.js +104 -11
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
  14. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +53 -16
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  19. package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
  20. package/dist/resources/extensions/gsd/auto-start.js +58 -57
  21. package/dist/resources/extensions/gsd/auto-verification.js +33 -0
  22. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  23. package/dist/resources/extensions/gsd/auto.js +70 -28
  24. package/dist/resources/extensions/gsd/blocked-models.js +68 -0
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +93 -1
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  27. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  28. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +3 -0
  29. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +12 -0
  30. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
  31. package/dist/resources/extensions/gsd/bootstrap/system-context.js +84 -23
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  33. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  34. package/dist/resources/extensions/gsd/commands-extract-learnings.js +54 -89
  35. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  36. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  37. package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
  38. package/dist/resources/extensions/gsd/db-writer.js +88 -16
  39. package/dist/resources/extensions/gsd/doctor-git-checks.js +23 -29
  40. package/dist/resources/extensions/gsd/doctor-providers.js +51 -5
  41. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +1 -0
  42. package/dist/resources/extensions/gsd/error-classifier.js +31 -3
  43. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  44. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  45. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  46. package/dist/resources/extensions/gsd/gsd-db.js +168 -23
  47. package/dist/resources/extensions/gsd/guided-flow.js +190 -1
  48. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  49. package/dist/resources/extensions/gsd/hook-emitter.js +108 -0
  50. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  51. package/dist/resources/extensions/gsd/key-manager.js +28 -0
  52. package/dist/resources/extensions/gsd/memory-backfill.js +126 -0
  53. package/dist/resources/extensions/gsd/memory-store.js +19 -0
  54. package/dist/resources/extensions/gsd/model-router.js +36 -3
  55. package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
  56. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  57. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  58. package/dist/resources/extensions/gsd/preferences.js +17 -17
  59. package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
  60. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  62. package/dist/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
  63. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  64. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  65. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  66. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  67. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  68. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -0
  69. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  70. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  71. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  72. package/dist/resources/extensions/gsd/state.js +43 -4
  73. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  74. package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
  75. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  76. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  77. package/dist/resources/extensions/gsd/tools/memory-tools.js +26 -1
  78. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  79. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  80. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  81. package/dist/resources/extensions/gsd/workflow-templates/spike.md +6 -0
  82. package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
  83. package/dist/resources/extensions/search-the-web/command-search-provider.js +5 -4
  84. package/dist/resources/extensions/search-the-web/native-search.js +45 -13
  85. package/dist/resources/skills/api-design/SKILL.md +190 -0
  86. package/dist/resources/skills/create-mcp-server/SKILL.md +121 -0
  87. package/dist/resources/skills/decompose-into-slices/SKILL.md +139 -0
  88. package/dist/resources/skills/dependency-upgrade/SKILL.md +158 -0
  89. package/dist/resources/skills/design-an-interface/SKILL.md +102 -0
  90. package/dist/resources/skills/forensics/SKILL.md +153 -0
  91. package/dist/resources/skills/grill-me/SKILL.md +93 -0
  92. package/dist/resources/skills/handoff/SKILL.md +121 -0
  93. package/dist/resources/skills/observability/SKILL.md +174 -0
  94. package/dist/resources/skills/security-review/SKILL.md +181 -0
  95. package/dist/resources/skills/spike-wrap-up/SKILL.md +138 -0
  96. package/dist/resources/skills/tdd/SKILL.md +112 -0
  97. package/dist/resources/skills/verify-before-complete/SKILL.md +98 -0
  98. package/dist/resources/skills/write-docs/SKILL.md +82 -0
  99. package/dist/resources/skills/write-milestone-brief/SKILL.md +135 -0
  100. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  101. package/dist/web/standalone/.next/BUILD_ID +1 -1
  102. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  103. package/dist/web/standalone/.next/build-manifest.json +2 -2
  104. package/dist/web/standalone/.next/required-server-files.json +1 -1
  105. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/index.html +1 -1
  122. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  129. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  130. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/middleware-manifest.json +1 -1
  132. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  133. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  134. package/dist/web/standalone/server.js +1 -1
  135. package/dist/welcome-screen.js +6 -1
  136. package/dist/wizard.js +2 -0
  137. package/package.json +1 -1
  138. package/packages/daemon/package.json +2 -2
  139. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  140. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  141. package/packages/mcp-server/dist/remote-questions.js +732 -0
  142. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  143. package/packages/mcp-server/dist/server.d.ts +7 -0
  144. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  145. package/packages/mcp-server/dist/server.js +70 -8
  146. package/packages/mcp-server/dist/server.js.map +1 -1
  147. package/packages/mcp-server/dist/session-manager.d.ts +14 -0
  148. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/session-manager.js +49 -1
  150. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  151. package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
  152. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  153. package/packages/mcp-server/dist/workflow-tools.js +163 -25
  154. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  155. package/packages/mcp-server/package.json +4 -3
  156. package/packages/mcp-server/src/mcp-server.test.ts +67 -0
  157. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  158. package/packages/mcp-server/src/remote-questions.ts +916 -0
  159. package/packages/mcp-server/src/server.ts +89 -14
  160. package/packages/mcp-server/src/session-manager.ts +43 -1
  161. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  162. package/packages/mcp-server/src/workflow-tools.ts +215 -43
  163. package/packages/mcp-server/tsconfig.test.json +19 -0
  164. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  165. package/packages/native/package.json +1 -1
  166. package/packages/pi-agent-core/dist/agent-loop.js +12 -0
  167. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  168. package/packages/pi-agent-core/dist/types.d.ts +30 -0
  169. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  170. package/packages/pi-agent-core/dist/types.js.map +1 -1
  171. package/packages/pi-agent-core/package.json +1 -1
  172. package/packages/pi-agent-core/src/agent-loop.ts +14 -0
  173. package/packages/pi-agent-core/src/types.ts +34 -0
  174. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  175. package/packages/pi-ai/dist/models/custom.d.ts +38 -0
  176. package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/models/custom.js +41 -0
  178. package/packages/pi-ai/dist/models/custom.js.map +1 -1
  179. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  180. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  181. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts +2 -0
  182. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts.map +1 -0
  183. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +13 -0
  184. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -0
  185. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  186. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  187. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  188. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  189. package/packages/pi-ai/dist/providers/anthropic.js +13 -4
  190. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  191. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  192. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  193. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  194. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  195. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  196. package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
  197. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  198. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  199. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  200. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  201. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  202. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
  203. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
  204. package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
  205. package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
  206. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
  207. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
  208. package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
  209. package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
  210. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  211. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +12 -2
  212. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  213. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +164 -14
  214. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
  215. package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
  216. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +15 -3
  217. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
  218. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts +2 -0
  219. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts.map +1 -0
  220. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +67 -0
  221. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -0
  222. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
  223. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +16 -3
  224. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
  225. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts +2 -0
  226. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts.map +1 -0
  227. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +67 -0
  228. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -0
  229. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +2 -0
  230. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +1 -0
  231. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +289 -0
  232. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +1 -0
  233. package/packages/pi-ai/package.json +1 -1
  234. package/packages/pi-ai/src/models/custom.ts +42 -0
  235. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  236. package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -0
  237. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  238. package/packages/pi-ai/src/providers/anthropic.ts +15 -4
  239. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  240. package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
  241. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  242. package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
  243. package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
  244. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +200 -23
  245. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +12 -2
  246. package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +84 -0
  247. package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +15 -5
  248. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +84 -0
  249. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +16 -5
  250. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +363 -0
  251. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  252. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
  253. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
  255. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  256. package/packages/pi-coding-agent/dist/core/agent-session.js +32 -2
  257. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  258. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  259. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  260. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  261. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/core/extensions/loader.js +4 -0
  263. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  264. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +35 -2
  265. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/core/extensions/runner.js +233 -0
  267. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +205 -2
  269. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  270. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts +53 -0
  272. package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts.map +1 -0
  273. package/packages/pi-coding-agent/dist/core/hooks-runner.js +337 -0
  274. package/packages/pi-coding-agent/dist/core/hooks-runner.js.map +1 -0
  275. package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts +2 -0
  276. package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts.map +1 -0
  277. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +234 -0
  278. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -0
  279. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  280. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  281. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  282. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
  284. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
  286. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
  288. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts +2 -0
  290. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts.map +1 -0
  291. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js +40 -0
  292. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js.map +1 -0
  293. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  294. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  295. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  296. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  297. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
  298. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
  300. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
  302. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  304. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  305. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  306. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  307. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  308. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  309. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  310. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  311. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  312. package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
  313. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  314. package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
  315. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  316. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +55 -0
  317. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  318. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  319. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  320. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  321. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  322. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  323. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  324. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  325. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  326. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  327. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
  328. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  329. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  330. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  331. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  332. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  333. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  334. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  335. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  336. package/packages/pi-coding-agent/package.json +1 -1
  337. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
  338. package/packages/pi-coding-agent/src/core/agent-session.ts +38 -2
  339. package/packages/pi-coding-agent/src/core/extensions/index.ts +16 -0
  340. package/packages/pi-coding-agent/src/core/extensions/loader.ts +5 -0
  341. package/packages/pi-coding-agent/src/core/extensions/runner.ts +351 -0
  342. package/packages/pi-coding-agent/src/core/extensions/types.ts +258 -0
  343. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +269 -0
  344. package/packages/pi-coding-agent/src/core/hooks-runner.ts +460 -0
  345. package/packages/pi-coding-agent/src/core/index.ts +10 -0
  346. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
  347. package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
  348. package/packages/pi-coding-agent/src/core/model-registry-auth-header.test.ts +44 -0
  349. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  350. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
  351. package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
  352. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  353. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  354. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
  355. package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
  356. package/packages/pi-coding-agent/src/core/settings-manager.ts +57 -0
  357. package/packages/pi-coding-agent/src/index.ts +16 -0
  358. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  359. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
  360. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  361. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  362. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  363. package/packages/pi-tui/package.json +1 -1
  364. package/packages/rpc-client/package.json +1 -1
  365. package/pkg/package.json +1 -1
  366. package/scripts/link-workspace-packages.cjs +1 -0
  367. package/src/resources/agents/researcher.md +1 -1
  368. package/src/resources/extensions/claude-code-cli/readiness.ts +32 -8
  369. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  370. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  371. package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
  372. package/src/resources/extensions/gsd/auto/loop.ts +9 -0
  373. package/src/resources/extensions/gsd/auto/phases.ts +131 -10
  374. package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
  375. package/src/resources/extensions/gsd/auto/session.ts +35 -2
  376. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  377. package/src/resources/extensions/gsd/auto-model-selection.ts +71 -15
  378. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  379. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  380. package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
  381. package/src/resources/extensions/gsd/auto-start.ts +60 -68
  382. package/src/resources/extensions/gsd/auto-verification.ts +33 -0
  383. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  384. package/src/resources/extensions/gsd/auto.ts +73 -28
  385. package/src/resources/extensions/gsd/blocked-models.ts +98 -0
  386. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +120 -1
  387. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  388. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  389. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +5 -0
  390. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -0
  391. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
  392. package/src/resources/extensions/gsd/bootstrap/system-context.ts +89 -26
  393. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  394. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  395. package/src/resources/extensions/gsd/commands-extract-learnings.ts +55 -90
  396. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  397. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  398. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
  399. package/src/resources/extensions/gsd/db-writer.ts +88 -17
  400. package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -27
  401. package/src/resources/extensions/gsd/doctor-providers.ts +59 -6
  402. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +2 -0
  403. package/src/resources/extensions/gsd/error-classifier.ts +36 -3
  404. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  405. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  406. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  407. package/src/resources/extensions/gsd/gsd-db.ts +186 -23
  408. package/src/resources/extensions/gsd/guided-flow.ts +222 -1
  409. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  410. package/src/resources/extensions/gsd/hook-emitter.ts +188 -0
  411. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  412. package/src/resources/extensions/gsd/journal.ts +2 -1
  413. package/src/resources/extensions/gsd/key-manager.ts +28 -0
  414. package/src/resources/extensions/gsd/memory-backfill.ts +140 -0
  415. package/src/resources/extensions/gsd/memory-store.ts +26 -0
  416. package/src/resources/extensions/gsd/model-router.ts +42 -1
  417. package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
  418. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  419. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  420. package/src/resources/extensions/gsd/preferences.ts +17 -17
  421. package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
  422. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  423. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  424. package/src/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
  425. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  426. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  427. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  428. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  429. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  430. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -0
  431. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  432. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  433. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  434. package/src/resources/extensions/gsd/state.ts +45 -4
  435. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
  436. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +95 -1
  437. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  438. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  439. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
  440. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
  441. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
  442. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
  443. package/src/resources/extensions/gsd/tests/blocked-models.test.ts +98 -0
  444. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +54 -0
  445. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  446. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +68 -66
  447. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  448. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
  449. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  450. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  451. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
  452. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  453. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +42 -0
  454. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +8 -4
  455. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +148 -3
  456. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  457. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +306 -1
  458. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  459. package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
  460. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  461. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  462. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +40 -9
  463. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +62 -0
  464. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
  465. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  466. package/src/resources/extensions/gsd/tests/integration/doctor-git-symlink-cwd.test.ts +11 -0
  467. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +78 -0
  468. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
  469. package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
  470. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
  471. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +1 -1
  472. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  473. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  474. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +37 -0
  475. package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
  476. package/src/resources/extensions/gsd/tests/load-memory-block.test.ts +36 -0
  477. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  478. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
  479. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  480. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  481. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
  482. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  483. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  484. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
  485. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  486. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  487. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
  488. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +103 -4
  489. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  490. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  491. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
  492. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  493. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  494. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
  495. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  496. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  497. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  498. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  499. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  500. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -3
  501. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  502. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
  503. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
  504. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
  505. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  506. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  507. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  508. package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
  509. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  510. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  511. package/src/resources/extensions/gsd/tools/memory-tools.ts +31 -1
  512. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  513. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  514. package/src/resources/extensions/gsd/workflow-logger.ts +4 -1
  515. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  516. package/src/resources/extensions/gsd/workflow-templates/spike.md +6 -0
  517. package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
  518. package/src/resources/extensions/search-the-web/command-search-provider.ts +5 -4
  519. package/src/resources/extensions/search-the-web/native-search.ts +48 -12
  520. package/src/resources/skills/api-design/SKILL.md +190 -0
  521. package/src/resources/skills/create-mcp-server/SKILL.md +121 -0
  522. package/src/resources/skills/decompose-into-slices/SKILL.md +139 -0
  523. package/src/resources/skills/dependency-upgrade/SKILL.md +158 -0
  524. package/src/resources/skills/design-an-interface/SKILL.md +102 -0
  525. package/src/resources/skills/forensics/SKILL.md +153 -0
  526. package/src/resources/skills/grill-me/SKILL.md +93 -0
  527. package/src/resources/skills/handoff/SKILL.md +121 -0
  528. package/src/resources/skills/observability/SKILL.md +174 -0
  529. package/src/resources/skills/security-review/SKILL.md +181 -0
  530. package/src/resources/skills/spike-wrap-up/SKILL.md +138 -0
  531. package/src/resources/skills/tdd/SKILL.md +112 -0
  532. package/src/resources/skills/verify-before-complete/SKILL.md +98 -0
  533. package/src/resources/skills/write-docs/SKILL.md +82 -0
  534. package/src/resources/skills/write-milestone-brief/SKILL.md +135 -0
  535. /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_buildManifest.js +0 -0
  536. /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_ssgManifest.js +0 -0
@@ -54,7 +54,8 @@ import type { MinimalModelRegistry } from "../context-budget.js";
54
54
  import { ensurePlanV2Graph } from "../uok/plan-v2.js";
55
55
  import { resolveUokFlags } from "../uok/flags.js";
56
56
  import { UokGateRunner } from "../uok/gate-runner.js";
57
- import { resetEvidence } from "../safety/evidence-collector.js";
57
+ import { resetEvidence, loadEvidenceFromDisk } from "../safety/evidence-collector.js";
58
+ import { parseUnitId } from "../unit-id.js";
58
59
  import { createCheckpoint, cleanupCheckpoint, rollbackToCheckpoint } from "../safety/git-checkpoint.js";
59
60
  import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
60
61
  import {
@@ -80,7 +81,11 @@ export function resetSessionTimeoutState(): void {
80
81
  * Exported for testing as _resolveReportBasePath.
81
82
  */
82
83
  export function _resolveReportBasePath(s: Pick<AutoSession, "originalBasePath" | "basePath">): string {
83
- return s.originalBasePath || s.basePath;
84
+ // Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
85
+ // originalBasePath is falsy — prevents reports landing in the worktree (#3729).
86
+ const resolved = s.originalBasePath || s.basePath;
87
+ const markerIdx = resolved.indexOf("/.gsd/worktrees/");
88
+ return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
84
89
  }
85
90
 
86
91
  /**
@@ -91,7 +96,12 @@ export function _resolveReportBasePath(s: Pick<AutoSession, "originalBasePath" |
91
96
  export function _resolveDispatchGuardBasePath(
92
97
  s: Pick<AutoSession, "originalBasePath" | "basePath">,
93
98
  ): string {
94
- return s.originalBasePath || s.basePath;
99
+ // Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
100
+ // originalBasePath is falsy — prevents guard checks running against the
101
+ // worktree instead of the project root (#3729).
102
+ const resolved = s.originalBasePath || s.basePath;
103
+ const markerIdx = resolved.indexOf("/.gsd/worktrees/");
104
+ return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
95
105
  }
96
106
 
97
107
  const PLAN_V2_GATE_PHASES: ReadonlySet<Phase> = new Set([
@@ -419,7 +429,7 @@ export async function runPreDispatch(
419
429
  findings: reason,
420
430
  milestoneId: state.activeMilestone?.id ?? undefined,
421
431
  });
422
- ctx.ui.notify(`Plan gate failed-closed: ${reason}`, "error");
432
+ ctx.ui.notify(`Plan gate failed-closed: ${reason}\n\nIf this keeps happening, try: /gsd doctor heal`, "error");
423
433
  await deps.pauseAuto(ctx, pi);
424
434
  return { action: "break", reason: "plan-v2-gate-failed" };
425
435
  }
@@ -545,6 +555,12 @@ export async function runPreDispatch(
545
555
  loopState.stuckRecoveryAttempts = 0;
546
556
 
547
557
  // Worktree lifecycle on milestone transition — merge current, enter next
558
+ // #2909: preflight — warn + stash dirty working tree before merge
559
+ const preflightTransition = deps.preflightCleanRoot(
560
+ s.originalBasePath || s.basePath,
561
+ s.currentMilestoneId!,
562
+ ctx.ui.notify.bind(ctx.ui),
563
+ );
548
564
  try {
549
565
  deps.resolver.mergeAndExit(s.currentMilestoneId!, ctx.ui);
550
566
  } catch (mergeErr) {
@@ -566,6 +582,14 @@ export async function runPreDispatch(
566
582
  await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
567
583
  return { action: "break", reason: "merge-failed" };
568
584
  }
585
+ // #2909: postflight — restore stashed changes after successful merge
586
+ if (preflightTransition.stashPushed) {
587
+ deps.postflightPopStash(
588
+ s.originalBasePath || s.basePath,
589
+ s.currentMilestoneId!,
590
+ ctx.ui.notify.bind(ctx.ui),
591
+ );
592
+ }
569
593
 
570
594
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
571
595
 
@@ -644,6 +668,12 @@ export async function runPreDispatch(
644
668
  if (incomplete.length === 0 && state.registry.length > 0) {
645
669
  // All milestones complete — merge milestone branch before stopping
646
670
  if (s.currentMilestoneId) {
671
+ // #2909: preflight — warn + stash dirty working tree before merge
672
+ const preflightAllComplete = deps.preflightCleanRoot(
673
+ s.originalBasePath || s.basePath,
674
+ s.currentMilestoneId,
675
+ ctx.ui.notify.bind(ctx.ui),
676
+ );
647
677
  try {
648
678
  deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
649
679
  // Prevent stopAuto from attempting the same merge (#2645)
@@ -665,6 +695,14 @@ export async function runPreDispatch(
665
695
  await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
666
696
  return { action: "break", reason: "merge-failed" };
667
697
  }
698
+ // #2909: postflight — restore stashed changes after successful merge
699
+ if (preflightAllComplete.stashPushed) {
700
+ deps.postflightPopStash(
701
+ s.originalBasePath || s.basePath,
702
+ s.currentMilestoneId,
703
+ ctx.ui.notify.bind(ctx.ui),
704
+ );
705
+ }
668
706
 
669
707
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
670
708
  }
@@ -758,6 +796,12 @@ export async function runPreDispatch(
758
796
  if (state.phase === "complete") {
759
797
  // Milestone merge on complete (before closeout so branch state is clean)
760
798
  if (s.currentMilestoneId) {
799
+ // #2909: preflight — warn + stash dirty working tree before merge
800
+ const preflightComplete = deps.preflightCleanRoot(
801
+ s.originalBasePath || s.basePath,
802
+ s.currentMilestoneId,
803
+ ctx.ui.notify.bind(ctx.ui),
804
+ );
761
805
  try {
762
806
  deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
763
807
  // Prevent stopAuto from attempting the same merge (#2645)
@@ -779,6 +823,14 @@ export async function runPreDispatch(
779
823
  await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
780
824
  return { action: "break", reason: "merge-failed" };
781
825
  }
826
+ // #2909: postflight — restore stashed changes after successful merge
827
+ if (preflightComplete.stashPushed) {
828
+ deps.postflightPopStash(
829
+ s.originalBasePath || s.basePath,
830
+ s.currentMilestoneId,
831
+ ctx.ui.notify.bind(ctx.ui),
832
+ );
833
+ }
782
834
 
783
835
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
784
836
  }
@@ -1131,16 +1183,42 @@ export async function runGuards(
1131
1183
  s.lastBudgetAlertLevel =
1132
1184
  newBudgetAlertLevel as AutoSession["lastBudgetAlertLevel"];
1133
1185
 
1134
- if (threshold.pct === 100 && budgetEnforcementAction !== "none") {
1186
+ // Emit Layer 2 budget_threshold event (post-plan hook recommendation).
1187
+ // Extensions / Layer 0 shell hooks may return an action override.
1188
+ let hookAction: "pause" | "downgrade" | "continue" | undefined;
1189
+ try {
1190
+ const { emitBudgetThreshold } = await import("../hook-emitter.js");
1191
+ const hookResult = await emitBudgetThreshold({
1192
+ fraction: budgetPct,
1193
+ spent: totalCost,
1194
+ limit: budgetCeiling,
1195
+ });
1196
+ if (hookResult?.action) hookAction = hookResult.action;
1197
+ } catch (hookErr) {
1198
+ logWarning("engine", `budget_threshold hook emission failed: ${(hookErr as Error).message}`);
1199
+ }
1200
+
1201
+ // Apply hook override to enforcement action. "continue" → "none" (no enforcement),
1202
+ // "pause" and "downgrade" map to the matching enforcement path below.
1203
+ let effectiveAction = budgetEnforcementAction;
1204
+ if (hookAction === "continue") {
1205
+ effectiveAction = "none";
1206
+ } else if (hookAction === "pause") {
1207
+ effectiveAction = "pause";
1208
+ } else if (hookAction === "downgrade") {
1209
+ effectiveAction = "warn";
1210
+ }
1211
+
1212
+ if (threshold.pct === 100 && effectiveAction !== "none") {
1135
1213
  // 100% — special enforcement logic (halt/pause/warn)
1136
1214
  const msg = `Budget ceiling ${deps.formatCost(budgetCeiling)} reached (spent ${deps.formatCost(totalCost)}).`;
1137
- if (budgetEnforcementAction === "halt") {
1215
+ if (effectiveAction === "halt") {
1138
1216
  deps.sendDesktopNotification("GSD", msg, "error", "budget", basename(s.originalBasePath || s.basePath));
1139
1217
  await deps.stopAuto(ctx, pi, "Budget ceiling reached");
1140
1218
  debugLog("autoLoop", { phase: "exit", reason: "budget-halt" });
1141
1219
  return { action: "break", reason: "budget-halt" };
1142
1220
  }
1143
- if (budgetEnforcementAction === "pause") {
1221
+ if (effectiveAction === "pause") {
1144
1222
  ctx.ui.notify(
1145
1223
  `${msg} Pausing auto-mode — /gsd auto to override and continue.`,
1146
1224
  "warning",
@@ -1359,6 +1437,14 @@ export async function runUnitPhase(
1359
1437
  );
1360
1438
  if (safetyConfig.enabled && safetyConfig.evidence_collection) {
1361
1439
  resetEvidence();
1440
+ // Restore persisted evidence so session-restart resumes don't produce
1441
+ // false-positive "no bash calls" warnings (Bug #4385).
1442
+ if (s.basePath && unitType === "execute-task") {
1443
+ const { milestone: eMid, slice: eSid, task: eTid } = parseUnitId(unitId);
1444
+ if (eMid && eSid && eTid) {
1445
+ loadEvidenceFromDisk(s.basePath, eMid, eSid, eTid);
1446
+ }
1447
+ }
1362
1448
  }
1363
1449
  // Only checkpoint code-executing units (not lifecycle/planning units)
1364
1450
  if (safetyConfig.enabled && safetyConfig.checkpoints && unitType === "execute-task") {
@@ -1445,6 +1531,7 @@ export async function runUnitPhase(
1445
1531
  sidecarItem ? undefined : { isRetry, previousTier },
1446
1532
  undefined,
1447
1533
  s.manualSessionModelOverride,
1534
+ s.autoModeStartThinkingLevel,
1448
1535
  );
1449
1536
  s.currentUnitRouting =
1450
1537
  modelResult.routing as AutoSession["currentUnitRouting"];
@@ -1459,6 +1546,9 @@ export async function runUnitPhase(
1459
1546
  if (match) {
1460
1547
  const ok = await pi.setModel(match, { persist: false });
1461
1548
  if (ok) {
1549
+ if (s.autoModeStartThinkingLevel) {
1550
+ pi.setThinkingLevel(s.autoModeStartThinkingLevel);
1551
+ }
1462
1552
  s.currentUnitModel = match as AutoSession["currentUnitModel"];
1463
1553
  ctx.ui.notify(`Hook model override: ${match.provider}/${match.id}`, "info");
1464
1554
  } else {
@@ -1589,21 +1679,24 @@ export async function runUnitPhase(
1589
1679
  }
1590
1680
 
1591
1681
  if (unitResult.status === "cancelled") {
1682
+ const errorCategory = unitResult.errorContext?.category;
1592
1683
  // Provider-error pause: pauseAuto already handled cleanup and scheduled
1593
1684
  // recovery. Don't hard-stop — just break out of the loop (#2762).
1594
- if (unitResult.errorContext?.category === "provider") {
1685
+ if (errorCategory === "provider") {
1595
1686
  await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1596
- debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext.isTransient });
1687
+ debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext?.isTransient });
1597
1688
  return { action: "break", reason: "provider-pause" };
1598
1689
  }
1599
1690
  // Timeout category covers two distinct scenarios:
1600
1691
  // 1. Session creation timeout (120s) — transient, auto-resume with backoff
1601
1692
  // 2. Unit hard timeout (30min+) — stuck agent, pause for manual review
1693
+ // Transient session-failed covers recoverable newSession failures and should
1694
+ // pause instead of hard-stopping.
1602
1695
  // Structural errors (TypeError, is not a function) are NOT transient
1603
1696
  // and must hard-stop to avoid infinite retry loops.
1604
1697
  if (
1605
1698
  unitResult.errorContext?.isTransient &&
1606
- unitResult.errorContext?.category === "timeout"
1699
+ errorCategory === "timeout"
1607
1700
  ) {
1608
1701
  const isSessionCreationTimeout = unitResult.errorContext.message?.includes("Session creation timed out");
1609
1702
 
@@ -1666,6 +1759,20 @@ export async function runUnitPhase(
1666
1759
  await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1667
1760
  return { action: "break", reason: "unit-hard-timeout" };
1668
1761
  }
1762
+ if (
1763
+ unitResult.errorContext?.isTransient &&
1764
+ errorCategory === "session-failed"
1765
+ ) {
1766
+ ctx.ui.notify(
1767
+ `Session creation failed transiently for ${unitType} ${unitId}: ${unitResult.errorContext?.message ?? "unknown"}. Pausing auto-mode (recoverable).`,
1768
+ "warning",
1769
+ );
1770
+ debugLog("autoLoop", { phase: "session-start-transient-pause", unitType, unitId, category: errorCategory });
1771
+ await deps.pauseAuto(ctx, pi);
1772
+ await deps.autoCommitUnit?.(s.basePath, unitType, unitId, ctx);
1773
+ await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1774
+ return { action: "break", reason: "session-timeout" };
1775
+ }
1669
1776
  // All other cancelled states (structural errors, non-transient failures): hard stop
1670
1777
  if (s.currentUnit) {
1671
1778
  await deps.closeoutUnit(
@@ -1881,6 +1988,20 @@ export async function runFinalize(
1881
1988
  debugLog("autoLoop", { phase: "sidecar-artifact-retry-skipped", iteration: ic.iteration });
1882
1989
  } else {
1883
1990
  // s.pendingVerificationRetry was set by postUnitPreVerification.
1991
+ // Emit a dedicated journal event so forensics can distinguish bounded
1992
+ // verification retries from genuine stuck-loop dispatch repetitions (#4540).
1993
+ const retryInfo = s.pendingVerificationRetry;
1994
+ deps.emitJournalEvent({
1995
+ ts: new Date().toISOString(),
1996
+ flowId: ic.flowId,
1997
+ seq: ic.nextSeq(),
1998
+ eventType: "artifact-verification-retry",
1999
+ data: {
2000
+ unitType: preUnitSnapshot?.type,
2001
+ unitId: retryInfo?.unitId,
2002
+ attempt: retryInfo?.attempt,
2003
+ },
2004
+ });
1884
2005
  // Continue the loop — next iteration will inject the retry context into the prompt.
1885
2006
  debugLog("autoLoop", { phase: "artifact-verification-retry", iteration: ic.iteration });
1886
2007
  return { action: "continue" };
@@ -42,16 +42,25 @@ export async function runUnit(
42
42
  let sessionResult: { cancelled: boolean };
43
43
  let sessionTimeoutHandle: ReturnType<typeof setTimeout> | undefined;
44
44
  const mySessionSwitchGeneration = ++sessionSwitchGeneration;
45
+ // #3731: Cancellation controller for newSession(). When the session-creation
46
+ // timeout fires, we abort this controller so that the still-in-flight
47
+ // newSession() discards itself after await this.abort() completes, preventing
48
+ // it from capturing the (now-root) process.cwd() and rebuilding the tool
49
+ // runtime with the wrong cwd.
50
+ const sessionAbortController = new AbortController();
45
51
  _setSessionSwitchInFlight(true);
46
52
  try {
47
- const sessionPromise = s.cmdCtx!.newSession().finally(() => {
53
+ const sessionPromise = s.cmdCtx!.newSession({ abortSignal: sessionAbortController.signal }).finally(() => {
48
54
  if (sessionSwitchGeneration === mySessionSwitchGeneration) {
49
55
  _setSessionSwitchInFlight(false);
50
56
  }
51
57
  });
52
58
  const timeoutPromise = new Promise<{ cancelled: true }>((resolve) => {
53
59
  sessionTimeoutHandle = setTimeout(
54
- () => resolve({ cancelled: true }),
60
+ () => {
61
+ sessionAbortController.abort();
62
+ resolve({ cancelled: true });
63
+ },
55
64
  NEW_SESSION_TIMEOUT_MS,
56
65
  );
57
66
  });
@@ -118,6 +127,35 @@ export async function runUnit(
118
127
  logWarning("engine", "Failed to chdir to basePath before dispatch", { basePath: s.basePath, error: String(e) });
119
128
  }
120
129
 
130
+ // ── Provider request-readiness pre-check (#4555) ──
131
+ // Verify the provider can accept requests before dispatching. If the token
132
+ // has expired since bootstrap, return cancelled immediately so the unit is
133
+ // not wasted on a guaranteed 401.
134
+ {
135
+ const provider = s.currentUnitModel?.provider ?? ctx.model?.provider;
136
+ const registry = (ctx as any).modelRegistry;
137
+
138
+ if (provider && registry != null && typeof registry.isProviderRequestReady === "function") {
139
+ let ready = false;
140
+ try {
141
+ ready = registry.isProviderRequestReady(provider);
142
+ } catch {
143
+ ready = false;
144
+ }
145
+
146
+ if (!ready) {
147
+ return {
148
+ status: "cancelled",
149
+ errorContext: {
150
+ message: `Provider ${provider} is not request-ready (login/token expired)`,
151
+ category: "provider",
152
+ isTransient: false,
153
+ },
154
+ };
155
+ }
156
+ }
157
+ }
158
+
121
159
  // ── Send the prompt ──
122
160
  debugLog("runUnit", { phase: "send-message", unitType, unitId });
123
161
 
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import type { Api, Model } from "@gsd/pi-ai";
20
- import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
20
+ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
21
21
  import type { GitServiceImpl } from "../git-service.js";
22
22
  import type { CaptureEntry } from "../captures.js";
23
23
  import type { BudgetAlertLevel } from "../auto-budget.js";
@@ -40,6 +40,8 @@ export interface StartModel {
40
40
  id: string;
41
41
  }
42
42
 
43
+ export type ThinkingLevelSnapshot = ReturnType<ExtensionAPI["getThinkingLevel"]>;
44
+
43
45
  export interface PendingVerificationRetry {
44
46
  unitId: string;
45
47
  failureContext: string;
@@ -62,6 +64,15 @@ export interface SidecarItem {
62
64
  captureId?: string;
63
65
  }
64
66
 
67
+ export interface PreExecFailure {
68
+ /** Milestone/slice that failed (e.g. "M001/S02"). */
69
+ unitId: string;
70
+ /** Verbatim blocking check strings from the failed gate run. */
71
+ blockingFindings: string[];
72
+ /** Condensed gate verdict excerpt for context (status + rationale). */
73
+ verdictExcerpt: string;
74
+ }
75
+
65
76
  // ─── Constants ───────────────────────────────────────────────────────────────
66
77
 
67
78
  export const MAX_UNIT_DISPATCHES = 3;
@@ -120,6 +131,8 @@ export class AutoSession {
120
131
  currentDispatchedModelId: string | null = null;
121
132
  originalModelId: string | null = null;
122
133
  originalModelProvider: string | null = null;
134
+ autoModeStartThinkingLevel: ThinkingLevelSnapshot | null = null;
135
+ originalThinkingLevel: ThinkingLevelSnapshot | null = null;
123
136
  lastBudgetAlertLevel: BudgetAlertLevel = 0;
124
137
 
125
138
  // ── Recovery ─────────────────────────────────────────────────────────────
@@ -135,6 +148,18 @@ export class AutoSession {
135
148
  // ── Sidecar queue ─────────────────────────────────────────────────────
136
149
  sidecarQueue: SidecarItem[] = [];
137
150
 
151
+ // ── Pre-exec gate failure context (#4551) ───────────────────────────
152
+ /**
153
+ * Persisted when a pre-execution gate fails on a plan-slice or refine-slice
154
+ * unit. The planning → plan-slice dispatch rule reads this field and injects
155
+ * the failure details into the next re-dispatch prompt so the LLM can fix the
156
+ * specific issues instead of producing an identical plan.
157
+ *
158
+ * Cleared after it has been consumed (injected into the prompt) to avoid
159
+ * stale context bleeding into unrelated slices.
160
+ */
161
+ lastPreExecFailure: PreExecFailure | null = null;
162
+
138
163
  // ── Tool invocation errors (#2883) ──────────────────────────────────
139
164
  /** Set when a GSD tool execution ends with isError due to malformed/truncated
140
165
  * JSON arguments. Checked by postUnitPreVerification to break retry loops. */
@@ -195,7 +220,12 @@ export class AutoSession {
195
220
  }
196
221
 
197
222
  get lockBasePath(): string {
198
- return this.originalBasePath || this.basePath;
223
+ // Prefer originalBasePath (project root); fall back to basePath.
224
+ // Strip /.gsd/worktrees/ suffix if basePath is itself a worktree path
225
+ // to avoid reading/writing the lock inside the worktree (#3729).
226
+ const resolved = this.originalBasePath || this.basePath;
227
+ const markerIdx = resolved.indexOf("/.gsd/worktrees/");
228
+ return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
199
229
  }
200
230
 
201
231
  reset(): void {
@@ -241,6 +271,8 @@ export class AutoSession {
241
271
  this.currentDispatchedModelId = null;
242
272
  this.originalModelId = null;
243
273
  this.originalModelProvider = null;
274
+ this.autoModeStartThinkingLevel = null;
275
+ this.originalThinkingLevel = null;
244
276
  this.lastBudgetAlertLevel = 0;
245
277
 
246
278
  // Recovery
@@ -261,6 +293,7 @@ export class AutoSession {
261
293
  this.sidecarQueue = [];
262
294
  this.rewriteAttemptCount = 0;
263
295
  this.consecutiveCompleteBootstraps = 0;
296
+ this.lastPreExecFailure = null;
264
297
  this.lastToolInvocationError = null;
265
298
  this.lastGitActionFailure = null;
266
299
  this.lastGitActionStatus = null;
@@ -568,15 +568,28 @@ export const DISPATCH_RULES: DispatchRule[] = [
568
568
  },
569
569
  {
570
570
  name: "planning → plan-slice",
571
- match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry }) => {
571
+ match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry, session }) => {
572
572
  if (state.phase !== "planning") return null;
573
573
  if (!state.activeSlice) return missingSliceStop(mid, state.phase);
574
574
  const sid = state.activeSlice!.id;
575
575
  const sTitle = state.activeSlice!.title;
576
+ // #4551: Consume any persisted pre-exec failure for this slice so the
577
+ // re-dispatched prompt includes the exact blocked references. Clear the
578
+ // field immediately after reading to prevent stale context leaking into
579
+ // a later, unrelated plan-slice run.
580
+ const unitId = `${mid}/${sid}`;
581
+ let priorPreExecFailure: { blockingFindings: string[]; verdictExcerpt: string } | undefined;
582
+ if (session?.lastPreExecFailure?.unitId === unitId) {
583
+ priorPreExecFailure = {
584
+ blockingFindings: session.lastPreExecFailure.blockingFindings,
585
+ verdictExcerpt: session.lastPreExecFailure.verdictExcerpt,
586
+ };
587
+ session.lastPreExecFailure = null;
588
+ }
576
589
  return {
577
590
  action: "dispatch",
578
591
  unitType: "plan-slice",
579
- unitId: `${mid}/${sid}`,
592
+ unitId,
580
593
  prompt: await buildPlanSlicePrompt(
581
594
  mid,
582
595
  midTitle,
@@ -584,7 +597,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
584
597
  sTitle,
585
598
  basePath,
586
599
  undefined,
587
- { sessionContextWindow, modelRegistry },
600
+ { sessionContextWindow, modelRegistry, priorPreExecFailure },
588
601
  ),
589
602
  };
590
603
  },
@@ -18,6 +18,7 @@ import { getSessionModelOverride } from "./session-model-override.js";
18
18
  import { logWarning } from "./workflow-logger.js";
19
19
  import { resolveUokFlags } from "./uok/flags.js";
20
20
  import { applyModelPolicyFilter } from "./uok/model-policy.js";
21
+ import { isModelBlocked } from "./blocked-models.js";
21
22
 
22
23
  export interface ModelSelectionResult {
23
24
  /** Routing metadata for metrics recording */
@@ -26,13 +27,32 @@ export interface ModelSelectionResult {
26
27
  appliedModel: Model<Api> | null;
27
28
  }
28
29
 
30
+ export interface PreferredModelConfig {
31
+ primary: string;
32
+ fallbacks: string[];
33
+ source: "explicit" | "synthesized";
34
+ }
35
+
36
+ function reapplyThinkingLevel(
37
+ pi: ExtensionAPI,
38
+ level: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined,
39
+ ): void {
40
+ if (!level) return;
41
+ pi.setThinkingLevel(level);
42
+ }
43
+
29
44
  export function resolvePreferredModelConfig(
30
45
  unitType: string,
31
46
  autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null,
32
47
  isAutoMode = true,
33
- ) {
48
+ ): PreferredModelConfig | undefined {
34
49
  const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
35
- if (explicitConfig) return explicitConfig;
50
+ if (explicitConfig) {
51
+ return {
52
+ ...explicitConfig,
53
+ source: "explicit",
54
+ };
55
+ }
36
56
 
37
57
  // In interactive mode, don't synthesize a routing-based model config.
38
58
  // The user's session model (/model) should be used as-is (#3962).
@@ -59,6 +79,7 @@ export function resolvePreferredModelConfig(
59
79
  return {
60
80
  primary: ceilingModel,
61
81
  fallbacks: [],
82
+ source: "synthesized",
62
83
  };
63
84
  }
64
85
 
@@ -84,6 +105,8 @@ export async function selectAndApplyModel(
84
105
  isAutoMode = true,
85
106
  /** Explicit /gsd model pin captured at bootstrap for long-running auto loops. */
86
107
  sessionModelOverride?: { provider: string; id: string } | null,
108
+ /** Thinking level captured at auto-mode start and re-applied after model swaps. */
109
+ autoModeStartThinkingLevel?: ReturnType<ExtensionAPI["getThinkingLevel"]> | null,
87
110
  ): Promise<ModelSelectionResult> {
88
111
  const uokFlags = resolveUokFlags(prefs);
89
112
  const effectiveSessionModelOverride = sessionModelOverride === undefined
@@ -124,6 +147,11 @@ export async function selectAndApplyModel(
124
147
  if (prefs?.token_profile === "burn-max") {
125
148
  routingConfig.enabled = false;
126
149
  }
150
+ if (modelConfig.source === "explicit") {
151
+ // Explicit per-phase model preferences express hard user intent.
152
+ // Dynamic routing may only treat synthesized tier ceilings as downgradeable.
153
+ routingConfig.enabled = false;
154
+ }
127
155
  let effectiveModelConfig = modelConfig;
128
156
  let routingTierLabel = "";
129
157
  let routingEligibleModels = availableModels;
@@ -286,6 +314,7 @@ export async function selectAndApplyModel(
286
314
  effectiveModelConfig = {
287
315
  primary: routingResult.modelId,
288
316
  fallbacks: routingResult.fallbacks,
317
+ source: modelConfig.source,
289
318
  };
290
319
  // Always notify on model downgrade — users should see when their
291
320
  // model selection is overridden, not just in verbose mode (#3962).
@@ -334,6 +363,18 @@ export async function selectAndApplyModel(
334
363
  attemptedPolicyEligible = true;
335
364
  }
336
365
 
366
+ // Skip models the provider has previously rejected for this account
367
+ // (issue #4513). The block is persisted in .gsd/runtime/blocked-models.json
368
+ // so it survives /gsd auto restarts — without this, the same dead model
369
+ // gets reselected after every restart.
370
+ if (isModelBlocked(basePath, model.provider, model.id)) {
371
+ ctx.ui.notify(
372
+ `Skipping blocked model ${model.provider}/${model.id} (provider rejected it for this account).`,
373
+ "warning",
374
+ );
375
+ continue;
376
+ }
377
+
337
378
  // Warn if the ID is ambiguous across providers
338
379
  if (!modelId.includes("/")) {
339
380
  const providers = availableModels.filter(m => m.id === modelId).map(m => m.provider);
@@ -349,11 +390,12 @@ export async function selectAndApplyModel(
349
390
  const ok = await pi.setModel(model, { persist: false });
350
391
  if (ok) {
351
392
  appliedModel = model;
393
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
352
394
 
353
395
  // ADR-005: Adjust active tool set for the selected model's provider capabilities.
354
396
  // Hard-filter incompatible tools, then let extensions override via adjust_tool_set hook.
355
397
  const activeToolNames = pi.getActiveTools();
356
- const { toolNames: compatibleTools, removedTools } = adjustToolSet(activeToolNames, model.api);
398
+ const { toolNames: compatibleTools, removedTools } = adjustToolSet(activeToolNames, model.api, model.provider);
357
399
  let finalToolNames = compatibleTools;
358
400
 
359
401
  // Fire adjust_tool_set hook — extensions can override the filtered tool set
@@ -407,19 +449,33 @@ export async function selectAndApplyModel(
407
449
  // No model preference for this unit type — re-apply the model captured
408
450
  // at auto-mode start to prevent bleed from shared global settings.json (#650).
409
451
  const availableModels = ctx.modelRegistry.getAvailable();
410
- const startModel = availableModels.find(
411
- m => m.provider === autoModeStartModel.provider && m.id === autoModeStartModel.id,
412
- );
413
- if (startModel) {
414
- const ok = await pi.setModel(startModel, { persist: false });
415
- if (!ok) {
416
- const byId = availableModels.find(m => m.id === autoModeStartModel.id);
417
- if (byId) {
418
- const fallbackOk = await pi.setModel(byId, { persist: false });
419
- if (fallbackOk) appliedModel = byId;
452
+ const startBlocked = isModelBlocked(basePath, autoModeStartModel.provider, autoModeStartModel.id);
453
+ if (startBlocked) {
454
+ ctx.ui.notify(
455
+ `Auto-mode start model ${autoModeStartModel.provider}/${autoModeStartModel.id} is blocked for this account. Using current session model instead.`,
456
+ "warning",
457
+ );
458
+ } else {
459
+ const startModel = availableModels.find(
460
+ m => m.provider === autoModeStartModel.provider && m.id === autoModeStartModel.id,
461
+ );
462
+ if (startModel) {
463
+ const ok = await pi.setModel(startModel, { persist: false });
464
+ if (!ok) {
465
+ const byId = availableModels.find(
466
+ m => m.id === autoModeStartModel.id && !isModelBlocked(basePath, m.provider, m.id),
467
+ );
468
+ if (byId) {
469
+ const fallbackOk = await pi.setModel(byId, { persist: false });
470
+ if (fallbackOk) {
471
+ appliedModel = byId;
472
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
473
+ }
474
+ }
475
+ } else {
476
+ appliedModel = startModel;
477
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
420
478
  }
421
- } else {
422
- appliedModel = startModel;
423
479
  }
424
480
  }
425
481
  }