@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
@@ -4,11 +4,11 @@
4
4
  * and fallback chains.
5
5
  */
6
6
 
7
- import type { Api, Model } from "@gsd/pi-ai";
8
- import { getProviderCapabilities } from "@gsd/pi-ai";
7
+ import type { Api, Model, ModelThinkingLevel } from "@gsd/pi-ai";
8
+ import { getProviderCapabilities, clampThinkingLevel } from "@gsd/pi-ai";
9
9
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
10
10
  import type { GSDPreferences } from "./preferences.js";
11
- import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
11
+ import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
12
12
  import type { ComplexityTier } from "./complexity-classifier.js";
13
13
  import { classifyUnitComplexity, extractTaskMetadata, tierLabel } from "./complexity-classifier.js";
14
14
  import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides, adjustToolSet, filterToolsForProvider } from "./model-router.js";
@@ -57,6 +57,12 @@ export interface ModelSelectionResult {
57
57
  routing: { tier: string; modelDowngraded: boolean } | null;
58
58
  /** Concrete model applied before dispatch so it can be restored after a fresh session. */
59
59
  appliedModel: Model<Api> | null;
60
+ /**
61
+ * Reasoning effort applied for this dispatch after per-phase resolution,
62
+ * floor, and capability clamping (ADR-026). Null when no level was applied
63
+ * (e.g. no start level captured). Surfaced for metrics/telemetry.
64
+ */
65
+ appliedThinkingLevel?: ReturnType<ExtensionAPI["getThinkingLevel"]> | null;
60
66
  }
61
67
 
