@opengsd/gsd-pi 1.5.0 → 1.6.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 (499) hide show
  1. package/README.md +10 -10
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/ask-user-questions.js +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
  5. package/dist/resources/extensions/gsd/activity-log.js +1 -1
  6. package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
  7. package/dist/resources/extensions/gsd/auto/orchestrator.js +20 -13
  8. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  9. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  10. package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -5
  11. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +69 -17
  13. package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
  14. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  15. package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
  16. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  17. package/dist/resources/extensions/gsd/auto-start.js +57 -33
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
  19. package/dist/resources/extensions/gsd/auto-timers.js +9 -2
  20. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  36. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  37. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  38. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  39. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  40. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  41. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  42. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  43. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  44. package/dist/resources/extensions/gsd/auto.js +106 -34
  45. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  46. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
  47. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  48. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
  49. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  50. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  51. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  52. package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
  53. package/dist/resources/extensions/gsd/config-overlay.js +7 -2
  54. package/dist/resources/extensions/gsd/db/engine.js +33 -0
  55. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  56. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  57. package/dist/resources/extensions/gsd/db-adapter.js +30 -3
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  59. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  60. package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
  61. package/dist/resources/extensions/gsd/forensics.js +1 -1
  62. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  63. package/dist/resources/extensions/gsd/git-service.js +45 -0
  64. package/dist/resources/extensions/gsd/gsd-db.js +23 -11
  65. package/dist/resources/extensions/gsd/guided-flow.js +22 -17
  66. package/dist/resources/extensions/gsd/journal.js +1 -1
  67. package/dist/resources/extensions/gsd/markdown-renderer.js +34 -79
  68. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  69. package/dist/resources/extensions/gsd/metrics.js +2 -2
  70. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  71. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  72. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  73. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  74. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  75. package/dist/resources/extensions/gsd/paths.js +87 -52
  76. package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
  77. package/dist/resources/extensions/gsd/preferences-models.js +66 -15
  78. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  79. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  80. package/dist/resources/extensions/gsd/preferences-validation.js +148 -10
  81. package/dist/resources/extensions/gsd/preferences.js +20 -0
  82. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  83. package/dist/resources/extensions/gsd/quick.js +12 -3
  84. package/dist/resources/extensions/gsd/rule-registry.js +106 -10
  85. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  86. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  87. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  88. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
  89. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
  90. package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
  91. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  92. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
  93. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
  94. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  95. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  96. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
  97. package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
  98. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  99. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  100. package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
  101. package/dist/resources/extensions/gsd/uok/audit.js +3 -0
  102. package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
  103. package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
  104. package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
  105. package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
  106. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  107. package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
  108. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  109. package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
  110. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  111. package/dist/resources/extensions/gsd/workspace.js +5 -14
  112. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  113. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  114. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  115. package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
  116. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  117. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  118. package/dist/web/standalone/.next/BUILD_ID +1 -1
  119. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  120. package/dist/web/standalone/.next/build-manifest.json +2 -2
  121. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  122. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/index.html +1 -1
  173. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  174. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  176. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  178. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  179. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  180. package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
  181. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  182. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  184. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  185. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  186. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  187. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  188. package/dist/worktree-cli-create.d.ts +10 -0
  189. package/dist/worktree-cli-create.js +11 -0
  190. package/dist/worktree-cli-format.d.ts +12 -0
  191. package/dist/worktree-cli-format.js +38 -0
  192. package/dist/worktree-cli-plan.d.ts +16 -0
  193. package/dist/worktree-cli-plan.js +19 -0
  194. package/dist/worktree-cli-session.d.ts +13 -0
  195. package/dist/worktree-cli-session.js +18 -0
  196. package/dist/worktree-cli-status.d.ts +35 -0
  197. package/dist/worktree-cli-status.js +54 -0
  198. package/dist/worktree-cli.d.ts +1 -16
  199. package/dist/worktree-cli.js +28 -122
  200. package/integrations/hermes/open_gsd_hermes/gsd_client.py +25 -36
  201. package/integrations/hermes/pyproject.toml +1 -1
  202. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  203. package/package.json +6 -6
  204. package/packages/cloud-mcp-gateway/package.json +2 -2
  205. package/packages/contracts/package.json +1 -1
  206. package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
  207. package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
  208. package/packages/daemon/dist/orchestrator-agent.js +113 -0
  209. package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
  210. package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
  211. package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
  212. package/packages/daemon/dist/orchestrator-tools.js +137 -0
  213. package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
  214. package/packages/daemon/dist/orchestrator.d.ts +4 -56
  215. package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
  216. package/packages/daemon/dist/orchestrator.js +13 -303
  217. package/packages/daemon/dist/orchestrator.js.map +1 -1
  218. package/packages/daemon/dist/orchestrator.test.js +11 -1
  219. package/packages/daemon/dist/orchestrator.test.js.map +1 -1
  220. package/packages/daemon/package.json +4 -4
  221. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  222. package/packages/gsd-agent-core/package.json +5 -5
  223. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  224. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  225. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  226. package/packages/gsd-agent-modes/package.json +7 -7
  227. package/packages/mcp-server/package.json +4 -4
  228. package/packages/native/package.json +1 -1
  229. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  230. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  231. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  232. package/packages/pi-agent-core/package.json +1 -1
  233. package/packages/pi-ai/dist/index.d.ts +1 -0
  234. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  235. package/packages/pi-ai/dist/index.js +1 -0
  236. package/packages/pi-ai/dist/index.js.map +1 -1
  237. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  238. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  239. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  240. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  241. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  242. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  243. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  244. package/packages/pi-ai/dist/types.d.ts +8 -20
  245. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  246. package/packages/pi-ai/dist/types.js.map +1 -1
  247. package/packages/pi-ai/package.json +1 -1
  248. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  249. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  250. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  251. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  253. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  255. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  257. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  258. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  259. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  260. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  261. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  263. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  264. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  265. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/index.js +1 -0
  267. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  268. package/packages/pi-coding-agent/package.json +7 -7
  269. package/packages/pi-tui/package.json +2 -2
  270. package/packages/rpc-client/package.json +2 -2
  271. package/pkg/dist/core/export-html/template.js +53 -4
  272. package/pkg/package.json +1 -1
  273. package/src/resources/extensions/ask-user-questions.ts +1 -1
  274. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
  275. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
  276. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
  277. package/src/resources/extensions/gsd/activity-log.ts +1 -1
  278. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  279. package/src/resources/extensions/gsd/auto/orchestrator.ts +21 -14
  280. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  281. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  282. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -4
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +77 -16
  285. package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
  286. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
  287. package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
  288. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  289. package/src/resources/extensions/gsd/auto-start.ts +62 -31
  290. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
  291. package/src/resources/extensions/gsd/auto-timers.ts +9 -2
  292. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  293. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  294. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  295. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  296. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  297. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  298. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  299. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  300. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  301. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  302. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  303. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  304. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  305. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  306. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  307. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  308. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  309. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  310. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  311. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  312. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  313. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  314. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  315. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  316. package/src/resources/extensions/gsd/auto.ts +114 -47
  317. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  318. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
  319. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  320. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
  321. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  322. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  323. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  324. package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
  325. package/src/resources/extensions/gsd/config-overlay.ts +7 -1
  326. package/src/resources/extensions/gsd/db/engine.ts +36 -0
  327. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  328. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  329. package/src/resources/extensions/gsd/db-adapter.ts +29 -3
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  331. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  332. package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
  333. package/src/resources/extensions/gsd/forensics.ts +1 -1
  334. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  335. package/src/resources/extensions/gsd/git-service.ts +53 -0
  336. package/src/resources/extensions/gsd/gsd-db.ts +33 -11
  337. package/src/resources/extensions/gsd/guided-flow.ts +26 -18
  338. package/src/resources/extensions/gsd/journal.ts +1 -1
  339. package/src/resources/extensions/gsd/markdown-renderer.ts +52 -91
  340. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  341. package/src/resources/extensions/gsd/metrics.ts +2 -2
  342. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  343. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  344. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  345. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  346. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  347. package/src/resources/extensions/gsd/paths.ts +108 -51
  348. package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
  349. package/src/resources/extensions/gsd/preferences-models.ts +74 -20
  350. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  351. package/src/resources/extensions/gsd/preferences-types.ts +54 -1
  352. package/src/resources/extensions/gsd/preferences-validation.ts +146 -9
  353. package/src/resources/extensions/gsd/preferences.ts +20 -0
  354. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  355. package/src/resources/extensions/gsd/quick.ts +11 -3
  356. package/src/resources/extensions/gsd/rule-registry.ts +111 -9
  357. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  358. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  359. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  360. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
  361. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
  362. package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
  363. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  364. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  365. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
  366. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
  367. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  368. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  369. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
  370. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
  371. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  372. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  373. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  374. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  375. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  376. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  377. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  378. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  379. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  380. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  381. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  382. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  383. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  384. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  385. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  386. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  387. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
  388. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
  389. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
  390. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  391. package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
  392. package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
  393. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  394. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  395. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  396. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
  397. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +46 -1
  398. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
  399. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  400. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  401. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  402. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  403. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  404. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  405. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  406. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  407. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  408. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  410. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  411. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
  412. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  413. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  414. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  415. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  416. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  417. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  418. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
  419. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  420. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  421. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  422. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  423. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  424. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
  425. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
  426. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
  427. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  428. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  429. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  430. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
  431. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
  432. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  433. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
  434. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  435. package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
  436. package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
  437. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  438. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
  439. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  440. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  442. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  443. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  444. package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
  445. package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
  446. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  447. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  448. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  449. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  450. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  451. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
  452. package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
  453. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  454. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  455. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
  456. package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
  457. package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
  458. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
  459. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  460. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  461. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  462. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  463. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  464. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  465. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  466. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  467. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  468. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  469. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  470. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  471. package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
  472. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
  473. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  474. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  475. package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
  476. package/src/resources/extensions/gsd/types.ts +23 -4
  477. package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
  478. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  479. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  480. package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
  481. package/src/resources/extensions/gsd/uok/audit.ts +2 -0
  482. package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
  483. package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
  484. package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
  485. package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
  486. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  487. package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
  488. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  489. package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
  490. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  491. package/src/resources/extensions/gsd/workspace.ts +5 -21
  492. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  493. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  494. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  495. package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
  496. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  497. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  498. /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_buildManifest.js +0 -0
  499. /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_ssgManifest.js +0 -0
