@opengsd/gsd-pi 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +10 -10
  2. package/dist/headless.js +2 -1
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +2 -2
  5. package/dist/resources/extensions/gsd/artifact-verification.js +3 -4
  6. package/dist/resources/extensions/gsd/auto/loop.js +24 -4
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
  8. package/dist/resources/extensions/gsd/auto/unit-phase.js +37 -5
  9. package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -8
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +2 -0
  11. package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
  12. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  13. package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -5
  14. package/dist/resources/extensions/gsd/auto-start.js +29 -9
  15. package/dist/resources/extensions/gsd/auto-tool-tracking.js +40 -0
  16. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +4 -0
  17. package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
  18. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +1 -1
  19. package/dist/resources/extensions/gsd/auto.js +15 -5
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -0
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -7
  22. package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -3
  23. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -24
  24. package/dist/resources/extensions/gsd/commands-verdict.js +10 -9
  25. package/dist/resources/extensions/gsd/compat/compat-marker.js +80 -5
  26. package/dist/resources/extensions/gsd/constants.js +12 -13
  27. package/dist/resources/extensions/gsd/context-masker.js +97 -21
  28. package/dist/resources/extensions/gsd/db/engine.js +5 -2
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +22 -0
  30. package/dist/resources/extensions/gsd/doctor-engine-checks.js +24 -8
  31. package/dist/resources/extensions/gsd/error-classifier.js +12 -2
  32. package/dist/resources/extensions/gsd/exec-sandbox.js +1 -0
  33. package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
  34. package/dist/resources/extensions/gsd/git-service.js +19 -7
  35. package/dist/resources/extensions/gsd/gsd-db.js +75 -31
  36. package/dist/resources/extensions/gsd/guided-flow.js +11 -5
  37. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -21
  38. package/dist/resources/extensions/gsd/md-importer.js +5 -6
  39. package/dist/resources/extensions/gsd/model-cost-table.js +2 -0
  40. package/dist/resources/extensions/gsd/model-router.js +11 -0
  41. package/dist/resources/extensions/gsd/paths.js +66 -15
  42. package/dist/resources/extensions/gsd/planning-subagent-policy.js +37 -0
  43. package/dist/resources/extensions/gsd/planning-subagent-registry.js +25 -0
  44. package/dist/resources/extensions/gsd/preferences-types.js +5 -0
  45. package/dist/resources/extensions/gsd/preferences-validation.js +48 -1
  46. package/dist/resources/extensions/gsd/preferences.js +17 -0
  47. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
  48. package/dist/resources/extensions/gsd/provider-payload-policy.js +16 -5
  49. package/dist/resources/extensions/gsd/safety/destructive-guard.js +1 -1
  50. package/dist/resources/extensions/gsd/skill-activation.js +17 -3
  51. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +98 -2
  52. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +4 -1
  53. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +5 -2
  54. package/dist/resources/extensions/gsd/stop-notice.js +9 -0
  55. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
  56. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -5
  57. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +14 -5
  58. package/dist/resources/extensions/gsd/tools/reopen-slice.js +15 -5
  59. package/dist/resources/extensions/gsd/tools/reopen-task.js +16 -10
  60. package/dist/resources/extensions/gsd/unit-context-composer.js +28 -5
  61. package/dist/resources/extensions/gsd/workflow-projections.js +2 -3
  62. package/dist/resources/extensions/gsd/workspace.js +2 -1
  63. package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
  64. package/dist/resources/extensions/shared/interview-ui.js +43 -6
  65. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  66. package/dist/web/standalone/.next/BUILD_ID +1 -1
  67. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  68. package/dist/web/standalone/.next/build-manifest.json +2 -2
  69. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.html +1 -1
  86. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  93. package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
  94. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  95. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  96. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  98. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  99. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  100. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  101. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  102. package/integrations/hermes/pyproject.toml +1 -1
  103. package/package.json +7 -6
  104. package/packages/cloud-mcp-gateway/package.json +2 -2
  105. package/packages/contracts/package.json +1 -1
  106. package/packages/daemon/package.json +4 -4
  107. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  108. package/packages/gsd-agent-core/dist/compaction/compaction.js +36 -4
  109. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  110. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  111. package/packages/gsd-agent-core/dist/sdk.js +1 -0
  112. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  113. package/packages/gsd-agent-core/package.json +5 -5
  114. package/packages/gsd-agent-modes/package.json +7 -7
  115. package/packages/gsd-cloud/README.md +9 -3
  116. package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
  117. package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
  118. package/packages/gsd-cloud/dist/background-cli.test.js +248 -0
  119. package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
  120. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
  121. package/packages/gsd-cloud/dist/cli.js +121 -15
  122. package/packages/gsd-cloud/dist/cli.js.map +1 -1
  123. package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
  124. package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
  125. package/packages/gsd-cloud/dist/cloud-config.js +7 -1
  126. package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
  127. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +1 -3
  128. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
  129. package/packages/gsd-cloud/dist/cloud-runtime.js +9 -7
  130. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
  131. package/packages/gsd-cloud/dist/runtime-process.d.ts +27 -0
  132. package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
  133. package/packages/gsd-cloud/dist/runtime-process.js +272 -0
  134. package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
  135. package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
  136. package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
  137. package/packages/gsd-cloud/dist/runtime-process.test.js +97 -0
  138. package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
  139. package/packages/gsd-cloud/package.json +2 -2
  140. package/packages/mcp-server/README.md +7 -2
  141. package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
  142. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
  143. package/packages/mcp-server/dist/cli-runner.js +119 -33
  144. package/packages/mcp-server/dist/cli-runner.js.map +1 -1
  145. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  146. package/packages/mcp-server/dist/server.js +5 -5
  147. package/packages/mcp-server/dist/server.js.map +1 -1
  148. package/packages/mcp-server/dist/workflow-tools.d.ts +4 -3
  149. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  150. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  151. package/packages/mcp-server/package.json +4 -4
  152. package/packages/native/package.json +1 -1
  153. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  154. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  155. package/packages/pi-agent-core/dist/agent.js +2 -0
  156. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  157. package/packages/pi-agent-core/package.json +1 -1
  158. package/packages/pi-ai/dist/models.generated.d.ts +76 -0
  159. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  160. package/packages/pi-ai/dist/models.generated.js +70 -0
  161. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  162. package/packages/pi-ai/dist/providers/anthropic.js +6 -1
  163. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  164. package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
  165. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  166. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  167. package/packages/pi-ai/dist/providers/openai-completions.js +103 -0
  168. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  169. package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
  170. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  171. package/packages/pi-ai/package.json +1 -1
  172. package/packages/pi-coding-agent/dist/core/capability-patches.js +1 -1
  173. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
  175. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +11 -1
  177. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  179. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
  181. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/skills.js +54 -0
  183. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  184. package/packages/pi-coding-agent/package.json +7 -7
  185. package/packages/pi-tui/package.json +2 -2
  186. package/packages/rpc-client/package.json +2 -2
  187. package/pkg/package.json +1 -1
  188. package/src/resources/GSD-WORKFLOW.md +2 -2
  189. package/src/resources/extensions/gsd/artifact-verification.ts +3 -8
  190. package/src/resources/extensions/gsd/auto/loop.ts +48 -4
  191. package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
  192. package/src/resources/extensions/gsd/auto/unit-phase.ts +41 -5
  193. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -7
  194. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
  195. package/src/resources/extensions/gsd/auto-prompts.ts +118 -19
  196. package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
  197. package/src/resources/extensions/gsd/auto-runtime-state.ts +12 -7
  198. package/src/resources/extensions/gsd/auto-start.ts +31 -8
  199. package/src/resources/extensions/gsd/auto-tool-tracking.ts +56 -0
  200. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +4 -0
  201. package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
  202. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +1 -1
  203. package/src/resources/extensions/gsd/auto.ts +20 -7
  204. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +1 -0
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +41 -12
  206. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -3
  207. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -29
  208. package/src/resources/extensions/gsd/commands-verdict.ts +16 -9
  209. package/src/resources/extensions/gsd/compat/compat-marker.ts +99 -5
  210. package/src/resources/extensions/gsd/constants.ts +14 -13
  211. package/src/resources/extensions/gsd/context-masker.ts +98 -2
  212. package/src/resources/extensions/gsd/db/engine.ts +5 -2
  213. package/src/resources/extensions/gsd/docs/preferences-reference.md +22 -0
  214. package/src/resources/extensions/gsd/doctor-engine-checks.ts +30 -7
  215. package/src/resources/extensions/gsd/error-classifier.ts +13 -2
  216. package/src/resources/extensions/gsd/exec-sandbox.ts +1 -0
  217. package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
  218. package/src/resources/extensions/gsd/git-service.ts +19 -6
  219. package/src/resources/extensions/gsd/gsd-db.ts +85 -33
  220. package/src/resources/extensions/gsd/guided-flow.ts +11 -4
  221. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -23
  222. package/src/resources/extensions/gsd/md-importer.ts +5 -5
  223. package/src/resources/extensions/gsd/model-cost-table.ts +2 -0
  224. package/src/resources/extensions/gsd/model-router.ts +12 -0
  225. package/src/resources/extensions/gsd/paths.ts +73 -15
  226. package/src/resources/extensions/gsd/planning-subagent-policy.ts +58 -0
  227. package/src/resources/extensions/gsd/planning-subagent-registry.ts +31 -0
  228. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  229. package/src/resources/extensions/gsd/preferences-validation.ts +62 -0
  230. package/src/resources/extensions/gsd/preferences.ts +25 -0
  231. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
  232. package/src/resources/extensions/gsd/provider-payload-policy.ts +18 -4
  233. package/src/resources/extensions/gsd/safety/destructive-guard.ts +1 -1
  234. package/src/resources/extensions/gsd/skill-activation.ts +17 -3
  235. package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +132 -2
  236. package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +4 -1
  237. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +4 -2
  238. package/src/resources/extensions/gsd/stop-notice.ts +11 -0
  239. package/src/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
  240. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +7 -7
  241. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +264 -0
  242. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +39 -0
  243. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
  244. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
  245. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +55 -0
  246. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +10 -0
  247. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +25 -0
  248. package/src/resources/extensions/gsd/tests/complete-task.test.ts +76 -1
  249. package/src/resources/extensions/gsd/tests/context-masker.test.ts +204 -8
  250. package/src/resources/extensions/gsd/tests/db-engine-cjs-compat.test.ts +12 -0
  251. package/src/resources/extensions/gsd/tests/db-tools-alias-suppression.test.ts +62 -0
  252. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +33 -0
  253. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +14 -0
  254. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +83 -0
  255. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +33 -1
  256. package/src/resources/extensions/gsd/tests/external-markdown-edit.test.ts +38 -1
  257. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
  258. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +20 -1
  259. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
  260. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +22 -0
  261. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
  262. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +22 -4
  263. package/src/resources/extensions/gsd/tests/integration-branch-meta-location.test.ts +62 -0
  264. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +5 -5
  265. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  266. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  267. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +7 -5
  268. package/src/resources/extensions/gsd/tests/preferences.test.ts +84 -0
  269. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +4 -0
  270. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +87 -0
  271. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +121 -1
  272. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +39 -0
  273. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +105 -0
  274. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  275. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +33 -7
  276. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -16
  277. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
  278. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +21 -1
  279. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +13 -7
  280. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +14 -0
  281. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  282. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -0
  283. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +39 -0
  284. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +7 -0
  285. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +4 -4
  286. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
  287. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
  288. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +34 -0
  289. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -5
  290. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +24 -5
  291. package/src/resources/extensions/gsd/tools/reopen-slice.ts +25 -5
  292. package/src/resources/extensions/gsd/tools/reopen-task.ts +23 -9
  293. package/src/resources/extensions/gsd/unit-context-composer.ts +34 -8
  294. package/src/resources/extensions/gsd/workflow-projections.ts +2 -3
  295. package/src/resources/extensions/gsd/workspace.ts +2 -1
  296. package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
  297. package/src/resources/extensions/shared/interview-ui.ts +47 -8
  298. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +29 -0
  299. /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → EAhnhYMt5UVOiG9-iPDjc}/_buildManifest.js +0 -0
  300. /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → EAhnhYMt5UVOiG9-iPDjc}/_ssgManifest.js +0 -0