62
68
  export interface PreferredModelConfig {
@@ -90,6 +96,32 @@ export function clearToolBaseline(pi: ExtensionAPI | object): void {
90
96
  TOOL_BASELINE.delete(pi as unknown as object);
91
97
  }
92
98
 
99
+ /**
100
+ * Return the union of the pre-dispatch baseline tool set and the current live
101
+ * active tools, or just the live tools when no baseline has been recorded yet.
102
+ *
103
+ * Use this instead of `pi.getActiveTools()` anywhere you need the full tool
104
+ * surface for a preflight/routing check that runs BEFORE `selectAndApplyModel`
105
+ * restores the baseline — e.g. in `runDispatch` and `decideNextUnit`.
106
+ *
107
+ * The union is intentional:
108
+ * - Baseline covers tools that a prior unit's per-provider narrowing (hook
109
+ * overrides, Groq 128-tool cap, etc.) has removed from the live set.
110
+ * Those tools will be restored by `selectAndApplyModel` before dispatch, so
111
+ * dropping them from the preflight check would be a false negative.
112
+ * - Live set covers tools connected after the baseline was first captured
113
+ * (e.g. MCP servers attached mid-session or after a paused resume).
114
+ * Without the live merge, a stale baseline permanently hides newly
115
+ * connected MCP tools and prevents transport-preflight from clearing on
116
+ * resume (#477 follow-up).
117
+ */
118
+ export function getToolBaselineSnapshot(pi: ExtensionAPI): string[] {
119
+ const live = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
120
+ const baseline = TOOL_BASELINE.get(pi as unknown as object);
121
+ if (baseline === undefined) return live;
122
+ return [...new Set([...baseline, ...live])];
123
+ }
124
+
93
125
  /**
94
126
  * Models eligible for the pre-dispatch policy gate. Prefer registry-available
95
127
  * models; when that list is empty (common after worktree resume before registry
@@ -252,12 +284,103 @@ function restoreToolBaseline(pi: ExtensionAPI): void {
252
284
  }
253
285
  }
254
286
 
255
- function reapplyThinkingLevel(
287
+ /**
288
+ * Apply the desired reasoning effort for the just-selected model, clamping to
289
+ * what the model actually supports (ADR-026). An unsupported level is never
290
+ * sent to the provider — it is clamped via `clampThinkingLevel` and the
291
+ * mismatch is surfaced once per (model, requested-level). Returns the level
292
+ * actually applied so callers can record it.
293
+ */
294
+ export function applyThinkingLevelForModel(
256
295
  pi: ExtensionAPI,
296
+ desired: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined,
297
+ model: Model<Api>,
298
+ ctx: ExtensionContext,
299
+ ): ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined {
300
+ if (!desired) return desired;
301
+ // Capability-clamp only when we have a bare string level AND the model
302
+ // advertises reasoning capability (`reasoning` is always present on real
303
+ // registry models). Richer host snapshot shapes (e.g. `{ effort: "high" }`)
304
+ // and partial model objects are applied verbatim — we never coerce an unknown
305
+ // shape into a string or guess capability we can't see.
306
+ if (typeof desired === "string" && model != null && typeof model === "object" && "reasoning" in model) {
307
+ const clamped = clampThinkingLevel(model, desired as ModelThinkingLevel) as ReturnType<ExtensionAPI["getThinkingLevel"]>;
308
+ pi.setThinkingLevel(clamped);
309
+ if (clamped !== desired) {
310
+ const key = `${model.provider}/${model.id}:${desired}`;
311
+ if (!_warnedThinkingClamp.has(key)) {
312
+ _warnedThinkingClamp.add(key);
313
+ ctx.ui.notify(
314
+ `Thinking level '${desired}' not supported by ${model.provider}/${model.id}; using '${clamped}'.`,
315
+ "warning",
316
+ );
317
+ }
318
+ }
319
+ return clamped;
320
+ }
321
+ pi.setThinkingLevel(desired);
322
+ return desired;
323
+ }
324
+
325
+ /** Warn-once guard for capability clamps, keyed by `provider/id:requested`. */
326
+ const _warnedThinkingClamp = new Set<string>();
327
+ /** Warn-once guard for the execute-task floor punch-through advisory. */
328
+ let _warnedExecuteTaskFloorBypass = false;
329
+
330
+ type EffectiveThinkingLevel = ReturnType<ExtensionAPI["getThinkingLevel"]>;
331
+
332
+ /**
333
+ * Ascending severity order for reasoning levels (matches @gsd/pi-agent-core
334
+ * `ThinkingLevel`). Used only for floor comparisons below.
335
+ */
336
+ const THINKING_LEVEL_ORDER: readonly EffectiveThinkingLevel[] = [
337
+ "off",
338
+ "minimal",
339
+ "low",
340
+ "medium",
341
+ "high",
342
+ "xhigh",
343
+ ] as EffectiveThinkingLevel[];
344
+
345
+ /**
346
+ * Minimum reasoning level for code-writing units.
347
+ *
348
+ * `execute-task` is the only unit that edits source. With a low/minimal
349
+ * thinking level a model does not plan its edits and compensates by re-reading
350
+ * the same files dozens of times per task (measured: index.html read ~49× in a
351
+ * single task on a minimal-thinking model) and shelling out to `nl`/`sed` to
352
+ * re-locate code after every edit invalidates its line numbers. Flooring the
353
+ * level for this unit type removes that read/bash thrash. Planning, research,
354
+ * and lifecycle units are unaffected.
355
+ */
356
+ const EXECUTE_TASK_MIN_THINKING_LEVEL: EffectiveThinkingLevel = "medium";
357
+
358
+ function thinkingLevelRank(level: EffectiveThinkingLevel): number {
359
+ const idx = THINKING_LEVEL_ORDER.indexOf(level);
360
+ return idx === -1 ? 0 : idx;
361
+ }
362
+
363
+ /**
364
+ * Raise (never lower) the thinking level for code-writing units to a sane
365
+ * floor. Returns the input unchanged for non-`execute-task` units, when no
366
+ * level was captured, or when the captured level already meets the floor.
367
+ */
368
+ export function floorThinkingLevelForUnit(
369
+ unitType: string,
257
370
  level: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined,
258
- ): void {
259
- if (!level) return;
260
- pi.setThinkingLevel(level);
371
+ ): ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined {
372
+ if (unitType !== "execute-task") return level;
373
+ if (!level) return level;
374
+ // Only act on the recognized string levels. Any other shape (e.g. a richer
375
+ // host snapshot object) is passed through untouched so we never coerce an
376
+ // unknown representation into a bare string the host can't apply.
377
+ if (!THINKING_LEVEL_ORDER.includes(level as EffectiveThinkingLevel)) {
378
+ return level;
379
+ }
380
+ if (thinkingLevelRank(level as EffectiveThinkingLevel) >= thinkingLevelRank(EXECUTE_TASK_MIN_THINKING_LEVEL)) {
381
+ return level;
382
+ }
383
+ return EXECUTE_TASK_MIN_THINKING_LEVEL;
261
384
  }
262
385
 
263
386
  export function resolvePreferredModelConfig(
@@ -328,6 +451,42 @@ export async function selectAndApplyModel(
328
451
  autoModeStartThinkingLevel?: ReturnType<ExtensionAPI["getThinkingLevel"]> | null,
329
452
  ): Promise<ModelSelectionResult> {
330
453
  const uokFlags = resolveUokFlags(prefs);
454
+ // Resolve reasoning effort for this dispatch (ADR-026). An explicit per-phase
455
+ // thinking config (inline `models.<phase>.thinking` or the separate `thinking`
456
+ // block) expresses hard user intent: it bypasses the execute-task floor and is
457
+ // honored verbatim, then capability-clamped per model at apply time below.
458
+ // With no explicit level, fall back to the auto-start session level and raise
459
+ // the code-writing floor — preserving prior behavior exactly. Recomputed per
460
+ // dispatch so neither the floor nor a phase override leaks to other units.
461
+ const explicitThinkingLevel =
462
+ resolveThinkingLevelForUnit(unitType) as ReturnType<ExtensionAPI["getThinkingLevel"]> | undefined;
463
+ const desiredThinkingLevel = explicitThinkingLevel
464
+ ?? floorThinkingLevelForUnit(unitType, autoModeStartThinkingLevel);
465
+ if (explicitThinkingLevel) {
466
+ if (
467
+ unitType === "execute-task" &&
468
+ thinkingLevelRank(explicitThinkingLevel) < thinkingLevelRank(EXECUTE_TASK_MIN_THINKING_LEVEL) &&
469
+ !_warnedExecuteTaskFloorBypass
470
+ ) {
471
+ _warnedExecuteTaskFloorBypass = true;
472
+ ctx.ui.notify(
473
+ `Explicit execution thinking '${explicitThinkingLevel}' is below the measured execute-task floor ` +
474
+ `(${EXECUTE_TASK_MIN_THINKING_LEVEL}); honoring it as configured. Low reasoning on code edits can ` +
475
+ `cause repeated file re-reads.`,
476
+ "warning",
477
+ );
478
+ }
479
+ } else if (
480
+ verbose &&
481
+ desiredThinkingLevel &&
482
+ desiredThinkingLevel !== autoModeStartThinkingLevel
483
+ ) {
484
+ ctx.ui.notify(
485
+ `Thinking level raised to ${desiredThinkingLevel} for ${unitType} (was ${autoModeStartThinkingLevel ?? "unset"})`,
486
+ "info",
487
+ );
488
+ }
489
+ let appliedThinkingLevel: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined = null;
331
490
  const effectiveSessionModelOverride = sessionModelOverride === undefined
332
491
  ? getSessionModelOverride(ctx.sessionManager.getSessionId())
333
492
  : (sessionModelOverride ?? undefined);
@@ -673,7 +832,7 @@ export async function selectAndApplyModel(
673
832
  const ok = await pi.setModel(model, { persist: false });
674
833
  if (ok) {
675
834
  appliedModel = model;
676
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
835
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, model, ctx);
677
836
 
678
837
  // ADR-005: Adjust active tool set for the selected model's provider capabilities.
679
838
  // Hard-filter incompatible tools, then let extensions override via adjust_tool_set hook.
@@ -733,7 +892,7 @@ export async function selectAndApplyModel(
733
892
  const ok = await pi.setModel(model, { persist: false });
734
893
  if (!ok) continue;
735
894
  appliedModel = model;
736
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
895
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, model, ctx);
737
896
  attemptedPolicyEligible = true;
738
897
  if (verbose) {
739
898
  ctx.ui.notify(
@@ -779,18 +938,37 @@ export async function selectAndApplyModel(
779
938
  const fallbackOk = await pi.setModel(byId, { persist: false });
780
939
  if (fallbackOk) {
781
940
  appliedModel = byId;
782
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
941
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, byId, ctx);
783
942
  }
784
943
  }
785
944
  } else {
786
945
  appliedModel = startModel;
787
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
946
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, startModel, ctx);
788
947
  }
789
948
  }
790
949
  }
791
950
  }
