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
@@ -30,7 +30,8 @@ import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
30
30
  import { ensurePlanV2Graph } from "../uok/plan-v2.js";
31
31
  import { resolveUokFlags } from "../uok/flags.js";
32
32
  import { UokGateRunner } from "../uok/gate-runner.js";
33
- import { resetEvidence } from "../safety/evidence-collector.js";
33
+ import { resetEvidence, loadEvidenceFromDisk } from "../safety/evidence-collector.js";
34
+ import { parseUnitId } from "../unit-id.js";
34
35
  import { createCheckpoint, cleanupCheckpoint, rollbackToCheckpoint } from "../safety/git-checkpoint.js";
35
36
  import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
36
37
  import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, supportsStructuredQuestions, } from "../workflow-mcp.js";
@@ -47,7 +48,11 @@ export function resetSessionTimeoutState() {
47
48
  * Exported for testing as _resolveReportBasePath.
48
49
  */
49
50
  export function _resolveReportBasePath(s) {
50
- return s.originalBasePath || s.basePath;
51
+ // Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
52
+ // originalBasePath is falsy — prevents reports landing in the worktree (#3729).
53
+ const resolved = s.originalBasePath || s.basePath;
54
+ const markerIdx = resolved.indexOf("/.gsd/worktrees/");
55
+ return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
51
56
  }
52
57
  /**
53
58
  * Resolve the authoritative project base for dispatch guards.
@@ -55,7 +60,12 @@ export function _resolveReportBasePath(s) {
55
60
  * unit is running inside an auto worktree.
56
61
  */
57
62
  export function _resolveDispatchGuardBasePath(s) {
58
- return s.originalBasePath || s.basePath;
63
+ // Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
64
+ // originalBasePath is falsy — prevents guard checks running against the
65
+ // worktree instead of the project root (#3729).
66
+ const resolved = s.originalBasePath || s.basePath;
67
+ const markerIdx = resolved.indexOf("/.gsd/worktrees/");
68
+ return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
59
69
  }
60
70
  const PLAN_V2_GATE_PHASES = new Set([
61
71
  "executing",
@@ -295,7 +305,7 @@ export async function runPreDispatch(ic, loopState) {
295
305
  findings: reason,
296
306
  milestoneId: state.activeMilestone?.id ?? undefined,
297
307
  });
298
- ctx.ui.notify(`Plan gate failed-closed: ${reason}`, "error");
308
+ ctx.ui.notify(`Plan gate failed-closed: ${reason}\n\nIf this keeps happening, try: /gsd doctor heal`, "error");
299
309
  await deps.pauseAuto(ctx, pi);
300
310
  return { action: "break", reason: "plan-v2-gate-failed" };
301
311
  }
@@ -389,6 +399,8 @@ export async function runPreDispatch(ic, loopState) {
389
399
  loopState.recentUnits.length = 0;
390
400
  loopState.stuckRecoveryAttempts = 0;
391
401
  // Worktree lifecycle on milestone transition — merge current, enter next
402
+ // #2909: preflight — warn + stash dirty working tree before merge
403
+ const preflightTransition = deps.preflightCleanRoot(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
392
404
  try {
393
405
  deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
394
406
  }
@@ -405,6 +417,10 @@ export async function runPreDispatch(ic, loopState) {
405
417
  await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
406
418
  return { action: "break", reason: "merge-failed" };
407
419
  }
420
+ // #2909: postflight — restore stashed changes after successful merge
421
+ if (preflightTransition.stashPushed) {
422
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
423
+ }
408
424
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
409
425
  deps.invalidateAllCaches();
410
426
  state = await deps.deriveState(s.basePath);
@@ -459,6 +475,8 @@ export async function runPreDispatch(ic, loopState) {
459
475
  if (incomplete.length === 0 && state.registry.length > 0) {
460
476
  // All milestones complete — merge milestone branch before stopping
461
477
  if (s.currentMilestoneId) {
478
+ // #2909: preflight — warn + stash dirty working tree before merge
479
+ const preflightAllComplete = deps.preflightCleanRoot(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
462
480
  try {
463
481
  deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
464
482
  // Prevent stopAuto from attempting the same merge (#2645)
@@ -475,6 +493,10 @@ export async function runPreDispatch(ic, loopState) {
475
493
  await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
476
494
  return { action: "break", reason: "merge-failed" };
477
495
  }
496
+ // #2909: postflight — restore stashed changes after successful merge
497
+ if (preflightAllComplete.stashPushed) {
498
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
499
+ }
478
500
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
479
501
  }
480
502
  deps.sendDesktopNotification("GSD", "All milestones complete!", "success", "milestone", basename(s.originalBasePath || s.basePath));
@@ -539,6 +561,8 @@ export async function runPreDispatch(ic, loopState) {
539
561
  if (state.phase === "complete") {
540
562
  // Milestone merge on complete (before closeout so branch state is clean)
541
563
  if (s.currentMilestoneId) {
564
+ // #2909: preflight — warn + stash dirty working tree before merge
565
+ const preflightComplete = deps.preflightCleanRoot(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
542
566
  try {
543
567
  deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
544
568
  // Prevent stopAuto from attempting the same merge (#2645)
@@ -555,6 +579,10 @@ export async function runPreDispatch(ic, loopState) {
555
579
  await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
556
580
  return { action: "break", reason: "merge-failed" };
557
581
  }
582
+ // #2909: postflight — restore stashed changes after successful merge
583
+ if (preflightComplete.stashPushed) {
584
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
585
+ }
558
586
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
559
587
  }
560
588
  deps.sendDesktopNotification("GSD", `Milestone ${mid} complete!`, "success", "milestone", basename(s.originalBasePath || s.basePath));
@@ -823,16 +851,44 @@ export async function runGuards(ic, mid) {
823
851
  if (threshold) {
824
852
  s.lastBudgetAlertLevel =
825
853
  newBudgetAlertLevel;
826
- if (threshold.pct === 100 && budgetEnforcementAction !== "none") {
854
+ // Emit Layer 2 budget_threshold event (post-plan hook recommendation).
855
+ // Extensions / Layer 0 shell hooks may return an action override.
856
+ let hookAction;
857
+ try {
858
+ const { emitBudgetThreshold } = await import("../hook-emitter.js");
859
+ const hookResult = await emitBudgetThreshold({
860
+ fraction: budgetPct,
861
+ spent: totalCost,
862
+ limit: budgetCeiling,
863
+ });
864
+ if (hookResult?.action)
865
+ hookAction = hookResult.action;
866
+ }
867
+ catch (hookErr) {
868
+ logWarning("engine", `budget_threshold hook emission failed: ${hookErr.message}`);
869
+ }
870
+ // Apply hook override to enforcement action. "continue" → "none" (no enforcement),
871
+ // "pause" and "downgrade" map to the matching enforcement path below.
872
+ let effectiveAction = budgetEnforcementAction;
873
+ if (hookAction === "continue") {
874
+ effectiveAction = "none";
875
+ }
876
+ else if (hookAction === "pause") {
877
+ effectiveAction = "pause";
878
+ }
879
+ else if (hookAction === "downgrade") {
880
+ effectiveAction = "warn";
881
+ }
882
+ if (threshold.pct === 100 && effectiveAction !== "none") {
827
883
  // 100% — special enforcement logic (halt/pause/warn)
828
884
  const msg = `Budget ceiling ${deps.formatCost(budgetCeiling)} reached (spent ${deps.formatCost(totalCost)}).`;
829
- if (budgetEnforcementAction === "halt") {
885
+ if (effectiveAction === "halt") {
830
886
  deps.sendDesktopNotification("GSD", msg, "error", "budget", basename(s.originalBasePath || s.basePath));
831
887
  await deps.stopAuto(ctx, pi, "Budget ceiling reached");
832
888
  debugLog("autoLoop", { phase: "exit", reason: "budget-halt" });
833
889
  return { action: "break", reason: "budget-halt" };
834
890
  }
835
- if (budgetEnforcementAction === "pause") {
891
+ if (effectiveAction === "pause") {
836
892
  ctx.ui.notify(`${msg} Pausing auto-mode — /gsd auto to override and continue.`, "warning");
837
893
  deps.sendDesktopNotification("GSD", msg, "warning", "budget", basename(s.originalBasePath || s.basePath));
838
894
  deps.logCmuxEvent(prefs, msg, "warning");
@@ -1000,6 +1056,14 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1000
1056
  const safetyConfig = resolveSafetyHarnessConfig(prefs?.safety_harness);
1001
1057
  if (safetyConfig.enabled && safetyConfig.evidence_collection) {
1002
1058
  resetEvidence();
1059
+ // Restore persisted evidence so session-restart resumes don't produce
1060
+ // false-positive "no bash calls" warnings (Bug #4385).
1061
+ if (s.basePath && unitType === "execute-task") {
1062
+ const { milestone: eMid, slice: eSid, task: eTid } = parseUnitId(unitId);
1063
+ if (eMid && eSid && eTid) {
1064
+ loadEvidenceFromDisk(s.basePath, eMid, eSid, eTid);
1065
+ }
1066
+ }
1003
1067
  }
1004
1068
  // Only checkpoint code-executing units (not lifecycle/planning units)
1005
1069
  if (safetyConfig.enabled && safetyConfig.checkpoints && unitType === "execute-task") {
@@ -1066,7 +1130,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1066
1130
  logWarning("engine", "Prompt reorder failed", { error: msg });
1067
1131
  }
1068
1132
  // Select and apply model (with tier escalation on retry — normal units only)
1069
- const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier }, undefined, s.manualSessionModelOverride);
1133
+ const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier }, undefined, s.manualSessionModelOverride, s.autoModeStartThinkingLevel);
1070
1134
  s.currentUnitRouting =
1071
1135
  modelResult.routing;
1072
1136
  s.currentUnitModel =
@@ -1079,6 +1143,9 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1079
1143
  if (match) {
1080
1144
  const ok = await pi.setModel(match, { persist: false });
1081
1145
  if (ok) {
1146
+ if (s.autoModeStartThinkingLevel) {
1147
+ pi.setThinkingLevel(s.autoModeStartThinkingLevel);
1148
+ }
1082
1149
  s.currentUnitModel = match;
1083
1150
  ctx.ui.notify(`Hook model override: ${match.provider}/${match.id}`, "info");
1084
1151
  }
@@ -1173,20 +1240,23 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1173
1240
  }
1174
1241
  }
1175
1242
  if (unitResult.status === "cancelled") {
1243
+ const errorCategory = unitResult.errorContext?.category;
1176
1244
  // Provider-error pause: pauseAuto already handled cleanup and scheduled
1177
1245
  // recovery. Don't hard-stop — just break out of the loop (#2762).
1178
- if (unitResult.errorContext?.category === "provider") {
1246
+ if (errorCategory === "provider") {
1179
1247
  await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1180
- debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext.isTransient });
1248
+ debugLog("autoLoop", { phase: "exit", reason: "provider-pause", isTransient: unitResult.errorContext?.isTransient });
1181
1249
  return { action: "break", reason: "provider-pause" };
1182
1250
  }
1183
1251
  // Timeout category covers two distinct scenarios:
1184
1252
  // 1. Session creation timeout (120s) — transient, auto-resume with backoff
1185
1253
  // 2. Unit hard timeout (30min+) — stuck agent, pause for manual review
1254
+ // Transient session-failed covers recoverable newSession failures and should
1255
+ // pause instead of hard-stopping.
1186
1256
  // Structural errors (TypeError, is not a function) are NOT transient
1187
1257
  // and must hard-stop to avoid infinite retry loops.
1188
1258
  if (unitResult.errorContext?.isTransient &&
1189
- unitResult.errorContext?.category === "timeout") {
1259
+ errorCategory === "timeout") {
1190
1260
  const isSessionCreationTimeout = unitResult.errorContext.message?.includes("Session creation timed out");
1191
1261
  if (isSessionCreationTimeout) {
1192
1262
  consecutiveSessionTimeouts += 1;
@@ -1229,6 +1299,15 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1229
1299
  await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1230
1300
  return { action: "break", reason: "unit-hard-timeout" };
1231
1301
  }
1302
+ if (unitResult.errorContext?.isTransient &&
1303
+ errorCategory === "session-failed") {
1304
+ ctx.ui.notify(`Session creation failed transiently for ${unitType} ${unitId}: ${unitResult.errorContext?.message ?? "unknown"}. Pausing auto-mode (recoverable).`, "warning");
1305
+ debugLog("autoLoop", { phase: "session-start-transient-pause", unitType, unitId, category: errorCategory });
1306
+ await deps.pauseAuto(ctx, pi);
1307
+ await deps.autoCommitUnit?.(s.basePath, unitType, unitId, ctx);
1308
+ await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, unitResult.errorContext);
1309
+ return { action: "break", reason: "session-timeout" };
1310
+ }
1232
1311
  // All other cancelled states (structural errors, non-transient failures): hard stop
1233
1312
  if (s.currentUnit) {
1234
1313
  await deps.closeoutUnit(ctx, s.basePath, unitType, unitId, s.currentUnit.startedAt, deps.buildSnapshotOpts(unitType, unitId));
@@ -1387,6 +1466,20 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
1387
1466
  }
1388
1467
  else {
1389
1468
  // s.pendingVerificationRetry was set by postUnitPreVerification.
1469
+ // Emit a dedicated journal event so forensics can distinguish bounded
1470
+ // verification retries from genuine stuck-loop dispatch repetitions (#4540).
1471
+ const retryInfo = s.pendingVerificationRetry;
1472
+ deps.emitJournalEvent({
1473
+ ts: new Date().toISOString(),
1474
+ flowId: ic.flowId,
1475
+ seq: ic.nextSeq(),
1476
+ eventType: "artifact-verification-retry",
1477
+ data: {
1478
+ unitType: preUnitSnapshot?.type,
1479
+ unitId: retryInfo?.unitId,
1480
+ attempt: retryInfo?.attempt,
1481
+ },
1482
+ });
1390
1483
  // Continue the loop — next iteration will inject the retry context into the prompt.
1391
1484
  debugLog("autoLoop", { phase: "artifact-verification-retry", iteration: ic.iteration });
1392
1485
  return { action: "continue" };
@@ -26,15 +26,24 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
26
26
  let sessionResult;
27
27
  let sessionTimeoutHandle;
28
28
  const mySessionSwitchGeneration = ++sessionSwitchGeneration;
29
+ // #3731: Cancellation controller for newSession(). When the session-creation
30
+ // timeout fires, we abort this controller so that the still-in-flight
31
+ // newSession() discards itself after await this.abort() completes, preventing
32
+ // it from capturing the (now-root) process.cwd() and rebuilding the tool
33
+ // runtime with the wrong cwd.
34
+ const sessionAbortController = new AbortController();
29
35
  _setSessionSwitchInFlight(true);
30
36
  try {
31
- const sessionPromise = s.cmdCtx.newSession().finally(() => {
37
+ const sessionPromise = s.cmdCtx.newSession({ abortSignal: sessionAbortController.signal }).finally(() => {
32
38
  if (sessionSwitchGeneration === mySessionSwitchGeneration) {
33
39
  _setSessionSwitchInFlight(false);
34
40
  }
35
41
  });
36
42
  const timeoutPromise = new Promise((resolve) => {
37
- sessionTimeoutHandle = setTimeout(() => resolve({ cancelled: true }), NEW_SESSION_TIMEOUT_MS);
43
+ sessionTimeoutHandle = setTimeout(() => {
44
+ sessionAbortController.abort();
45
+ resolve({ cancelled: true });
46
+ }, NEW_SESSION_TIMEOUT_MS);
38
47
  });
39
48
  sessionResult = await Promise.race([sessionPromise, timeoutPromise]);
40
49
  }
@@ -92,6 +101,33 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
92
101
  catch (e) {
93
102
  logWarning("engine", "Failed to chdir to basePath before dispatch", { basePath: s.basePath, error: String(e) });
94
103
  }
104
+ // ── Provider request-readiness pre-check (#4555) ──
105
+ // Verify the provider can accept requests before dispatching. If the token
106
+ // has expired since bootstrap, return cancelled immediately so the unit is
107
+ // not wasted on a guaranteed 401.
108
+ {
109
+ const provider = s.currentUnitModel?.provider ?? ctx.model?.provider;
110
+ const registry = ctx.modelRegistry;
111
+ if (provider && registry != null && typeof registry.isProviderRequestReady === "function") {
112
+ let ready = false;
113
+ try {
114
+ ready = registry.isProviderRequestReady(provider);
115
+ }
116
+ catch {
117
+ ready = false;
118
+ }
119
+ if (!ready) {
120
+ return {
121
+ status: "cancelled",
122
+ errorContext: {
123
+ message: `Provider ${provider} is not request-ready (login/token expired)`,
124
+ category: "provider",
125
+ isTransient: false,
126
+ },
127
+ };
128
+ }
129
+ }
130
+ }
95
131
  // ── Send the prompt ──
96
132
  debugLog("runUnit", { phase: "send-message", unitType, unitId });
97
133
  pi.sendMessage({ customType: "gsd-auto", content: prompt, display: s.verbose }, { triggerTurn: true });
@@ -65,6 +65,8 @@ export class AutoSession {
65
65
  currentDispatchedModelId = null;
66
66
  originalModelId = null;
67
67
  originalModelProvider = null;
68
+ autoModeStartThinkingLevel = null;
69
+ originalThinkingLevel = null;
68
70
  lastBudgetAlertLevel = 0;
69
71
  // ── Recovery ─────────────────────────────────────────────────────────────
70
72
  pendingCrashRecovery = null;
@@ -77,6 +79,17 @@ export class AutoSession {
77
79
  lastStateRebuildAt = 0;
78
80
  // ── Sidecar queue ─────────────────────────────────────────────────────
79
81
  sidecarQueue = [];
82
+ // ── Pre-exec gate failure context (#4551) ───────────────────────────
83
+ /**
84
+ * Persisted when a pre-execution gate fails on a plan-slice or refine-slice
85
+ * unit. The planning → plan-slice dispatch rule reads this field and injects
86
+ * the failure details into the next re-dispatch prompt so the LLM can fix the
87
+ * specific issues instead of producing an identical plan.
88
+ *
89
+ * Cleared after it has been consumed (injected into the prompt) to avoid
90
+ * stale context bleeding into unrelated slices.
91
+ */
92
+ lastPreExecFailure = null;
80
93
  // ── Tool invocation errors (#2883) ──────────────────────────────────
81
94
  /** Set when a GSD tool execution ends with isError due to malformed/truncated
82
95
  * JSON arguments. Checked by postUnitPreVerification to break retry loops. */
@@ -137,7 +150,12 @@ export class AutoSession {
137
150
  this.unitLifetimeDispatches.clear();
138
151
  }
139
152
  get lockBasePath() {
140
- return this.originalBasePath || this.basePath;
153
+ // Prefer originalBasePath (project root); fall back to basePath.
154
+ // Strip /.gsd/worktrees/ suffix if basePath is itself a worktree path
155
+ // to avoid reading/writing the lock inside the worktree (#3729).
156
+ const resolved = this.originalBasePath || this.basePath;
157
+ const markerIdx = resolved.indexOf("/.gsd/worktrees/");
158
+ return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
141
159
  }
142
160
  reset() {
143
161
  this.clearTimers();
@@ -177,6 +195,8 @@ export class AutoSession {
177
195
  this.currentDispatchedModelId = null;
178
196
  this.originalModelId = null;
179
197
  this.originalModelProvider = null;
198
+ this.autoModeStartThinkingLevel = null;
199
+ this.originalThinkingLevel = null;
180
200
  this.lastBudgetAlertLevel = 0;
181
201
  // Recovery
182
202
  this.pendingCrashRecovery = null;
@@ -195,6 +215,7 @@ export class AutoSession {
195
215
  this.sidecarQueue = [];
196
216
  this.rewriteAttemptCount = 0;
197
217
  this.consecutiveCompleteBootstraps = 0;
218
+ this.lastPreExecFailure = null;
198
219
  this.lastToolInvocationError = null;
199
220
  this.lastGitActionFailure = null;
200
221
  this.lastGitActionStatus = null;
@@ -451,18 +451,31 @@ export const DISPATCH_RULES = [
451
451
  },
452
452
  {
453
453
  name: "planning → plan-slice",
454
- match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry }) => {
454
+ match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry, session }) => {
455
455
  if (state.phase !== "planning")
456
456
  return null;
457
457
  if (!state.activeSlice)
458
458
  return missingSliceStop(mid, state.phase);
459
459
  const sid = state.activeSlice.id;
460
460
  const sTitle = state.activeSlice.title;
461
+ // #4551: Consume any persisted pre-exec failure for this slice so the
462
+ // re-dispatched prompt includes the exact blocked references. Clear the
463
+ // field immediately after reading to prevent stale context leaking into
464
+ // a later, unrelated plan-slice run.
465
+ const unitId = `${mid}/${sid}`;
466
+ let priorPreExecFailure;
467
+ if (session?.lastPreExecFailure?.unitId === unitId) {
468
+ priorPreExecFailure = {
469
+ blockingFindings: session.lastPreExecFailure.blockingFindings,
470
+ verdictExcerpt: session.lastPreExecFailure.verdictExcerpt,
471
+ };
472
+ session.lastPreExecFailure = null;
473
+ }
461
474
  return {
462
475
  action: "dispatch",
463
476
  unitType: "plan-slice",
464
- unitId: `${mid}/${sid}`,
465
- prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, { sessionContextWindow, modelRegistry }),
477
+ unitId,
478
+ prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, { sessionContextWindow, modelRegistry, priorPreExecFailure }),
466
479
  };
467
480
  },
468
481
  },
@@ -12,10 +12,20 @@ import { getSessionModelOverride } from "./session-model-override.js";
12
12
  import { logWarning } from "./workflow-logger.js";
13
13
  import { resolveUokFlags } from "./uok/flags.js";
14
14
  import { applyModelPolicyFilter } from "./uok/model-policy.js";
15
+ import { isModelBlocked } from "./blocked-models.js";
16
+ function reapplyThinkingLevel(pi, level) {
17
+ if (!level)
18
+ return;
19
+ pi.setThinkingLevel(level);
20
+ }
15
21
  export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode = true) {
16
22
  const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
17
- if (explicitConfig)
18
- return explicitConfig;
23
+ if (explicitConfig) {
24
+ return {
25
+ ...explicitConfig,
26
+ source: "explicit",
27
+ };
28
+ }
19
29
  // In interactive mode, don't synthesize a routing-based model config.
20
30
  // The user's session model (/model) should be used as-is (#3962).
21
31
  if (!isAutoMode)
@@ -38,6 +48,7 @@ export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAuto
38
48
  return {
39
49
  primary: ceilingModel,
40
50
  fallbacks: [],
51
+ source: "synthesized",
41
52
  };
42
53
  }
43
54
  /**
@@ -52,7 +63,9 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
52
63
  * Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
53
64
  isAutoMode = true,
54
65
  /** Explicit /gsd model pin captured at bootstrap for long-running auto loops. */
55
- sessionModelOverride) {
66
+ sessionModelOverride,
67
+ /** Thinking level captured at auto-mode start and re-applied after model swaps. */
68
+ autoModeStartThinkingLevel) {
56
69
  const uokFlags = resolveUokFlags(prefs);
57
70
  const effectiveSessionModelOverride = sessionModelOverride === undefined
58
71
  ? getSessionModelOverride(ctx.sessionManager.getSessionId())
@@ -90,6 +103,11 @@ sessionModelOverride) {
90
103
  if (prefs?.token_profile === "burn-max") {
91
104
  routingConfig.enabled = false;
92
105
  }
106
+ if (modelConfig.source === "explicit") {
107
+ // Explicit per-phase model preferences express hard user intent.
108
+ // Dynamic routing may only treat synthesized tier ceilings as downgradeable.
109
+ routingConfig.enabled = false;
110
+ }
93
111
  let effectiveModelConfig = modelConfig;
94
112
  let routingTierLabel = "";
95
113
  let routingEligibleModels = availableModels;
@@ -210,6 +228,7 @@ sessionModelOverride) {
210
228
  effectiveModelConfig = {
211
229
  primary: routingResult.modelId,
212
230
  fallbacks: routingResult.fallbacks,
231
+ source: modelConfig.source,
213
232
  };
214
233
  // Always notify on model downgrade — users should see when their
215
234
  // model selection is overridden, not just in verbose mode (#3962).
@@ -249,6 +268,14 @@ sessionModelOverride) {
249
268
  }
250
269
  attemptedPolicyEligible = true;
251
270
  }
271
+ // Skip models the provider has previously rejected for this account
272
+ // (issue #4513). The block is persisted in .gsd/runtime/blocked-models.json
273
+ // so it survives /gsd auto restarts — without this, the same dead model
274
+ // gets reselected after every restart.
275
+ if (isModelBlocked(basePath, model.provider, model.id)) {
276
+ ctx.ui.notify(`Skipping blocked model ${model.provider}/${model.id} (provider rejected it for this account).`, "warning");
277
+ continue;
278
+ }
252
279
  // Warn if the ID is ambiguous across providers
253
280
  if (!modelId.includes("/")) {
254
281
  const providers = availableModels.filter(m => m.id === modelId).map(m => m.provider);
@@ -260,10 +287,11 @@ sessionModelOverride) {
260
287
  const ok = await pi.setModel(model, { persist: false });
261
288
  if (ok) {
262
289
  appliedModel = model;
290
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
263
291
  // ADR-005: Adjust active tool set for the selected model's provider capabilities.
264
292
  // Hard-filter incompatible tools, then let extensions override via adjust_tool_set hook.
265
293
  const activeToolNames = pi.getActiveTools();
266
- const { toolNames: compatibleTools, removedTools } = adjustToolSet(activeToolNames, model.api);
294
+ const { toolNames: compatibleTools, removedTools } = adjustToolSet(activeToolNames, model.api, model.provider);
267
295
  let finalToolNames = compatibleTools;
268
296
  // Fire adjust_tool_set hook — extensions can override the filtered tool set
269
297
  if (routingConfig.hooks !== false) {
@@ -314,19 +342,28 @@ sessionModelOverride) {
314
342
  // No model preference for this unit type — re-apply the model captured
315
343
  // at auto-mode start to prevent bleed from shared global settings.json (#650).
316
344
  const availableModels = ctx.modelRegistry.getAvailable();
317
- const startModel = availableModels.find(m => m.provider === autoModeStartModel.provider && m.id === autoModeStartModel.id);
318
- if (startModel) {
319
- const ok = await pi.setModel(startModel, { persist: false });
320
- if (!ok) {
321
- const byId = availableModels.find(m => m.id === autoModeStartModel.id);
322
- if (byId) {
323
- const fallbackOk = await pi.setModel(byId, { persist: false });
324
- if (fallbackOk)
325
- appliedModel = byId;
345
+ const startBlocked = isModelBlocked(basePath, autoModeStartModel.provider, autoModeStartModel.id);
346
+ if (startBlocked) {
347
+ ctx.ui.notify(`Auto-mode start model ${autoModeStartModel.provider}/${autoModeStartModel.id} is blocked for this account. Using current session model instead.`, "warning");
348
+ }
349
+ else {
350
+ const startModel = availableModels.find(m => m.provider === autoModeStartModel.provider && m.id === autoModeStartModel.id);
351
+ if (startModel) {
352
+ const ok = await pi.setModel(startModel, { persist: false });
353
+ if (!ok) {
354
+ const byId = availableModels.find(m => m.id === autoModeStartModel.id && !isModelBlocked(basePath, m.provider, m.id));
355
+ if (byId) {
356
+ const fallbackOk = await pi.setModel(byId, { persist: false });
357
+ if (fallbackOk) {
358
+ appliedModel = byId;
359
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
360
+ }
361
+ }
362
+ }
363
+ else {
364
+ appliedModel = startModel;
365
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
326
366
  }
327
- }
328
- else {
329
- appliedModel = startModel;
330
367
  }
331
368
  }
332
369
  }
@@ -30,7 +30,7 @@ import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookSta
30
30
  import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures } from "./captures.js";
31
31
  import { debugLog } from "./debug-logger.js";
32
32
  import { runSafely } from "./auto-utils.js";
33
- import { getEvidence } from "./safety/evidence-collector.js";
33
+ import { getEvidence, clearEvidenceFromDisk } from "./safety/evidence-collector.js";
34
34
  import { validateFileChanges } from "./safety/file-change-validator.js";
35
35
  // crossReferenceEvidence available for future use when verification_evidence is stored in DB
36
36
  // import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
@@ -535,7 +535,7 @@ export async function postUnitPreVerification(pctx, opts) {
535
535
  if (taskRow) {
536
536
  const expectedOutput = taskRow.expected_output ?? [];
537
537
  const plannedFiles = taskRow.files ?? [];
538
- const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles);
538
+ const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles, safetyConfig.file_change_allowlist);
539
539
  if (audit && audit.violations.length > 0) {
540
540
  const warnings = audit.violations.filter(v => v.severity === "warning");
541
541
  for (const v of warnings) {
@@ -588,6 +588,16 @@ export async function postUnitPreVerification(pctx, opts) {
588
588
  debugLog("postUnit", { phase: "safety-content-validation", error: String(e) });
589
589
  }
590
590
  }
591
+ // Clear persisted evidence file now that post-unit processing is complete
592
+ // (Bug #4385 — prevents stale evidence from affecting retries of same unit ID).
593
+ if (safetyConfig.evidence_collection && s.currentUnit.type === "execute-task" && sMid && sSid && sTid) {
594
+ try {
595
+ clearEvidenceFromDisk(s.basePath, sMid, sSid, sTid);
596
+ }
597
+ catch (e) {
598
+ debugLog("postUnit", { phase: "safety-evidence-clear", error: String(e) });
599
+ }
600
+ }
591
601
  }
592
602
  }
593
603
  catch (e) {
@@ -950,12 +960,25 @@ export async function postUnitPostVerification(pctx) {
950
960
  const suffix = blockingChecks.length > 3 ? `\n \u2022 ...and ${blockingChecks.length - 3} more` : "";
951
961
  const evidenceNote = `\nSee ${sid}-PRE-EXEC-VERIFY.json for full details.`;
952
962
  ctx.ui.notify(`Pre-execution checks failed: ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} found\n${details}${suffix}${evidenceNote}`, "error");
963
+ // Persist failure context so the next plan-slice re-dispatch can inject
964
+ // it into the prompt and break the infinite loop (#4551).
965
+ s.lastPreExecFailure = {
966
+ unitId: currentUnit.id,
967
+ blockingFindings: blockingChecks.map(c => `[${c.category}] ${c.target}: ${c.message}`),
968
+ verdictExcerpt: `status=${result.status}; ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} detected`,
969
+ };
953
970
  preExecPauseNeeded = true;
954
971
  }
955
972
  else if (result.status === "warn") {
956
973
  ctx.ui.notify(`Pre-execution checks passed with warnings`, "warning");
957
974
  // Strict mode: treat warnings as blocking
958
975
  if (prefs?.enhanced_verification_strict === true) {
976
+ const warnChecks = result.checks.filter(c => !c.passed);
977
+ s.lastPreExecFailure = {
978
+ unitId: currentUnit.id,
979
+ blockingFindings: warnChecks.map(c => `[${c.category}] ${c.target}: ${c.message}`),
980
+ verdictExcerpt: `status=${result.status} (strict mode); ${warnChecks.length} warning${warnChecks.length === 1 ? "" : "s"} treated as blocking`,
981
+ };
959
982
  preExecPauseNeeded = true;
960
983
  }
961
984
  }
@@ -1223,6 +1223,20 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
1223
1223
  prependBlocks.push(`## Prior Sketch Scope (soft hint — non-binding)\n\n${options.softScopeHint.trim()}\n\n` +
1224
1224
  `This scope was captured during an earlier progressive-planning pass that was later disabled. Treat it as context only — you may plan beyond it if the work genuinely requires more scope. Do NOT treat this as a hard boundary.`);
1225
1225
  }
1226
+ // #4551: inject pre-exec failure context so the re-dispatched plan-slice
1227
+ // addresses the exact blocked references rather than reproducing the same plan.
1228
+ if (options?.priorPreExecFailure) {
1229
+ const { blockingFindings, verdictExcerpt } = options.priorPreExecFailure;
1230
+ const findingsList = blockingFindings.length > 0
1231
+ ? blockingFindings.map(f => `- ${f}`).join("\n")
1232
+ : "- (no specific findings recorded)";
1233
+ prependBlocks.push(`## Fix these specific issues from the prior pre-exec check\n\n` +
1234
+ `The previous plan-slice attempt was blocked by pre-execution validation.\n` +
1235
+ `Gate verdict: ${verdictExcerpt}\n\n` +
1236
+ `Blocked references that must be resolved in this plan:\n${findingsList}\n\n` +
1237
+ `Revise the plan so that every reference listed above is satisfied before execution begins. ` +
1238
+ `Do not reproduce the same file paths, package names, or task ordering that caused these failures.`);
1239
+ }
1226
1240
  return renderSlicePrompt({
1227
1241
  mid, sid, sTitle, base,
1228
1242
  level: level ?? resolveInlineLevel(),