@@ -29,12 +29,14 @@ import {
29
29
 
30
30
  const SKILL_NAME = "testskill";
31
31
  const COMPLETE_SLICE_SKILL_NAME = "complete-slice-policies";
32
- const SKILL_ACTIVATION_SUBSTRING = `Read the installed '${SKILL_NAME}' skill file from <available_skills>`;
33
- const COMPLETE_SLICE_SKILL_ACTIVATION_SUBSTRING = `Read the installed '${COMPLETE_SLICE_SKILL_NAME}' skill file from <available_skills>`;
34
32
 
35
33
  const tmpDirs: string[] = [];
36
34
  let savedCwd: string | undefined;
37
35
 
36
+ function expectedSkillRead(base: string, name: string): string {
37
+ return `Read the skill file at \`${join(base, ".agents", "skills", name, "SKILL.md")}\``;
38
+ }
39
+
38
40
  function setupProjectWithSkill(options: {
39
41
  skillName?: string;
40
42
  preferencesLines?: string[];
@@ -139,7 +141,7 @@ test("worker prompt (buildResearchSlicePrompt) includes <skill_activation> from
139
141
  "research-slice prompt should contain a <skill_activation> block",
140
142
  );
141
143
  assert.ok(
142
- prompt.includes(SKILL_ACTIVATION_SUBSTRING),
144
+ prompt.includes(expectedSkillRead(base, SKILL_NAME)),
143
145
  `research-slice prompt should reference the always-used skill '${SKILL_NAME}'`,
144
146
  );
145
147
  });
@@ -164,7 +166,7 @@ test("complete-slice prompt includes <skill_activation> from unit-specific skill
164
166
  "complete-slice prompt should contain a <skill_activation> block",
165
167
  );
166
168
  assert.ok(
167
- prompt.includes(COMPLETE_SLICE_SKILL_ACTIVATION_SUBSTRING),
169
+ prompt.includes(expectedSkillRead(base, COMPLETE_SLICE_SKILL_NAME)),
168
170
  `complete-slice prompt should reference the skill-rule skill '${COMPLETE_SLICE_SKILL_NAME}'`,
169
171
  );
170
172
  });