792
951
 
793
- return { routing, appliedModel };
952
+ // If no model branch applied a thinking level (e.g. interactive guided-flow
953
+ // with a `thinking:` block but no per-phase model and no start model), still
954
+ // honor an explicitly configured phase thinking level against the current
955
+ // session model. Only the explicit path runs here — the floored session
956
+ // default is intentionally left untouched so no-config interactive runs keep
957
+ // the user's /model thinking level. (ADR-026)
958
+ if (appliedThinkingLevel == null && explicitThinkingLevel && ctx.model) {
959
+ // Prefer the full registry model (carries reasoning capability so the level
960
+ // can be clamped); fall back to ctx.model. Always route through
961
+ // applyThinkingLevelForModel so the clamp runs whenever capability metadata
962
+ // exists — never a raw verbatim setThinkingLevel that bypasses it (ADR-026).
963
+ const current = resolveModelId(
964
+ `${ctx.model.provider}/${ctx.model.id}`,
965
+ ctx.modelRegistry?.getAvailable?.() ?? [],
966
+ ctx.model.provider,
967
+ ) ?? (ctx.model as Model<Api>);
968
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, explicitThinkingLevel, current, ctx);
969
+ }
970
+
971
+ return { routing, appliedModel, appliedThinkingLevel };
794
972
  }
