@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
@@ -1,10 +1,17 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Shared closeout detection and merge actions for /gsd home and smart entry.
3
+ import { setAutoOutcomeWidget } from "./auto-dashboard.js";
3
4
  import { invalidateAllCaches } from "./cache.js";
4
5
  import { mergeCompletedMilestone } from "./parallel-merge.js";
5
6
  import { cleanupQuickBranch, detectStrandedQuickBranch } from "./quick.js";
6
7
  import { findUnmergedCompletedMilestones, } from "./unmerged-milestone-guard.js";
7
8
  import { appendRequirementsBacklogToSummary } from "./requirements-backlog.js";
9
+ const MILESTONE_MERGE_CLOSEOUT_COMMANDS = [
10
+ "/gsd status for overview",
11
+ "/gsd visualize to inspect",
12
+ "/gsd notifications for history",
13
+ "/gsd start for new work",
14
+ ];
8
15
  export async function loadCloseoutContext(basePath) {
9
16
  const unmergedMilestones = await findUnmergedCompletedMilestones(basePath);
10
17
  return {
@@ -65,6 +72,18 @@ export function buildIdleMenuSummary(state, closeout) {
65
72
  }
66
73
  return [state.nextAction || "No active milestone."];
67
74
  }
75
+ export function showMilestoneMergeCloseout(ctx, blocker) {
76
+ ctx.ui.setStatus?.("gsd-auto", undefined);
77
+ ctx.ui.setStatus?.("gsd-step", undefined);
78
+ ctx.ui.setWidget?.("gsd-progress", undefined);
79
+ setAutoOutcomeWidget(ctx, {
80
+ status: "complete",
81
+ title: `Milestone ${blocker.milestoneId} merged`,
82
+ detail: `Merged ${blocker.branch} into ${blocker.integrationBranch}. Product changes are now on ${blocker.integrationBranch}.`,
83
+ nextAction: "Review the closeout, then start the next milestone when ready.",
84
+ commands: MILESTONE_MERGE_CLOSEOUT_COMMANDS,
85
+ });
86
+ }
68
87
  export async function runMergeQuickTask(ctx, basePath, strandedQuick) {
69
88
  const merged = cleanupQuickBranch(basePath);
70
89
  if (merged) {
@@ -75,6 +94,18 @@ export async function runMergeQuickTask(ctx, basePath, strandedQuick) {
75
94
  ctx.ui.notify("Could not merge the quick-task branch automatically. Run `git status`, resolve any conflicts, then retry /gsd.", "error");
76
95
  return false;
77
96
  }
97
+ export async function runMergeMilestoneBlocker(ctx, basePath, blocker) {
98
+ ctx.ui.notify(`Completing preserved milestone merge for ${blocker.milestoneId} from ${blocker.branch} into ${blocker.integrationBranch}.`, "info");
99
+ const result = await mergeCompletedMilestone(basePath, blocker.milestoneId);
100
+ if (result.success) {
101
+ invalidateAllCaches();
102
+ showMilestoneMergeCloseout(ctx, blocker);
103
+ ctx.ui.notify(`Milestone ${blocker.milestoneId} merged to ${blocker.integrationBranch}. Closeout is complete.`, "info");
104
+ return true;
105
+ }
106
+ ctx.ui.notify(`Milestone ${blocker.milestoneId} merge failed: ${result.error ?? "unknown error"}`, "error");
107
+ return false;
108
+ }
78
109
  export async function runMergeMilestone(ctx, basePath, milestoneId) {
79
110
  const blockers = await findUnmergedCompletedMilestones(basePath);
80
111
  const blocker = milestoneId
@@ -84,15 +115,7 @@ export async function runMergeMilestone(ctx, basePath, milestoneId) {
84
115
  ctx.ui.notify("No unmerged completed milestone found.", "warning");
85
116
  return false;
86
117
  }
87
- ctx.ui.notify(`Completing preserved milestone merge for ${blocker.milestoneId} from ${blocker.branch} into ${blocker.integrationBranch}.`, "info");
88
- const result = await mergeCompletedMilestone(basePath, blocker.milestoneId);
89
- if (result.success) {
90
- ctx.ui.notify(`Milestone ${blocker.milestoneId} merged to ${blocker.integrationBranch}. Run /gsd again when ready.`, "info");
91
- invalidateAllCaches();
92
- return true;
93
- }
94
- ctx.ui.notify(`Milestone ${blocker.milestoneId} merge failed: ${result.error ?? "unknown error"}`, "error");
95
- return false;
118
+ return runMergeMilestoneBlocker(ctx, basePath, blocker);
96
119
  }
97
120
  export async function handleCloseoutChoice(ctx, basePath, choice, closeout) {
98
121
  if (choice === "finish_quick") {
@@ -198,6 +198,16 @@ export async function handleAutoCommand(trimmed, ctx, pi) {
198
198
  if (!(await guardRemoteSession(ctx, pi)))
199
199
  return true;
200
200
  const basePath = projectRoot();
201
+ // Cold start after /quit lands at the project root, not the worktree. If the
202
+ // active milestone has a live worktree, chdir back into it now so the agent
203
+ // doesn't have to search for it. Best-effort; resolves to a no-op otherwise.
204
+ try {
205
+ const { reenterActiveWorktreeIfNeeded } = await import("../../worktree-reentry.js");
206
+ await reenterActiveWorktreeIfNeeded(basePath, {
207
+ notify: (message) => ctx.ui.notify(message, "info"),
208
+ });
209
+ }
210
+ catch { /* non-fatal */ }
201
211
  const { hasGsdBootstrapArtifacts } = await import("../../detection.js");
202
212
  const { gsdRoot } = await import("../../paths.js");
203
213
  if (!hasGsdBootstrapArtifacts(gsdRoot(basePath))) {
@@ -14,7 +14,7 @@ import { handleSessionReport } from "../../commands-session-report.js";
14
14
  import { handlePrBranch } from "../../commands-pr-branch.js";
15
15
  import { currentDirectoryRoot, projectRoot } from "../context.js";
16
16
  import { findUnmergedCompletedMilestones } from "../../unmerged-milestone-guard.js";
17
- import { mergeCompletedMilestone } from "../../parallel-merge.js";
17
+ import { runMergeMilestoneBlocker } from "../../closeout-wizard.js";
18
18
  async function handleCompletedMilestoneRecovery(phase, ctx, basePath) {
19
19
  const tokens = phase.split(/\s+/).filter(Boolean);
20
20
  const dispatchPhase = tokens[0] ?? "";
@@ -27,14 +27,7 @@ async function handleCompletedMilestoneRecovery(phase, ctx, basePath) {
27
27
  : blockers[0];
28
28
  if (!blocker)
29
29
  return false;
30
- ctx.ui.notify(`Completing preserved milestone merge for ${blocker.milestoneId} from ${blocker.branch} into ${blocker.integrationBranch}.`, "info");
31
- const result = await mergeCompletedMilestone(basePath, blocker.milestoneId);
32
- if (result.success) {
33
- ctx.ui.notify(`Milestone ${blocker.milestoneId} merged to ${blocker.integrationBranch}. Run /gsd again when ready.`, "info");
34
- }
35
- else {
36
- ctx.ui.notify(`Milestone ${blocker.milestoneId} merge recovery failed: ${result.error}`, "error");
37
- }
30
+ await runMergeMilestoneBlocker(ctx, basePath, blocker);
38
31
  return true;
39
32
  }
40
33
  export function normalizeReportExportArgs(trimmed) {
@@ -448,22 +448,57 @@ function recoverConfirmed(args) {
448
448
  .map((part) => part.trim().toLowerCase())
449
449
  .some((part) => part === "--confirm" || part === "--yes" || part === "confirm");
450
450
  }
451
- async function confirmRecover(ctx, args) {
452
- if (recoverConfirmed(args))
453
- return true;
451
+ function recoverAllowsDataLoss(args) {
452
+ return args
453
+ .split(/\s+/)
454
+ .map((part) => part.trim().toLowerCase())
455
+ .some((part) => part === "--allow-data-loss" || part === "--force");
456
+ }
457
+ async function confirmRecover(ctx, args, markdown, beforeDb, dataLoss) {
454
458
  const warning = [
455
459
  "gsd recover imports markdown into the database.",
456
460
  "It clears and reconstructs milestone, slice, and task hierarchy rows from rendered markdown.",
457
461
  "Use /gsd rebuild markdown for normal DB-to-markdown realignment.",
458
- ].join("\n");
462
+ "",
463
+ ` Markdown on disk: ${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T`,
464
+ ` Current DB: ${beforeDb.milestones}M/${beforeDb.slices}S/${beforeDb.tasks}T`,
465
+ ];
466
+ if (dataLoss) {
467
+ warning.push("", "⚠ The DB holds rows the markdown lacks. Recover will permanently DELETE", " those rows. A snapshot is written to .gsd/backups/ first, but if the DB", " is the source of truth you almost certainly want /gsd rebuild markdown.");
468
+ }
469
+ const warningText = warning.join("\n");
470
+ if (recoverConfirmed(args)) {
471
+ // Non-interactive --confirm still refuses a data-loss recover unless the
472
+ // caller explicitly opts in with --allow-data-loss / --force.
473
+ if (dataLoss && !recoverAllowsDataLoss(args)) {
474
+ ctx.ui.notify(`${warningText}\n\nRefusing: this would delete authoritative DB rows. Re-run with ` +
475
+ `/gsd recover --confirm --allow-data-loss to proceed, or use /gsd rebuild markdown ` +
476
+ `to re-project markdown from the DB instead.`, "error");
477
+ return false;
478
+ }
479
+ return true;
480
+ }
459
481
  if (typeof ctx.ui.confirm === "function") {
460
- const confirmed = await ctx.ui.confirm("Import markdown into the DB?", `${warning}\n\nContinue only if the DB is lost or corrupt and markdown is the source you intend to import.`);
461
- if (confirmed)
462
- return true;
463
- ctx.ui.notify("gsd recover cancelled. No database changes made.", "info");
464
- return false;
482
+ const confirmed = await ctx.ui.confirm("Import markdown into the DB?", `${warningText}\n\nContinue only if the DB is lost or corrupt and markdown is the source you intend to import.`);
483
+ if (!confirmed) {
484
+ ctx.ui.notify("gsd recover cancelled. No database changes made.", "info");
485
+ return false;
486
+ }
487
+ // Data loss requires a second, explicit acknowledgement — the interactive
488
+ // equivalent of the --allow-data-loss opt-in the non-interactive paths
489
+ // demand. A single generic "yes" must not silently delete DB rows.
490
+ if (dataLoss) {
491
+ const acknowledged = await ctx.ui.confirm("Permanently delete DB rows the markdown lacks?", "This recover will DELETE authoritative DB rows the markdown does not contain. " +
492
+ "A snapshot is saved to .gsd/backups/ first, but /gsd rebuild markdown is usually " +
493
+ "what you want. Proceed with the deletion?");
494
+ if (!acknowledged) {
495
+ ctx.ui.notify("gsd recover cancelled. No database changes made.", "info");
496
+ return false;
497
+ }
498
+ }
499
+ return true;
465
500
  }
466
- ctx.ui.notify(`${warning}\n\nNo database changes made. Re-run /gsd recover --confirm to proceed.`, "warning");
501
+ ctx.ui.notify(`${warningText}\n\nNo database changes made. Re-run /gsd recover --confirm to proceed.`, "warning");
467
502
  return false;
468
503
  }
469
504
  /**
@@ -476,16 +511,27 @@ async function confirmRecover(ctx, args) {
476
511
  * Prints counts of recovered items and the resulting project phase.
477
512
  */
478
513
  export async function handleRecover(ctx, basePath, args = "") {
479
- const { isDbAvailable: dbAvailable, clearEngineHierarchy, transaction: dbTransaction } = await import("./gsd-db.js");
514
+ const { isDbAvailable: dbAvailable, clearEngineHierarchy, transaction: dbTransaction, backupDatabaseSnapshot } = await import("./gsd-db.js");
480
515
  const { migrateHierarchyToDb } = await import("./md-importer.js");
481
516
  const { invalidateStateCache } = await import("./state.js");
517
+ const { countDbHierarchy, countMarkdownHierarchy, recoverWouldDeleteDbRows } = await import("./migration-auto-check.js");
518
+ const { renderAllFromDb } = await import("./markdown-renderer.js");
482
519
  if (!dbAvailable()) {
483
520
  ctx.ui.notify("gsd recover: No database open. Run a GSD command first to initialize the DB.", "error");
484
521
  return;
485
522
  }
486
- if (!(await confirmRecover(ctx, args)))
523
+ // Compare markdown-on-disk against the live DB so the confirmation prompt can
524
+ // surface exactly what recover will overwrite (and refuse silent data loss).
525
+ // The data-loss check is identity-based, not count-based: it flags any DB row
526
+ // markdown lacks, including equal-count divergence (DB S99 vs markdown S01).
527
+ const markdown = countMarkdownHierarchy(basePath);
528
+ const beforeDb = countDbHierarchy();
529
+ const dataLoss = recoverWouldDeleteDbRows(basePath);
530
+ if (!(await confirmRecover(ctx, args, markdown, beforeDb, dataLoss)))
487
531
  return;
488
532
  try {
533
+ // 0. Snapshot the DB before the destructive clear so recover is reversible.
534
+ const backupPath = backupDatabaseSnapshot("pre-recover");
489
535
  // 1. Delete + re-populate inside a single transaction for atomicity.
490
536
  // clearEngineHierarchy() uses transaction() internally but transaction()
491
537
  // is re-entrant, so wrapping in dbTransaction() keeps the whole
@@ -494,8 +540,14 @@ export async function handleRecover(ctx, basePath, args = "") {
494
540
  clearEngineHierarchy();
495
541
  return migrateHierarchyToDb(basePath);
496
542
  });
497
- // 3. Invalidate state cache so deriveState() picks up fresh DB data
543
+ // 2. Invalidate state cache so deriveState() picks up fresh DB data
498
544
  invalidateStateCache();
545
+ // 3. Re-project markdown from the freshly imported DB so disk and DB agree
546
+ // immediately (otherwise the markdown still reflects the pre-import state
547
+ // and the next startup check would flag fresh drift). renderAllFromDb
548
+ // swallows per-artifact failures into its result, so inspect them — a
549
+ // silent projection failure must not be reported as a clean success.
550
+ const renderResult = await renderAllFromDb(basePath);
499
551
  // 4. Derive state to verify sanity
500
552
  const state = await deriveState(basePath);
501
553
  // 5. Report
@@ -507,6 +559,18 @@ export async function handleRecover(ctx, basePath, args = "") {
507
559
  ``,
508
560
  ` Phase: ${state.phase}`,
509
561
  ];
562
+ // Post-import verification: markdown that failed to parse imports as fewer
563
+ // rows than countMarkdownHierarchy saw on disk. Surface the shortfall.
564
+ if (counts.milestones < markdown.milestones ||
565
+ counts.slices < markdown.slices ||
566
+ counts.tasks < markdown.tasks) {
567
+ lines.push(``, ` ⚠ Imported fewer rows than markdown contained ` +
568
+ `(${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T on disk). ` +
569
+ `Some markdown may have failed to parse — review before continuing.`);
570
+ }
571
+ if (backupPath) {
572
+ lines.push(``, ` Backup: ${backupPath}`);
573
+ }
510
574
  if (state.activeMilestone) {
511
575
  lines.push(` Active: ${state.activeMilestone.id}: ${state.activeMilestone.title}`);
512
576
  }
@@ -516,8 +580,22 @@ export async function handleRecover(ctx, basePath, args = "") {
516
580
  if (state.activeTask) {
517
581
  lines.push(` Task: ${state.activeTask.id}: ${state.activeTask.title}`);
518
582
  }
519
- process.stderr.write(`gsd-recover: recovered ${counts.milestones}M/${counts.slices}S/${counts.tasks}T hierarchy\n`);
520
- ctx.ui.notify(lines.join("\n"), "success");
583
+ // Surface markdown projection failures: renderAllFromDb resolves even when
584
+ // individual artifacts fail to render, so a clean exit here would otherwise
585
+ // hide a stale/partial projection.
586
+ const renderFailed = renderResult.errors.length > 0;
587
+ if (renderFailed) {
588
+ lines.push(``, ` ⚠ ${renderResult.errors.length} markdown projection(s) failed to render — ` +
589
+ `markdown may be stale. Re-run /gsd rebuild markdown.`);
590
+ for (const e of renderResult.errors.slice(0, 5))
591
+ lines.push(` - ${e}`);
592
+ if (renderResult.errors.length > 5) {
593
+ lines.push(` …and ${renderResult.errors.length - 5} more`);
594
+ }
595
+ }
596
+ process.stderr.write(`gsd-recover: recovered ${counts.milestones}M/${counts.slices}S/${counts.tasks}T hierarchy` +
597
+ `${renderFailed ? ` (${renderResult.errors.length} projection errors)` : ""}\n`);
598
+ ctx.ui.notify(lines.join("\n"), renderFailed ? "warning" : "success");
521
599
  }
522
600
  catch (err) {
523
601
  const msg = err instanceof Error ? err.message : String(err);
@@ -34,7 +34,7 @@ export function formatMcpInitResult(status, configPath, targetPath) {
34
34
  `Config: ${configPath}`,
35
35
  "",
36
36
  "MCP-capable clients can now load the GSD workflow and gsd-browser MCP servers from this folder.",
37
- "Pi Providers use the managed gsd-browser engine directly; this project config is for External MCP Clients.",
37
+ "Pi Providers use built-in browser tools directly; this project config is for External MCP Clients.",
38
38
  "Restart or reconnect any client that already has this project open.",
39
39
  ].join("\n");
40
40
  }
@@ -1236,7 +1236,7 @@ async function configureHooks(ctx, prefs) {
1236
1236
  if (geEnabled !== undefined)
1237
1237
  ge.enabled = geEnabled;
1238
1238
  const currentSliceGates = Array.isArray(ge.slice_gates) ? ge.slice_gates : [];
1239
- const sgInput = await ctx.ui.input(`Slice gates to evaluate (comma-separated, blank keeps)${currentSliceGates.length ? ` (current: ${currentSliceGates.join(", ")})` : " (default: Q3,Q4)"}:`, currentSliceGates.join(", "));
1239
+ const sgInput = await ctx.ui.input(`Gate-evaluate slice gates (Q3,Q4; comma-separated, blank keeps)${currentSliceGates.length ? ` (current: ${currentSliceGates.join(", ")})` : " (default: Q3,Q4)"}:`, currentSliceGates.join(", "));
1240
1240
  if (sgInput !== null && sgInput !== undefined) {
1241
1241
  const parsed = parseStringList(sgInput);
1242
1242
  if (parsed.length > 0)
@@ -1697,7 +1697,7 @@ export function serializePreferencesToFrontmatter(prefs) {
1697
1697
  // Ordered keys for consistent output
1698
1698
  const orderedKeys = [
1699
1699
  "version", "mode", "always_use_skills", "prefer_skills", "avoid_skills",
1700
- "skill_rules", "custom_instructions", "models", "skill_discovery",
1700
+ "skill_rules", "custom_instructions", "models", "thinking", "skill_discovery",
1701
1701
  "skill_staleness_days", "auto_supervisor", "uat_dispatch", "unique_milestone_ids",
1702
1702
  "budget_ceiling", "budget_enforcement", "context_pause_threshold",
1703
1703
  "notifications", "cmux", "remote_questions", "git",
@@ -122,6 +122,7 @@ function collectConfigSections() {
122
122
  supRows.push({ label: "Model", value: sup.model });
123
123
  supRows.push({ label: "Soft timeout", value: `${sup.soft_timeout_minutes}m` });
124
124
  supRows.push({ label: "Idle timeout", value: `${sup.idle_timeout_minutes}m` });
125
+ supRows.push({ label: "Stalled tool timeout", value: `${sup.stalled_tool_timeout_minutes}m` });
125
126
  supRows.push({ label: "Hard timeout", value: `${sup.hard_timeout_minutes}m` });
126
127
  sections.push({ title: "Auto Supervisor", rows: supRows });
127
128
  }
@@ -5,13 +5,36 @@
5
5
  * Reduces context bloat between compactions with zero LLM overhead.
6
6
  * Preserves message ordering, roles, and all assistant/user messages.
7
7
  *
8
- * Operates on the pi-ai Message[] format (post-convertToLlm, pre-provider):
8
+ * Operates on provider payloads after convertToLlm:
9
+ *
10
+ * pi-ai Message[] payloads:
9
11
  * - toolResult messages: { role: "toolResult", content: TextContent[] }
10
12
  * - bash results are already converted to: { role: "user", content: [{type:"text",text:"..."}] }
11
13
  * and start with "Ran `" from bashExecutionToText.
14
+ *
15
+ * OpenAI/Codex Responses payloads:
16
+ * - conversation items live in `input`, not `messages`
17
+ * - tool results are { type: "function_call_output", output: string | content[] }
18
+ * - bash results are user items with input_text content starting with "Ran `"
12
19
  */
13
20
  const MASK_PLACEHOLDER = "[result masked — within summarized history]";
14
21
  const MASK_CONTENT_BLOCK = [{ type: "text", text: MASK_PLACEHOLDER }];
22
+ const RESPONSES_MASK_CONTENT_BLOCK = [{ type: "input_text", text: MASK_PLACEHOLDER }];
23
+ const TRUNCATION_MARKER = "\n…[truncated]";
24
+ function isTextLikeBlock(block) {
25
+ return Boolean(block && typeof block === "object" && "text" in block);
26
+ }
27
+ function firstTextFromContent(content) {
28
+ if (typeof content === "string")
29
+ return content;
30
+ if (!Array.isArray(content))
31
+ return undefined;
32
+ const first = content.find(isTextLikeBlock);
33
+ return typeof first?.text === "string" ? first.text : undefined;
34
+ }
35
+ function isBashResultText(text) {
36
+ return typeof text === "string" && text.startsWith("Ran `");
37
+ }
15
38
  function findTurnBoundary(messages, keepRecentTurns) {
16
39
  let turnsSeen = 0;
17
40
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -35,11 +58,9 @@ function findTurnBoundary(messages, keepRecentTurns) {
35
58
  * The bashExecutionToText format always starts with "Ran `".
36
59
  */
37
60
  function isBashResultUserMessage(m) {
38
- if (m.role !== "user" || !Array.isArray(m.content))
61
+ if (m.role !== "user")
39
62
  return false;
40
- const first = m.content[0];
41
- return first && typeof first === "object" && "text" in first &&
42
- typeof first.text === "string" && first.text.startsWith("Ran `");
63
+ return isBashResultText(firstTextFromContent(m.content));
43
64
  }
44
65
  function isMaskableMessage(m) {
45
66
  // Tool result messages (role: "toolResult" in pi-ai format)
@@ -66,3 +87,106 @@ export function createObservationMask(keepRecentTurns = 8) {
66
87
  });
67
88
  };
68
89
  }
90
+ function isResponsesBashResultUserItem(item) {
91
+ if (item.role !== "user")
92
+ return false;
93
+ return isBashResultText(firstTextFromContent(item.content));
94
+ }
95
+ function findResponsesTurnBoundary(items, keepRecentTurns) {
96
+ let turnsSeen = 0;
97
+ for (let i = items.length - 1; i >= 0; i--) {
98
+ const item = items[i];
99
+ if (item.role === "user" && !isResponsesBashResultUserItem(item)) {
100
+ turnsSeen++;
101
+ if (turnsSeen >= keepRecentTurns)
102
+ return i;
103
+ }
104
+ }
105
+ return 0;
106
+ }
107
+ /**
108
+ * Observation masking for OpenAI/Codex Responses API payloads.
109
+ *
110
+ * Responses payloads store the conversation under `input` instead of
111
+ * `messages`, with tool results as `function_call_output` items. Keep this
112
+ * separate from createObservationMask so each payload shape stays explicit.
113
+ */
114
+ export function createResponsesInputObservationMask(keepRecentTurns = 8) {
115
+ return (items) => {
116
+ const boundary = findResponsesTurnBoundary(items, keepRecentTurns);
117
+ if (boundary === 0)
118
+ return items;
119
+ return items.map((item, i) => {
120
+ if (i >= boundary)
121
+ return item;
122
+ if (item.type === "function_call_output") {
123
+ return { ...item, output: MASK_PLACEHOLDER };
124
+ }
125
+ if (isResponsesBashResultUserItem(item)) {
126
+ return { ...item, content: RESPONSES_MASK_CONTENT_BLOCK };
127
+ }
128
+ return item;
129
+ });
130
+ };
131
+ }
132
+ function truncateText(text, maxChars) {
133
+ if (text.length <= maxChars)
134
+ return text;
135
+ return text.slice(0, maxChars) + TRUNCATION_MARKER;
136
+ }
137
+ function truncateTextBlocks(content, maxChars) {
138
+ if (typeof content === "string") {
139
+ return truncateText(content, maxChars);
140
+ }
141
+ if (!Array.isArray(content))
142
+ return content;
143
+ let remaining = maxChars;
144
+ let didTruncate = false;
145
+ const nextBlocks = [];
146
+ for (const block of content) {
147
+ if (!isTextLikeBlock(block) || typeof block.text !== "string") {
148
+ nextBlocks.push(block);
149
+ continue;
150
+ }
151
+ if (remaining <= 0) {
152
+ didTruncate = true;
153
+ continue;
154
+ }
155
+ const text = block.text;
156
+ if (text.length <= remaining) {
157
+ nextBlocks.push(block);
158
+ remaining -= text.length;
159
+ continue;
160
+ }
161
+ nextBlocks.push({ ...block, text: truncateText(text, remaining) });
162
+ remaining = 0;
163
+ didTruncate = true;
164
+ }
165
+ return didTruncate ? nextBlocks : content;
166
+ }
167
+ function normalizedMaxChars(maxChars) {
168
+ return Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : 800;
169
+ }
170
+ export function truncateContextResultMessages(messages, maxChars = 800) {
171
+ const limit = normalizedMaxChars(maxChars);
172
+ return messages.map((message) => {
173
+ if (!isMaskableMessage(message))
174
+ return message;
175
+ const content = truncateTextBlocks(message.content, limit);
176
+ return content === message.content ? message : { ...message, content };
177
+ });
178
+ }
179
+ export function truncateResponsesInputResultItems(items, maxChars = 800) {
180
+ const limit = normalizedMaxChars(maxChars);
181
+ return items.map((item) => {
182
+ if (item.type === "function_call_output") {
183
+ const output = truncateTextBlocks(item.output, limit);
184
+ return output === item.output ? item : { ...item, output };
185
+ }
186
+ if (isResponsesBashResultUserItem(item)) {
187
+ const content = truncateTextBlocks(item.content, limit);
188
+ return content === item.content ? item : { ...item, content };
189
+ }
190
+ return item;
191
+ });
192
+ }
@@ -397,6 +397,41 @@ let _decisionSaveLock = Promise.resolve();
397
397
  export function _resetDecisionSaveLock() {
398
398
  _decisionSaveLock = Promise.resolve();
399
399
  }
400
+ /**
401
+ * Re-project root DECISIONS.md from the authoritative decision records, with no
402
+ * new decision being added. Mirrors the projection saveDecisionToDb performs
403
+ * after a save, but over the full set — so a DB → markdown re-projection
404
+ * (recover, rebuild, reconcile) re-derives DECISIONS.md and never leaves it
405
+ * showing a stale subset (e.g. after a worktree merge that accepted one
406
+ * branch's DECISIONS.md while the DB holds the union of both branches'
407
+ * decisions). DB stays the single source of truth; this only writes markdown.
408
+ */
409
+ export async function regenerateDecisionsMarkdown(basePath) {
410
+ const { getAllDecisionsFromMemories } = await import('./context-store.js');
411
+ const allDecisions = getAllDecisionsFromMemories();
412
+ const filePath = resolveGsdRootFile(basePath, 'DECISIONS');
413
+ let existingContent = null;
414
+ if (existsSync(filePath)) {
415
+ existingContent = readFileSync(filePath, 'utf-8');
416
+ }
417
+ // Nothing to project: no decisions in the DB and no file to normalize.
418
+ if (allDecisions.length === 0 && existingContent === null)
419
+ return;
420
+ let md;
421
+ if (existingContent && !isDecisionsTableFormat(existingContent)) {
422
+ // Preserve freeform content; refresh only the appended decisions table.
423
+ const marker = '---\n\n## Decisions Table';
424
+ const markerIdx = existingContent.indexOf(marker);
425
+ const freeformPart = markerIdx >= 0
426
+ ? existingContent.substring(0, markerIdx).trimEnd()
427
+ : existingContent.trimEnd();
428
+ md = freeformPart + '\n' + generateDecisionsAppendBlock(allDecisions);
429
+ }
430
+ else {
431
+ md = generateDecisionsMd(allDecisions);
432
+ }
433
+ await saveFile(filePath, md);
434
+ }
400
435
  /**
401
436
  * Save a new decision to DB and regenerate DECISIONS.md.
402
437
  * Auto-assigns the next ID via nextDecisionId().
@@ -104,14 +104,22 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
104
104
 
105
105
  - `language`: preferred response language for all GSD interactions. Accepts any language name or code — `"Chinese"`, `"zh"`, `"German"`, `"de"`, `"日本語"`, etc. When set, GSD injects "Always respond in \<language\>" into every agent's system prompt, including after `/clear`. Quickest way to set it: `/gsd language <name>`. To clear: `/gsd language off`.
106
106
 
107
- - `models`: per-stage model selection (applies to both auto-mode and guided-flow dispatches). Keys: `research`, `planning`, `discuss`, `execution`, `execution_simple`, `completion`, `validation`, `subagent`. Values can be:
107
+ - `models`: per-stage model selection (applies to both auto-mode and guided-flow dispatches). Keys: `research`, `planning`, `discuss`, `execution`, `execution_simple`, `completion`, `validation`, `subagent`, `uat`. Values can be:
108
108
  - Simple string: `"claude-sonnet-4-6"` — single model, no fallbacks
109
109
  - Provider-qualified string: `"bedrock/claude-sonnet-4-6"` — targets a specific provider when the same model ID exists across multiple providers
110
110
  - Object with fallbacks: `{ model: "claude-opus-4-6", fallbacks: ["glm-5", "minimax-m2.5"] }` — tries fallbacks in order if primary fails
111
111
  - Object with provider: `{ model: "claude-opus-4-6", provider: "bedrock" }` — explicit provider targeting in object format
112
+ - Object with thinking: `{ model: "claude-opus-4-6", thinking: "xhigh" }` — pins the reasoning effort for that phase (see `thinking` below)
112
113
  - Omit a key to use whatever model is currently active (except `discuss` and `validation` which fall back to `planning` when unset). Fallbacks are tried when model switching fails (provider unavailable, rate limited, etc.).
113
114
  - `discuss` — used for milestone/slice discussion (interactive context gathering). Falls back to `planning` if unset.
114
115
  - `validation` — used for gate evaluation, roadmap reassessment, milestone validation, and doc rewrites. Falls back to `planning` if unset.
116
+ - `uat` — used for UAT runs. Falls back to `completion` if unset.
117
+
118
+ - `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
119
+ - Two equivalent ways to set it: inline as `models.<phase>.thinking`, or as a separate `thinking:` block keyed by phase. For the same phase, the inline value wins over the block; project preferences win over global; a phase that's unset inherits via the same sibling chain as `models` (e.g. `discuss → planning`).
120
+ - If no thinking is configured for a phase, the session level (set via `/model`) is used, exactly as before — this is fully backward-compatible.
121
+ - `execute-task` (code-writing) has a measured reasoning floor of `medium`: at lower levels models stop planning edits and thrash on file re-reads. The floor applies to the session/default path. An **explicit** `execution` thinking level bypasses the floor and is honored verbatim (with a one-time advisory) — set `execution: low` deliberately if you want it.
122
+ - Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` is only honored by select model families.
115
123
 
116
124
  - `skill_staleness_days`: number — skills unused for this many days get deprioritized during discovery. Set to `0` to disable staleness tracking. Default: `60`.
117
125
 
@@ -448,6 +456,47 @@ models:
448
456
  ---
449
457
  ```
450
458
 
459
+ ## Per-Phase Thinking Level Example
460
+
461
+ Model choice and reasoning effort are independent controls (ADR-026). You can use one model across phases at different reasoning levels, or mix per-phase models with per-phase thinking.
462
+
463
+ Inline form — thinking pinned alongside the model:
464
+
465
+ ```yaml
466
+ ---
467
+ version: 1
468
+ models:
469
+ planning:
470
+ model: openai-codex/gpt-5.4
471
+ thinking: xhigh
472
+ execution:
473
+ model: openai-codex/gpt-5.4
474
+ thinking: low # explicit → bypasses the execute-task medium floor
475
+ validation:
476
+ model: openai-codex/gpt-5.4
477
+ thinking: high
478
+ ---
479
+ ```
480
+
481
+ Separate-block form — set thinking without pinning a model (the session model is used):
482
+
483
+ ```yaml
484
+ ---
485
+ version: 1
486
+ thinking:
487
+ research: medium
488
+ planning: xhigh
489
+ discuss: high
490
+ execution: low
491
+ execution_simple: low
492
+ completion: medium
493
+ validation: high
494
+ uat: medium
495
+ ---
496
+ ```
497
+
498
+ For a single phase, an inline `models.<phase>.thinking` value wins over the same phase in the `thinking:` block; project preferences win over global. Unsupported levels are clamped to the nearest level the resolved model supports.
499
+
451
500
  When a model fails to switch (provider unavailable, rate limited, credits exhausted), GSD automatically tries the next model in the `fallbacks` list. This ensures auto-mode continues even when your preferred provider hits limits.
452
501
 
453
502
  ## Provider Targeting