@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
@@ -51,7 +51,9 @@ import {
51
51
  confirmDestructiveCommand,
52
52
  consumeDestructiveConfirmation,
53
53
  isDestructiveConfirmGateId,
54
+ peekPendingDestructiveCommand,
54
55
  requestDestructiveConfirmation,
56
+ resetDestructiveConfirmation,
55
57
  } from "../safety/destructive-confirmation.js";
56
58
  import { logWarning as safetyLogWarning, setStderrLoggingEnabled } from "../workflow-logger.js";
57
59
  import { isUnitCloseoutTool, runInteractiveUnitCloseout } from "../unit-closeout.js";
@@ -59,6 +61,7 @@ import { installNotifyInterceptor } from "./notify-interceptor.js";
59
61
  import { initNotificationStore } from "../notification-store.js";
60
62
  import { initNotificationWidget } from "../notification-widget.js";
61
63
  import { notifyPreferenceDiagnostics } from "../preferences-diagnostics.js";
64
+ import { resolveEffectivePlanningToolsPolicy } from "../planning-subagent-policy.js";
62
65
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
63
66
  import { extractSubagentAgentClasses } from "./subagent-input.js";
64
67
  import {
@@ -187,6 +190,7 @@ function suppressWelcomeHeader(ctx: ExtensionContext): void {
187
190
  * are bounded — cleared on activation, session boundaries, and verification.
188
191
  */
189
192
  const deferredApprovalGates = new Map<string, string>();
193
+ const deferredDestructiveConfirmationPauses = new Set<string>();
190
194
 
191
195
  export const MINIMAL_GSD_TOOL_NAMES = [
192
196
  "gsd_exec",
@@ -618,6 +622,23 @@ function clearDeferredApprovalGate(basePath?: string): void {
618
622
  }
619
623
  }
620
624
 
625
+ function deferDestructiveConfirmationPause(basePath: string): void {
626
+ deferredDestructiveConfirmationPauses.add(basePath);
627
+ }
628
+
629
+ function clearDeferredDestructiveConfirmationPause(basePath?: string): void {
630
+ if (!basePath) {
631
+ deferredDestructiveConfirmationPauses.clear();
632
+ } else {
633
+ deferredDestructiveConfirmationPauses.delete(basePath);
634
+ }
635
+ }
636
+
637
+ function isDestructiveConfirmationBlocking(basePath: string): boolean {
638
+ return deferredDestructiveConfirmationPauses.has(basePath)
639
+ && Boolean(peekPendingDestructiveCommand(basePath));
640
+ }
641
+
621
642
  function deferApprovalGate(gateId: string, basePath: string): void {
622
643
  // Verified-on-disk wins (same adapter policy as activation/re-arm): if the
623
644
  // workflow MCP child already verified this gate, deferring would block
@@ -1015,6 +1036,7 @@ export function registerHooks(
1015
1036
  await applyToolCallLoopGuardConfig(basePath);
1016
1037
  approvalQuestionAbortInFlight = false;
1017
1038
  clearDeferredApprovalGate();
1039
+ clearDeferredDestructiveConfirmationPause();
1018
1040
  await resetAskUserQuestionsTurnCache();
1019
1041
  await syncServiceTierStatus(ctx);
1020
1042
  await applyDisabledModelProviderPolicy(ctx);
@@ -1106,6 +1128,7 @@ export function registerHooks(
1106
1128
  resetToolCallLoopGuard();
1107
1129
  await applyToolCallLoopGuardConfig(basePath);
1108
1130
  clearDeferredApprovalGate();
1131
+ clearDeferredDestructiveConfirmationPause();
1109
1132
  await resetAskUserQuestionsTurnCache();
1110
1133
  clearDiscussionFlowState(basePath);
1111
1134
  // /clear or /new destroys the conversation holding a discuss interview, so
@@ -1157,6 +1180,7 @@ export function registerHooks(
1157
1180
  }
1158
1181
  }
1159
1182
  clearDeferredApprovalGate(beforeAgentBasePath);
1183
+ clearDeferredDestructiveConfirmationPause(beforeAgentBasePath);
1160
1184
 
1161
1185
  // session_start can fire before the active provider has settled. By
1162
1186
  // before_agent_start, Claude Code CLI sessions should get the same
@@ -1244,12 +1268,20 @@ export function registerHooks(
1244
1268
  await handleAgentEnd(pi, event, ctx);
1245
1269
  } finally {
1246
1270
  activateDeferredApprovalGate(agentEndBasePath);
1271
+ const destructiveConfirmationBlocking = isDestructiveConfirmationBlocking(agentEndBasePath);
1272
+ clearDeferredDestructiveConfirmationPause(agentEndBasePath);
1247
1273
  await maybePauseAutoForApprovalGate(
1248
1274
  ctx,
1249
1275
  pi,
1250
1276
  isApprovalGateBlocking(agentEndBasePath),
1251
1277
  "Depth confirmation is waiting for your answer — pausing auto-mode.",
1252
1278
  );
1279
+ await maybePauseAutoForApprovalGate(
1280
+ ctx,
1281
+ pi,
1282
+ destructiveConfirmationBlocking,
1283
+ "Destructive-command confirmation is waiting for your answer — pausing auto-mode.",
1284
+ );
1253
1285
  }
1254
1286
  });
1255
1287
 
@@ -1559,6 +1591,7 @@ export function registerHooks(
1559
1591
  const activeUnitType = dash.currentUnit?.type ?? guidedUnit?.unitType;
1560
1592
  if (activeUnitType) {
1561
1593
  const manifest = resolveManifest(activeUnitType);
1594
+ const planningBasePath = dash.basePath || guidedUnit?.basePath || discussionBasePath;
1562
1595
  let planningInput = "";
1563
1596
  let agentClasses: string[] | undefined;
1564
1597
  if (isToolCallEventType("write", event)) {
@@ -1574,9 +1607,9 @@ export function registerHooks(
1574
1607
  const planningGuard = shouldBlockPlanningUnit(
1575
1608
  event.toolName,
1576
1609
  planningInput,
1577
- dash.basePath || guidedUnit?.basePath || discussionBasePath,
1610
+ planningBasePath,
1578
1611
  activeUnitType,
1579
- manifest?.tools,
1612
+ resolveEffectivePlanningToolsPolicy(activeUnitType, manifest?.tools, planningBasePath),
1580
1613
  agentClasses,
1581
1614
  (event as { input?: unknown }).input,
1582
1615
  dash.currentUnit?.id,
@@ -1685,6 +1718,7 @@ export function registerHooks(
1685
1718
  // Record the command as pending so an affirmative answer to a
1686
1719
  // destructive_confirm gate (handled in tool_result) can confirm it.
1687
1720
  requestDestructiveConfirmation(command, guardBasePath);
1721
+ deferDestructiveConfirmationPause(guardBasePath);
1688
1722
  const reason = [
1689
1723
  "HARD BLOCK: destructive Bash command requires explicit human confirmation.",
1690
1724
  `Detected: ${classification.labels.join(", ")}`,
@@ -1695,14 +1729,6 @@ export function registerHooks(
1695
1729
  safetyLogWarning("safety", `destructive command: ${classification.labels.join(", ")}`, {
1696
1730
  command: String(command).slice(0, 200),
1697
1731
  });
1698
- if (ctx) {
1699
- await maybePauseAutoForApprovalGate(
1700
- ctx,
1701
- pi,
1702
- isAutoActive(),
1703
- "Destructive-command confirmation is waiting for your answer — pausing auto-mode.",
1704
- );
1705
- }
1706
1732
  return { block: true, reason };
1707
1733
  }
1708
1734
  }
@@ -1743,7 +1769,7 @@ export function registerHooks(
1743
1769
  recordToolInvocationError(event.toolName, errorText);
1744
1770
  recordRetryableHarnessToolError(toolName, resultPayload, errorText);
1745
1771
  } else if (isAutoActive()) {
1746
- clearToolInvocationError();
1772
+ clearToolInvocationError(event.toolName);
1747
1773
  }
1748
1774
  // Interactive Closeout adapter (ADR-032): auto-mode owns closeout for its
1749
1775
  // own units; interactive completions get the durable git subset (commit +
@@ -1884,7 +1910,10 @@ export function registerHooks(
1884
1910
  const answer = details.response?.answers?.[question.id];
1885
1911
  if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
1886
1912
  confirmDestructiveCommand(basePath);
1913
+ } else {
1914
+ resetDestructiveConfirmation(basePath);
1887
1915
  }
1916
+ clearDeferredDestructiveConfirmationPause(basePath);
1888
1917
  break;
1889
1918
  }
1890
1919
  }
@@ -1951,7 +1980,7 @@ export function registerHooks(
1951
1980
  recordToolInvocationError(event.toolName, errorText);
1952
1981
  recordRetryableHarnessToolError(toolName, event.result, errorText);
1953
1982
  } else if (isAutoActive()) {
1954
- clearToolInvocationError();
1983
+ clearToolInvocationError(event.toolName);
1955
1984
  }
1956
1985
  // Safety harness: record tool execution results for evidence cross-referencing
1957
1986
  if (isAutoActive()) {
@@ -21,6 +21,14 @@ import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile,
21
21
  import { toPosixPath } from "../../shared/mod.js";
22
22
  import { autoEnableCmuxPreferences } from "../commands-cmux.js";
23
23
  import { gsdHome } from "../gsd-home.js";
24
+ import { GSD_CONTEXT_MESSAGE_SENTINEL } from "../constants.js";
25
+
26
+ // Single source of truth lives in ../constants.js; re-exported here because
27
+ // buildContextMessage() stamps this marker on every context injection and the
28
+ // provider payload policy (filterSupersededContextInjections in
29
+ // context-masker.ts) matches it to dedupe. Adding a new context customType?
30
+ // Ensure buildContextMessage still prepends this sentinel.
31
+ export { GSD_CONTEXT_MESSAGE_SENTINEL };
24
32
 
25
33
  const DEFAULT_CONTEXT_MESSAGE_MAX_CHARS = 4_000;
26
34
  const DEFAULT_KNOWLEDGE_MAX_CHARS = 12_000;
@@ -448,14 +456,14 @@ export function buildContextMessage(opts: {
448
456
  const memoryContent = markMemoryContextSupplied(opts.memoryBlock.trim());
449
457
  if (opts.injection) {
450
458
  const content = limitContextMessageContent(
451
- memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection,
459
+ `${GSD_CONTEXT_MESSAGE_SENTINEL}\n${memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection}`,
452
460
  contextCharLimit,
453
461
  );
454
462
  return { customType: "gsd-guided-context", content, display: false as const };
455
463
  }
456
464
  if (opts.forensicsInjection) {
457
465
  const content = limitContextMessageContent(
458
- memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection,
466
+ `${GSD_CONTEXT_MESSAGE_SENTINEL}\n${memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection}`,
459
467
  contextCharLimit,
460
468
  );
461
469
  return { customType: "gsd-forensics", content, display: false as const };
@@ -463,7 +471,7 @@ export function buildContextMessage(opts: {
463
471
  if (memoryContent) {
464
472
  return {
465
473
  customType: "gsd-memory",
466
- content: limitContextMessageContent(memoryContent, contextCharLimit),
474
+ content: limitContextMessageContent(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n${memoryContent}`, contextCharLimit),
467
475
  display: false as const,
468
476
  };
469
477
  }
@@ -9,6 +9,10 @@ import { canonicalToolName } from "../engine-hook-contract.js";
9
9
  import { loadJsonFileOrNull } from "../json-persistence.js";
10
10
  import { getIsolationMode } from "../preferences.js";
11
11
  import { compileSubagentPermissionContract, type ToolsPolicy } from "../unit-context-manifest.js";
12
+ import {
13
+ allowedPlanningDispatchAgentsList,
14
+ isReadOnlyPlanningDispatchAgent,
15
+ } from "../planning-subagent-registry.js";
12
16
  import { logWarning } from "../workflow-logger.js";
13
17
  import { acquireSyncLock, releaseSyncLock } from "../sync-lock.js";
14
18
  import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
@@ -1191,36 +1195,10 @@ export function shouldBlockQueueExecutionInSnapshot(
1191
1195
  const PLANNING_WRITE_TOOLS = new Set(["write", "edit", "multi_edit", "notebook_edit"]);
1192
1196
  const PLANNING_SUBAGENT_TOOLS = new Set(["subagent", "task"]);
1193
1197
 
1194
- /**
1195
- * Canonical registry for agents that planning-dispatch may consider. Unit
1196
- * manifests still declare per-unit subsets via ToolsPolicy.allowedSubagents.
1197
- */
1198
- const PLANNING_DISPATCH_AGENT_REGISTRY = {
1199
- mnemo: { readOnlySpecialist: true },
1200
- scout: { readOnlySpecialist: true },
1201
- planner: { readOnlySpecialist: true },
1202
- reviewer: { readOnlySpecialist: true },
1203
- security: { readOnlySpecialist: true },
1204
- tester: { readOnlySpecialist: true },
1205
- } as const satisfies Record<string, { readonly readOnlySpecialist: boolean }>;
1206
-
1207
- export const ALLOWED_PLANNING_DISPATCH_AGENTS = new Set<string>(
1208
- Object.entries(PLANNING_DISPATCH_AGENT_REGISTRY)
1209
- .filter(([, metadata]) => metadata.readOnlySpecialist)
1210
- .map(([agentId]) => agentId),
1211
- );
1198
+ export { ALLOWED_PLANNING_DISPATCH_AGENTS } from "../planning-subagent-registry.js";
1212
1199
 
1213
1200
  let warnedMissingControlledDispatchAgentClasses = false;
1214
1201
 
1215
- function isReadOnlySpecialist(agentId: string): boolean {
1216
- const metadata = PLANNING_DISPATCH_AGENT_REGISTRY[agentId as keyof typeof PLANNING_DISPATCH_AGENT_REGISTRY];
1217
- return metadata?.readOnlySpecialist === true;
1218
- }
1219
-
1220
- function allowedPlanningDispatchAgentsList(): string {
1221
- return [...ALLOWED_PLANNING_DISPATCH_AGENTS].join(", ");
1222
- }
1223
-
1224
1202
  function allowsControlledSubagentDispatch(
1225
1203
  policy: ToolsPolicy,
1226
1204
  ): policy is ToolsPolicy & { readonly allowedSubagents: readonly string[] } {
@@ -1393,7 +1371,7 @@ export function shouldBlockPlanningUnit(
1393
1371
  if (requested.length === 0) {
1394
1372
  return { block: false };
1395
1373
  }
1396
- const globallyDisallowed = requested.find(a => !isReadOnlySpecialist(a));
1374
+ const globallyDisallowed = requested.find(a => !isReadOnlyPlanningDispatchAgent(a));
1397
1375
  if (globallyDisallowed) {
1398
1376
  return planningBlock(
1399
1377
  unitType,
@@ -1406,7 +1384,7 @@ export function shouldBlockPlanningUnit(
1406
1384
  return planningBlock(
1407
1385
  unitType,
1408
1386
  policy.mode,
1409
- `subagent dispatch of "${disallowedByPolicy}" not permitted by ToolsPolicy.allowedSubagents; permitted agents for this unit: ${allowedSubagents.join(", ")}`,
1387
+ `subagent dispatch of "${disallowedByPolicy}" not permitted by unit allowlist (ToolsPolicy.allowedSubagents/planning_subagents); permitted agents for this unit: ${allowedSubagents.join(", ")}`,
1410
1388
  );
1411
1389
  }
1412
1390
  return { block: false };
@@ -11,6 +11,7 @@ import { executeValidateMilestone } from "./tools/workflow-tool-executors.js";
11
11
  import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
12
12
  import { getLatestAssessmentByScope } from "./gsd-db.js";
13
13
  import { checkpointWorkflowDatabase } from "./db-workspace.js";
14
+ import { formatVerdictRecordedNotice, formatVerdictRejectedNotice } from "./stop-notice.js";
14
15
  import {
15
16
  VALIDATION_VERDICTS,
16
17
  extractVerdict,
@@ -165,17 +166,17 @@ export async function handleVerdict(
165
166
  basePath: string,
166
167
  ): Promise<void> {
167
168
  if (!rawArgs.trim()) {
168
- ctx.ui.notify(USAGE, "warning");
169
+ ctx.ui.notify(formatVerdictRejectedNotice(USAGE), "warning");
169
170
  return;
170
171
  }
171
172
 
172
173
  const parsed = parseArgs(rawArgs);
173
174
  if ("error" in parsed) {
174
- ctx.ui.notify(`${parsed.error}\n${USAGE}`, "warning");
175
+ ctx.ui.notify(formatVerdictRejectedNotice(`${parsed.error}\n${USAGE}`), "warning");
175
176
  return;
176
177
  }
177
178
  if (!parsed.verdict) {
178
- ctx.ui.notify(USAGE, "warning");
179
+ ctx.ui.notify(formatVerdictRejectedNotice(USAGE), "warning");
179
180
  return;
180
181
  }
181
182
 
@@ -184,7 +185,7 @@ export async function handleVerdict(
184
185
  const state = await deriveState(basePath);
185
186
  if (!state.activeMilestone) {
186
187
  ctx.ui.notify(
187
- "No active milestone — pass --milestone Mxxx to target a specific milestone.",
188
+ formatVerdictRejectedNotice("No active milestone — pass --milestone Mxxx to target a specific milestone."),
188
189
  "warning",
189
190
  );
190
191
  return;
@@ -195,7 +196,9 @@ export async function handleVerdict(
195
196
  const existingValidation = await loadExistingValidation(basePath, milestoneId);
196
197
  if (!existingValidation) {
197
198
  ctx.ui.notify(
198
- `No milestone validation found for ${milestoneId}. Run /gsd auto to validate first, then retry /gsd verdict.`,
199
+ formatVerdictRejectedNotice(
200
+ `No milestone validation found for ${milestoneId}. Run /gsd auto to validate first, then retry /gsd verdict.`,
201
+ ),
199
202
  "warning",
200
203
  );
201
204
  return;
@@ -205,7 +208,7 @@ export async function handleVerdict(
205
208
 
206
209
  if (parsed.verdict !== "pass" && !parsed.rationale) {
207
210
  ctx.ui.notify(
208
- `--rationale is required when overriding to ${parsed.verdict}.`,
211
+ formatVerdictRejectedNotice(`--rationale is required when overriding to ${parsed.verdict}.`),
209
212
  "warning",
210
213
  );
211
214
  return;
@@ -234,7 +237,7 @@ export async function handleVerdict(
234
237
  if (result.isError) {
235
238
  const msg =
236
239
  result.content[0]?.type === "text" ? result.content[0].text : "Unknown error";
237
- ctx.ui.notify(msg, "error");
240
+ ctx.ui.notify(formatVerdictRejectedNotice(msg), "error");
238
241
  return;
239
242
  }
240
243
 
@@ -244,12 +247,16 @@ export async function handleVerdict(
244
247
  const effectiveVerdict = extractEffectiveVerdict(result.details, parsed.verdict);
245
248
  if (effectiveVerdict !== parsed.verdict) {
246
249
  ctx.ui.notify(
247
- `Milestone ${milestoneId} verdict requested: ${parsed.verdict}, effective: ${effectiveVerdict} (${existingValidation.source})`,
250
+ formatVerdictRecordedNotice(
251
+ `Milestone ${milestoneId} verdict requested: ${parsed.verdict}, effective: ${effectiveVerdict} (${existingValidation.source})`,
252
+ ),
248
253
  "warning",
249
254
  );
250
255
  } else {
251
256
  ctx.ui.notify(
252
- `Milestone ${milestoneId} verdict: ${prevVerdict} -> ${effectiveVerdict} (${existingValidation.source})`,
257
+ formatVerdictRecordedNotice(
258
+ `Milestone ${milestoneId} verdict: ${prevVerdict} -> ${effectiveVerdict} (${existingValidation.source})`,
259
+ ),
253
260
  "success",
254
261
  );
255
262
  }
@@ -6,8 +6,8 @@
6
6
  // (drift to import). gsd-core is oblivious to this file and ignores it.
7
7
 
8
8
  import { createHash } from "node:crypto";
9
- import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
10
- import { dirname, join, isAbsolute, normalize } from "node:path";
9
+ import { existsSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
10
+ import { dirname, join, isAbsolute, normalize, relative, resolve } from "node:path";
11
11
 
12
12
  /** Current marker schema version. Bump on breaking format changes + migrate. */
13
13
  export const COMPAT_MARKER_SCHEMA = 2;
@@ -54,6 +54,13 @@ export interface CompatMarker {
54
54
  piVersion: string;
55
55
  }
56
56
 
57
+ export interface ReadCompatMarkerOptions {
58
+ /** Rewrite invalid-but-derivable marker keys back to safe root-relative keys. */
59
+ healInvalidKeys?: boolean;
60
+ /** Quarantine malformed/unsafe markers. Disable only for read-only previews. */
61
+ quarantineInvalid?: boolean;
62
+ }
63
+
57
64
  /** Marker returned when no marker exists yet (fresh project, first gsd-pi run). */
58
65
  export const EMPTY_MARKER: CompatMarker = {
59
66
  schema: COMPAT_MARKER_SCHEMA,
@@ -81,6 +88,37 @@ export function computeProjectionSha(content: string): string {
81
88
  return createHash("sha256").update(normalizeForHash(content)).digest("hex").slice(0, 16);
82
89
  }
83
90
 
91
+ function normalizeRealPath(p: string): string {
92
+ try {
93
+ return realpathSync.native(p);
94
+ } catch {
95
+ return resolve(p);
96
+ }
97
+ }
98
+
99
+ function rootRelativeKey(rootPath: string, absPath: string): string | null {
100
+ const root = normalizeRealPath(rootPath);
101
+ const abs = normalizeRealPath(absPath);
102
+ const rel = relative(root, abs);
103
+ if (!rel || rel === ".." || rel.startsWith(`..${sepForRelative(rel)}`) || isAbsolute(rel)) {
104
+ return null;
105
+ }
106
+ return rel.replace(/\\/g, "/");
107
+ }
108
+
109
+ function sepForRelative(rel: string): string {
110
+ return rel.includes("\\") ? "\\" : "/";
111
+ }
112
+
113
+ export function deriveCompatProjectionKey(absPath: string, roots: readonly string[]): string {
114
+ for (const root of roots) {
115
+ const key = rootRelativeKey(root, absPath);
116
+ if (key) return key;
117
+ }
118
+ const fallbackRoot = roots[roots.length - 1] ?? dirname(absPath);
119
+ return relative(fallbackRoot, absPath).replace(/\\/g, "/");
120
+ }
121
+
84
122
  /**
85
123
  * Read & validate the marker. A missing marker → EMPTY_MARKER (treat every
86
124
  * projection as external on next reconcile). A malformed marker is quarantined
@@ -88,7 +126,12 @@ export function computeProjectionSha(content: string): string {
88
126
  * EMPTY_MARKER. A schema-mismatch returns EMPTY_MARKER (forward-compat: refuse
89
127
  * to act on a future format we don't understand).
90
128
  */
91
- export function readCompatMarker(basePath: string): CompatMarker {
129
+ export function readCompatMarker(
130
+ basePath: string,
131
+ options: ReadCompatMarkerOptions = {},
132
+ ): CompatMarker {
133
+ const healInvalidKeys = options.healInvalidKeys ?? true;
134
+ const quarantineInvalid = options.quarantineInvalid ?? true;
92
135
  const path = compatMarkerPath(basePath);
93
136
  if (!existsSync(path)) return emptyMarker();
94
137
 
@@ -103,12 +146,16 @@ export function readCompatMarker(basePath: string): CompatMarker {
103
146
  try {
104
147
  parsed = JSON.parse(raw);
105
148
  } catch {
106
- quarantine(basePath, raw);
149
+ if (quarantineInvalid) quarantine(basePath, raw);
107
150
  return emptyMarker();
108
151
  }
109
152
 
153
+ if (healInvalidKeys && healMarkerProjectionKeys(basePath, parsed) && isValidMarker(parsed)) {
154
+ writeCompatMarker(basePath, parsed);
155
+ }
156
+
110
157
  if (!isValidMarker(parsed)) {
111
- quarantine(basePath, raw);
158
+ if (quarantineInvalid) quarantine(basePath, raw);
112
159
  return emptyMarker();
113
160
  }
114
161
  // Promote older markers by defaulting absent fields. Schema 1 → 2 only adds
@@ -245,6 +292,53 @@ function isValidProjectionMap(x: unknown): boolean {
245
292
  return true;
246
293
  }
247
294
 
295
+ function healMarkerProjectionKeys(basePath: string, marker: unknown): marker is CompatMarker {
296
+ if (typeof marker !== "object" || marker === null) return false;
297
+ const m = marker as Record<string, unknown>;
298
+ let changed = false;
299
+
300
+ if (typeof m.projections === "object" && m.projections !== null) {
301
+ changed = healProjectionMapKeys(basePath, ".gsd", m.projections as Record<string, ProjectionEntry>) || changed;
302
+ }
303
+
304
+ const planning = m.planning;
305
+ if (typeof planning === "object" && planning !== null) {
306
+ const p = planning as Record<string, unknown>;
307
+ if (typeof p.projections === "object" && p.projections !== null) {
308
+ changed = healProjectionMapKeys(basePath, ".planning", p.projections as Record<string, ProjectionEntry>) || changed;
309
+ }
310
+ if (typeof p.passthrough === "object" && p.passthrough !== null) {
311
+ changed = healProjectionMapKeys(basePath, ".planning", p.passthrough as Record<string, ProjectionEntry>) || changed;
312
+ }
313
+ }
314
+
315
+ return changed;
316
+ }
317
+
318
+ function healProjectionMapKeys(
319
+ basePath: string,
320
+ rootName: ".gsd" | ".planning",
321
+ map: Record<string, ProjectionEntry>,
322
+ ): boolean {
323
+ let changed = false;
324
+ const root = join(basePath, rootName);
325
+
326
+ for (const key of Object.keys(map)) {
327
+ if (isSafeProjectionKey(key)) continue;
328
+ if (key.includes("\0") || isAbsolute(key) || /^[A-Za-z]:/.test(key)) continue;
329
+ const healedKey = rootRelativeKey(root, resolve(root, key));
330
+ if (!healedKey || !isSafeProjectionKey(healedKey)) continue;
331
+
332
+ if (!map[healedKey]) {
333
+ map[healedKey] = map[key]!;
334
+ }
335
+ delete map[key];
336
+ changed = true;
337
+ }
338
+
339
+ return changed;
340
+ }
341
+
248
342
  function isValidPlanningMarker(x: unknown): x is PlanningMarker {
249
343
  if (typeof x !== "object" || x === null) return false;
250
344
  const p = x as Record<string, unknown>;
@@ -31,36 +31,37 @@ export const CACHE_MAX = 50;
31
31
  * By scoping tools to this allowlist during discuss dispatches, the grammar
32
32
  * sent to the provider stays well under provider limits.
33
33
  *
34
- * Included tools and why:
34
+ * Included tools and why (canonical names only — aliases are no longer
35
+ * advertised to models by default, see plan 035):
35
36
  * - gsd_summary_save: writes CONTEXT.md artifacts (all discuss prompts)
36
- * - gsd_save_summary: alias for above
37
37
  * - gsd_decision_save: records decisions (discuss.md output phase)
38
- * - gsd_save_decision: alias for above
39
38
  * - gsd_plan_milestone: writes roadmap (discuss.md single/multi milestone)
40
- * - gsd_milestone_plan: alias for above
41
39
  * - gsd_milestone_generate_id: generates milestone IDs (discuss.md multi-milestone)
42
- * - gsd_generate_milestone_id: alias for above
43
40
  * - gsd_requirement_save: creates requirements during discuss
44
- * - gsd_save_requirement: alias for above
45
41
  * - gsd_requirement_update: updates requirements during discuss
46
- * - gsd_update_requirement: alias for above
47
42
  */
48
43
  export const DISCUSS_TOOLS_ALLOWLIST: readonly string[] = [
49
44
  // Context / summary writing
50
45
  "gsd_summary_save",
51
- "gsd_save_summary",
52
46
  // Decision recording
53
47
  "gsd_decision_save",
54
- "gsd_save_decision",
55
48
  // Milestone planning (needed for discuss.md output phase)
56
49
  "gsd_plan_milestone",
57
- "gsd_milestone_plan",
58
50
  // Milestone ID generation (multi-milestone flow)
59
51
  "gsd_milestone_generate_id",
60
- "gsd_generate_milestone_id",
61
52
  // Requirement updates
62
53
  "gsd_requirement_save",
63
- "gsd_save_requirement",
64
54
  "gsd_requirement_update",
65
- "gsd_update_requirement",
66
55
  ];
56
+
57
+ // ─── Context Injection ────────────────────────────────────────────────────────
58
+
59
+ /**
60
+ * Leading marker stamped on every buildContextMessage() output
61
+ * (bootstrap/system-context.ts). Single source of truth for the producer and
62
+ * the consumer: the provider payload policy (filterSupersededContextInjections
63
+ * in context-masker.ts) matches this prefix to find and dedupe injected
64
+ * memory/guided/forensics messages after convertToLlm strips their customType.
65
+ * Both sides import it from here — do not inline the literal in either module.
66
+ */
67
+ export const GSD_CONTEXT_MESSAGE_SENTINEL = "[GSD Context Injection]";