@@ -30,9 +30,10 @@ import { playNotificationBell, sendDesktopNotification } from "./notifications.j
30
30
  import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, HARD_CONTEXT_REROOT_THRESHOLD_PERCENT, resolveCompactionThresholdPercent, shouldRerootStepSessionForContext, shouldWarnStepSessionForContext, } from "./auto-budget.js";
31
31
  import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, isQueuedUserMessageSkip, isDeterministicPolicyError, } from "./auto-tool-tracking.js";
32
32
  import { closeoutUnit } from "./auto-unit-closeout.js";
33
- import { selectAndApplyModel, resolveModelId, clearToolBaseline } from "./auto-model-selection.js";
33
+ import { selectAndApplyModel, resolveModelId, clearToolBaseline, isModelUnavailable, applyThinkingLevelForModel } from "./auto-model-selection.js";
34
+ import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
34
35
  import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
35
- import { resetHookState, runPreDispatchHooks, restoreHookState, clearPersistedHookState, } from "./post-unit-hooks.js";
36
+ import { resetHookState, runPreDispatchHooks, restoreHookState, reconcileRestoredHookDispatch, clearPersistedHookState, } from "./post-unit-hooks.js";
36
37
  import { runGSDDoctor, rebuildState } from "./doctor.js";
37
38
  import { preDispatchHealthGate, resetProactiveHealing, setLevelChangeCallback, } from "./doctor-proactive.js";
38
39
  import { clearSkillSnapshot } from "./skill-discovery.js";
