@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262

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 (325) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  4. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  5. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  6. package/dist/resources/extensions/browser-tools/index.js +68 -24
  7. package/dist/resources/extensions/browser-tools/state.js +12 -0
  8. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  9. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  10. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +87 -12
  13. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  14. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
  19. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  20. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  21. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  22. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  23. package/dist/resources/extensions/gsd/auto.js +40 -15
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  27. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  30. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  31. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  33. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  34. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  35. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  37. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  38. package/dist/resources/extensions/gsd/guided-flow.js +4 -1
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  40. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  41. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  42. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  43. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  44. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  45. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  46. package/dist/resources/extensions/gsd/preferences-models.js +111 -43
  47. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  49. package/dist/resources/extensions/gsd/preferences.js +4 -1
  50. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  55. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  56. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  57. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  58. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  59. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  61. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  62. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  63. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  64. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  65. package/dist/resources/extensions/gsd/state.js +7 -3
  66. package/dist/resources/extensions/gsd/tool-contract.js +15 -1
  67. package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
  68. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
  69. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  70. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  71. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
  72. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  73. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  74. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  75. package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
  76. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  77. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
  79. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  81. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  82. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  83. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  84. package/dist/resources/extensions/subagent/agents.js +1 -0
  85. package/dist/resources/extensions/subagent/index.js +27 -12
  86. package/dist/resources/extensions/subagent/launch.js +7 -2
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  89. package/dist/web/standalone/.next/build-manifest.json +2 -2
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  115. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  121. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  122. package/package.json +4 -4
  123. package/packages/cloud-mcp-gateway/package.json +2 -2
  124. package/packages/contracts/package.json +1 -1
  125. package/packages/daemon/package.json +4 -4
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  143. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  144. package/packages/gsd-agent-modes/package.json +7 -7
  145. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  146. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  147. package/packages/mcp-server/package.json +3 -3
  148. package/packages/native/dist/native.js +22 -0
  149. package/packages/native/package.json +1 -1
  150. package/packages/pi-agent-core/package.json +1 -1
  151. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  152. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  153. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  154. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  155. package/packages/pi-ai/dist/models.generated.d.ts +174 -29
  156. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models.generated.js +178 -54
  158. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  159. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  160. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  161. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  162. package/packages/pi-ai/package.json +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  164. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  165. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  166. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  167. package/packages/pi-coding-agent/package.json +7 -7
  168. package/packages/pi-tui/dist/utils.d.ts +11 -0
  169. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/utils.js +119 -6
  171. package/packages/pi-tui/dist/utils.js.map +1 -1
  172. package/packages/pi-tui/package.json +2 -1
  173. package/packages/rpc-client/package.json +2 -2
  174. package/pkg/dist/theme/themes.js +1 -1
  175. package/pkg/dist/theme/themes.js.map +1 -1
  176. package/pkg/package.json +1 -1
  177. package/scripts/install/handoff.js +16 -3
  178. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  179. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  180. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  181. package/src/resources/extensions/browser-tools/index.ts +75 -27
  182. package/src/resources/extensions/browser-tools/state.ts +13 -0
  183. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  184. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  185. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  186. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  187. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  188. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  189. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  190. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  191. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  192. package/src/resources/extensions/gsd/auto/phases.ts +89 -15
  193. package/src/resources/extensions/gsd/auto/session.ts +24 -1
  194. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  195. package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
  196. package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
  197. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  198. package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
  199. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  200. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  201. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  202. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  203. package/src/resources/extensions/gsd/auto.ts +41 -14
  204. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
  206. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  207. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  208. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  209. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  210. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  211. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  212. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  213. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  214. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  215. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  216. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  217. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  218. package/src/resources/extensions/gsd/guided-flow.ts +4 -1
  219. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  220. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  221. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  222. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  223. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  224. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  225. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  226. package/src/resources/extensions/gsd/preferences-models.ts +113 -43
  227. package/src/resources/extensions/gsd/preferences-types.ts +47 -0
  228. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  229. package/src/resources/extensions/gsd/preferences.ts +5 -0
  230. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  231. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  232. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  234. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  235. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  236. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  237. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  238. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  239. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  240. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  241. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  242. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  243. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  244. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  245. package/src/resources/extensions/gsd/state.ts +7 -4
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
  247. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  249. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  250. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  251. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  252. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  253. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  254. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  255. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  256. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  257. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  258. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  259. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  260. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
  261. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  262. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  263. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  264. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  265. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  266. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
  267. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  268. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
  269. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  270. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  272. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  273. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  274. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  276. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  277. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  278. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
  279. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  280. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  281. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  282. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
  283. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  284. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  285. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  286. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  287. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  288. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  289. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  290. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  291. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  292. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  293. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  294. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  295. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  296. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
  297. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  298. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  299. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  300. package/src/resources/extensions/gsd/tool-contract.ts +29 -1
  301. package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
  302. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
  303. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  304. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  305. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
  306. package/src/resources/extensions/gsd/types.ts +1 -0
  307. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  308. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  309. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  310. package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
  311. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  312. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  313. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
  314. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  315. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  316. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  317. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  318. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  319. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  320. package/src/resources/extensions/subagent/agents.ts +4 -0
  321. package/src/resources/extensions/subagent/index.ts +28 -3
  322. package/src/resources/extensions/subagent/launch.ts +8 -0
  323. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  324. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  325. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -46,13 +46,20 @@ async function loadExtensionModules() {
46
46
  const dbModule = await jiti.import(gsdExtensionPath('gsd-db.ts'), {});
47
47
  const importerModule = await jiti.import(gsdExtensionPath('md-importer.ts'), {});
48
48
  const dynamicToolsModule = await jiti.import(gsdExtensionPath('bootstrap/dynamic-tools.ts'), {});
49
+ const migrationCheckModule = await jiti.import(gsdExtensionPath('migration-auto-check.ts'), {});
50
+ const rendererModule = await jiti.import(gsdExtensionPath('markdown-renderer.ts'), {});
49
51
  return {
50
52
  ensureDbOpen: dynamicToolsModule.ensureDbOpen,
51
53
  isDbAvailable: dbModule.isDbAvailable,
52
54
  clearEngineHierarchy: dbModule.clearEngineHierarchy,
53
55
  transaction: dbModule.transaction,
56
+ backupDatabaseSnapshot: dbModule.backupDatabaseSnapshot,
54
57
  migrateHierarchyToDb: importerModule.migrateHierarchyToDb,
55
58
  invalidateStateCache: stateModule.invalidateStateCache,
59
+ countDbHierarchy: migrationCheckModule.countDbHierarchy,
60
+ countMarkdownHierarchy: migrationCheckModule.countMarkdownHierarchy,
61
+ recoverWouldDeleteDbRows: migrationCheckModule.recoverWouldDeleteDbRows,
62
+ renderAllFromDb: rendererModule.renderAllFromDb,
56
63
  };
57
64
  }
