@opengsd/gsd-pi 1.9.0 → 1.10.0-dev.a5e42e99

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 (502) hide show
  1. package/README.md +10 -9
  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-model-selection.js +1 -0
  12. package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
  13. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  14. package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -5
  15. package/dist/resources/extensions/gsd/auto-start.js +30 -10
  16. package/dist/resources/extensions/gsd/auto-tool-tracking.js +40 -0
  17. package/dist/resources/extensions/gsd/auto-verification.js +25 -12
  18. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +4 -0
  19. package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
  20. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +1 -1
  21. package/dist/resources/extensions/gsd/auto.js +15 -5
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -0
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -7
  24. package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -3
  25. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -26
  26. package/dist/resources/extensions/gsd/commands-verdict.js +10 -9
  27. package/dist/resources/extensions/gsd/compat/compat-marker.js +80 -5
  28. package/dist/resources/extensions/gsd/constants.js +12 -13
  29. package/dist/resources/extensions/gsd/context-masker.js +97 -21
  30. package/dist/resources/extensions/gsd/db/engine.js +5 -2
  31. package/dist/resources/extensions/gsd/docs/preferences-reference.md +33 -2
  32. package/dist/resources/extensions/gsd/doctor-engine-checks.js +40 -12
  33. package/dist/resources/extensions/gsd/error-classifier.js +12 -2
  34. package/dist/resources/extensions/gsd/exec-sandbox.js +1 -0
  35. package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
  36. package/dist/resources/extensions/gsd/git-service.js +19 -7
  37. package/dist/resources/extensions/gsd/gsd-db.js +75 -31
  38. package/dist/resources/extensions/gsd/guided-flow.js +11 -5
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -21
  40. package/dist/resources/extensions/gsd/md-importer.js +5 -6
  41. package/dist/resources/extensions/gsd/model-cost-table.js +6 -1
  42. package/dist/resources/extensions/gsd/model-router.js +16 -0
  43. package/dist/resources/extensions/gsd/paths.js +66 -15
  44. package/dist/resources/extensions/gsd/planning-subagent-policy.js +37 -0
  45. package/dist/resources/extensions/gsd/planning-subagent-registry.js +33 -0
  46. package/dist/resources/extensions/gsd/preferences-models.js +50 -10
  47. package/dist/resources/extensions/gsd/preferences-types.js +6 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +86 -4
  49. package/dist/resources/extensions/gsd/preferences.js +24 -0
  50. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
  51. package/dist/resources/extensions/gsd/provider-payload-policy.js +16 -5
  52. package/dist/resources/extensions/gsd/safety/destructive-guard.js +1 -1
  53. package/dist/resources/extensions/gsd/skill-activation.js +17 -3
  54. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +98 -2
  55. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +4 -1
  56. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +5 -2
  57. package/dist/resources/extensions/gsd/stop-notice.js +9 -0
  58. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +8 -0
  59. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -5
  60. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -1
  61. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +14 -5
  62. package/dist/resources/extensions/gsd/tools/reopen-slice.js +15 -5
  63. package/dist/resources/extensions/gsd/tools/reopen-task.js +16 -10
  64. package/dist/resources/extensions/gsd/unit-context-composer.js +28 -5
  65. package/dist/resources/extensions/gsd/verification-evidence.js +5 -3
  66. package/dist/resources/extensions/gsd/workflow-projections.js +2 -3
  67. package/dist/resources/extensions/gsd/workspace.js +2 -1
  68. package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
  69. package/dist/resources/extensions/ollama/ollama-chat-provider.js +2 -2
  70. package/dist/resources/extensions/shared/interview-ui.js +43 -6
  71. package/dist/resources/extensions/subagent/index.js +1 -1
  72. package/dist/startup-model-validation.d.ts +1 -1
  73. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  74. package/dist/web/standalone/.next/BUILD_ID +1 -1
  75. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  76. package/dist/web/standalone/.next/build-manifest.json +3 -3
  77. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  78. package/dist/web/standalone/.next/required-server-files.json +3 -3
  79. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  80. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  90. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  140. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  150. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  156. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  172. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  176. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/index.html +1 -1
  186. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  187. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  188. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  189. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  190. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  191. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  192. package/dist/web/standalone/.next/server/app/page.js +2 -2
  193. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  195. package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
  196. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  197. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  198. package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
  199. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/middleware.js +3 -3
  201. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  202. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  203. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  204. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
  206. package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
  207. package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
  208. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
  209. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  210. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  211. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  212. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  213. package/dist/web/standalone/server.js +1 -1
  214. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  215. package/integrations/hermes/pyproject.toml +1 -1
  216. package/package.json +8 -6
  217. package/packages/cloud-mcp-gateway/package.json +2 -2
  218. package/packages/contracts/dist/rpc.d.ts +1 -1
  219. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  220. package/packages/contracts/dist/rpc.js +1 -1
  221. package/packages/contracts/dist/rpc.js.map +1 -1
  222. package/packages/contracts/dist/rpc.test.js +1 -1
  223. package/packages/contracts/dist/rpc.test.js.map +1 -1
  224. package/packages/contracts/package.json +1 -1
  225. package/packages/daemon/package.json +4 -4
  226. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  227. package/packages/gsd-agent-core/dist/compaction/compaction.js +36 -4
  228. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  229. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  230. package/packages/gsd-agent-core/dist/sdk.js +1 -0
  231. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  232. package/packages/gsd-agent-core/package.json +5 -5
  233. package/packages/gsd-agent-modes/dist/cli/args.js +2 -2
  234. package/packages/gsd-agent-modes/dist/cli/args.js.map +1 -1
  235. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  236. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +1 -0
  237. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  238. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  239. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js +1 -0
  240. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  242. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js +1 -0
  243. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  244. package/packages/gsd-agent-modes/package.json +7 -7
  245. package/packages/gsd-cloud/README.md +21 -5
  246. package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
  247. package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
  248. package/packages/gsd-cloud/dist/background-cli.test.js +325 -0
  249. package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
  250. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
  251. package/packages/gsd-cloud/dist/cli.js +146 -16
  252. package/packages/gsd-cloud/dist/cli.js.map +1 -1
  253. package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
  254. package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
  255. package/packages/gsd-cloud/dist/cloud-config.js +7 -1
  256. package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
  257. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +7 -4
  258. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
  259. package/packages/gsd-cloud/dist/cloud-runtime.js +115 -31
  260. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
  261. package/packages/gsd-cloud/dist/cloud-runtime.test.js +224 -59
  262. package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -1
  263. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts +3 -0
  264. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts.map +1 -0
  265. package/packages/gsd-cloud/dist/runtime-artifacts.js +36 -0
  266. package/packages/gsd-cloud/dist/runtime-artifacts.js.map +1 -0
  267. package/packages/gsd-cloud/dist/runtime-process.d.ts +32 -0
  268. package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
  269. package/packages/gsd-cloud/dist/runtime-process.js +640 -0
  270. package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
  271. package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
  272. package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
  273. package/packages/gsd-cloud/dist/runtime-process.test.js +543 -0
  274. package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
  275. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts +117 -0
  276. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts.map +1 -0
  277. package/packages/gsd-cloud/dist/runtime-telemetry.js +291 -0
  278. package/packages/gsd-cloud/dist/runtime-telemetry.js.map +1 -0
  279. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts +2 -0
  280. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts.map +1 -0
  281. package/packages/gsd-cloud/dist/runtime-telemetry.test.js +434 -0
  282. package/packages/gsd-cloud/dist/runtime-telemetry.test.js.map +1 -0
  283. package/packages/gsd-cloud/package.json +2 -2
  284. package/packages/mcp-server/README.md +7 -2
  285. package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
  286. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
  287. package/packages/mcp-server/dist/cli-runner.js +119 -33
  288. package/packages/mcp-server/dist/cli-runner.js.map +1 -1
  289. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  290. package/packages/mcp-server/dist/server.js +5 -5
  291. package/packages/mcp-server/dist/server.js.map +1 -1
  292. package/packages/mcp-server/dist/workflow-tools.d.ts +4 -3
  293. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  294. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  295. package/packages/mcp-server/package.json +4 -4
  296. package/packages/native/package.json +1 -1
  297. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  298. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  299. package/packages/pi-agent-core/dist/agent.js +2 -0
  300. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  301. package/packages/pi-agent-core/dist/types.d.ts +2 -2
  302. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  303. package/packages/pi-agent-core/dist/types.js.map +1 -1
  304. package/packages/pi-agent-core/package.json +1 -1
  305. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  306. package/packages/pi-ai/dist/models.generated.d.ts +187 -0
  307. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  308. package/packages/pi-ai/dist/models.generated.js +145 -0
  309. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  310. package/packages/pi-ai/dist/models.js +18 -6
  311. package/packages/pi-ai/dist/models.js.map +1 -1
  312. package/packages/pi-ai/dist/providers/amazon-bedrock.js +3 -2
  313. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  314. package/packages/pi-ai/dist/providers/anthropic.js +6 -1
  315. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  316. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
  317. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  318. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  319. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  320. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  321. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +1 -1
  322. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  323. package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
  324. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  325. package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
  326. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  327. package/packages/pi-ai/dist/providers/openai-completions.js +103 -0
  328. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  329. package/packages/pi-ai/dist/providers/openai-responses.d.ts +1 -1
  330. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  331. package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
  332. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  333. package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
  334. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  335. package/packages/pi-ai/dist/providers/simple-options.js +1 -1
  336. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  337. package/packages/pi-ai/dist/types.d.ts +9 -1
  338. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  339. package/packages/pi-ai/dist/types.js.map +1 -1
  340. package/packages/pi-ai/package.json +1 -1
  341. package/packages/pi-coding-agent/dist/core/capability-patches.js +1 -1
  342. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  344. package/packages/pi-coding-agent/dist/core/model-registry.js +1 -0
  345. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  346. package/packages/pi-coding-agent/dist/core/model-resolver.js +2 -2
  347. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +14 -4
  349. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  351. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  352. package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
  353. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  354. package/packages/pi-coding-agent/dist/core/skills.js +54 -0
  355. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/theme/theme.d.ts +1 -1
  357. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/theme/theme.js +1 -0
  359. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  360. package/packages/pi-coding-agent/package.json +7 -7
  361. package/packages/pi-tui/package.json +2 -2
  362. package/packages/rpc-client/package.json +2 -2
  363. package/pkg/dist/theme/theme.d.ts +1 -1
  364. package/pkg/dist/theme/theme.d.ts.map +1 -1
  365. package/pkg/dist/theme/theme.js +1 -0
  366. package/pkg/dist/theme/theme.js.map +1 -1
  367. package/pkg/package.json +1 -1
  368. package/src/resources/GSD-WORKFLOW.md +2 -2
  369. package/src/resources/extensions/gsd/artifact-verification.ts +3 -8
  370. package/src/resources/extensions/gsd/auto/loop.ts +48 -4
  371. package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
  372. package/src/resources/extensions/gsd/auto/unit-phase.ts +41 -5
  373. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -7
  374. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
  375. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -0
  376. package/src/resources/extensions/gsd/auto-prompts.ts +118 -19
  377. package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
  378. package/src/resources/extensions/gsd/auto-runtime-state.ts +12 -7
  379. package/src/resources/extensions/gsd/auto-start.ts +32 -9
  380. package/src/resources/extensions/gsd/auto-tool-tracking.ts +56 -0
  381. package/src/resources/extensions/gsd/auto-verification.ts +46 -12
  382. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +4 -0
  383. package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
  384. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +1 -1
  385. package/src/resources/extensions/gsd/auto.ts +20 -7
  386. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +1 -0
  387. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +41 -12
  388. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -3
  389. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +10 -31
  390. package/src/resources/extensions/gsd/commands-verdict.ts +16 -9
  391. package/src/resources/extensions/gsd/compat/compat-marker.ts +99 -5
  392. package/src/resources/extensions/gsd/constants.ts +14 -13
  393. package/src/resources/extensions/gsd/context-masker.ts +98 -2
  394. package/src/resources/extensions/gsd/db/engine.ts +5 -2
  395. package/src/resources/extensions/gsd/docs/preferences-reference.md +33 -2
  396. package/src/resources/extensions/gsd/doctor-engine-checks.ts +45 -11
  397. package/src/resources/extensions/gsd/error-classifier.ts +13 -2
  398. package/src/resources/extensions/gsd/exec-sandbox.ts +1 -0
  399. package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
  400. package/src/resources/extensions/gsd/git-service.ts +19 -6
  401. package/src/resources/extensions/gsd/gsd-db.ts +85 -33
  402. package/src/resources/extensions/gsd/guided-flow.ts +11 -4
  403. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -23
  404. package/src/resources/extensions/gsd/md-importer.ts +5 -5
  405. package/src/resources/extensions/gsd/model-cost-table.ts +14 -1
  406. package/src/resources/extensions/gsd/model-router.ts +17 -0
  407. package/src/resources/extensions/gsd/paths.ts +73 -15
  408. package/src/resources/extensions/gsd/planning-subagent-policy.ts +58 -0
  409. package/src/resources/extensions/gsd/planning-subagent-registry.ts +45 -0
  410. package/src/resources/extensions/gsd/preferences-models.ts +54 -10
  411. package/src/resources/extensions/gsd/preferences-types.ts +26 -2
  412. package/src/resources/extensions/gsd/preferences-validation.ts +108 -3
  413. package/src/resources/extensions/gsd/preferences.ts +38 -0
  414. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
  415. package/src/resources/extensions/gsd/provider-payload-policy.ts +18 -4
  416. package/src/resources/extensions/gsd/safety/destructive-guard.ts +1 -1
  417. package/src/resources/extensions/gsd/skill-activation.ts +17 -3
  418. package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +132 -2
  419. package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +4 -1
  420. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +4 -2
  421. package/src/resources/extensions/gsd/stop-notice.ts +11 -0
  422. package/src/resources/extensions/gsd/templates/PREFERENCES.md +8 -0
  423. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +7 -7
  424. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +264 -0
  425. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +39 -0
  426. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
  427. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
  428. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +55 -0
  429. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +10 -0
  430. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +25 -0
  431. package/src/resources/extensions/gsd/tests/complete-task.test.ts +76 -1
  432. package/src/resources/extensions/gsd/tests/context-masker.test.ts +204 -8
  433. package/src/resources/extensions/gsd/tests/db-engine-cjs-compat.test.ts +12 -0
  434. package/src/resources/extensions/gsd/tests/db-tools-alias-suppression.test.ts +62 -0
  435. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +33 -0
  436. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +14 -0
  437. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +147 -0
  438. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +33 -1
  439. package/src/resources/extensions/gsd/tests/external-markdown-edit.test.ts +38 -1
  440. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
  441. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +20 -1
  442. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
  443. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +22 -0
  444. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
  445. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +22 -4
  446. package/src/resources/extensions/gsd/tests/integration-branch-meta-location.test.ts +62 -0
  447. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +5 -5
  448. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +23 -0
  449. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +55 -0
  450. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  451. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +7 -5
  452. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +24 -0
  453. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +38 -1
  454. package/src/resources/extensions/gsd/tests/preferences.test.ts +151 -0
  455. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +7 -0
  456. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +87 -0
  457. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +121 -1
  458. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +39 -0
  459. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +105 -0
  460. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  461. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +33 -7
  462. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -16
  463. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
  464. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +21 -1
  465. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +13 -7
  466. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +14 -0
  467. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +1 -1
  468. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  469. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -0
  470. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +39 -0
  471. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +7 -0
  472. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +12 -0
  473. package/src/resources/extensions/gsd/tests/workflow-authority-faults.test.ts +238 -0
  474. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.test.ts +48 -0
  475. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.ts +141 -0
  476. package/src/resources/extensions/gsd/tests/workflow-authority-projection-conflict.test.ts +177 -0
  477. package/src/resources/extensions/gsd/tests/workflow-fault-harness.test.ts +39 -0
  478. package/src/resources/extensions/gsd/tests/workflow-fault-harness.ts +72 -0
  479. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +4 -4
  480. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
  481. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
  482. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +74 -0
  483. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -5
  484. package/src/resources/extensions/gsd/tools/plan-slice.ts +5 -1
  485. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +24 -5
  486. package/src/resources/extensions/gsd/tools/reopen-slice.ts +25 -5
  487. package/src/resources/extensions/gsd/tools/reopen-task.ts +23 -9
  488. package/src/resources/extensions/gsd/unit-context-composer.ts +34 -8
  489. package/src/resources/extensions/gsd/verification-evidence.ts +5 -2
  490. package/src/resources/extensions/gsd/workflow-projections.ts +2 -3
  491. package/src/resources/extensions/gsd/workspace.ts +2 -1
  492. package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
  493. package/src/resources/extensions/ollama/ollama-chat-provider.ts +2 -2
  494. package/src/resources/extensions/ollama/tests/ollama-chat-provider-think.test.ts +4 -0
  495. package/src/resources/extensions/shared/interview-ui.ts +47 -8
  496. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +29 -0
  497. package/src/resources/extensions/subagent/index.ts +1 -1
  498. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
  499. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
  500. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
  501. /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → xwUaOqui04jIIFVsIBLiL}/_buildManifest.js +0 -0
  502. /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → xwUaOqui04jIIFVsIBLiL}/_ssgManifest.js +0 -0