@@ -51,10 +52,16 @@ import { atomicWriteSync } from "./atomic-write.js";
51
52
  import { captureIntegrationBranch, detectWorktreeName, getCurrentBranch, getMainBranch, setActiveMilestoneId, } from "./worktree.js";
52
53
  import { GitServiceImpl } from "./git-service.js";
53
54
  import { getPriorSliceCompletionBlocker } from "./dispatch-guard.js";
54
- import { createAutoWorktree, teardownAutoWorktree, isInAutoWorktree, getAutoWorktreePath, mergeMilestoneToMain, autoWorktreeBranch, checkResourcesStale, escapeStaleWorktree, } from "./auto-worktree.js";
55
+ import { autoWorktreeBranch } from "./auto-worktree-branch-lifecycle.js";
56
+ import { createAutoWorktree } from "./auto-worktree-creation.js";
57
+ import { isInAutoWorktree } from "./auto-worktree-entry.js";
58
+ import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
59
+ import { checkResourcesStale } from "./auto-worktree-resource-version.js";
60
+ import { escapeStaleWorktree } from "./auto-worktree-runtime-cleanup.js";
61
+ import { teardownAutoWorktree } from "./auto-worktree-teardown.js";
55
62
  import { pruneQueueOrder } from "./queue-order.js";
56
63
  import { startCommandPolling as _startCommandPolling, isRemoteConfigured } from "../remote-questions/manager.js";
57
- import { createMilestoneMergeTransaction } from "./milestone-merge-transaction.js";
64
+ import { createDefaultMilestoneMergeTransaction } from "./milestone-merge-transaction.js";
58
65
  import { debugLog, isDebugEnabled, writeDebugSummary } from "./debug-logger.js";
59
66
  import { reconcileMergeState, verifyExpectedArtifact, } from "./auto-recovery.js";
60
67
  import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.js";
@@ -326,11 +333,24 @@ function withDetachedAutoKeepalive(run) {
326
333
  }
327
334
  export const _withDetachedAutoKeepaliveForTest = withDetachedAutoKeepalive;
328
335
  export function startAutoDetached(ctx, pi, base, verboseMode, options) {
329
- void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch((err) => {
336
+ void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch(async (err) => {
330
337
  const message = getErrorMessage(err);
331
338
  ctx.ui.notify(`Auto-start failed: ${message}`, "error");
332
339
  logWarning("engine", `auto start error: ${message}`, { file: "auto.ts" });
333
340
  debugLog("auto-start-failed", { error: message });
341
+ // Backstop cleanup (#1235): if startAuto threw after auto-mode was activated
342
+ // (e.g. an exception during bootstrap, before the loop's try/finally could
343
+ // run cleanupAfterLoopExit), s.active and the on-disk auto lock leak, so the
344
+ // re-entry guard silently no-ops every later /gsd auto until restart. Clear
345
+ // the leaked activation here so a failed start is always recoverable.
346
+ if (s.active) {
347
+ try {
348
+ await cleanupAfterLoopExit(ctx);
349
+ }
350
+ catch (cleanupErr) {
351
+ logWarning("engine", `auto start cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`, { file: "auto.ts" });
352
+ }
353
+ }
334
354
  });
335
355
  }
336
356
  /** Returns true if the project is configured for `isolation:worktree` mode. */
@@ -1746,7 +1766,7 @@ export function buildWorktreeLifecycleDeps() {
1746
1766
  return new GitServiceImpl(basePath, gitConfig);
1747
1767
  },
1748
1768
  worktreeProjection: new WorktreeStateProjection(),
1749
- mergeMilestone: createMilestoneMergeTransaction(mergeMilestoneToMain),
1769
+ mergeMilestone: createDefaultMilestoneMergeTransaction(),
1750
1770
  };
1751
1771
  }
1752
1772
  function buildLifecycle() {
@@ -2169,6 +2189,10 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
2169
2189
  ctx.ui.setWidget("gsd-health", undefined);
2170
2190
  ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
2171
2191
  restoreHookState(s.basePath);
2192
+ // A restored activeHook has no live dispatch (the sidecar queue is not
2193
+ // persisted); re-enqueue it so the hook runs instead of blocking the next
2194
+ // unrelated unit's close-out (#1246).
2195
+ reconcileRestoredHookDispatch(s.basePath, s.sidecarQueue);
2172
2196
  // Re-sync managed resources on resume so long-lived auto sessions pick up
2173
2197
  // bundled extension updates before resume-time verification/state logic runs.
2174
2198
  // GSD_PKG_ROOT is set by loader.ts and points to the gsd-pi package root.
@@ -2222,15 +2246,19 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
2222
2246
  debugLog("resume-orchestration-resume", { error: err instanceof Error ? err.message : String(err) });
2223
2247
  }
2224
2248
  startAutoCommandPolling(s.basePath);
2225
- await runAutoLoopWithUok({
2226
- ctx,
2227
- pi,
2228
- s,
2229
- deps: loopDeps,
2230
- runKernelLoop: runUokKernelLoop,
2231
- runLegacyLoop: runLegacyAutoLoop,
2232
- });
2233
- await cleanupAfterLoopExit(ctx);
2249
+ try {
2250
+ await runAutoLoopWithUok({
2251
+ ctx,
2252
+ pi,
2253
+ s,
2254
+ deps: loopDeps,
2255
+ runKernelLoop: runUokKernelLoop,
2256
+ runLegacyLoop: runLegacyAutoLoop,
2257
+ });
2258
+ }
2259
+ finally {
2260
+ await cleanupAfterLoopExit(ctx);
2261
+ }
2234
2262
  return;
2235
2263
  }
2236
2264
  // ── Fresh start path — delegated to auto-start.ts ──
@@ -2246,8 +2274,16 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
2246
2274
  // for crash-recovery fidelity (#5405).
2247
2275
  registerAutoWorkerForSession(s, base);
2248
2276
  const ready = await bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, bootstrapDeps, freshStartAssessment);