58
65
  export async function handleRecover(basePath) {
@@ -75,6 +82,24 @@ export async function handleRecover(basePath) {
75
82
  process.stderr.write(`[headless] recover: failed to open or create the GSD database at ${basePath}\n`);
76
83
  return { exitCode: 1 };
77
84
  }
85
+ // Refuse a destructive recover that would delete authoritative DB rows the
86
+ // markdown lacks, unless explicitly allowed. The DB is the source of truth;
87
+ // re-projecting via rebuild is almost always what's wanted instead. The
88
+ // check is identity-based, so it also catches equal-count divergence (DB S99
89
+ // vs markdown S01) that a cardinality-only comparison would miss.
90
+ const markdown = modules.countMarkdownHierarchy(basePath);
91
+ const beforeDb = modules.countDbHierarchy();
92
+ const dataLoss = modules.recoverWouldDeleteDbRows(basePath);
93
+ const allowDataLoss = process.env.GSD_RECOVER_ALLOW_DATA_LOSS === '1';
94
+ if (dataLoss && !allowDataLoss) {
95
+ process.stderr.write(`[headless] recover refused: the DB (${beforeDb.milestones}M/${beforeDb.slices}S/${beforeDb.tasks}T) ` +
96
+ `holds rows the markdown (${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T) lacks; ` +
97
+ `recover would delete them. Set GSD_RECOVER_ALLOW_DATA_LOSS=1 to override, ` +
98
+ `or run a DB-to-markdown rebuild instead.\n`);
99
+ return { exitCode: 1 };
100
+ }
101
+ // Snapshot the DB before the destructive clear so recover is reversible.
102
+ const backupPath = modules.backupDatabaseSnapshot('pre-recover');
78
103
  let counts;
79
104
  try {
80
105
  counts = modules.transaction(() => {
@@ -88,6 +113,36 @@ export async function handleRecover(basePath) {
88
113
  return { exitCode: 1 };
89
114
  }
90
115
  modules.invalidateStateCache();
91
- process.stderr.write(`gsd-recover: recovered ${counts.milestones}M/${counts.slices}S/${counts.tasks}T hierarchy\n`);
116
+ // Re-project markdown from the freshly imported DB so disk and DB agree.
117
+ // renderAllFromDb resolves even when individual artifacts fail, so inspect
118
+ // its error list — a silent projection failure must surface, not pass as a
119
+ // clean recover.
120
+ let projectionErrors = 0;
121
+ try {
122
+ const renderResult = await modules.renderAllFromDb(basePath);
123
+ projectionErrors = renderResult.errors.length;
124
+ if (projectionErrors > 0) {
125
+ process.stderr.write(`[headless] recover: ${projectionErrors} markdown projection(s) failed to render — markdown may be stale:\n`);
126
+ for (const e of renderResult.errors.slice(0, 10)) {
127
+ process.stderr.write(` - ${e}\n`);
128
+ }
129
+ }
130
+ }
131
+ catch (err) {
132
+ const msg = err instanceof Error ? err.message : String(err);
133
+ process.stderr.write(`[headless] recover: re-render after import failed: ${msg}\n`);
134
+ projectionErrors = 1;
135
+ }
136
+ if (counts.milestones < markdown.milestones ||
137
+ counts.slices < markdown.slices ||
138
+ counts.tasks < markdown.tasks) {
139
+ process.stderr.write(`[headless] recover: imported fewer rows than markdown contained ` +
140
+ `(${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T on disk); some markdown may have failed to parse\n`);
141
+ }
142
+ if (backupPath) {
143
+ process.stderr.write(`[headless] recover: pre-recover snapshot saved to ${backupPath}\n`);
144
+ }
145
+ process.stderr.write(`gsd-recover: recovered ${counts.milestones}M/${counts.slices}S/${counts.tasks}T hierarchy` +
146
+ `${projectionErrors > 0 ? ` (${projectionErrors} projection error(s) — run rebuild markdown)` : ''}\n`);
92
147
  return { exitCode: 0 };
93
148
  }
@@ -1 +1 @@
1
- fe5e2f79a21b7d4e
1
+ fc4ebdcd3723ade9
@@ -435,11 +435,27 @@ function formatManagedBrowserError(toolName, error) {
435
435
  return [
436
436
  `gsd-browser engine or tool unavailable for ${toolName}: ${message}`,
437
437
  "",
438
- "GSD browser automation now uses the managed gsd-browser engine by default.",
438
+ "The managed gsd-browser engine is enabled for this session but is unavailable.",
439
439
  "Run /gsd doctor or reinstall dependencies so @opengsd/gsd-browser is available.",
440
- "Set GSD_BROWSER_ENGINE=legacy only when you intentionally need the Playwright compatibility engine.",
440
+ "Unset GSD_BROWSER_ENGINE or set GSD_BROWSER_ENGINE=playwright to use the default Playwright engine.",
441
441
  ].join("\n");
442
442
  }
443
+ /**
444
+ * Eagerly establish the managed gsd-browser connection so browser tools are
445
+ * ready before first use. Best-effort: returns the error instead of throwing so
446
+ * callers (e.g. session-start warm-up) can surface a warning without failing the
447
+ * session. Connecting only spawns the gsd-browser MCP daemon; it does not launch
448
+ * Chrome (that happens lazily on the first navigation).
449
+ */
450
+ export async function warmUpManagedGsdBrowser(ctx, signal) {
451
+ try {
452
+ await getOrConnectManagedGsdBrowser(ctx, signal);
453
+ return { ok: true };
454
+ }
455
+ catch (error) {
456
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
457
+ }
458
+ }
443
459
  export function registerManagedGsdBrowserTools(pi) {
444
460
  for (const tool of MANAGED_BROWSER_TOOLS) {
445
461
  pi.registerTool({
@@ -1,4 +1,4 @@
1
- const DEFAULT_BROWSER_ENGINE = "gsd-browser";
1
+ const DEFAULT_BROWSER_ENGINE = "legacy";
2
2
  export function resolveBrowserEngineMode(env = process.env) {
3
3
  const raw = env.GSD_BROWSER_ENGINE?.trim();
4
4
  if (!raw)
@@ -2,7 +2,7 @@
2
2
  "id": "browser-tools",
3
3
  "name": "Browser Tools",
4
4
  "version": "1.0.0",
5
- "description": "GSD browser automation contract adapter backed by the managed gsd-browser engine",
5
+ "description": "GSD browser automation contract adapter backed by Playwright with optional managed gsd-browser support",
6
6
  "tier": "bundled",
7
7
  "requires": { "platform": ">=2.29.0" },
8
8
  "provides": {
@@ -1,7 +1,9 @@
1
1
  /** browser-tools — Pi Browser Automation Contract adapter. */
2
2
  import { importExtensionModule } from "@gsd/pi-coding-agent";
3
- import { closeManagedGsdBrowser, registerManagedGsdBrowserTools } from "./engine/managed-gsd-browser.js";
3
+ import { closeManagedGsdBrowser, registerManagedGsdBrowserTools, warmUpManagedGsdBrowser } from "./engine/managed-gsd-browser.js";
4
4
  import { resolveBrowserEngineMode } from "./engine/selection.js";
5
+ import { setArtifactRootForCwd } from "./state.js";
6
+ import { detectWebApp } from "./web-app-detect.js";
5
7
  let legacyRegistrationPromise = null;
6
8
  let managedRegistrationPromise = null;
7
9
  let registeredEngine = null;
@@ -83,28 +85,29 @@ async function registerLegacyBrowserTools(pi) {
83
85
  sanitizeArtifactName: utils.sanitizeArtifactName,
84
86
  formatArtifactTimestamp: utils.formatArtifactTimestamp,
85
87
  };
86
- navigation.registerNavigationTools(pi, deps);
87
- screenshot.registerScreenshotTools(pi, deps);
88
- interaction.registerInteractionTools(pi, deps);
89
- inspection.registerInspectionTools(pi, deps);
90
- session.registerSessionTools(pi, deps);
91
- assertions.registerAssertionTools(pi, deps);
92
- refTools.registerRefTools(pi, deps);
93
- wait.registerWaitTools(pi, deps);
94
- pages.registerPageTools(pi, deps);
95
- forms.registerFormTools(pi, deps);
96
- intent.registerIntentTools(pi, deps);
97
- pdf.registerPdfTools(pi, deps);
98
- statePersistence.registerStatePersistenceTools(pi, deps);
99
- networkMock.registerNetworkMockTools(pi, deps);
100
- device.registerDeviceTools(pi, deps);
101
- extract.registerExtractTools(pi, deps);
102
- visualDiff.registerVisualDiffTools(pi, deps);
103
- zoom.registerZoomTools(pi, deps);
104
- codegen.registerCodegenTools(pi, deps);
105
- actionCache.registerActionCacheTools(pi, deps);
106
- injectionDetection.registerInjectionDetectionTools(pi, deps);
107
- verify.registerVerifyTools(pi, deps);
88
+ const cwdScopedPi = withBrowserArtifactCwdScope(pi);
89
+ navigation.registerNavigationTools(cwdScopedPi, deps);
90
+ screenshot.registerScreenshotTools(cwdScopedPi, deps);
91
+ interaction.registerInteractionTools(cwdScopedPi, deps);
92
+ inspection.registerInspectionTools(cwdScopedPi, deps);
93
+ session.registerSessionTools(cwdScopedPi, deps);
94
+ assertions.registerAssertionTools(cwdScopedPi, deps);
95
+ refTools.registerRefTools(cwdScopedPi, deps);
96
+ wait.registerWaitTools(cwdScopedPi, deps);
97
+ pages.registerPageTools(cwdScopedPi, deps);
98
+ forms.registerFormTools(cwdScopedPi, deps);
99
+ intent.registerIntentTools(cwdScopedPi, deps);
100
+ pdf.registerPdfTools(cwdScopedPi, deps);
101
+ statePersistence.registerStatePersistenceTools(cwdScopedPi, deps);
102
+ networkMock.registerNetworkMockTools(cwdScopedPi, deps);
103
+ device.registerDeviceTools(cwdScopedPi, deps);
104
+ extract.registerExtractTools(cwdScopedPi, deps);
105
+ visualDiff.registerVisualDiffTools(cwdScopedPi, deps);
106
+ zoom.registerZoomTools(cwdScopedPi, deps);
107
+ codegen.registerCodegenTools(cwdScopedPi, deps);
108
+ actionCache.registerActionCacheTools(cwdScopedPi, deps);
109
+ injectionDetection.registerInjectionDetectionTools(cwdScopedPi, deps);
110
+ verify.registerVerifyTools(cwdScopedPi, deps);
108
111
  })().catch((error) => {
109
112
  legacyRegistrationPromise = null;
110
113
  throw error;
@@ -112,6 +115,21 @@ async function registerLegacyBrowserTools(pi) {
112
115
  }
113
116
  return legacyRegistrationPromise;
114
117
  }
118
+ function withBrowserArtifactCwdScope(pi) {
119
+ return {
120
+ ...pi,
121
+ registerTool(definition) {
122
+ pi.registerTool({
123
+ ...definition,
124
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
125
+ if (ctx?.cwd)
126
+ setArtifactRootForCwd(ctx.cwd);
127
+ return definition.execute(toolCallId, params, signal, onUpdate, ctx);
128
+ },
129
+ });
130
+ },
131
+ };
132
+ }
115
133
  async function registerBrowserTools(pi) {
116
134
  const engine = resolveBrowserEngineMode();
117
135
  if (engine === "off")
@@ -147,6 +165,29 @@ async function registerBrowserTools(pi) {
147
165
  throw error;
148
166
  }
149
167
  }
168
+ function isWarmUpDisabled() {
169
+ const value = process.env.GSD_BROWSER_WARMUP?.trim().toLowerCase();
170
+ return value === "0" || value === "false" || value === "off";
171
+ }
172
+ /**
173
+ * Auto-initialize the managed gsd-browser engine only when explicitly selected
174
+ * for a web app. Best-effort and non-blocking: warm-up runs in the background
175
+ * and only surfaces a warning if it fails.
176
+ */
177
+ function maybeWarmUpManagedEngine(pi, ctx) {
178
+ if (isWarmUpDisabled())
179
+ return;
180
+ if (resolveBrowserEngineMode() !== "gsd-browser")
181
+ return;
182
+ const projectRoot = ctx.cwd || process.cwd();
183
+ if (!detectWebApp(projectRoot))
184
+ return;
185
+ void warmUpManagedGsdBrowser(ctx).then((result) => {
186
+ if (!result.ok && ctx.hasUI) {
187
+ ctx.ui.notify(`gsd-browser auto-init failed: ${result.error}. Browser UAT tools will retry on first use; run /gsd doctor if this persists.`, "warning");
188
+ }
189
+ });
190
+ }
150
191
  async function closeActiveBrowserEngines() {
151
192
  await closeManagedGsdBrowser();
152
193
  if (legacyRegistrationPromise) {
@@ -157,12 +198,15 @@ async function closeActiveBrowserEngines() {
157
198
  export default function (pi) {
158
199
  pi.on("session_start", async (_event, ctx) => {
159
200
  if (ctx.hasUI) {
160
- void registerBrowserTools(pi).catch((error) => {
201
+ void registerBrowserTools(pi)
202
+ .then(() => maybeWarmUpManagedEngine(pi, ctx))
203
+ .catch((error) => {
161
204
  ctx.ui.notify(`browser-tools failed to load: ${error instanceof Error ? error.message : String(error)}`, "warning");
162
205
  });
163
206
  return;
164
207
  }
165
208
  await registerBrowserTools(pi);
209
+ maybeWarmUpManagedEngine(pi, ctx);
166
210
  });
167
211
  pi.on("session_shutdown", async () => {
168
212
  await closeActiveBrowserEngines();
@@ -18,6 +18,17 @@ export const HAR_FILENAME = "session.har";
18
18
  // ---------------------------------------------------------------------------
19
19
  // Mutable state variables — accessed only via get/set functions
20
20
  // ---------------------------------------------------------------------------
21
+ // 0. artifactRoot
22
+ let _artifactRoot = ARTIFACT_ROOT;
23
+ export function getArtifactRoot() { return _artifactRoot; }
24
+ export function setArtifactRootForCwd(cwd) {
25
+ const newRoot = path.resolve(cwd, ".artifacts", "browser");
26
+ if (newRoot !== _artifactRoot) {
27
+ _artifactRoot = newRoot;
28
+ _sessionArtifactDir = null;
29
+ }
30
+ return _artifactRoot;
31
+ }
21
32
  // 1. browser
22
33
  let _browser = null;
23
34
  export function getBrowser() { return _browser; }
@@ -102,6 +113,7 @@ export function setHarState(h) { _harState = h; }
102
113
  // resetAllState — mirrors closeBrowser()'s reset logic
103
114
  // ---------------------------------------------------------------------------
104
115
  export function resetAllState() {
116
+ _artifactRoot = ARTIFACT_ROOT;
105
117
  _browser = null;
106
118
  _context = null;
107
119
  pageRegistry.pages = [];
@@ -2,7 +2,7 @@ import { Type } from "@sinclair/typebox";
2
2
  import { stat } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { formatTimelineEntries, buildFailureHypothesis, summarizeBrowserSession, } from "../core.js";
5
- import { ARTIFACT_ROOT, HAR_FILENAME, getPageRegistry, getConsoleLogs, getNetworkLogs, getDialogLogs, getActionTimeline, getActiveTraceSession, setActiveTraceSession, getHarState, setHarState, getSessionStartedAt, getSessionArtifactDir, } from "../state.js";
5
+ import { HAR_FILENAME, getArtifactRoot, getPageRegistry, getConsoleLogs, getNetworkLogs, getDialogLogs, getActionTimeline, getActiveTraceSession, setActiveTraceSession, getHarState, setHarState, getSessionStartedAt, getSessionArtifactDir, } from "../state.js";
6
6
  import { getActiveFrameMetadata, ensureDir, } from "../utils.js";
7
7
  export function registerSessionTools(pi, deps) {
8
8
  // -------------------------------------------------------------------------
@@ -289,7 +289,8 @@ export function registerSessionTools(pi, deps) {
289
289
  const { page: p } = await deps.ensureBrowser();
290
290
  const startedAt = Date.now();
291
291
  const sessionDir = await deps.ensureSessionArtifactDir();
292
- const bundleDir = path.join(ARTIFACT_ROOT, `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`);
292
+ const bundleName = `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`;
293
+ const bundleDir = path.join(getArtifactRoot(), bundleName);
293
294
  await ensureDir(bundleDir);
294
295
  const pages = await deps.getLivePagesSnapshot();
295
296
  const actionTimeline = getActionTimeline();
@@ -8,7 +8,7 @@ import { mkdir, stat, writeFile, copyFile } from "node:fs/promises";
8
8
  import path from "node:path";
9
9
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, } from "@gsd/pi-coding-agent";
10
10
  import { beginAction, finishAction, findAction, toActionParamsSummary, registryListPages, } from "./core.js";
11
- import { ARTIFACT_ROOT, getActiveFrame, getActiveTraceSession, getConsoleLogs, getDialogLogs, getHarState, getNetworkLogs, getSessionArtifactDir, getSessionStartedAt, setSessionArtifactDir, setSessionStartedAt, pageRegistry, actionTimeline, getPendingCriticalRequestsByPage, } from "./state.js";
11
+ import { getActiveFrame, getArtifactRoot, getActiveTraceSession, getConsoleLogs, getDialogLogs, getHarState, getNetworkLogs, getSessionArtifactDir, getSessionStartedAt, setSessionArtifactDir, setSessionStartedAt, pageRegistry, actionTimeline, getPendingCriticalRequestsByPage, } from "./state.js";
12
12
  // ---------------------------------------------------------------------------
13
13
  // Text truncation
14
14
  // ---------------------------------------------------------------------------
@@ -60,7 +60,7 @@ export async function ensureSessionArtifactDir() {
60
60
  return existing;
61
61
  }
62
62
  const startedAt = ensureSessionStartedAt();
63
- const dir = path.join(ARTIFACT_ROOT, `${formatArtifactTimestamp(startedAt)}-session`);
63
+ const dir = path.join(getArtifactRoot(), `${formatArtifactTimestamp(startedAt)}-session`);
64
64
  setSessionArtifactDir(dir);
65
65
  await ensureDir(dir);
66
66
  return dir;
@@ -95,7 +95,7 @@ export function getActiveFrameMetadata() {
95
95
  }
96
96
  export function getSessionArtifactMetadata() {
97
97
  return {
98
- artifactRoot: ARTIFACT_ROOT,
98
+ artifactRoot: getArtifactRoot(),
99
99
  sessionStartedAt: getSessionStartedAt(),
100
100
  sessionArtifactDir: getSessionArtifactDir(),
101
101
  activeTraceSession: getActiveTraceSession(),
@@ -0,0 +1,52 @@
1
+ /**
2
+ * web-app-detect — lightweight, synchronous heuristic for deciding whether the
3
+ * project under development is a web app. Used only when the optional managed
4
+ * gsd-browser engine is selected and can be warmed before first use.
5
+ */
6
+ import { existsSync, readFileSync } from "node:fs";
7
+ import { resolve } from "node:path";
8
+ // Frontend frameworks / bundlers whose presence in dependencies indicates a
9
+ // browser-facing web app worth warming the optional managed engine for.
10
+ const WEB_DEPENDENCY_RE = /^(react|react-dom|next|nuxt|vue|@vue\/|svelte|@sveltejs\/|solid-js|astro|@remix-run\/|gatsby|preact|@angular\/core|vite|@vitejs\/|@builder\.io\/qwik|@web\/dev-server|@11ty\/eleventy)/;
11
+ // package.json scripts that imply a dev server / browser-facing build.
12
+ const WEB_SCRIPT_RE = /\b(vite|next|nuxt|astro|remix|webpack(-dev-server)?|parcel|ng serve|serve\b|http-server|live-server|gatsby)\b/;
13
+ function readPackageJson(projectRoot) {
14
+ const packageJsonPath = resolve(projectRoot, "package.json");
15
+ if (!existsSync(packageJsonPath))
16
+ return null;
17
+ try {
18
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
19
+ return parsed && typeof parsed === "object" ? parsed : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ function dependencyNames(pkg) {
26
+ return [
27
+ ...Object.keys(pkg.dependencies ?? {}),
28
+ ...Object.keys(pkg.devDependencies ?? {}),
29
+ ...Object.keys(pkg.peerDependencies ?? {}),
30
+ ];
31
+ }
32
+ /**
33
+ * Returns true when the project looks like a browser-facing web app. Conservative
34
+ * and dependency-free: a false negative just means lazy connection (the prior
35
+ * behavior); a false positive only warms an idle engine connection.
36
+ */
37
+ export function detectWebApp(projectRoot) {
38
+ const pkg = readPackageJson(projectRoot);
39
+ if (pkg) {
40
+ if (dependencyNames(pkg).some((name) => WEB_DEPENDENCY_RE.test(name)))
41
+ return true;
42
+ const scriptValues = Object.values(pkg.scripts ?? {}).filter((value) => typeof value === "string");
43
+ if (scriptValues.some((script) => WEB_SCRIPT_RE.test(script)))
44
+ return true;
45
+ }
46
+ // No package.json signal — fall back to a top-level index.html (static sites).
47
+ if (existsSync(resolve(projectRoot, "index.html")))
48
+ return true;
49
+ if (existsSync(resolve(projectRoot, "public", "index.html")))
50
+ return true;
51
+ return false;
52
+ }
@@ -1106,7 +1106,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
1106
1106
  unitId: iterData.unitId,
1107
1107
  });
1108
1108
  const finalizeReason = finalizeResult.action === "break" ? finalizeResult.reason : undefined;
1109
- const finalizeStatus = finalizeReason === "step-wizard"
1109
+ const finalizeStatus = (finalizeReason === "step-wizard" || finalizeReason === "milestone-complete")
1110
1110
  ? "completed"
1111
1111
  : finalizeResult.action === "next"
1112
1112
  ? "completed"
@@ -1172,7 +1172,9 @@ export async function autoLoop(ctx, pi, s, deps, options) {
1172
1172
  stuckStatePersistedThisIteration = true;
1173
1173
  finishTurn("completed");
1174
1174
  if (finalizeDecision.action === "complete-and-break") {
1175
- s.preserveStepSurfaceAfterLoopExit = true;
1175
+ if (!s.completionStopInProgress) {
1176
+ s.preserveStepSurfaceAfterLoopExit = true;
1177
+ }
1176
1178
  break;
1177
1179
  }
1178
1180
  }
@@ -16,6 +16,8 @@ import { detectStuck } from "./detect-stuck.js";
16
16
  import { runUnit } from "./run-unit.js";
17
17
  import { debugLog } from "../debug-logger.js";
18
18
  import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
19
+ import { buildManualValidationGuidance } from "../worktree-manager.js";
20
+ import { relSliceFile } from "../paths.js";
19
21
  import { classifyProject } from "../detection.js";
20
22
  import { MergeConflictError } from "../git-service.js";
21
23
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
@@ -47,6 +49,7 @@ import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
47
49
  import { getContextPauseAction } from "../auto-budget.js";
48
50
  import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, supportsStructuredQuestions, } from "../workflow-mcp.js";
49
51
  import { prepareWorkflowMcpForProject } from "../workflow-mcp-auto-prep.js";
52
+ import { getToolBaselineSnapshot, applyThinkingLevelForModel, floorThinkingLevelForUnit } from "../auto-model-selection.js";
50
53
  import { resolveManifest } from "../unit-context-manifest.js";
51
54
  import { createWorktreeSafetyModule } from "../worktree-safety.js";
52
55
  import { isSuspiciousGhostCompletion } from "../auto-unit-closeout.js";
@@ -302,6 +305,8 @@ async function validateSourceWriteWorktreeSafety(ic, unitType, unitId, milestone
302
305
  // ─── Session timeout auto-resume state ────────────────────────────────────────
303
306
  let consecutiveSessionTimeouts = 0;
304
307
  const MAX_SESSION_TIMEOUT_AUTO_RESUMES = 3;
308
+ /** Maximum zero-tool-call retries before pausing — context exhaustion is deterministic. */
309
+ const MAX_ZERO_TOOL_RETRIES = 1;
305
310
  export function resetSessionTimeoutState() {
306
311
  consecutiveSessionTimeouts = 0;
307
312
  }
@@ -388,7 +393,7 @@ async function generateMilestoneReport(s, ctx, milestoneId) {
388
393
  async function closeoutAndStop(ctx, pi, s, deps, reason) {
389
394
  if (s.currentUnit) {
390
395
  await deps.closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
391
- s.currentUnit = null;
396
+ s.clearCurrentUnit();
392
397
  }
393
398
  await deps.stopAuto(ctx, pi, reason);
394
399
  }
@@ -576,7 +581,7 @@ async function failClosedOnFinalizeTimeout(ic, iterData, loopState, stage, start
576
581
  });
577
582
  ctx.ui.notify(`${stage === "pre" ? "postUnitPreVerification" : "postUnitPostVerification"} timed out after ${timeoutMs / 1000}s for ${unitType} ${unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — pausing auto-mode for recovery.`, "warning");
578
583
  await deps.pauseAuto(ctx, pi);
579
- s.currentUnit = null;
584
+ s.clearCurrentUnit();
580
585
  clearCurrentPhase();
581
586
  drainLogs();
582
587
  return { action: "break", reason: progressKind };
@@ -1024,7 +1029,7 @@ export async function runPreDispatch(ic, loopState) {
1024
1029
  deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
1025
1030
  if (s.currentUnit) {
1026
1031
  await deps.closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
1027
- s.currentUnit = null;
1032
+ s.clearCurrentUnit();
1028
1033
  }
1029
1034
  await deps.stopAuto(ctx, pi, `Milestone ${mid} complete`, {
1030
1035
  completionWidget: {
@@ -1070,7 +1075,13 @@ export async function runDispatch(ic, preData, loopState) {
1070
1075
  const authMode = provider && typeof ctx.modelRegistry?.getProviderAuthMode === "function"
1071
1076
  ? ctx.modelRegistry.getProviderAuthMode(provider)
1072
1077
  : undefined;
1073
- const activeTools = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
1078
+ // Use the baseline snapshot rather than the live active-tool set: a prior
1079
+ // unit's per-provider narrowing (hook overrides, Groq 128-tool cap, etc.)
1080
+ // can strip required MCP tools from the live set even though
1081
+ // selectAndApplyModel will restore them before the unit is dispatched.
1082
+ // Checking a stale-narrowed set causes false transport-preflight warnings
1083
+ // that repeat on every /gsd auto resume (#477 follow-up).
1084
+ const activeTools = getToolBaselineSnapshot(pi);
1074
1085
  // Deep planning intentionally keeps human checkpoints in plain chat. In
1075
1086
  // Claude Code/local MCP transports, structured question requests can be
1076
1087
  // cancelled outside the normal chat flow, which made approval gates easy to
@@ -1093,6 +1104,9 @@ export async function runDispatch(ic, preData, loopState) {
1093
1104
  sessionContextWindow: ctx.model?.contextWindow,
1094
1105
  sessionProvider: ctx.model?.provider,
1095
1106
  modelRegistry: ctx.modelRegistry,
1107
+ activeTools,
1108
+ sessionBaseUrl: ctx.model?.baseUrl,
1109
+ sessionAuthMode: authMode,
1096
1110
  });
1097
1111
  if (isUnhandledPhaseWarning(dispatchResult)) {
1098
1112
  deps.invalidateAllCaches();
@@ -1116,6 +1130,9 @@ export async function runDispatch(ic, preData, loopState) {
1116
1130
  sessionContextWindow: ctx.model?.contextWindow,
1117
1131
  sessionProvider: ctx.model?.provider,
1118
1132
  modelRegistry: ctx.modelRegistry,
1133
+ activeTools,
1134
+ sessionBaseUrl: ctx.model?.baseUrl,
1135
+ sessionAuthMode: authMode,
1119
1136
  });
1120
1137
  }
1121
1138
  if (dispatchResult.action === "stop") {
@@ -1695,9 +1712,16 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1695
1712
  if (match) {
1696
1713
  const ok = await pi.setModel(match, { persist: false });
1697
1714
  if (ok) {
1698
- if (s.autoModeStartThinkingLevel) {
1699
- pi.setThinkingLevel(s.autoModeStartThinkingLevel);
1700
- }
1715
+ // Apply the per-phase reasoning effort selectAndApplyModel resolved for
1716
+ // this unit — not the auto-start session snapshot — but route it through
1717
+ // the same floor + capability-clamp pipeline against the *hook* model
1718
+ // (ADR-026). The hook override can pick a different model family than the
1719
+ // one selectAndApplyModel clamped against, so re-clamping here prevents
1720
+ // sending an unsupported level; the floor fills in when no phase level
1721
+ // resolved so a hook-overridden execute-task still meets the floor.
1722
+ const hookThinkingBase = modelResult.appliedThinkingLevel
1723
+ ?? floorThinkingLevelForUnit(unitType, s.autoModeStartThinkingLevel);
1724
+ applyThinkingLevelForModel(pi, hookThinkingBase, match, ctx);
1701
1725
  s.currentUnitModel = match;
1702
1726
  ctx.ui.notify(`Hook model override: ${match.provider}/${match.id}`, "info");
1703
1727
  }
@@ -1766,7 +1790,21 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1766
1790
  _resetLogs();
1767
1791
  const unitStartedAt = Date.now();
1768
1792
  s.unitDispatchCount.set(dispatchKey, nextDispatchCount);
1769
- s.currentUnit = { type: unitType, id: unitId, startedAt: unitStartedAt, workspaceRoot: s.basePath };
1793
+ s.setCurrentUnit({ type: unitType, id: unitId, startedAt: unitStartedAt, workspaceRoot: s.basePath });
1794
+ if (unitType === "execute-task") {
1795
+ const { milestone, slice, task } = parseUnitId(unitId);
1796
+ if (milestone && slice && task && isDbAvailable()) {
1797
+ try {
1798
+ const taskRow = getTask(milestone, slice, task);
1799
+ if (taskRow)
1800
+ s.sourceObservations.observePlanTask(taskRow);
1801
+ }
1802
+ catch (err) {
1803
+ const message = err instanceof Error ? err.message : String(err);
1804
+ logWarning("prompt", `failed to preload source observations for ${unitId}: ${message}`);
1805
+ }
1806
+ }
1807
+ }
1770
1808
  s.rootWriteBaseline = isIsolatedWorktreeSession(s)
1771
1809
  ? captureRootDirtySnapshot(s.originalBasePath)
1772
1810
  : null;
@@ -1852,7 +1890,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1852
1890
  category: "unknown",
1853
1891
  isTransient: true,
1854
1892
  });
1855
- s.currentUnit = null;
1893
+ s.clearCurrentUnit();
1856
1894
  await deps.pauseAuto(ctx, pi);
1857
1895
  return { action: "break", reason: "ghost-completion" };
1858
1896
  }
@@ -2059,13 +2097,23 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
2059
2097
  });
2060
2098
  }
2061
2099
  else {
2100
+ const zeroToolKey = `${unitType}/${unitId}`;
2101
+ const attempt = (s.zeroToolRetryCount.get(zeroToolKey) ?? 0) + 1;
2062
2102
  debugLog("runUnitPhase", {
2063
2103
  phase: "zero-tool-calls",
2064
2104
  unitType,
2065
2105
  unitId,
2106
+ attempt,
2066
2107
  warning: "Unit completed with 0 tool calls — likely context exhaustion, marking as failed",
2067
2108
  });
2068
- ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls — context exhaustion, will retry`, "warning");
2109
+ if (attempt > MAX_ZERO_TOOL_RETRIES) {
2110
+ s.zeroToolRetryCount.delete(zeroToolKey);
2111
+ ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls — context exhaustion, pausing auto-mode after ${MAX_ZERO_TOOL_RETRIES} retry.`, "error");
2112
+ await deps.pauseAuto(ctx, pi);
2113
+ return { action: "break", reason: "zero-tool-calls-exhausted" };
2114
+ }
2115
+ s.zeroToolRetryCount.set(zeroToolKey, attempt);
2116
+ ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls — context exhaustion, will retry (attempt ${attempt}/${MAX_ZERO_TOOL_RETRIES})`, "warning");
2069
2117
  return {
2070
2118
  action: "retry",
2071
2119
  reason: "zero-tool-calls",
@@ -2087,6 +2135,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
2087
2135
  if (artifactVerified) {
2088
2136
  s.unitDispatchCount.delete(dispatchKey);
2089
2137
  s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
2138
+ s.zeroToolRetryCount.delete(dispatchKey);
2090
2139
  }
2091
2140
  // Write phase handoff anchor after successful research/planning completion
2092
2141
  const anchorPhases = new Set(["research-milestone", "research-slice", "plan-milestone", "plan-slice"]);
@@ -2176,7 +2225,7 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
2176
2225
  s.currentUnit?.type === preUnitSnapshot.type &&
2177
2226
  s.currentUnit?.id === preUnitSnapshot.id &&
2178
2227
  s.currentUnit?.startedAt === preUnitSnapshot.startedAt) {
2179
- s.currentUnit = null;
2228
+ s.clearCurrentUnit();
2180
2229
  }
2181
2230
  s.rootWriteBaseline = null;
2182
2231
  };
@@ -2232,7 +2281,21 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
2232
2281
  }
2233
2282
  }
2234
2283
  if (pauseAfterUatDispatch) {
2235
- ctx.ui.notify("UAT requires human execution. Auto-mode will pause after this unit writes the result file.", "info");
2284
+ const pauseMid = iterData.mid;
2285
+ const pauseSliceId = pauseMid && iterData.unitId.startsWith(`${pauseMid}/`)
2286
+ ? iterData.unitId.slice(pauseMid.length + 1)
2287
+ : undefined;
2288
+ const guidance = pauseMid
2289
+ ? buildManualValidationGuidance(s.basePath, pauseMid, {
2290
+ uatPath: pauseSliceId
2291
+ ? relSliceFile(s.basePath, pauseMid, pauseSliceId, "UAT")
2292
+ : undefined,
2293
+ })
2294
+ : null;
2295
+ const pauseMessage = guidance
2296
+ ? `UAT requires human execution. Auto-mode will pause after this unit writes the result file.\n\n${guidance}`
2297
+ : "UAT requires human execution. Auto-mode will pause after this unit writes the result file.";
2298
+ ctx.ui.notify(pauseMessage, "info");
2236
2299
  await deps.pauseAuto(ctx, pi);
2237
2300
  debugLog("autoLoop", { phase: "exit", reason: "uat-pause" });
2238
2301
  clearFinalizingUnit();
@@ -2397,5 +2460,17 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
2397
2460
  ctx.ui.notify(formatForNotification(logs), severity);
2398
2461
  }
2399
2462
  }
2463
+ if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
2464
+ // cleanupAfterLoopExit skips gsd-progress when preserveCompletionSurface is true, so clear stale controls here.
2465
+ ctx.ui.setStatus?.("gsd-step", undefined);
2466
+ ctx.ui.setWidget?.("gsd-progress", undefined);
2467
+ await deps.stopAuto(ctx, pi, `Milestone ${s.currentMilestoneId} complete`, {
2468
+ completionWidget: {
2469
+ milestoneId: s.currentMilestoneId,
2470
+ milestoneTitle: iterData.midTitle,
2471
+ },
2472
+ });
2473
+ return { action: "break", reason: "milestone-complete" };
2474
+ }
2400
2475
  return { action: "next", data: undefined };
2401
2476
  }