795
973
 
796
974
  /**
@@ -1521,6 +1521,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1521
1521
  }
1522
1522
  }
1523
1523
 
1524
+ let blockingContentViolation: string | null = null;
1525
+
1524
1526
  // ── Safety harness: post-unit validation ──
1525
1527
  try {
1526
1528
  const { loadEffectiveGSDPreferences } = await import("./preferences.js");
@@ -1668,8 +1670,14 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1668
1670
  const artifactPath = resolveArtifactForContent(s.currentUnit.type, s.currentUnit.id, s.basePath);
1669
1671
  const contentViolations = validateContent(s.currentUnit.type, artifactPath);
1670
1672
  for (const v of contentViolations) {
1671
- logWarning("safety", `content: ${v.reason}`);
1672
- ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
1673
+ if (v.severity === "error") {
1674
+ blockingContentViolation ??= v.reason;
1675
+ logError("safety", `content: ${v.reason}`);
1676
+ ctx.ui.notify(`Content validation: ${v.reason}`, "error");
1677
+ } else {
1678
+ logWarning("safety", `content: ${v.reason}`);
1679
+ ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
1680
+ }
1673
1681
  }
1674
1682
  } catch (e) {
1675
1683
  debugLog("postUnit", { phase: "safety-content-validation", error: String(e) });
@@ -1868,6 +1876,16 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1868
1876
  }
1869
1877
  }
1870
1878
 
1879
+ if (blockingContentViolation && triggerArtifactVerified) {
1880
+ triggerArtifactVerified = false;
1881
+ debugLog("postUnit", {
1882
+ phase: "content-validation-blocked-artifact",
1883
+ unitType: s.currentUnit.type,
1884
+ unitId: s.currentUnit.id,
1885
+ reason: blockingContentViolation,
1886
+ });
1887
+ }
1888
+
1871
1889
  // When artifact verification fails for a unit type that has a known expected
1872
1890
  // artifact, ask the caller to retry so it re-dispatches with failure context
1873
1891
  // instead of blindly re-dispatching the same unit (#1571).
@@ -10,8 +10,8 @@
10
10
  */
11
11
 
12
12
  import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection, parseTaskPlanFile } from "./files.js";