@@ -192,7 +194,7 @@ test("subagent dispatch prompt (buildParallelResearchSlicesPrompt) carries <skil
192
194
  `expected at least 2 <skill_activation> blocks (one per slice), got ${blockCount}`,
193
195
  );
194
196
  assert.ok(
195
- prompt.includes(SKILL_ACTIVATION_SUBSTRING),
197
+ prompt.includes(expectedSkillRead(base, SKILL_NAME)),
196
198
  `parallel-research-slices prompt should reference the always-used skill '${SKILL_NAME}'`,
197
199
  );
198
200
  assert.ok(
@@ -823,6 +823,90 @@ test("pre-dispatch hook action validation via validatePreferences", () => {
823
823
  assert.ok(e3.some(e => e.includes("invalid action")));
824
824
  });
825
825
 
826
+ test("planning_subagents validation accepts configured read-only planning specialists", () => {
827
+ const { preferences, errors } = validatePreferences({
828
+ planning_subagents: {
829
+ "plan-milestone": { allowed: ["security", "reviewer"] },
830
+ "plan-slice": { allowed: ["scout", "security", "security"] },
831
+ },
832
+ } as any);
833
+
834
+ assert.equal(errors.length, 0);
835
+ assert.deepEqual(preferences.planning_subagents?.["plan-milestone"]?.allowed, ["security", "reviewer"]);
836
+ assert.deepEqual(preferences.planning_subagents?.["plan-slice"]?.allowed, ["scout", "security"]);
837
+ });
838
+
839
+ test("planning_subagents validation rejects unsupported units and unsafe agents", () => {
840
+ const { preferences, errors } = validatePreferences({
841
+ planning_subagents: {
842
+ "execute-task": { allowed: ["security"] },
843
+ "plan-slice": { allowed: ["worker"] },
844
+ },
845
+ } as any);
846
+
847
+ assert.equal(preferences.planning_subagents, undefined);
848
+ assert.ok(errors.some(e => e.includes("unsupported unit \"execute-task\"")));
849
+ assert.ok(errors.some(e => e.includes("\"worker\"") && e.includes("read-only planning specialist")));
850
+ });
851
+
852
+ test("loadEffectiveGSDPreferences merges planning_subagents allowlists", (t) => {
853
+ const originalCwd = process.cwd();
854
+ const originalGsdHome = process.env.GSD_HOME;
855
+ const tempProject = mkdtempSync(join(tmpdir(), "gsd-planning-subagents-project-"));
856
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-planning-subagents-home-"));
857
+
858
+ t.after(() => {
859
+ clearGSDPreferencesCache();
860
+ process.chdir(originalCwd);
861
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
862
+ else process.env.GSD_HOME = originalGsdHome;
863
+ rmSync(tempProject, { recursive: true, force: true });
864
+ rmSync(tempGsdHome, { recursive: true, force: true });
865
+ });
866
+
867
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
868
+ process.env.GSD_HOME = tempGsdHome;
869
+ process.chdir(tempProject);
870
+ clearGSDPreferencesCache();
871
+
872
+ writeFileSync(
873
+ getGlobalGSDPreferencesPath(),
874
+ [
875
+ "---",
876
+ "version: 1",
877
+ "planning_subagents:",
878
+ " plan-slice:",
879
+ " allowed: [scout, reviewer]",
880
+ "---",
881
+ "",
882
+ ].join("\n"),
883
+ "utf-8",
884
+ );
885
+ writeFileSync(
886
+ getProjectGSDPreferencesPath(tempProject),
887
+ [
888
+ "---",
889
+ "version: 1",
890
+ "planning_subagents:",
891
+ " plan-slice:",
892
+ " allowed: [security]",
893
+ " plan-milestone:",
894
+ " allowed: [planner]",
895
+ "---",
896
+ "",
897
+ ].join("\n"),
898
+ "utf-8",
899
+ );
900
+
901
+ const loaded = loadEffectiveGSDPreferences(tempProject);
902
+ assert.deepEqual(loaded?.preferences.planning_subagents?.["plan-slice"]?.allowed, [
903
+ "scout",
904
+ "reviewer",
905
+ "security",
906
+ ]);
907
+ assert.deepEqual(loaded?.preferences.planning_subagents?.["plan-milestone"]?.allowed, ["planner"]);
908
+ });
909
+
826
910
  // ── Model config parsing ─────────────────────────────────────────────────────
827
911
 
828
912
  test("parses OpenRouter model config with org/model IDs and fallbacks", () => {
@@ -54,6 +54,10 @@ const PREF_SAMPLE_VALUES: Record<string, unknown> = {
54
54
  },
55
55
  post_unit_hooks: [{ command: "npm test" }],
56
56
  pre_dispatch_hooks: [{ command: "npm run lint" }],
57
+ planning_subagents: {
58
+ "plan-milestone": { allowed: ["scout", "planner"] },
59
+ "plan-slice": { allowed: ["scout"] },
60
+ },
57
61
  dynamic_routing: { enabled: true },
58
62
  disabled_model_providers: ["slow-provider"],
59
63
  uok: { enabled: true },
@@ -13,6 +13,7 @@ import { tmpdir } from "node:os";
13
13
  import { fileURLToPath } from "node:url";
14
14
 
15
15
  import { buildExecuteTaskPrompt, buildPlanSlicePrompt, buildReactiveExecutePrompt, buildResearchSlicePrompt, inlineDependencySummaries } from "../auto-prompts.js";
16
+ import { buildDiscussSlicePrompt } from "../guided-flow.js";
16
17
  import { computeBudgets, truncateAtSectionBoundary } from "../context-budget.js";
17
18
 
18
19
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -735,3 +736,89 @@ describe("prompt-budget: modelRegistry + sessionContextWindow behavior", () => {
735
736
  }
736
737
  });
737
738
  });