2249
- if (!ready)
2277
+ if (!ready) {
2278
+ // bootstrapAutoSession sets s.active = true (auto-start.ts) before several
2279
+ // post-activation bail-outs return false without a loop ever running (e.g.
2280
+ // the SQLite-unavailable gate). Without this, s.active and the auto lock
2281
+ // leak and every later /gsd auto silently no-ops via the re-entry guard
2282
+ // (#1235). Run the canonical cleanup so a failed bootstrap stays recoverable.
2283
+ if (s.active)
2284
+ await cleanupAfterLoopExit(ctx);
2250
2285
  return;
2286
+ }
2251
2287
  // Build scope after bootstrap has populated s.basePath / s.originalBasePath /
2252
2288
  // s.currentMilestoneId (including worktree setup inside bootstrapAutoSession).
2253
2289
  rebuildScope(s.basePath, s.currentMilestoneId);
@@ -2271,15 +2307,19 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
2271
2307
  }
2272
2308
  startAutoCommandPolling(s.basePath);
2273
2309
  // Dispatch the first unit
2274
- await runAutoLoopWithUok({
2275
- ctx,
2276
- pi,
2277
- s,
2278
- deps: loopDeps,
2279
- runKernelLoop: runUokKernelLoop,
2280
- runLegacyLoop: runLegacyAutoLoop,
2281
- });
2282
- await cleanupAfterLoopExit(ctx);
2310
+ try {
2311
+ await runAutoLoopWithUok({
2312
+ ctx,
2313
+ pi,
2314
+ s,
2315
+ deps: loopDeps,
2316
+ runKernelLoop: runUokKernelLoop,
2317
+ runLegacyLoop: runLegacyAutoLoop,
2318
+ });
2319
+ }
2320
+ finally {
2321
+ await cleanupAfterLoopExit(ctx);
2322
+ }
2283
2323
  }
2284
2324
  // describeNextUnit is imported from auto-dashboard.ts and re-exported
2285
2325
  export { describeNextUnit } from "./auto-dashboard.js";
@@ -2396,20 +2436,52 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
2396
2436
  startedAt: hookStartedAt,
2397
2437
  workspaceRoot: s.basePath,
2398
2438
  });