@@ -3,7 +3,8 @@
3
3
 
4
4
  import { describe, test } from "node:test";
5
5
  import assert from "node:assert/strict";
6
- import { mkdtempSync, mkdirSync, realpathSync, rmSync } from "node:fs";
6
+ import { mkdtempSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
7
+ import { execSync } from "node:child_process";
7
8
  import { join } from "node:path";
8
9
  import { tmpdir } from "node:os";
9
10
 
@@ -96,6 +97,78 @@ describe("hook dispatch session workspace root", () => {
96
97
  assert.equal(dispatched, true);
97
98
  assert.deepEqual(newSessionOptions, { workspaceRoot: basePath });
98
99
  });
100
+
101
+ test("dispatchHookUnit commits in-flight work when command context lacks newSession", async (t) => {
102
+ const originalCwd = process.cwd();
103
+ const basePath = realpathSync(mkdtempSync(join(tmpdir(), "gsd-hook-nosnap-")));
104
+ mkdirSync(join(basePath, ".gsd"), { recursive: true });
105
+ execSync("git init --initial-branch=main", { cwd: basePath, stdio: "ignore" });
106
+ execSync("git config user.email test@test.com", { cwd: basePath, stdio: "ignore" });
107
+ execSync("git config user.name Test", { cwd: basePath, stdio: "ignore" });
108
+ execSync("git commit --allow-empty -m init", { cwd: basePath, stdio: "ignore" });
109
+
110
+ autoSession.reset();
111
+ autoSession.active = true;
112
+ autoSession.basePath = basePath;
113
+ autoSession.originalBasePath = basePath;
114
+ // Simulate an in-flight unit whose executor already wrote files, then a hook
115
+ // fires with a command context missing newSession.
116
+ autoSession.currentUnit = {
117
+ type: "execute-task",
118
+ id: "M001/S01/T01",
119
+ startedAt: Date.now(),
120
+ };
121
+ t.after(() => {
122
+ try {
123
+ process.chdir(originalCwd);
124
+ } catch {
125
+ // best effort cleanup after cwd-sensitive dispatch tests
126
+ }
127
+ autoSession.reset();
128
+ rmSync(basePath, { recursive: true, force: true });
129
+ });
130
+
131
+ const outputPath = join(basePath, "executor-output.txt");
132
+ writeFileSync(outputPath, "in-flight executor work\n", "utf-8");
133
+
134
+ // Command context intentionally lacks newSession — the guard must snapshot
135
+ // the dirty work before stopping instead of discarding it.
136
+ const ctx = {
137
+ ui: {
138
+ notify: () => {},
139
+ setStatus: () => {},
140
+ setWidget: () => {},
141
+ },
142
+ modelRegistry: {
143
+ getAvailable: () => [],
144
+ },
145
+ sessionManager: {
146
+ getSessionFile: () => join(basePath, "session.jsonl"),
147
+ },
148
+ };
149
+ const pi = {
150
+ sendMessage: () => {},
151
+ setModel: async () => true,
152
+ };
153
+
154
+ const dispatched = await dispatchHookUnit(
155
+ ctx as any,
156
+ pi as any,
157
+ "review",
158
+ "execute-task",
159
+ "M001/S01/T01",
160
+ "review the completed unit",
161
+ undefined,
162
+ basePath,
163
+ );
164
+
165
+ assert.equal(dispatched, false);
166
+ const committed = execSync("git show HEAD:executor-output.txt", {
167
+ cwd: basePath,
168
+ encoding: "utf-8",
169
+ });
170
+ assert.equal(committed, "in-flight executor work\n");
171
+ });
99
172
  });