13
- import type { Override, UatType } from "./files.js";
14
- import { hasVerdict, getUatType, extractVerdict } from "./verdict-parser.js";
13
+ import type { Override } from "./files.js";
14
+ import { hasVerdict, extractVerdict } from "./verdict-parser.js";
15
15
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
16
16
  import {
17
17
  resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
@@ -42,11 +42,11 @@ import { logWarning } from "./workflow-logger.js";
42
42
  import { inlineGraphSubgraph } from "./graph-context.js";
43
43
  import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
44
44
  import { classifyProject, type ProjectClassification } from "./detection.js";
45
- import { hasBrowserRequiredText } from "./browser-evidence.js";
46
45
  import { debugLog } from "./debug-logger.js";
47
46
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
48
47
  import { findMilestoneIds } from "./milestone-ids.js";
49
- import { buildRunUatResultPresentation, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
48
+ import { buildRunUatPresentationForType, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
49
+ import { resolveEffectiveUatType, shouldDispatchUatForContent, type UatType } from "./uat-policy.js";
50
50
 
51
51
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
52
52
 
@@ -286,19 +286,6 @@ function prependContextModeToBlock(
286
286
  return `${contextMode}\n\n${block}`;
287
287
  }
288
288
 
289
- function resolveEffectiveUatType(content: string): UatType {
290
- const uatType = getUatType(content);
291
- if (uatType === "artifact-driven" && hasBrowserRequiredText(content)) {
292
- return "browser-executable";
293
- }
294
- return uatType;
295
- }
296
-
297
- function shouldDispatchUatForContent(content: string, prefs: GSDPreferences | undefined): boolean {
298
- const uatType = resolveEffectiveUatType(content);
299
- return !!prefs?.uat_dispatch || uatType !== "artifact-driven" || hasBrowserRequiredText(content);
300
- }
301
-
302
289
  // ─── Executor Constraints ─────────────────────────────────────────────────────
303
290
 
304
291
  /**
@@ -3385,7 +3372,7 @@ export async function buildRunUatPrompt(
3385
3372
 
3386
3373
  const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "ASSESSMENT"));
3387
3374
  const uatType = resolveEffectiveUatType(uatContent);
3388
- const canonicalPresentation = JSON.stringify(buildRunUatResultPresentation(), null, 2);
3375
+ const canonicalPresentation = JSON.stringify(buildRunUatPresentationForType(uatType), null, 2);
3389
3376
 
3390
3377
  return loadPrompt("run-uat", {
3391
3378
  workingDirectory: base,
@@ -3543,11 +3530,25 @@ export async function buildReassessRoadmapPrompt(
3543
3530
 
3544
3531
  // ─── Reactive Execute Prompt ──────────────────────────────────────────────
3545
3532
 
3533
+ /**
3534
+ * Build the `with model: "…" and thinking: "…"` suffix injected into a prompt
3535
+ * that instructs the coordinator how to dispatch a `subagent` call. Either or
3536
+ * both may be absent (ADR-026 / #508).
3537
+ */
3538
+ function subagentCallSuffix(model?: string, thinking?: string): string {
3539
+ const parts: string[] = [];
3540
+ if (model) parts.push(`model: "${model}"`);
3541
+ if (thinking) parts.push(`thinking: "${thinking}"`);
3542
+ return parts.length > 0 ? ` with ${parts.join(" and ")}` : "";
3543
+ }
3544
+
3546
3545
  export async function buildReactiveExecutePrompt(
3547
3546
  mid: string, midTitle: string, sid: string, sTitle: string,
3548
3547
  readyTaskIds: string[], base: string,
3549
3548
  subagentModel?: string,
3550
- opts?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry; sessionProvider?: string },
3549
+ // Reasoning effort travels inside opts here (not as a positional param) so
3550
+ // existing positional `opts` callers don't shift (#508).
3551
+ opts?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry; sessionProvider?: string; subagentThinking?: string },
3551
3552
  ): Promise<string> {
3552
3553
  const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
3553
3554
 
@@ -3640,7 +3641,7 @@ export async function buildReactiveExecutePrompt(
3640
3641
  `When done, say: "Task ${tid} complete."`,
3641
3642
  ].join("\n");
3642
3643
 
3643
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3644
+ const modelSuffix = subagentCallSuffix(subagentModel, opts?.subagentThinking);
3644
3645
  subagentSections.push([
3645
3646
  `### ${tid}: ${tTitle}`,
3646
3647
  "",
@@ -3724,10 +3725,11 @@ export async function buildParallelResearchSlicesPrompt(
3724
3725
  slices: Array<{ id: string; title: string }>,
3725
3726
  basePath: string,
3726
3727
  subagentModel?: string,
3728
+ subagentThinking?: string,
3727
3729
  ): Promise<string> {
3728
3730
  // Build individual research-slice prompts for each slice
3729
3731
  const subagentSections: string[] = [];
3730
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3732
+ const modelSuffix = subagentCallSuffix(subagentModel, subagentThinking);
3731
3733
  for (const slice of slices) {
3732
3734
  const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath, { contextModeRenderMode: "nested" });
3733
3735
  subagentSections.push([
@@ -3755,6 +3757,7 @@ export async function buildGateEvaluatePrompt(
3755
3757
  mid: string, midTitle: string, sid: string, sTitle: string,
3756
3758
  base: string,
3757
3759
  subagentModel?: string,
3760
+ subagentThinking?: string,
3758
3761
  ): Promise<string> {
3759
3762
  // Pull only the gates this turn actually owns (Q3/Q4). Filter via the
3760
3763
  // registry so that scope:"slice" gates owned by other turns (Q8) can't
@@ -3811,7 +3814,7 @@ export async function buildGateEvaluatePrompt(
3811
3814
  "- `findings`: detailed markdown findings (or empty if omitted)",
3812
3815
  ].join("\n");
3813
3816
 
3814
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3817
+ const modelSuffix = subagentCallSuffix(subagentModel, subagentThinking);
3815
3818
  subagentSections.push([
3816
3819
  `### ${def.id}: ${def.question}`,
3817
3820
  "",
@@ -15,7 +15,25 @@ import { appendEvent } from "./workflow-events.js";
15
15
  import { atomicWriteSync } from "./atomic-write.js";
16
16
  import { clearParseCache } from "./files.js";
17
17
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
18
- import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, getMilestoneSlices, getLatestAssessmentByScope, updateMilestoneStatus, refreshOpenDatabaseFromDisk, getCompletedMilestoneTaskFileHints, getMilestoneCommitAttributionShas, recordMilestoneCommitAttribution, transaction } from "./gsd-db.js";
18
+ import {
19
+ isDbAvailable,
20
+ getTask,
21
+ getSlice,
22
+ getSliceTasks,
23
+ getPendingGatesForTurn,
24
+ updateTaskStatus,
25
+ updateSliceStatus,
26
+ insertSlice,
27
+ getMilestone,
28
+ getMilestoneSlices,
29
+ getLatestAssessmentByScope,
30
+ updateMilestoneStatus,
31
+ refreshOpenDatabaseFromDisk,
32
+ getCompletedMilestoneTaskFileHints,
33
+ getMilestoneCommitAttributionShas,
34
+ recordMilestoneCommitAttribution,
35
+ transaction,
36
+ } from "./gsd-db.js";
19
37
  import { isValidationTerminal } from "./state.js";
20
38
  import { getErrorMessage } from "./error-utils.js";
21
39
  import { logWarning, logError } from "./workflow-logger.js";
@@ -390,8 +408,9 @@ export function verifyExpectedArtifact(
390
408
  if (gateIds.length === 0) return true;
391
409
 
392
410
  try {
393
- const pending = getPendingGates(mid, sid, "slice");
394
- const pendingIds = new Set(pending.map((g: any) => g.gate_id));
411
+ if (!isDbAvailable()) return false;
412
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
413
+ const pendingIds = new Set<string>(pending.map((g) => g.gate_id));
395
414
  // All dispatched gates must no longer be pending
396
415
  for (const gid of gateIds) {
397
416
  if (pendingIds.has(gid)) return false;
@@ -1,6 +1,7 @@
1
1
  // GSD auto-mode runtime state
2
2
  import { AutoSession } from "./auto/session.js";
3
3
  import type { CurrentUnit } from "./auto/session.js";
4
+ import type { SourceObservationStore } from "./source-observations.js";
4
5
  import {
5
6
  isDeterministicPolicyError,
6
7
  isQueuedUserMessageSkip,
@@ -65,3 +66,7 @@ export function clearToolInvocationError(): void {
65
66
  if (!autoSession.active) return;
66
67
  autoSession.lastToolInvocationError = null;
67
68
  }
69
+
70
+ export function getSourceObservationStore(): SourceObservationStore {
71
+ return autoSession.sourceObservations;
72
+ }
@@ -1557,7 +1557,7 @@ export async function bootstrapAutoSession(
1557
1557
  s.autoStartTime = Date.now();
1558
1558
  s.resourceVersionOnStart = readResourceVersion();
1559
1559
  s.pendingQuickTasks = [];
1560
- s.currentUnit = null;
1560
+ s.clearCurrentUnit();
1561
1561
  s.currentMilestoneId ??=
1562
1562
  strandedRecoveryAction?.milestoneId ??
1563
1563
  (deepProjectStagePending ? null : state.activeMilestone?.id ?? null);
@@ -147,6 +147,15 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
147
147
  const softTimeoutMs = supervisionTimeouts.softTimeoutMs;
148
148
  const idleTimeoutMs = supervisionTimeouts.idleTimeoutMs;
149
149
  const hardTimeoutMs = supervisionTimeouts.hardTimeoutMs;
150
+ // A single hung tool gets its own short budget, NOT the general idle window:
151
+ // a long-but-progressing session is not idle, but a tool stuck for minutes
152
+ // is. Falls back to the idle window only if misconfigured to zero. The
153
+ // hung-tool budget is intentionally not scaled by task estimate — a stuck
154
+ // tool call is stuck regardless of how long the overall task should take.
155
+ const stalledToolTimeoutMs =
156
+ (supervisor.stalled_tool_timeout_minutes ?? 0) > 0
157
+ ? supervisor.stalled_tool_timeout_minutes! * 60 * 1000
158
+ : idleTimeoutMs;
150
159
 
151
160
  // ── 1. Soft timeout warning ──
152
161
  s.wrapupWarningHandle = setTimeout(() => {
@@ -189,10 +198,13 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
189
198
  };
190
199
  const runtime = readUnitRuntimeRecord(s.basePath, unitType, unitId);
191
200
  if (!runtime) return;
192
- if (Date.now() - runtime.lastProgressAt < idleTimeoutMs) return;
193
201
 
194
- // Agent has tool calls currently executing not idle, just waiting.
195
- // But only suppress recovery if the tool started recently.
202
+ // In-flight tool handling runs on its own dedicated hung-tool budget,
203
+ // independent of the general idle gate below, so a genuinely stuck tool
204
+ // is caught in minutes instead of waiting out the (typically much longer)
205
+ // idle window (#2527, follow-up). A tool actively executing within budget
206
+ // is real progress, so refreshing lastProgressAt here also keeps the idle
207
+ // gate from firing during legitimate long-running tool calls.
196
208
  let stalledToolDetected = false;
197
209
  if (getInFlightToolCount() > 0) {
198
210
  // User-interactive tools (ask_user_questions, secure_env_collect) block
@@ -206,25 +218,29 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
206
218
  }
207
219
  const oldestStart = getOldestInFlightToolStart()!;
208
220
  const toolAgeMs = Date.now() - oldestStart;
209
- if (toolAgeMs < idleTimeoutMs) {
221
+ if (toolAgeMs < stalledToolTimeoutMs) {
210
222
  writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
211
223
  lastProgressAt: Date.now(),
212
224
  lastProgressKind: "tool-in-flight",
213
225
  });
214
226
  return;
215
227
  }
216
- // Tool has been in-flight longer than idle timeout — treat as hung.
217
- // Clear the stale entries so subsequent ticks don't re-detect them,
218
- // and set the flag so the filesystem-activity check below does not
219
- // override the stall verdict (#2527).
228
+ // Tool has been in-flight longer than the hung-tool budget — treat as
229
+ // hung. Clear the stale entries so subsequent ticks don't re-detect
230
+ // them, and set the flag so the idle gate and filesystem-activity check
231
+ // below do not override the stall verdict (#2527).
220
232
  stalledToolDetected = true;
221
233
  clearInFlightTools();
222
234
  ctx.ui.notify(
223
- `Stalled tool detected: a tool has been in-flight for ${Math.round(toolAgeMs / 60000)}min. Treating as hung — attempting idle recovery.`,
235
+ `Stalled tool detected: a tool has been in-flight for ${Math.round(toolAgeMs / 60000)}min (budget ${Math.round(stalledToolTimeoutMs / 60000)}min). Treating as hung — attempting idle recovery.`,
224
236
  "warning",
225
237
  );
226
238
  }
227
239
 
240
+ // No hung tool — apply the general idle gate. A unit that has made
241
+ // meaningful progress within the idle window is not idle yet.
242
+ if (!stalledToolDetected && Date.now() - runtime.lastProgressAt < idleTimeoutMs) return;
243
+
228
244
  // Check if the agent is producing work on disk.
229
245
  // Skip this when a stalled tool was just detected — filesystem changes
230
246
  // from earlier in the task should not override the stall verdict (#2527).