739
+
740
+ // ─── execute-task + discuss-slice inline caps (plan 039) ──────────────────────
741
+
742
+ describe("prompt-budget: execute-task inline cap (039)", () => {
743
+ it("keeps the authoritative task plan whole but drops trailing templates when the plan alone exceeds the inline budget", async () => {
744
+ const base = createFixtureBase();
745
+ try {
746
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
747
+ const taskDir = join(sliceDir, "tasks");
748
+ mkdirSync(taskDir, { recursive: true });
749
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap\n");
750
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), "# Slice Plan\n");
751
+ // Oversized task plan (~60K) — well past the 20K inline ceiling.
752
+ const bigPlan = "# Task Plan\n\nMARKER_TASKPLAN_HEAD\n\n" + Array.from({ length: 30 }, (_, i) =>
753
+ `### Step ${i}\n\n${"Implementation detail. ".repeat(90)}`
754
+ ).join("\n\n");
755
+ writeFileSync(join(taskDir, "T01-PLAN.md"), bigPlan);
756
+
757
+ const prompt = await buildExecuteTaskPrompt("M001", "S01", "Slice", "T01", "Task", base, {
758
+ level: "standard",
759
+ sessionContextWindow: 32_000,
760
+ });
761
+
762
+ // The task plan is the authoritative execution contract — never truncated;
763
+ // its content survives whole even when it alone exceeds the inline budget.
764
+ assert.match(prompt, /MARKER_TASKPLAN_HEAD/);
765
+ assert.match(prompt, /### Step 29/);
766
+ // Trailing lower-priority templates are dropped — the on-demand decisions
767
+ // template that "standard" normally inlines is gone because the task plan
768
+ // consumed the entire inline budget. (Baseline: the tiny-plan test above
769
+ // asserts this block IS present.)
770
+ assert.doesNotMatch(prompt, /### On-demand Decisions Template/);
771
+ } finally {
772
+ cleanup(base);
773
+ }
774
+ });
775
+ });
776
+
777
+ describe("prompt-budget: discuss-slice inline cap (039)", () => {
778
+ it("prepends the configured response language", async () => {
779
+ const base = createFixtureBase();
780
+ const gsdHome = mkdtempSync(join(tmpdir(), "gsd-discuss-slice-language-home-"));
781
+ const previousGsdHome = process.env.GSD_HOME;
782
+ process.env.GSD_HOME = gsdHome;
783
+
784
+ try {
785
+ writeFileSync(join(gsdHome, "PREFERENCES.md"), "---\nversion: 1\nlanguage: 中文\n---\n", "utf-8");
786
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
787
+
788
+ const prompt = await buildDiscussSlicePrompt("M001", "S01", "Current", base);
789
+
790
+ assert.match(prompt, /^## Response Language\n\nAlways respond in 中文/);
791
+ } finally {
792
+ if (previousGsdHome === undefined) delete process.env.GSD_HOME;
793
+ else process.env.GSD_HOME = previousGsdHome;
794
+ cleanup(base);
795
+ cleanup(gsdHome);
796
+ }
797
+ });
798
+
799
+ it("caps the inlined block — roadmap survives first, the trailing decisions register truncates", async () => {
800
+ const base = createFixtureBase();
801
+ try {
802
+ const msDir = join(base, ".gsd", "milestones", "M001");
803
+ mkdirSync(join(msDir, "slices", "S01"), { recursive: true });
804
+ writeFileSync(join(msDir, "M001-ROADMAP.md"), "# Roadmap\n\nMARKER_ROADMAP\n\n- [ ] **S01: Current** `risk:low`\n");
805
+ writeFileSync(join(msDir, "M001-CONTEXT.md"), "# Context\n\nMARKER_CONTEXT\n");
806
+ // Huge decisions register (~60K) — the unbounded-growth surface the cap bounds.
807
+ const bigDecisions = "# Decisions\n\n" + Array.from({ length: 40 }, (_, i) =>
808
+ `### D${String(i).padStart(3, "0")}\n\n${"Decision rationale detail. ".repeat(60)}`
809
+ ).join("\n\n");
810
+ writeFileSync(join(base, ".gsd", "DECISIONS.md"), bigDecisions);
811
+
812
+ const prompt = await buildDiscussSlicePrompt("M001", "S01", "Current", base);
813
+
814
+ // Roadmap is first in the inlined block → survives the cap.
815
+ assert.match(prompt, /MARKER_ROADMAP/);
816
+ // The cap fired (capPreamble ceiling is 20K; the block is ~60K).
817
+ assert.match(prompt, /\[\.\.\.truncated/);
818
+ // The last decision (trailing content) is dropped.
819
+ assert.doesNotMatch(prompt, /### D039/);
820
+ } finally {
821
+ cleanup(base);
822
+ }
823
+ });
824
+ });
@@ -23,7 +23,10 @@ import {
23
23
  } from "../bootstrap/agent-end-recovery.ts";
24
24
  import { blockModelUntil, clearTemporaryModelBlocksForTest } from "../blocked-models.ts";
25
25
  import { _buildCancelledUnitStopReason } from "../auto/phase-helpers.ts";
26
- import { _classifyZeroToolProviderMessageForTest } from "../auto/unit-phase.ts";
26
+ import {
27
+ _classifyZeroToolProviderMessageForTest,
28
+ _zeroToolPseudoToolCallSnippetForTest,
29
+ } from "../auto/unit-phase.ts";
27
30
  import { autoSession } from "../auto-runtime-state.ts";
28
31
  import { getNextFallbackModel } from "../preferences.ts";
29
32
  import { clearGuidedUnitContext, getGuidedUnitContext, setGuidedUnitContext } from "../guided-unit-context.ts";
@@ -77,6 +80,21 @@ test("zero-tool provider classifier treats weekly limit wording as transient rat
77
80
  assert.equal(result.kind, "rate-limit");
78
81
  });
79
82
 
83
+ test("zero-tool pseudo tool-call detector identifies serialization drift", () => {
84
+ const snippet = _zeroToolPseudoToolCallSnippetForTest(
85
+ 'bash<arg_key>command</arg_key><arg_value>ls -la /tmp && echo "---SRC---"</arg_value></tool_call>',
86
+ );
87
+ assert.ok(snippet, "pseudo tool-call text should be recognized");
88
+ assert.match(snippet, /bash<arg_key>command<\/arg_key>/);
89
+ });
90
+
91
+ test("zero-tool provider classifier does not treat pseudo tool-call text as an error class", () => {
92
+ const result = _classifyZeroToolProviderMessageForTest(
93
+ 'bash<arg_key>command</arg_key><arg_value>ls -la /tmp && echo "---SRC---"</arg_value></tool_call>',
94
+ );
95
+ assert.equal(result, null, "pseudo tool-call text is drift, not a provider error");
96
+ });
97
+
80
98
  test("classifyError treats extra-usage phrasing as transient rate-limit (#4397)", () => {
81
99
  const result = classifyError("You are out of extra usage. Please wait before retrying.");
82
100
  assert.ok(isTransient(result));
@@ -651,6 +669,108 @@ test("rate-limit agent_end walks past unavailable fallback models before pausing
651
669
  }
652
670
  });
653
671
 
672
+ test("retry-exhausted abort/unknown provider stop reasons trigger model fallback (#1364)", async () => {
673
+ const originalCwd = process.cwd();
674
+ const originalSetTimeout = globalThis.setTimeout;
675
+
676
+ globalThis.setTimeout = ((fn: (...args: unknown[]) => void, delay?: number) => {
677
+ if ((delay ?? 0) === 0) {
678
+ fn();
679
+ }
680
+ return 0 as unknown as ReturnType<typeof setTimeout>;
681
+ }) as typeof setTimeout;
682
+
683
+ try {
684
+ for (const reason of ["abort", "unknown"]) {
685
+ const base = mkdtempSync(join(tmpdir(), `gsd-stop-reason-${reason}-`));
686
+ const notifications: Array<{ message: string; level?: string }> = [];
687
+ const setModelCalls: string[] = [];
688
+ const sendMessageCalls: unknown[][] = [];
689
+
690
+ try {
691
+ resetTransientRetryState();
692
+ clearTemporaryModelBlocksForTest();
693
+ autoSession.reset();
694
+ mkdirSync(join(base, ".gsd"), { recursive: true });
695
+ writeFileSync(
696
+ join(base, ".gsd", "PREFERENCES.md"),
697
+ [
698
+ "---",
699
+ "models:",
700
+ " execution:",
701
+ " model: gpt-5.5",
702
+ " provider: openai-codex",
703
+ " fallbacks:",
704
+ " - anthropic/claude-sonnet-4-6",
705
+ "---",
706
+ ].join("\n"),
707
+ "utf-8",
708
+ );
709
+ process.chdir(base);
710
+
711
+ autoSession.active = true;
712
+ autoSession.basePath = base;
713
+ autoSession.currentUnit = { type: "execute-task", id: "M001/S01/T01", startedAt: Date.now() };
714
+ autoSession.autoModeStartModel = { provider: "openai-codex", id: "gpt-5.5" };
715
+
716
+ const availableModels = [
717
+ { id: "gpt-5.5", provider: "openai-codex", api: "responses" },
718
+ { id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" },
719
+ ];
720
+ const ctx = {
721
+ model: availableModels[0],
722
+ modelRegistry: { getAvailable: () => availableModels },
723
+ ui: {
724
+ notify(message: string, level?: "info" | "warning" | "error" | "success") {
725
+ notifications.push({ message, level });
726
+ },
727
+ setStatus: () => {},
728
+ setWidget: () => {},
729
+ setWorkingMessage: () => {},
730
+ },
731
+ } as any;
732
+ const pi = {
733
+ setModel: async (model: { provider: string; id: string }) => {
734
+ setModelCalls.push(`${model.provider}/${model.id}`);
735
+ return true;
736
+ },
737
+ sendMessage: (...args: unknown[]) => {
738
+ sendMessageCalls.push(args);
739
+ },
740
+ } as any;
741
+
742
+ await handleAgentEnd(pi, {
743
+ messages: [{
744
+ role: "assistant",
745
+ stopReason: "error",
746
+ errorMessage: `Retry failed after 3 attempts: Unhandled stop reason: ${reason}`,
747
+ }],
748
+ } as any, ctx);
749
+
750
+ assert.deepEqual(setModelCalls, ["anthropic/claude-sonnet-4-6"], `${reason} should switch to configured fallback`);
751
+ assert.equal(sendMessageCalls.length, 1, `${reason} fallback recovery must trigger a continuation turn`);
752
+ assert.deepEqual(sendMessageCalls[0][1], { triggerTurn: true });
753
+ assert.ok(
754
+ notifications.some((n) => n.message.includes("anthropic/claude-sonnet-4-6")),
755
+ `${reason} notification should name the selected fallback`,
756
+ );
757
+ } finally {
758
+ process.chdir(originalCwd);
759
+ clearTemporaryModelBlocksForTest();
760
+ resetTransientRetryState();
761
+ autoSession.reset();
762
+ rmSync(base, { recursive: true, force: true });
763
+ }
764
+ }
765
+ } finally {
766
+ globalThis.setTimeout = originalSetTimeout;
767
+ process.chdir(originalCwd);
768
+ clearTemporaryModelBlocksForTest();
769
+ resetTransientRetryState();
770
+ autoSession.reset();
771
+ }
772
+ });
773
+
654
774
  test("isTerminalDeletedWorktreeProviderError matches removed auto-worktree paths only", () => {
655
775
  assert.equal(
656
776
  isTerminalDeletedWorktreeProviderError('Path "/Users/dev/.gsd/projects/abc123/worktrees/M005" does not exist'),
@@ -608,3 +608,42 @@ test("getDependencyTaskSummaryPaths handles missing dependency summaries gracefu
608
608
  rmSync(repo, { recursive: true, force: true });
609
609
  }
610
610
  });
611
+
612
+ test("#1343: getPriorTaskSummaryPaths excludes sibling-slice summaries in flat-phase", async () => {
613
+ const { getPriorTaskSummaryPaths } = await import("../auto-prompts.ts");
614
+ const repo = mkdtempSync(join(tmpdir(), "gsd-reactive-flat-prior-"));
615
+ try {
616
+ // Flat-phase: slices S01 and S02 share the phase dir and overlap task ids.
617
+ const phaseDir = join(repo, ".gsd", "phases", "01-test");
618
+ mkdirSync(phaseDir, { recursive: true });
619
+ writeFileSync(join(phaseDir, "S01-T01-SUMMARY.md"), "---\nid: T01\n---\n# S01 T01\n");
620
+ writeFileSync(join(phaseDir, "S02-T01-SUMMARY.md"), "---\nid: T01\n---\n# S02 T01\n");
621
+
622
+ // S02/T02 prior summaries must not pull the sibling S01-T01 summary.
623
+ const paths = await getPriorTaskSummaryPaths("M001", "S02", "T02", repo);
624
+ assert.equal(paths.length, 1, "Should only return the current slice's prior summary");
625
+ assert.ok(paths[0].includes("S02-T01-SUMMARY"), "Should include S02's T01");
626
+ assert.ok(!paths.some((p) => p.includes("S01-T01-SUMMARY")), "Should NOT include sibling S01's T01");
627
+ } finally {
628
+ rmSync(repo, { recursive: true, force: true });
629
+ }
630
+ });
631
+
632
+ test("#1343: getDependencyTaskSummaryPaths excludes sibling-slice summaries in flat-phase", async () => {
633
+ const { getDependencyTaskSummaryPaths } = await import("../auto-prompts.ts");
634
+ const repo = mkdtempSync(join(tmpdir(), "gsd-reactive-flat-dep-"));
635
+ try {
636
+ const phaseDir = join(repo, ".gsd", "phases", "01-test");
637
+ mkdirSync(phaseDir, { recursive: true });
638
+ writeFileSync(join(phaseDir, "S01-T01-SUMMARY.md"), "---\nid: T01\n---\n# S01 T01\n");
639
+ writeFileSync(join(phaseDir, "S02-T01-SUMMARY.md"), "---\nid: T01\n---\n# S02 T01\n");
640
+
641
+ // S02/T02 depends on T01 — must resolve S02's T01, not the sibling S01's.
642
+ const paths = await getDependencyTaskSummaryPaths("M001", "S02", "T02", ["T01"], repo);
643
+ assert.equal(paths.length, 1, "Should only return the current slice's dependency summary");
644
+ assert.ok(paths[0].includes("S02-T01-SUMMARY"), "Should include S02's T01");
645
+ assert.ok(!paths.some((p) => p.includes("S01-T01-SUMMARY")), "Should NOT include sibling S01's T01");
646
+ } finally {
647
+ rmSync(repo, { recursive: true, force: true });
648
+ }
649
+ });
@@ -6,8 +6,10 @@ import { join } from "node:path";
6
6
  import { randomUUID } from "node:crypto";
7
7
 
8
8
  import { autoSession } from "../auto-runtime-state.ts";
9
+ import { postUnitPreVerification } from "../auto-post-unit.ts";
9
10
  import { registerHooks } from "../bootstrap/register-hooks.ts";
10
11
  import { resetToolCallLoopGuard } from "../bootstrap/tool-call-loop-guard.ts";
12
+ import { closeDatabase, openDatabase } from "../gsd-db.ts";
11
13
  import { readUnitHarnessAbort } from "../unit-runtime.ts";
12
14
 
13
15
  type Handler = (event: any, ctx?: any) => Promise<any> | any;
@@ -166,6 +168,109 @@ test("register-hooks does not record normal product tool failures as harness abo
166
168
  assert.equal(abort, null);
167
169
  });
168
170
 
171
+ test("tool_execution_end latches a failed ScheduleWakeup across unrelated tool success", async (t) => {
172
+ const base = makeRuntimeBase();
173
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
174
+ openDatabase(join(base, ".gsd", "gsd.db"));
175
+ autoSession.reset();
176
+ autoSession.active = true;
177
+ autoSession.basePath = base;
178
+ autoSession.currentUnit = { type: "discuss-milestone", id: "M001", startedAt: Date.now() };
179
+ t.after(() => {
180
+ closeDatabase();
181
+ autoSession.reset();
182
+ rmSync(base, { recursive: true, force: true });
183
+ });
184
+
185
+ const { emitToolExecutionEnd } = makeHookHarness();
186
+ const error = "`prompt` is required when `stop` is not true";
187
+
188
+ await emitToolExecutionEnd({
189
+ toolName: "ScheduleWakeup",
190
+ isError: true,
191
+ result: error,
192
+ });
193
+ assert.equal(autoSession.lastToolInvocationError, `ScheduleWakeup: ${error}`);
194
+
195
+ await emitToolExecutionEnd({
196
+ toolName: "read",
197
+ isError: false,
198
+ result: "file contents",
199
+ });
200
+ assert.equal(
201
+ autoSession.lastToolInvocationError,
202
+ `ScheduleWakeup: ${error}`,
203
+ "an unrelated success must not erase a failed continuation before the unit boundary",
204
+ );
205
+
206
+ await emitToolExecutionEnd({
207
+ toolName: "parse_payload",
208
+ isError: true,
209
+ result: "Unexpected end of JSON input",
210
+ });
211
+ assert.equal(
212
+ autoSession.lastToolInvocationError,
213
+ `ScheduleWakeup: ${error}`,
214
+ "a later invocation error must not replace the failed continuation",
215
+ );
216
+
217
+ let pauseCalled = false;
218
+ const notifications: Array<{ message: string; severity?: string }> = [];
219
+ const result = await postUnitPreVerification(
220
+ {
221
+ s: autoSession,
222
+ ctx: {
223
+ ui: {
224
+ notify: (message: string, severity?: string) => notifications.push({ message, severity }),
225
+ },
226
+ } as any,
227
+ pi: {} as any,
228
+ buildSnapshotOpts: () => ({}) as any,
229
+ lockBase: () => base,
230
+ stopAuto: async () => {},
231
+ pauseAuto: async () => { pauseCalled = true; },
232
+ updateProgressWidget: () => {},
233
+ },
234
+ { skipSettleDelay: true, skipWorktreeSync: true },
235
+ );
236
+
237
+ assert.equal(result, "dispatched");
238
+ assert.equal(pauseCalled, true);
239
+ assert.equal(autoSession.pendingVerificationRetry, null);
240
+ assert.ok(
241
+ notifications.some(({ message, severity }) => severity === "error" && message.includes(error)),
242
+ "the unit boundary must surface the rejected continuation",
243
+ );
244
+ assert.equal(autoSession.lastToolInvocationError, null);
245
+
246
+ await emitToolExecutionEnd({
247
+ toolName: "ScheduleWakeup",
248
+ isError: true,
249
+ result: error,
250
+ });
251
+ await emitToolExecutionEnd({
252
+ toolName: "ScheduleWakeup",
253
+ isError: false,
254
+ result: "Wakeup scheduled",
255
+ });
256
+ assert.equal(autoSession.lastToolInvocationError, null);
257
+ });
258
+
259
+ test("tool_execution_end ignores ScheduleWakeup-shaped business validation from other tools", async (t) => {
260
+ autoSession.reset();
261
+ autoSession.active = true;
262
+ t.after(() => autoSession.reset());
263
+
264
+ const { emitToolExecutionEnd } = makeHookHarness();
265
+ await emitToolExecutionEnd({
266
+ toolName: "submit_review",
267
+ isError: true,
268
+ result: "`prompt` is required when `stop` is not true",
269
+ });
270
+
271
+ assert.equal(autoSession.lastToolInvocationError, null);
272
+ });
273
+
169
274
  test("register-hooks does not classify normal gsd_uat_exec nonzero exits as harness aborts", async (t) => {
170
275
  const base = makeRuntimeBase();
171
276
  const startedAt = Date.now();
@@ -35,6 +35,10 @@ describe("supportsServiceTier", () => {
35
35
  assert.equal(supportsServiceTier("gpt-5.5"), false);
36
36
  });
37
37
 
38
+ test("returns false for gpt-5.6 until service_tier payload support is verified", () => {
39
+ assert.equal(supportsServiceTier("gpt-5.6"), false);
40
+ });
41
+
38
42
  test("returns false for provider-only identifier without gpt-5.4 model suffix", () => {
39
43
  assert.equal(supportsServiceTier("vibeproxy-openai"), false);
40
44
  });
@@ -7,8 +7,9 @@
7
7
  * self-identify (Stream 2: actor identity passthrough).
8
8
  *
9
9
  * 2. The 3 reversibility handlers (reopen-task/slice/milestone) are
10
- * registered as MCP tools with both canonical and alias names
11
- * (Stream 3: reversibility tools).
10
+ * registered as MCP tools under their canonical names, and additionally
11
+ * under their alias names when GSD_ADVERTISE_TOOL_ALIASES=1 (plan 035
12
+ * suppresses alias schemas by default) (Stream 3: reversibility tools).
12
13
  *
13
14
  * 3. The reopen tools accept the documented core params plus optional
14
15
  * reason/actorName/triggerReason without rejecting valid payloads.
@@ -36,6 +37,18 @@ function getTool(name: string) {
36
37
  return pi.tools.find((t: any) => t.name === name);
37
38
  }
38
39
 
40
+ function withEnv(name: string, value: string | undefined, fn: () => void): void {
41
+ const previous = process.env[name];
42
+ if (value === undefined) delete process.env[name];
43
+ else process.env[name] = value;
44
+ try {
45
+ fn();
46
+ } finally {
47
+ if (previous === undefined) delete process.env[name];
48
+ else process.env[name] = previous;
49
+ }
50
+ }
51
+
39
52
  function getRequiredProps(tool: any): string[] {
40
53
  return tool.parameters.required ?? [];
41
54
  }
@@ -78,7 +91,9 @@ for (const name of ACTOR_TOOLS) {
78
91
  });
79
92
  }
80
93
 
81
- // ─── Stream 3: reopen tools registered with canonical + alias names ─────────
94
+ // ─── Stream 3: reopen tools registered under canonical names ────────────────
95
+ // Plan 035 suppresses alias schemas by default; the canonical reopen tools are
96
+ // always registered, and their aliases only when GSD_ADVERTISE_TOOL_ALIASES=1.
82
97
 
83
98
  const REOPEN_TOOLS = [
84
99
  { canonical: "gsd_task_reopen", alias: "gsd_reopen_task" },
@@ -87,13 +102,24 @@ const REOPEN_TOOLS = [
87
102
  ];
88
103
 
89
104
  for (const { canonical, alias } of REOPEN_TOOLS) {
90
- test(`${canonical} — registered with alias ${alias}`, () => {
105
+ test(`${canonical} — registered under its canonical name, no alias by default`, () => {
91
106
  const canonicalTool = getTool(canonical);
92
- const aliasTool = getTool(alias);
93
107
  assert.ok(canonicalTool, `${canonical} must be registered`);
94
- assert.ok(aliasTool, `${alias} must be registered as alias`);
95
108
  assert.ok(typeof canonicalTool.execute === "function", `${canonical} must have an execute function`);
96
- assert.ok(typeof aliasTool.execute === "function", `${alias} must have an execute function`);
109
+ assert.ok(!getTool(alias), `${alias} must not be registered by default (plan 035 alias suppression)`);
110
+ });
111
+
112
+ test(`${canonical} — registered with alias ${alias} when GSD_ADVERTISE_TOOL_ALIASES=1`, () => {
113
+ withEnv("GSD_ADVERTISE_TOOL_ALIASES", "1", () => {
114
+ const aliasPi = makeMockPi();
115
+ registerDbTools(aliasPi);
116
+ const canonicalTool = aliasPi.tools.find((t: any) => t.name === canonical);
117
+ const aliasTool = aliasPi.tools.find((t: any) => t.name === alias);
118
+ assert.ok(canonicalTool, `${canonical} must be registered`);
119
+ assert.ok(aliasTool, `${alias} must be registered as alias`);
120
+ assert.ok(typeof canonicalTool.execute === "function", `${canonical} must have an execute function`);
121
+ assert.ok(typeof aliasTool.execute === "function", `${alias} must have an execute function`);
122
+ });
97
123
  });
98
124
  }
99
125