100
173
 
101
174
  describe("deep setup approval questions pause immediately", () => {
@@ -143,3 +143,58 @@ test("successful shell result clears source context before provider injection",
143
143
  assert.equal(payload.messages.length, 1);
144
144
  assert.doesNotMatch(payload.messages[0].content[0].text, /Source Context Block/);
145
145
  });
146
+
147
+ test("before_provider_request keeps only the latest GSD context injection across a 5-turn session", async (t) => {
148
+ const dir = mkdtempSync(join(tmpdir(), "gsd-before-provider-injections-"));
149
+ const gsdHome = join(dir, "home");
150
+ const project = join(dir, "project");
151
+ const previousCwd = process.cwd();
152
+ const previousGsdHome = process.env.GSD_HOME;
153
+
154
+ mkdirSync(join(project, ".gsd"), { recursive: true });
155
+ mkdirSync(gsdHome, { recursive: true });
156
+ process.env.GSD_HOME = gsdHome;
157
+ process.chdir(project);
158
+ _setAutoActiveForTest(false);
159
+
160
+ t.after(() => {
161
+ _setAutoActiveForTest(false);
162
+ process.chdir(previousCwd);
163
+ if (previousGsdHome === undefined) delete process.env.GSD_HOME;
164
+ else process.env.GSD_HOME = previousGsdHome;
165
+ rmSync(dir, { recursive: true, force: true });
166
+ });
167
+
168
+ const beforeProviderRequest = requireHook(createHookHandlers(), "before_provider_request");
169
+
170
+ const sentinel = "[GSD Context Injection]";
171
+ const contextInjection = (turn: number) => ({
172
+ role: "user",
173
+ content: [{ type: "text", text: `${sentinel}\n[MEMORY] turn=${turn}` }],
174
+ });
175
+
176
+ const payload = {
177
+ messages: [
178
+ { role: "user", content: [{ type: "text", text: "turn 1" }] },
179
+ contextInjection(1),
180
+ { role: "assistant", content: [{ type: "text", text: "r1" }] },
181
+ { role: "user", content: [{ type: "text", text: "turn 2" }] },
182
+ contextInjection(2),
183
+ { role: "assistant", content: [{ type: "text", text: "r2" }] },
184
+ { role: "user", content: [{ type: "text", text: "turn 3" }] },
185
+ contextInjection(3),
186
+ { role: "assistant", content: [{ type: "text", text: "r3" }] },
187
+ { role: "user", content: [{ type: "text", text: "turn 4" }] },
188
+ contextInjection(4),
189
+ { role: "assistant", content: [{ type: "text", text: "r4" }] },
190
+ { role: "user", content: [{ type: "text", text: "turn 5" }] },
191
+ contextInjection(5),
192
+ ],
193
+ };
194
+
195
+ await beforeProviderRequest({ payload });
196
+
197
+ const survivors = (payload.messages as any[]).filter((m) => (m.content?.[0]?.text ?? "").startsWith(sentinel));
198
+ assert.equal(survivors.length, 1);
199
+ assert.match(survivors[0].content[0].text, /turn=5/);
200
+ });
@@ -201,6 +201,7 @@ test("handleVerdict rejects missing verdict", async () => {
201
201
  const { ctx, calls } = makeMockCtx();
202
202
  await handleVerdict("", ctx, "/tmp/unused");
203
203
  assert.equal(calls.length, 1);
204
+ assert.match(calls[0].message, /^Verdict rejected:/);
204
205
  assert.match(calls[0].message, /Usage: \/gsd verdict/);
205
206
  assert.equal(calls[0].kind, "warning");
206
207
  });
@@ -209,6 +210,7 @@ test("handleVerdict rejects invalid verdict", async () => {
209
210
  const { ctx, calls } = makeMockCtx();
210
211
  await handleVerdict("yolo", ctx, "/tmp/unused");
211
212
  assert.equal(calls.length, 1);
213
+ assert.match(calls[0].message, /^Verdict rejected:/);
212
214
  assert.match(calls[0].message, /Invalid verdict "yolo"/);
213
215
  assert.equal(calls[0].kind, "warning");
214
216
  });
@@ -250,6 +252,10 @@ test("handleVerdict rejects when milestone validation is missing", async () => {
250
252
  calls.some((c) => /No milestone validation found/.test(c.message)),
251
253
  `expected missing-validation warning, got: ${JSON.stringify(calls)}`,
252
254
  );
255
+ assert.ok(
256
+ calls.some((c) => /^Verdict rejected:/.test(c.message)),
257
+ `expected terminal rejection prefix, got: ${JSON.stringify(calls)}`,
258
+ );
253
259
  } finally {
254
260
  closeDatabase();
255
261
  invalidateStateCache();
@@ -280,6 +286,10 @@ test("handleVerdict pass override flips verdict and preserves sections", async (
280
286
  calls.some((c) => c.kind === "success" && /needs-attention.*->.*pass/.test(c.message)),
281
287
  `expected success notification, got: ${JSON.stringify(calls)}`,
282
288
  );
289
+ assert.ok(
290
+ calls.some((c) => c.kind === "success" && /^Verdict recorded:/.test(c.message)),
291
+ `expected terminal recorded prefix, got: ${JSON.stringify(calls)}`,
292
+ );
283
293
  } finally {
284
294
  closeDatabase();
285
295
  invalidateStateCache();
@@ -242,6 +242,31 @@ test("readCompatMarker preserves legitimate nested projection keys", () => {
242
242
  assert.equal(Object.keys(marker.projections).length, 2);
243
243
  });
244
244
 
245
+ test("readCompatMarker heals invalid keys that canonicalize back under .gsd", () => {
246
+ const base = makeTmpBase();
247
+ const safeKey = "m1/roadmap.md";
248
+ mkdirSync(join(base, ".gsd", "m1"), { recursive: true });
249
+ writeFileSync(join(base, ".gsd", safeKey), "# Roadmap\n", "utf-8");
250
+ writeCompatMarker(base, {
251
+ schema: 2,
252
+ lastWriter: "gsd-pi",
253
+ lastProjectedAt: "2026-07-07T00:00:00.000Z",
254
+ projections: {
255
+ [`../.gsd/${safeKey}`]: { sha: "aaaaaaaaaaaaaaaa", entities: ["M001"] },
256
+ },
257
+ planning: { active: false, layout: null, projections: {}, passthrough: {} },
258
+ piVersion: "1.8.1",
259
+ });
260
+
261
+ const marker = readCompatMarker(base);
262
+ assert.ok(marker.projections[safeKey], "derivable traversal-form key must be rewritten");
263
+ assert.equal(marker.projections[`../.gsd/${safeKey}`], undefined);
264
+ assert.ok(
265
+ !readdirSync(join(base, ".gsd")).some((f: string) => f.startsWith(".compat.json.bad-")),
266
+ "healed marker must not be quarantined",
267
+ );
268
+ });
269
+
245
270
  test("an escaping key in planning.passthrough also invalidates the whole marker", () => {
246
271
  const base = makeTmpBase();
247
272
  writeCompatMarker(base, {
@@ -23,6 +23,7 @@ import {
23
23
  SCHEMA_VERSION,
24
24
  } from '../gsd-db.ts';
25
25
  import { handleCompleteTask } from '../tools/complete-task.ts';
26
+ import { resolveTaskFile } from '../paths.ts';
26
27
 
27
28
  const { assertEq, assertTrue, assertMatch, report } = createTestContext();
28
29
 
@@ -381,6 +382,80 @@ console.log('\n=== complete-task: handler happy path ===');
381
382
  cleanup(dbPath);
382
383
  }
383
384
 
385
+ // ═══════════════════════════════════════════════════════════════════════════
386
+ // complete-task: Flat-phase duplicate task IDs are slice-qualified
387
+ // ═══════════════════════════════════════════════════════════════════════════
388
+
389
+ console.log('\n=== complete-task: flat-phase duplicate task IDs are slice-qualified ===');
390
+ {
391
+ const dbPath = tempDbPath();
392
+ openDatabase(dbPath);
393
+
394
+ const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-flat-task-summary-'));
395
+ const phaseDir = path.join(basePath, '.gsd', 'phases', '01-test');
396
+ fs.mkdirSync(phaseDir, { recursive: true });
397
+ fs.writeFileSync(path.join(phaseDir, '01-01-PLAN.md'), `# S01: First Slice
398
+
399
+ ## Tasks
400
+
401
+ - [ ] **T03: Shared task id** \`est:30m\`
402
+ - Do: Complete first slice task
403
+ - Verify: Run first check
404
+ `);
405
+ fs.writeFileSync(path.join(phaseDir, '01-02-PLAN.md'), `# S02: Second Slice
406
+
407
+ ## Tasks
408
+
409
+ - [ ] **T03: Shared task id** \`est:30m\`
410
+ - Do: Complete second slice task
411
+ - Verify: Run second check
412
+ `);
413
+
414
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
415
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First Slice', sequence: 1 });
416
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second Slice', sequence: 2 });
417
+
418
+ const first = await handleCompleteTask({
419
+ ...makeValidParams(),
420
+ taskId: 'T03',
421
+ sliceId: 'S01',
422
+ oneLiner: 'Completed first slice T03',
423
+ }, basePath);
424
+ const second = await handleCompleteTask({
425
+ ...makeValidParams(),
426
+ taskId: 'T03',
427
+ sliceId: 'S02',
428
+ oneLiner: 'Completed second slice T03',
429
+ }, basePath);
430
+
431
+ assertTrue(!('error' in first), 'first duplicate task ID completion should succeed');
432
+ assertTrue(!('error' in second), 'second duplicate task ID completion should succeed');
433
+ if (!('error' in first) && !('error' in second)) {
434
+ const firstSummary = path.join(phaseDir, 'S01-T03-SUMMARY.md');
435
+ const secondSummary = path.join(phaseDir, 'S02-T03-SUMMARY.md');
436
+ const legacySummary = path.join(phaseDir, 'T03-SUMMARY.md');
437
+
438
+ assertEq(first.summaryPath, firstSummary, 'S01/T03 should write a slice-qualified summary path');
439
+ assertEq(second.summaryPath, secondSummary, 'S02/T03 should write a slice-qualified summary path');
440
+ assertTrue(fs.existsSync(firstSummary), 'S01/T03 summary should exist');
441
+ assertTrue(fs.existsSync(secondSummary), 'S02/T03 summary should exist');
442
+ assertTrue(!fs.existsSync(legacySummary), 'flat-phase completion should not create legacy T03-SUMMARY.md');
443
+
444
+ const firstContent = fs.readFileSync(firstSummary, 'utf-8');
445
+ const secondContent = fs.readFileSync(secondSummary, 'utf-8');
446
+ assertMatch(firstContent, /parent: S01/, 'S01/T03 summary should keep parent S01');
447
+ assertMatch(secondContent, /parent: S02/, 'S02/T03 summary should keep parent S02');
448
+ assertEq(resolveTaskFile(basePath, 'M001', 'S02', 'T03', 'SUMMARY'), secondSummary, 'resolver should prefer S02-specific summary');
449
+
450
+ const legacyFallback = path.join(phaseDir, 'T99-SUMMARY.md');
451
+ fs.writeFileSync(legacyFallback, '# Legacy task summary\n');
452
+ assertEq(resolveTaskFile(basePath, 'M001', 'S09', 'T99', 'SUMMARY'), legacyFallback, 'resolver should fall back to old flat summary names');
453
+ }
454
+
455
+ cleanupDir(basePath);
456
+ cleanup(dbPath);
457
+ }
458
+
384
459
  // ═══════════════════════════════════════════════════════════════════════════
385
460
  // complete-task: Projection failure rolls DB completion back
386
461
  // ═══════════════════════════════════════════════════════════════════════════
@@ -416,7 +491,7 @@ console.log('\n=== complete-task: projection failure rolls DB completion back ==
416
491
  ).all();
417
492
  assertEq(evRows.length, 0, 'verification evidence should be deleted when projection rollback runs');
418
493
 
419
- const summaryPath = path.join(path.dirname(planPath), 'T01-SUMMARY.md');
494
+ const summaryPath = path.join(path.dirname(planPath), 'S01-T01-SUMMARY.md');
420
495
  assertTrue(!fs.existsSync(summaryPath), 'SUMMARY.md should be removed so disk state stays pending');
421
496
 
422
497
  cleanupDir(basePath);
@@ -4,9 +4,12 @@ import assert from "node:assert/strict";
4
4
  import {
5
5
  createObservationMask,
6
6
  createResponsesInputObservationMask,
7
+ filterSupersededContextInjections,
8
+ filterSupersededResponsesContextInjections,
7
9
  truncateContextResultMessages,
8
10
  truncateResponsesInputResultItems,
9
11
  } from "../context-masker.js";
12
+ import { GSD_CONTEXT_MESSAGE_SENTINEL } from "../bootstrap/system-context.js";
10
13
 
11
14
  // These helpers produce messages in the pi-ai LLM payload format
12
15
  // (post-convertToLlm, pre-provider), which is what before_provider_request sees.
@@ -43,25 +46,35 @@ test("masks nothing when message count is within keepRecentTurns", () => {
43
46
  assert.deepEqual((result[2].content as any)[0].text, "file contents");
44
47
  });
45
48
 
46
- test("masks tool results older than keepRecentTurns", () => {
49
+ test("masks tool results older than a full quantization block", () => {
50
+ // With keepRecentTurns=2, masking is quantized in blocks of 2 turns: nothing
51
+ // is masked until a full block (2 turns) of excess history accumulates.
47
52
  const mask = createObservationMask(2);
48
53
  const messages = [
49
54
  userMsg("turn 1"),
50
- toolResult("old tool output"),
55
+ toolResult("turn 1 tool output"),
51
56
  assistantMsg("response 1"),
52
57
  userMsg("turn 2"),
53
- toolResult("newer tool output"),
58
+ toolResult("turn 2 tool output"),
54
59
  assistantMsg("response 2"),
55
60
  userMsg("turn 3"),
56
- toolResult("newest tool output"),
61
+ toolResult("turn 3 tool output"),
57
62
  assistantMsg("response 3"),
63
+ userMsg("turn 4"),
64
+ toolResult("turn 4 tool output"),
65
+ assistantMsg("response 4"),
66
+ userMsg("turn 5"),
67
+ toolResult("turn 5 tool output"),
68
+ assistantMsg("response 5"),
58
69
  ];
59
70
  const result = mask(messages as any);
60
- // Old tool result (before boundary) should be masked
71
+ // First block (turns 1-2) is masked
61
72
  assert.equal((result[1].content as any)[0].text, MASK_TEXT);
62
- // Recent tool results (within keep window) should be preserved
63
- assert.equal((result[4].content as any)[0].text, "newer tool output");
64
- assert.equal((result[7].content as any)[0].text, "newest tool output");
73
+ assert.equal((result[4].content as any)[0].text, MASK_TEXT);
74
+ // Remaining turns (3-5) are within the unmasked window
75
+ assert.equal((result[7].content as any)[0].text, "turn 3 tool output");
76
+ assert.equal((result[10].content as any)[0].text, "turn 4 tool output");
77
+ assert.equal((result[13].content as any)[0].text, "turn 5 tool output");
65
78
  });
66
79
 
67
80
  test("never masks assistant messages", () => {
@@ -126,6 +139,67 @@ test("masks toolResult by role, not by type field", () => {
126
139
  assert.equal((result[1].content as any)[0].text, MASK_TEXT);
127
140
  });
128
141
 
142
+ function buildTurns(count: number) {
143
+ const messages: unknown[] = [];
144
+ for (let i = 1; i <= count; i++) {
145
+ messages.push(userMsg(`turn ${i}`));
146
+ messages.push(toolResult(`turn ${i} tool output`));
147
+ messages.push(assistantMsg(`response ${i}`));
148
+ }
149
+ return messages;
150
+ }
151
+
152
+ function maskedTextsByTurn(mask: (m: any) => any[], messages: unknown[], turnCount: number): (string | undefined)[] {
153
+ const result = mask(messages as any);
154
+ const texts: (string | undefined)[] = [];
155
+ for (let i = 0; i < turnCount; i++) {
156
+ const toolResultIndex = i * 3 + 1;
157
+ texts.push((result[toolResultIndex].content as any)[0].text);
158
+ }
159
+ return texts;
160
+ }
161
+
162
+ test("quantized mask invariant: at least keepRecentTurns most recent turns are always unmasked", () => {
163
+ const N = 3;
164
+ const mask = createObservationMask(N);
165
+ for (let totalTurns = 1; totalTurns <= 20; totalTurns++) {
166
+ const messages = buildTurns(totalTurns);
167
+ const texts = maskedTextsByTurn(mask, messages, totalTurns);
168
+ for (let i = Math.max(0, totalTurns - N); i < totalTurns; i++) {
169
+ assert.notEqual(texts[i], MASK_TEXT, `turn ${i + 1} of ${totalTurns} should be unmasked`);
170
+ }
171
+ }
172
+ });
173
+
174
+ test("quantized mask invariant: at most 2*keepRecentTurns-1 turns are unmasked", () => {
175
+ const N = 3;
176
+ const mask = createObservationMask(N);
177
+ for (let totalTurns = 1; totalTurns <= 20; totalTurns++) {
178
+ const messages = buildTurns(totalTurns);
179
+ const texts = maskedTextsByTurn(mask, messages, totalTurns);
180
+ const unmaskedCount = texts.filter((t) => t !== MASK_TEXT).length;
181
+ assert.ok(unmaskedCount <= 2 * N - 1, `expected <= ${2 * N - 1} unmasked turns at total=${totalTurns}, got ${unmaskedCount}`);
182
+ }
183
+ });
184
+
185
+ test("quantized mask invariant: prefix is byte-stable except at block rollover", () => {
186
+ const N = 3;
187
+ const mask = createObservationMask(N);
188
+ let previousTexts: (string | undefined)[] = [];
189
+ for (let totalTurns = 1; totalTurns <= 20; totalTurns++) {
190
+ const messages = buildTurns(totalTurns);
191
+ const texts = maskedTextsByTurn(mask, messages, totalTurns);
192
+ // Compare the shared prefix (turns 1..totalTurns-1) against the previous turn's run.
193
+ let changedCount = 0;
194
+ for (let i = 0; i < previousTexts.length; i++) {
195
+ if (previousTexts[i] !== texts[i]) changedCount++;
196
+ }
197
+ // A block rollover flips at most N turns from unmasked to masked; otherwise 0 should change.
198
+ assert.ok(changedCount === 0 || changedCount <= N, `turn ${totalTurns}: ${changedCount} prefix turns changed status`);
199
+ previousTexts = texts;
200
+ }
201
+ });
202
+
129
203
  test("truncates recent bash result user messages", () => {
130
204
  const messages = [
131
205
  userMsg("turn 1"),
@@ -175,3 +249,125 @@ test("truncates Responses API function outputs and recent bash result items", ()
175
249
  assert.ok((result[1].output as string).length < (items[1].output as string).length);
176
250
  assert.ok((result[2].content as any)[0].text.length < (items[2].content as any)[0].text.length);
177
251
  });
252
+
253
+ function contextInjectionMsg(label: string) {
254
+ return userMsg(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n[MEMORY] turn=${label}`);
255
+ }
256
+
257
+ test("filterSupersededContextInjections keeps only the latest of three same-kind injections", () => {
258
+ const messages = [
259
+ userMsg("turn 1 prompt"),
260
+ contextInjectionMsg("1"),
261
+ assistantMsg("response 1"),
262
+ userMsg("turn 2 prompt"),
263
+ contextInjectionMsg("2"),
264
+ assistantMsg("response 2"),
265
+ userMsg("turn 3 prompt"),
266
+ contextInjectionMsg("3"),
267
+ ];
268
+ const original = JSON.parse(JSON.stringify(messages));
269
+
270
+ const result = filterSupersededContextInjections(messages as any);
271
+
272
+ const survivors = result.filter((m: any) => (m.content?.[0]?.text ?? "").startsWith(GSD_CONTEXT_MESSAGE_SENTINEL));
273
+ assert.equal(survivors.length, 1);
274
+ assert.match((survivors[0] as any).content[0].text, /turn=3/);
275
+ assert.equal(result.length, messages.length - 2);
276
+ // Pure function — input array is untouched.
277
+ assert.deepEqual(messages, original);
278
+ });
279
+
280
+ test("filterSupersededContextInjections keeps the latest injection overall across mixed kinds", () => {
281
+ // buildContextMessage emits one message per turn (guided > forensics > memory);
282
+ // the latest one already embeds the memory block when present, so "keep the
283
+ // latest overall" is correct — no separate per-kind tracking needed.
284
+ const memoryOnly = userMsg(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n[GSD Context Metadata]\n- Memory supplied: yes\n\n[MEMORY]`);
285
+ const guided = userMsg(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n[GSD Context Metadata]\n- Memory supplied: yes\n\n[GUIDED EXECUTE]`);
286
+ const messages = [userMsg("turn 1"), memoryOnly, assistantMsg("r1"), userMsg("turn 2"), guided];
287
+
288
+ const result = filterSupersededContextInjections(messages as any);
289
+
290
+ const survivors = result.filter((m: any) => (m.content?.[0]?.text ?? "").startsWith(GSD_CONTEXT_MESSAGE_SENTINEL));
291
+ assert.equal(survivors.length, 1);
292
+ assert.match((survivors[0] as any).content[0].text, /GUIDED EXECUTE/);
293
+ });
294
+
295
+ test("filterSupersededContextInjections dedupes legacy pre-sentinel injections via bracketed GSD markers", () => {
296
+ // Sessions created before the sentinel existed carry the stable
297
+ // "[GSD Context Metadata]" / "[GSD Guided Execute Context]" labels but no
298
+ // sentinel prefix; resuming them must still collapse to the latest injection.
299
+ const legacyMemory1 = userMsg("[GSD Context Metadata]\n- Memory supplied: yes\n\n[MEMORY] turn=1");
300
+ const legacyMemory2 = userMsg("[GSD Context Metadata]\n- Memory supplied: yes\n\n[MEMORY] turn=2");
301
+ const messages = [userMsg("turn 1"), legacyMemory1, assistantMsg("r1"), userMsg("turn 2"), legacyMemory2];
302
+
303
+ const result = filterSupersededContextInjections(messages as any);
304
+
305
+ const survivors = result.filter((m: any) => (m.content?.[0]?.text ?? "").startsWith("[GSD Context Metadata]"));
306
+ assert.equal(survivors.length, 1);
307
+ assert.match((survivors[0] as any).content[0].text, /turn=2/);
308
+ assert.equal(result.length, messages.length - 1);
309
+ });
310
+
311
+ test("filterSupersededContextInjections never drops a real user message that starts with forensics prose", () => {
312
+ // "Debug GSD itself." is the forensics prompt text — generic prose, not a
313
+ // bracketed GSD marker. A user can legitimately type it; treating it as an
314
+ // injection would silently delete real user input. Only sentinel/bracketed
315
+ // injections may be superseded.
316
+ const userAsk = userMsg("Debug GSD itself. Why does resume hang after a crash?");
317
+ const injection = userMsg(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n[GSD Context Metadata]\n- Memory supplied: yes\n\n[MEMORY]`);
318
+ const messages = [userAsk, assistantMsg("r1"), userMsg("turn 2"), injection];
319
+
320
+ const result = filterSupersededContextInjections(messages as any);
321
+
322
+ assert.ok(result.some((m: any) => (m.content?.[0]?.text ?? "").startsWith("Debug GSD itself.")));
323
+ assert.equal(result.length, messages.length);
324
+ });
325
+
326
+ test("filterSupersededContextInjections returns the array unchanged when no injections present", () => {
327
+ const messages = [userMsg("hi"), assistantMsg("hello"), toolResult("data")];
328
+ const result = filterSupersededContextInjections(messages as any);
329
+ assert.equal(result, messages);
330
+ });
331
+
332
+ test("filterSupersededContextInjections preserves forensics opening prompt across follow-up reinjection", () => {
333
+ const forensicsOpening =
334
+ "Debug GSD itself. Trace the symptom to root cause in current source and produce a filing-ready GitHub issue.";
335
+ const messages = [
336
+ userMsg(forensicsOpening),
337
+ assistantMsg("I found the root cause in auto-loop.ts."),
338
+ userMsg("yes, create the issue"),
339
+ userMsg(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n${forensicsOpening}`),
340
+ assistantMsg("Creating the issue now."),
341
+ userMsg("also add the stack trace"),
342
+ userMsg(`${GSD_CONTEXT_MESSAGE_SENTINEL}\n${forensicsOpening}\n\n## Follow-up\nalso add the stack trace`),
343
+ ];
344
+
345
+ const result = filterSupersededContextInjections(messages as any);
346
+
347
+ assert.equal(result.length, messages.length - 1);
348
+ assert.equal((result[0] as any).content[0].text, forensicsOpening);
349
+ assert.match((result[5] as any).content[0].text, /Follow-up/);
350
+ });
351
+
352
+ test("filterSupersededResponsesContextInjections keeps only the latest injection in payload.input", () => {
353
+ const items = [
354
+ { role: "user", content: [{ type: "input_text", text: "turn 1" }] },
355
+ { role: "user", content: [{ type: "input_text", text: `${GSD_CONTEXT_MESSAGE_SENTINEL}\n[MEMORY] turn=1` }] },
356
+ { type: "function_call_output", call_id: "call_1", output: "ok" },
357
+ { role: "user", content: [{ type: "input_text", text: `${GSD_CONTEXT_MESSAGE_SENTINEL}\n[MEMORY] turn=2` }] },
358
+ ];
359
+ const original = JSON.parse(JSON.stringify(items));
360
+
361
+ const result = filterSupersededResponsesContextInjections(items as any);
362
+
363
+ const survivors = result.filter((i: any) => (i.content?.[0]?.text ?? "").startsWith(GSD_CONTEXT_MESSAGE_SENTINEL));
364
+ assert.equal(survivors.length, 1);
365
+ assert.match((survivors[0] as any).content[0].text, /turn=2/);
366
+ assert.deepEqual(items, original);
367
+ });
368
+
369
+ test("filterSupersededResponsesContextInjections returns the array unchanged when no injections present", () => {
370
+ const items = [{ role: "user", content: [{ type: "input_text", text: "hi" }] }];
371
+ const result = filterSupersededResponsesContextInjections(items as any);
372
+ assert.equal(result, items);
373
+ });
@@ -0,0 +1,12 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ test("db engine stays free of import.meta syntax for CommonJS chunks", () => {
8
+ const enginePath = join(dirname(fileURLToPath(import.meta.url)), "..", "db", "engine.ts");
9
+ const source = readFileSync(enginePath, "utf-8");
10
+
11
+ assert.doesNotMatch(source, /\bimport\.meta\b/);
12
+ });
@@ -0,0 +1,62 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Verifies GSD_ADVERTISE_TOOL_ALIASES gates alias registration on the native tool path (plan 035).
3
+
4
+ import { test } from 'node:test';
5
+ import assert from 'node:assert/strict';
6
+ import { registerDbTools } from '../bootstrap/db-tools.ts';
7
+ import { WORKFLOW_TOOL_ALIAS_NAMES, WORKFLOW_TOOL_CONTRACTS } from '../workflow-tool-surface.ts';
8
+
9
+ function makeMockPi() {
10
+ const tools: any[] = [];
11
+ return {
12
+ registerTool: (tool: any) => tools.push(tool),
13
+ tools,
14
+ } as any;
15
+ }
16
+
17
+ function withEnv(name: string, value: string | undefined, fn: () => void): void {
18
+ const previous = process.env[name];
19
+ if (value === undefined) delete process.env[name];
20
+ else process.env[name] = value;
21
+ try {
22
+ fn();
23
+ } finally {
24
+ if (previous === undefined) delete process.env[name];
25
+ else process.env[name] = previous;
26
+ }
27
+ }
28
+
29
+ test('default: only canonical names are registered, no aliases', () => {
30
+ withEnv('GSD_ADVERTISE_TOOL_ALIASES', undefined, () => {
31
+ const pi = makeMockPi();
32
+ registerDbTools(pi);
33
+ const names = new Set(pi.tools.map((tool: any) => tool.name));
34
+
35
+ for (const alias of WORKFLOW_TOOL_ALIAS_NAMES) {
36
+ assert.ok(!names.has(alias), `alias "${alias}" should not be registered by default`);
37
+ }
38
+ const registeredCanonical = WORKFLOW_TOOL_CONTRACTS
39
+ .map((tool) => tool.canonicalName)
40
+ .filter((name) => names.has(name));
41
+ assert.ok(registeredCanonical.length > 0, 'canonical tools should still be registered');
42
+ });
43
+ });
44
+
45
+ test('GSD_ADVERTISE_TOOL_ALIASES=1: aliases are registered with the "(alias for ...)" description suffix', () => {
46
+ withEnv('GSD_ADVERTISE_TOOL_ALIASES', '1', () => {
47
+ const pi = makeMockPi();
48
+ registerDbTools(pi);
49
+ const toolByName = new Map<string, any>(pi.tools.map((tool: any) => [tool.name, tool]));
50
+
51
+ const registeredAliases = WORKFLOW_TOOL_ALIAS_NAMES.filter((alias) => toolByName.has(alias));
52
+ assert.ok(registeredAliases.length > 0, 'at least one alias should be registered when opted in');
53
+
54
+ for (const alias of registeredAliases) {
55
+ const aliasTool = toolByName.get(alias);
56
+ assert.ok(
57
+ aliasTool.description.includes('(alias for '),
58
+ `alias "${alias}" description should include "(alias for ...)"`,
59
+ );
60
+ }
61
+ });
62
+ });