2399
- if (hookModel) {
2400
- const availableModels = ctx.modelRegistry.getAvailable();
2401
- const match = resolveModelId(hookModel, availableModels, ctx.model?.provider);
2402
- if (match) {
2439
+ // Resolve the hook's model honoring the configured `fallbacks[]` chain
2440
+ // (#1229). The manual trigger path bypasses selectAndApplyModel, so resolve
2441
+ // the primary→fallbacks order here and apply the first candidate available in
2442
+ // the registry — a blocked or unconfigured primary then transparently drops
2443
+ // to a fallback instead of silently reverting to the session model.
2444
+ const availableModels = ctx.modelRegistry.getAvailable();
2445
+ const availableModelIds = availableModels.map((m) => `${m.provider}/${m.id}`);
2446
+ const hookModelConfig = resolveModelWithFallbacksForUnit(hookUnitType, targetBasePath, availableModelIds);
2447
+ const modelCandidates = hookModelConfig
2448
+ ? [hookModelConfig.primary, ...hookModelConfig.fallbacks]
2449
+ : hookModel
2450
+ ? [hookModel]
2451
+ : [];
2452
+ if (modelCandidates.length > 0) {
2453
+ let applied = false;
2454
+ for (const candidate of modelCandidates) {
2455
+ const match = resolveModelId(candidate, availableModels, ctx.model?.provider);
2456
+ if (!match)
2457
+ continue;
2458
+ // Skip models the runtime has marked blocked or temporarily unavailable
2459
+ // (e.g. a primary that just tripped a provider limit) so the configured
2460
+ // fallbacks[] chain actually engages — parity with auto-mode's
2461
+ // selectAndApplyModel, which consults the same blocked-models store (#1229).
2462
+ if (isModelUnavailable(targetBasePath, match.provider, match.id))
2463
+ continue;
2403
2464
  try {
2404
- await pi.setModel(match);
2465
+ if (await pi.setModel(match)) {
2466
+ // The manual trigger path bypasses selectAndApplyModel, so apply the
2467
+ // hook's per-field `thinking` (from `post_unit_hooks[].model`'s object
2468
+ // form) here against the just-set model rather than leaving the hook at
2469
+ // the session level (#1269). Absent → session level, unchanged.
2470
+ if (hookModelConfig?.thinking) {
2471
+ applyThinkingLevelForModel(pi, hookModelConfig.thinking, match, ctx);
2472
+ }
2473
+ applied = true;
2474
+ break;
2475
+ }
2405
2476
  }
2406
2477
  catch (err) {
2407
- /* non-fatal */
2408
- logWarning("dispatch", `hook model set failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
2478
+ /* non-fatal — try the next fallback */
2479
+ logWarning("dispatch", `hook model set failed for ${candidate}: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
2409
2480
  }
2410
2481
  }
2411
- else {
2412
- ctx.ui.notify(`Hook model "${hookModel}" not found in available models. Falling back to current session model. ` +
2482
+ if (!applied) {
2483
+ ctx.ui.notify(`Hook model${modelCandidates.length > 1 ? "s" : ""} "${modelCandidates.join(", ")}" not available. ` +
2484
+ `Falling back to current session model. ` +
2413
2485
  `Ensure the model is defined in models.json and has auth configured.`, "warning");
2414
2486
  }
2415
2487
  }
@@ -6,7 +6,7 @@ import { join } from "node:path";
6
6
  import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
7
7
  import { logWarning } from "../workflow-logger.js";
8
8
  import { getWorkflowDatabaseStatus, openWorkflowDatabase, } from "../db-workspace.js";
9
- import { getAutoWorktreePath } from "../auto-worktree.js";
9
+ import { getAutoWorktreePath } from "../auto-worktree-path-resolution.js";
10
10
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
11
11
  import { worktreesDirs } from "../worktree-placement.js";
12
12
  export function safeWorkspaceCwd() {
@@ -15,7 +15,7 @@ import { loadFile, saveFile, formatContinue } from "../files.js";
15
15
  import { clearAutoCompletionStopInProgress, clearToolInvocationError, getAutoRuntimeSnapshot, getSourceObservationStore, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, isInteractiveElicitationInFlight, markToolEnd, markToolStart, recordAutoToolSurfaceSnapshot, recordToolInvocationError, } from "../auto-runtime-state.js";
16
16
  import { isDeterministicPolicyError, isQueuedUserMessageSkip, isToolInvocationError, isToolUnavailableError, } from "../auto-tool-tracking.js";
17
17
  import { applyProviderPayloadPolicy } from "../provider-payload-policy.js";
18
- import { checkToolCallLoop, recordToolCallLoopMutation, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
18
+ import { checkToolCallLoop, configureToolCallLoopGuard, recordToolCallLoopMutation, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
19
19
  import { MINIMAL_AUTO_BASE_TOOL_NAMES } from "./core-session-tools.js";
20
20
  import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
21
21
  import { saveActivityLog } from "../activity-log.js";
@@ -432,6 +432,22 @@ async function applyCompactionThresholdOverride(ctx) {
432
432
  ctx.setCompactionThresholdOverride?.(0.6);
433
433
  }
434
434
  }
435
+ /**
436
+ * Apply user-tunable tool-call loop guard thresholds (#1198) from GSD
437
+ * preferences plus GSD_TOOL_LOOP_* env overrides. Runs at session boundaries
438
+ * so both interactive sessions and `/gsd auto` pick up the configuration.
439
+ * Non-fatal: falls back to built-in defaults when preferences cannot be loaded.
440
+ */
441
+ async function applyToolCallLoopGuardConfig(basePath) {
442
+ try {
443
+ const { loadEffectiveGSDPreferences } = await import("../preferences.js");
444
+ const prefs = loadEffectiveGSDPreferences(basePath);
445
+ configureToolCallLoopGuard(prefs?.preferences.tool_call_loop_guard);
446
+ }
447
+ catch {
448
+ configureToolCallLoopGuard(null);
449
+ }
450
+ }
435
451
  function clearDeferredApprovalGate(basePath) {
436
452
  if (!basePath) {
437
453
  deferredApprovalGates.clear();
@@ -779,6 +795,7 @@ export function registerHooks(pi, ecosystemHandlers) {
779
795
  }
780
796
  resetWriteGateState(basePath);
781
797
  resetToolCallLoopGuard();
798
+ await applyToolCallLoopGuardConfig(basePath);
782
799
  approvalQuestionAbortInFlight = false;
783
800
  clearDeferredApprovalGate();
784
801
  await resetAskUserQuestionsTurnCache();
@@ -864,6 +881,7 @@ export function registerHooks(pi, ecosystemHandlers) {
864
881
  initSessionNotifications(ctx);
865
882
  resetWriteGateState(basePath);
866
883
  resetToolCallLoopGuard();
884
+ await applyToolCallLoopGuardConfig(basePath);
867
885
  clearDeferredApprovalGate();
868
886
  await resetAskUserQuestionsTurnCache();
869
887
  clearDiscussionFlowState(basePath);
@@ -1389,7 +1407,7 @@ export function registerHooks(pi, ecosystemHandlers) {
1389
1407
  }
1390
1408
  }
1391
1409
  if (!event.isError) {
1392
- recordToolCallLoopMutation(toolName);
1410
+ recordToolCallLoopMutation(toolName, event.details);
1393
1411
  refreshSourceObservationAfterMutation(toolName, event.input, ctx);
1394
1412
  clearSourceObservationsAfterShell(toolName);
1395
1413
  }
@@ -11,7 +11,7 @@ import { resolveModelWithFallbacksForUnit } from "../preferences-models.js";
11
11
  import { resolveGsdRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile } from "../paths.js";
12
12
  import { extractIntroAndRules } from "../knowledge-parser.js";
13
13
  import { ensureCodebaseMapFresh, readCodebaseMap } from "../codebase-generator.js";
14
- import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
14
+ import { getActiveAutoWorktreeContext } from "../auto-worktree-session-registry.js";
15
15
  import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-session-state.js";
16
16
  import { deriveState } from "../state.js";
17
17
  import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
@@ -14,36 +14,173 @@
14
14
  * to stop tooling for the rest of that turn and answer in text.
15
15
  *
16
16
  * The per-tool-name check (#783 Brief C) tracks call counts within a
17
- * turn regardless of args, decaying after successful state-mutating tools.
17
+ * turn regardless of args, decaying after successful state-progressing tools.
18
18
  * This catches improvisation
19
19
  * loops where the model attempts the same missing workflow tool through
20
20
  * varied surfaces (bash → `node -e` → CLI), each with a different
21
21
  * signature, so the identical-args streak never trips, while allowing
22
- * tool-heavy turns that are making file-mutation progress. Whichever guard
23
- * trips first blocks.
22
+ * tool-heavy turns that are making progress. Progress means a successful file
23
+ * mutation (edit/write) or a successful shell/exec call (bg_shell,
24
+ * gsd_uat_exec, …) — so a local model running a legitimate iterative
25
+ * debugging loop (restart server, npm install, curl an endpoint) is not
26
+ * blocked as a false loop (#1206). Whichever guard trips first blocks.
27
+ *
28
+ * Thresholds, exempt tools, and enable flags are user-tunable (#1198) via the
29
+ * `tool_call_loop_guard` key in `.gsd/PREFERENCES.md` and `GSD_TOOL_LOOP_*`
30
+ * environment variables, applied through {@link configureToolCallLoopGuard}.
31
+ * Defaults preserve the original hardcoded behavior.
24
32
  */
25
33
  import { createHash } from "node:crypto";
26
34
  import { INHERENTLY_REPEATABLE_TOOL_SET } from "./core-session-tools.js";
27
35
  import { hasBrowserContractPrefix } from "../../shared/browser-contract.js";
28
36
  import { canonicalToolName } from "../engine-hook-contract.js";
29
- const MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
37
+ /** Built-in defaults. Preserved when preferences/env do not override them. */
38
+ const DEFAULT_MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
39
+ const DEFAULT_PER_TOOL_DEFAULT_CAP = 6;
40
+ const DEFAULT_PER_TOOL_REPEATABLE_CAP = 15;
41
+ const DEFAULT_PER_TOOL_CAP_EXEMPT_TOOLS = ["find", "glob", "grep", "ls", "read", "search_and_read"];
30
42
  /** Interactive/user-facing tools where even 1 duplicate is confusing. */
31
43
  const STRICT_LOOP_TOOLS = new Set(["ask_user_questions"]);
32
44
  const MAX_CONSECUTIVE_STRICT = 1;
45
+ const STATE_MUTATING_TOOL_SET = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
33
46
  /**
34
- * Per-turn cap on calls to the SAME tool name, regardless of args (#783).
47
+ * Successful shell/exec calls are state progression too (#1206), not just file
48
+ * mutations. A local model debugging a UAT scenario legitimately restarts
49
+ * servers, runs `npm install`, and curls endpoints via bg_shell / gsd_uat_exec
50
+ * with varied args — productive work that never touches a file, so Guard 2's
51
+ * per-tool cap would otherwise misfire as a false loop. Decaying on these
52
+ * successful calls keeps the guard from aborting a progressing turn.
35
53
  *
36
- * General-purpose execution tools are routinely called many times per turn
37
- * (touching multiple files, running several commands), so they get a higher
38
- * ceiling. Everything else workflow one-shot tools (e.g. gsd_complete_milestone)
39
- * and any non-allowlisted tool gets the default cap. The default is generous
40
- * enough to absorb legitimate retries but catches the reported improvisation
41
- * loop (~51 calls) well before a cost spike.
54
+ * Only successful calls reach {@link recordToolCallLoopMutation}: the
55
+ * tool_result hook skips `isError` results, and the bash tool throws on
56
+ * non-zero exit, so a genuinely stuck improvisation loop of failing commands
57
+ * (#783) never decays and still trips the cap. `async_bash` is excluded
58
+ * because it only registers a background job; command success is reported
59
+ * later via `await_job`. `bg_shell` may return `isError: false` for failed
60
+ * `run`/`start` outcomes, so the hook passes `details` for validation.
42
61
  */
43
- const PER_TOOL_DEFAULT_CAP = 6;
44
- const PER_TOOL_REPEATABLE_CAP = 15;
45
- const PER_TOOL_CAP_EXEMPT_TOOLS = new Set(["find", "glob", "grep", "ls", "read", "search_and_read"]);
46
- const STATE_MUTATING_TOOL_SET = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
62
+ const STATE_PROGRESSING_EXEC_TOOL_SET = new Set([
63
+ "bash",
64
+ "bg_shell",
65
+ "shell",
66
+ "powershell",
67
+ "gsd_exec",
68
+ "gsd_uat_exec",
69
+ ]);
70
+ function isSuccessfulBgShellLoopProgress(details) {
71
+ if (!details || typeof details !== "object")
72
+ return false;
73
+ const record = details;
74
+ switch (record.action) {
75
+ case "run":
76
+ return record.exitCode === 0 && record.timedOut !== true;
77
+ case "start":
78
+ case "restart": {
79
+ const process = record.process;
80
+ if (!process || typeof process !== "object")
81
+ return false;
82
+ return process.alive === true;
83
+ }
84
+ case "wait_for_ready":
85
+ return record.ready === true;
86
+ case "send_and_wait":
87
+ return record.matched === true;
88
+ default:
89
+ return true;
90
+ }
91
+ }
92
+ function defaultGuardConfig() {
93
+ return {
94
+ guardEnabled: true,
95
+ identicalEnabled: true,
96
+ maxConsecutiveIdentical: DEFAULT_MAX_CONSECUTIVE_IDENTICAL_CALLS,
97
+ repeatedEnabled: true,
98
+ perToolDefaultCap: DEFAULT_PER_TOOL_DEFAULT_CAP,
99
+ perToolRepeatableCap: DEFAULT_PER_TOOL_REPEATABLE_CAP,
100
+ perToolExempt: new Set(DEFAULT_PER_TOOL_CAP_EXEMPT_TOOLS),
101
+ };
102
+ }
103
+ let config = defaultGuardConfig();
104
+ /** Parse a positive-integer env var, returning undefined when unset/invalid. */
105
+ function envPositiveInt(name) {
106
+ const raw = process.env[name];
107
+ if (raw === undefined || raw.trim() === "")
108
+ return undefined;
109
+ const n = Number(raw);
110
+ return Number.isFinite(n) && n >= 1 ? Math.floor(n) : undefined;
111
+ }
112
+ /** Parse a boolean env var (`true`/`1` vs `false`/`0`), undefined when unset. */
113
+ function envBool(name) {
114
+ const raw = process.env[name];
115
+ if (raw === undefined || raw.trim() === "")
116
+ return undefined;
117
+ const v = raw.trim().toLowerCase();
118
+ if (v === "true" || v === "1" || v === "yes" || v === "on")
119
+ return true;
120
+ if (v === "false" || v === "0" || v === "no" || v === "off")
121
+ return false;
122
+ return undefined;
123
+ }
124
+ /** Parse a comma-separated tool-name list env var. */
125
+ function envToolList(name) {
126
+ const raw = process.env[name];
127
+ if (!raw)
128
+ return [];
129
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
130
+ }
131
+ /**
132
+ * Resolve preferences + environment overrides into the active configuration.
133
+ * Environment variables win over preferences; preferences win over built-in
134
+ * defaults. User-supplied exempt tools are additive to the built-in exempt
135
+ * set so defaults are always preserved.
136
+ *
137
+ * Applied to both interactive sessions and `/gsd auto` (called from the
138
+ * session_start / session_switch hooks).
139
+ */
140
+ export function configureToolCallLoopGuard(prefs) {
141
+ const next = defaultGuardConfig();
142
+ if (prefs) {
143
+ if (typeof prefs.enabled === "boolean")
144
+ next.guardEnabled = prefs.enabled;
145
+ if (prefs.identical_args) {
146
+ if (typeof prefs.identical_args.enabled === "boolean")
147
+ next.identicalEnabled = prefs.identical_args.enabled;
148
+ if (typeof prefs.identical_args.max_consecutive_calls === "number" && prefs.identical_args.max_consecutive_calls >= 1) {
149
+ next.maxConsecutiveIdentical = Math.floor(prefs.identical_args.max_consecutive_calls);
150
+ }
151
+ }
152
+ if (prefs.repeated_tool) {
153
+ if (typeof prefs.repeated_tool.enabled === "boolean")
154
+ next.repeatedEnabled = prefs.repeated_tool.enabled;
155
+ if (typeof prefs.repeated_tool.default_cap === "number" && prefs.repeated_tool.default_cap >= 1) {
156
+ next.perToolDefaultCap = Math.floor(prefs.repeated_tool.default_cap);
157
+ }
158
+ if (typeof prefs.repeated_tool.repeatable_cap === "number" && prefs.repeated_tool.repeatable_cap >= 1) {
159
+ next.perToolRepeatableCap = Math.floor(prefs.repeated_tool.repeatable_cap);
160
+ }
161
+ for (const tool of prefs.repeated_tool.exempt_tools ?? []) {
162
+ if (typeof tool === "string" && tool.trim())
163
+ next.perToolExempt.add(tool.trim());
164
+ }
165
+ }
166
+ }
167
+ // Environment overrides (win over preferences).
168
+ const envEnabled = envBool("GSD_TOOL_LOOP_GUARD_ENABLED");
169
+ if (envEnabled !== undefined)
170
+ next.guardEnabled = envEnabled;
171
+ const envIdenticalMax = envPositiveInt("GSD_TOOL_LOOP_IDENTICAL_MAX");
172
+ if (envIdenticalMax !== undefined)
173
+ next.maxConsecutiveIdentical = envIdenticalMax;
174
+ const envDefaultCap = envPositiveInt("GSD_TOOL_LOOP_REPEATED_DEFAULT_CAP");
175
+ if (envDefaultCap !== undefined)
176
+ next.perToolDefaultCap = envDefaultCap;
177
+ const envRepeatableCap = envPositiveInt("GSD_TOOL_LOOP_REPEATED_REPEATABLE_CAP");
178
+ if (envRepeatableCap !== undefined)
179
+ next.perToolRepeatableCap = envRepeatableCap;
180
+ for (const tool of envToolList("GSD_TOOL_LOOP_EXEMPT_TOOLS"))
181
+ next.perToolExempt.add(tool);
182
+ config = next;
183
+ }
47
184
  let consecutiveCount = 0;
48
185
  let lastSignature = "";
49
186
  let lastToolName = "";
@@ -72,14 +209,17 @@ function hashToolCall(toolName, args) {
72
209
  * Returns `{ block: true, reason }` when the loop threshold is exceeded.
73
210
  *
74
211
  * Two independent guards run; whichever trips first blocks:
75
- * 1. Identical-signature streak (MAX_CONSECUTIVE_IDENTICAL_CALLS, strict for
212
+ * 1. Identical-signature streak (config.maxConsecutiveIdentical, strict for
76
213
  * ask_user_questions).
77
- * 2. Per-tool-name cap (PER_TOOL_DEFAULT_CAP / PER_TOOL_REPEATABLE_CAP),
214
+ * 2. Per-tool-name cap (config.perToolDefaultCap / config.perToolRepeatableCap),
78
215
  * independent of args, reset after file-mutation progress — catches
79
216
  * improvisation loops (#783).
217
+ *
218
+ * Both guards, their thresholds, and the per-tool exempt set are user-tunable
219
+ * via {@link configureToolCallLoopGuard} (#1198).
80
220
  */
81
221
  export function checkToolCallLoop(toolName, args) {
82
- if (!enabled)
222
+ if (!enabled || !config.guardEnabled)
83
223
  return { block: false, count: 0 };
84
224
  const sig = hashToolCall(toolName, args);
85
225
  if (sig === lastSignature) {
@@ -93,12 +233,14 @@ export function checkToolCallLoop(toolName, args) {
93
233
  // ── Guard 1: identical-signature streak ──
94
234
  const threshold = STRICT_LOOP_TOOLS.has(toolName)
95
235
  ? MAX_CONSECUTIVE_STRICT
96
- : MAX_CONSECUTIVE_IDENTICAL_CALLS;
97
- if (consecutiveCount > threshold) {
236
+ : config.maxConsecutiveIdentical;
237
+ if (config.identicalEnabled && consecutiveCount > threshold) {
98
238
  return {
99
239
  block: true,
100
240
  reason: `Tool loop detected (identical args): ${toolName} called ${consecutiveCount} times ` +
101
- `with identical arguments. Blocking to prevent infinite loop. ` +
241
+ `with identical arguments (max ${threshold}). Blocking to prevent infinite loop. ` +
242
+ `Raise tool_call_loop_guard.identical_args.max_consecutive_calls in .gsd/PREFERENCES.md ` +
243
+ `(or GSD_TOOL_LOOP_IDENTICAL_MAX) if this is expected. ` +
102
244
  `Do not retry this tool or call other tools this turn — stop and respond to the user in text.`,
103
245
  count: consecutiveCount,
104
246
  };
@@ -119,30 +261,46 @@ export function checkToolCallLoop(toolName, args) {
119
261
  // so the arg-independent per-tool cap misfires on legitimate verification;
120
262
  // Guard 1's identical-signature streak still catches a genuinely stuck
121
263
  // browser loop.
122
- if (PER_TOOL_CAP_EXEMPT_TOOLS.has(toolName) ||
264
+ if (!config.repeatedEnabled ||
265
+ config.perToolExempt.has(toolName) ||
123
266
  hasBrowserContractPrefix(canonicalToolName(toolName))) {
124
267
  return { block: false, count: consecutiveCount };
125
268
  }
126
269
  const perToolCap = INHERENTLY_REPEATABLE_TOOL_SET.has(toolName)
127
- ? PER_TOOL_REPEATABLE_CAP
128
- : PER_TOOL_DEFAULT_CAP;
270
+ ? config.perToolRepeatableCap
271
+ : config.perToolDefaultCap;
129
272
  if (perToolCount > perToolCap) {
130
273
  return {
131
274
  block: true,
132
275
  reason: `Tool loop detected (repeated tool): ${toolName} called ${perToolCount} times ` +
133
276
  `this turn (cap ${perToolCap}). Blocking to prevent infinite loop. ` +
134
277
  `The tool may be unavailable or failing repeatedly. ` +
278
+ `Raise tool_call_loop_guard.repeated_tool.default_cap/repeatable_cap or add "${toolName}" ` +
279
+ `to tool_call_loop_guard.repeated_tool.exempt_tools in .gsd/PREFERENCES.md ` +
280
+ `(or GSD_TOOL_LOOP_REPEATED_* / GSD_TOOL_LOOP_EXEMPT_TOOLS) if this is expected. ` +
135
281
  `Do not retry this tool or pivot to other tools this turn — stop and respond to the user in text.`,
136
282
  count: perToolCount,
137
283
  };
138
284
  }
139
285
  return { block: false, count: consecutiveCount };
140
286
  }
141
- /** Record successful mutation progress so Guard 2 can decay on the next count. */
142
- export function recordToolCallLoopMutation(toolName) {
287
+ /**
288
+ * Record successful state progress so Guard 2 can decay on the next count.
289
+ * File mutations (edit/write/…) and successful shell/exec calls (bash,
290
+ * bg_shell, gsd_uat_exec, …) both count as progress (#1092, #1206). The caller
291
+ * only invokes this for non-error results, so failing improvisation loops
292
+ * (#783) never decay and still trip the per-tool cap.
293
+ */
294
+ export function recordToolCallLoopMutation(toolName, details) {
143
295
  if (!enabled)
144
296
  return;
145
- if (!STATE_MUTATING_TOOL_SET.has(toolName))
297
+ if (STATE_MUTATING_TOOL_SET.has(toolName)) {
298
+ mutationEpoch++;
299
+ return;
300
+ }
301
+ if (!STATE_PROGRESSING_EXEC_TOOL_SET.has(toolName))
302
+ return;
303
+ if (toolName === "bg_shell" && details !== undefined && !isSuccessfulBgShellLoopProgress(details))
146
304
  return;
147
305
  mutationEpoch++;
148
306
  }
@@ -506,9 +506,15 @@ export function ensureCodebaseMapFresh(basePath, options, ensureOptions) {
506
506
  }
507
507
  const metadata = parseCodebaseMapMetadata(existing);
508
508
  const existingDescriptions = parseCodebaseMap(existing);
509
+ // Compare against the truncated count: when the repository has more tracked
510
+ // files than maxFiles, the map only lists the first maxFiles entries, so a
511
+ // stored fileCount above the cap (e.g. from an older map that recorded the
512
+ // full count) still matches the current truncated listing. Comparing raw
513
+ // values would flag "file-count-changed" on every turn, rewriting the
514
+ // timestamp and invalidating the provider KV cache prefix.
509
515
  const staleReason = !metadata ? "missing-metadata"
510
516
  : metadata.fingerprint !== fingerprint ? "files-changed"
511
- : metadata.fileCount !== listed.files.length ? "file-count-changed"
517
+ : Math.min(metadata.fileCount, resolved.maxFiles) !== listed.files.length ? "file-count-changed"
512
518
  : metadata.truncated !== listed.truncated ? "truncation-changed"
513
519
  : undefined;
514
520
  if (!staleReason) {
@@ -16,7 +16,7 @@ import { appendCapture, hasPendingCaptures, loadPendingCaptures } from "./captur
16
16
  import { appendOverride, appendKnowledge } from "./files.js";
17
17
  import { formatDoctorIssuesForPrompt, formatDoctorReport, formatDoctorReportJson, runGSDDoctor, selectDoctorScope, filterDoctorIssues, } from "./doctor.js";
18
18
  import { isAutoActive, checkRemoteAutoSession } from "./auto.js";
19
- import { getAutoWorktreePath } from "./auto-worktree.js";
19
+ import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
20
20
  import { currentDirectoryRoot, projectRoot } from "./commands/context.js";
21
21
  import { loadPrompt } from "./prompt-loader.js";
22
22
  import { buildClaudeRuntimeFloorAdvisory } from "../../shared/claude-runtime-floor.js";
@@ -5,7 +5,7 @@ import { join } from "node:path";
5
5
  import { resolveByName, autoDetect, listTemplates, getTemplateInfo, loadWorkflowTemplate, loadRegistry, isLegacyWorkflowMode, } from "./workflow-templates.js";
6
6
  import { loadPrompt } from "./prompt-loader.js";
7
7
  import { gsdRoot } from "./paths.js";
8
- import { createGitService, runGit } from "./git-service.js";
8
+ import { createGitService, runGit, taskBranchArgs } from "./git-service.js";
9
9
  import { isAutoActive, isAutoPaused } from "./auto.js";
10
10
  import { getErrorMessage } from "./error-utils.js";
11
11
  import { resolvePlugin } from "./workflow-plugins.js";
@@ -349,7 +349,7 @@ export async function handleStart(args, ctx, pi) {
349
349
  git.autoCommit("workflow-template", templateId, []);
350
350
  }
351
351
  catch { /* nothing to commit */ }
352
- runGit(basePath, ["checkout", "-b", branchName]);
352
+ runGit(basePath, taskBranchArgs(basePath, branchName, current, git.getMainBranch()));
353
353
  branchCreated = true;
354
354
  }
355
355
  }
@@ -481,7 +481,7 @@ export function dispatchMarkdownPhasePlugin(plugin, description, ctx, pi) {
481
481
  git.autoCommit("workflow-template", templateId, []);
482
482
  }
483
483
  catch { /* nothing to commit */ }
484
- runGit(basePath, ["checkout", "-b", branchName]);
484
+ runGit(basePath, taskBranchArgs(basePath, branchName, current, git.getMainBranch()));
485
485
  branchCreated = true;
486
486
  }
487
487
  }