@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.72e3af2a

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 (357) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
  3. package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
  4. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
  6. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  7. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
  9. package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
  10. package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
  11. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  12. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  13. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
  14. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
  15. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
  16. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +10 -19
  18. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
  19. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  21. package/dist/resources/extensions/gsd/commands-context.js +19 -1
  22. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
  23. package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
  24. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
  25. package/dist/resources/extensions/gsd/db/queries.js +60 -0
  26. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  27. package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
  28. package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
  29. package/dist/resources/extensions/gsd/forensics.js +2 -32
  30. package/dist/resources/extensions/gsd/git-service.js +4 -4
  31. package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
  32. package/dist/resources/extensions/gsd/health-widget.js +55 -29
  33. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  34. package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
  35. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
  36. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  37. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  38. package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
  39. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  40. package/dist/resources/extensions/gsd/quick.js +45 -2
  41. package/dist/resources/extensions/gsd/session-forensics.js +11 -1
  42. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  43. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  44. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  45. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  46. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  47. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  48. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
  49. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  50. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  51. package/dist/resources/extensions/gsd/state.js +12 -611
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  53. package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
  54. package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
  55. package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
  56. package/dist/resources/extensions/gsd/unit-registry.js +32 -4
  57. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
  58. package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
  59. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  60. package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
  61. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  62. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  65. package/dist/web/standalone/.next/build-manifest.json +3 -3
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  68. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  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/page_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/index.html +1 -1
  88. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  89. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  90. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  91. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  96. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  99. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  100. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  101. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  102. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  103. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  104. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  105. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  106. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  107. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  108. package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
  109. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  110. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  111. package/package.json +1 -1
  112. package/packages/cloud-mcp-gateway/package.json +2 -2
  113. package/packages/contracts/dist/workflow.d.ts +1 -0
  114. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  115. package/packages/contracts/dist/workflow.js +2 -0
  116. package/packages/contracts/dist/workflow.js.map +1 -1
  117. package/packages/contracts/package.json +1 -1
  118. package/packages/daemon/package.json +4 -4
  119. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  120. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  121. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  122. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  123. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  124. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  125. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  126. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  127. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  128. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  129. package/packages/gsd-agent-core/dist/index.js +2 -0
  130. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  131. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  132. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  133. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  134. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  135. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  136. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  137. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  138. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  139. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  140. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  141. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  142. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  143. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  144. package/packages/gsd-agent-core/package.json +5 -5
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  148. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  150. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  151. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  152. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  153. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  154. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  155. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
  156. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  157. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  158. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  159. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  160. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  161. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  162. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  163. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  164. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  165. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  166. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  170. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  171. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  173. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  174. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  175. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  176. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  177. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  178. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  179. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  180. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  182. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  187. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  189. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  190. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  191. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  193. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  194. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  195. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  196. package/packages/gsd-agent-modes/package.json +7 -7
  197. package/packages/mcp-server/README.md +1 -1
  198. package/packages/mcp-server/dist/server.d.ts +1 -1
  199. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  200. package/packages/mcp-server/dist/server.js +3 -3
  201. package/packages/mcp-server/dist/server.js.map +1 -1
  202. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  203. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  204. package/packages/mcp-server/dist/workflow-tools.js +34 -20
  205. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  206. package/packages/mcp-server/package.json +4 -4
  207. package/packages/native/package.json +1 -1
  208. package/packages/pi-agent-core/package.json +1 -1
  209. package/packages/pi-ai/package.json +1 -1
  210. package/packages/pi-coding-agent/README.md +3 -2
  211. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  212. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  214. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  216. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  218. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  220. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  221. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  222. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  223. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  224. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  226. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  228. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  229. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  230. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  231. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  232. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  234. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  237. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  238. package/packages/pi-coding-agent/package.json +7 -7
  239. package/packages/pi-tui/package.json +2 -2
  240. package/packages/rpc-client/package.json +2 -2
  241. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  242. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  243. package/pkg/dist/theme/theme-schema.js +1 -1
  244. package/pkg/dist/theme/theme-schema.js.map +1 -1
  245. package/pkg/dist/theme/theme.d.ts.map +1 -1
  246. package/pkg/dist/theme/theme.js +11 -7
  247. package/pkg/dist/theme/theme.js.map +1 -1
  248. package/pkg/package.json +1 -1
  249. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
  250. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
  251. package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
  252. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  253. package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
  254. package/src/resources/extensions/gsd/auto/session.ts +5 -0
  255. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  256. package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
  257. package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
  258. package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
  259. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  260. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  261. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
  262. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
  263. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
  264. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
  265. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -19
  266. package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
  267. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
  268. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  269. package/src/resources/extensions/gsd/commands-context.ts +18 -1
  270. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
  271. package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
  272. package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
  273. package/src/resources/extensions/gsd/db/queries.ts +79 -0
  274. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  275. package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
  276. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  277. package/src/resources/extensions/gsd/forensics.ts +2 -33
  278. package/src/resources/extensions/gsd/git-service.ts +5 -5
  279. package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
  280. package/src/resources/extensions/gsd/health-widget.ts +69 -32
  281. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  282. package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
  283. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
  284. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  285. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  286. package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
  287. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  288. package/src/resources/extensions/gsd/quick.ts +43 -2
  289. package/src/resources/extensions/gsd/session-forensics.ts +11 -1
  290. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  291. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  292. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  293. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  294. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  295. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  296. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
  297. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  298. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  299. package/src/resources/extensions/gsd/state.ts +32 -732
  300. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  301. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
  302. package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
  303. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  304. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
  305. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
  306. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
  307. package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
  308. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
  309. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
  310. package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
  311. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
  312. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
  313. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
  314. package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
  315. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
  316. package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
  317. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +119 -1
  318. package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
  319. package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
  320. package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
  321. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
  322. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  323. package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
  324. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
  325. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
  326. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
  327. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
  328. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  329. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
  330. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
  331. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
  332. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  334. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  335. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
  336. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
  337. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
  338. package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
  339. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
  340. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
  341. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
  342. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  343. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  344. package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
  345. package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
  346. package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
  347. package/src/resources/extensions/gsd/unit-registry.ts +32 -4
  348. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
  349. package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
  350. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  351. package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
  352. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  353. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  354. package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
  355. package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
  356. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_buildManifest.js +0 -0
  357. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_ssgManifest.js +0 -0
@@ -42,6 +42,8 @@ import {
42
42
  type ReconciliationDeps,
43
43
  } from "../state-reconciliation.ts";
44
44
  import { classifyFailure } from "../recovery-classification.ts";
45
+ import { handlerPhaseIndex, RECONCILIATION_REPAIR_PHASES } from "../state-reconciliation/registry.ts";
46
+ import { staleRenderHandler } from "../state-reconciliation/drift/stale-render.ts";
45
47
  import type { GSDState } from "../types.ts";
46
48
 
47
49
  function makeState(overrides: Partial<GSDState> = {}): GSDState {
@@ -159,6 +161,29 @@ test("ADR-017 (#5700): repair failure throws ReconciliationFailedError with shap
159
161
  );
160
162
  });
161
163
 
164
+ test("ADR-017 (#5700): custom registry handlers outside built-in phases are repaired", async () => {
165
+ const drift = { kind: "custom-drift", id: "D001" } as unknown as DriftRecord;
166
+ let repaired = false;
167
+ const handler = {
168
+ kind: "custom-drift",
169
+ detect: () => (repaired ? [] : [drift]),
170
+ repair: () => {
171
+ repaired = true;
172
+ },
173
+ } as unknown as DriftHandler;
174
+
175
+ const result = await reconcileBeforeDispatch("/project", {
176
+ invalidateStateCache: () => {},
177
+ deriveState: async () => makeState(),
178
+ registry: [handler],
179
+ });
180
+
181
+ assert.equal(result.ok, true);
182
+ assert.equal(repaired, true);
183
+ assert.equal(result.repaired.length, 1);
184
+ assert.equal(result.repaired[0]?.kind, "custom-drift");
185
+ });
186
+
162
187
  test("ADR-017 (#5700): a detector failure degrades to a blocker without aborting other handlers", async () => {
163
188
  // A single detector throwing (e.g. a transient file read error) must NOT
164
189
  // abort the whole cycle and hide every later handler's drift. It is collected
@@ -696,6 +721,126 @@ test("ADR-017 (#5702): stale-render drift detected and repaired end-to-end", asy
696
721
  assert.match(repairedContent, /\[x\][^\n]*\*\*T02\*\*/, "T02 checkbox should be checked after repair");
697
722
  });
698
723
 
724
+ test("#1003: stale-render plan repair reopens DB before rendering", async (t) => {
725
+ const base = mkdtempSync(join(tmpdir(), "gsd-stale-render-reopen-"));
726
+ const sliceDir = join(base, ".gsd", "phases", "01-test");
727
+ mkdirSync(sliceDir, { recursive: true });
728
+ t.after(() => {
729
+ try { closeDatabase(); } catch { /* noop */ }
730
+ rmTreeQuiet(base);
731
+ });
732
+
733
+ openDatabase(join(base, ".gsd", "gsd.db"));
734
+ clearRendererCaches();
735
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
736
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
737
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "First task", status: "done" });
738
+
739
+ const planPath = join(sliceDir, "01-01-PLAN.md");
740
+ writeFileSync(planPath, makeStalePlanContent("S01", [
741
+ { id: "T01", title: "First task", done: false },
742
+ ]));
743
+ closeDatabase();
744
+
745
+ await staleRenderHandler.repair(
746
+ {
747
+ kind: "stale-render",
748
+ renderPath: planPath,
749
+ reason: "T01 is done in DB but unchecked in plan",
750
+ },
751
+ { basePath: base, state: makeState() },
752
+ );
753
+
754
+ const repairedContent = readFileSync(planPath, "utf-8");
755
+ assert.match(repairedContent, /\[x\][^\n]*\*\*T01\*\*/, "T01 checkbox should be checked after DB reopen repair");
756
+ assert.equal(getSliceTasks("M001", "S01").length, 1, "DB should be reopened on the original project database");
757
+ });
758
+
759
+ test("#1003: stale-render plan repair switches back from an open wrong DB", async (t) => {
760
+ const base = mkdtempSync(join(tmpdir(), "gsd-stale-render-wrong-db-"));
761
+ const wrongBase = mkdtempSync(join(tmpdir(), "gsd-stale-render-other-db-"));
762
+ const sliceDir = join(base, ".gsd", "phases", "01-test");
763
+ mkdirSync(sliceDir, { recursive: true });
764
+ mkdirSync(join(wrongBase, ".gsd"), { recursive: true });
765
+ t.after(() => {
766
+ try { closeDatabase(); } catch { /* noop */ }
767
+ rmTreeQuiet(base);
768
+ rmTreeQuiet(wrongBase);
769
+ });
770
+
771
+ openDatabase(join(base, ".gsd", "gsd.db"));
772
+ clearRendererCaches();
773
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
774
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
775
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "First task", status: "done" });
776
+
777
+ const planPath = join(sliceDir, "01-01-PLAN.md");
778
+ writeFileSync(planPath, makeStalePlanContent("S01", [
779
+ { id: "T01", title: "First task", done: false },
780
+ ]));
781
+ closeDatabase();
782
+
783
+ openDatabase(join(wrongBase, ".gsd", "gsd.db"));
784
+
785
+ await staleRenderHandler.repair(
786
+ {
787
+ kind: "stale-render",
788
+ renderPath: planPath,
789
+ reason: "T01 is done in DB but unchecked in plan",
790
+ },
791
+ { basePath: base, state: makeState() },
792
+ );
793
+
794
+ const repairedContent = readFileSync(planPath, "utf-8");
795
+ assert.match(repairedContent, /\[x\][^\n]*\*\*T01\*\*/, "T01 checkbox should be checked after switching back to the project DB");
796
+ assert.equal(getSliceTasks("M001", "S01").length, 1, "repair should leave the project DB active");
797
+ });
798
+
799
+ test("#1034: validation-blocked milestone summary drift returns blocker instead of exhausting repair passes", async () => {
800
+ const drift: Extract<DriftRecord, { kind: "stale-render" }> = {
801
+ kind: "stale-render",
802
+ renderPath: "/repo/.gsd/milestones/M001/M001-SUMMARY.md",
803
+ reason: "M001 is complete with summary in DB but SUMMARY.md missing on disk",
804
+ };
805
+ let repairCalled = false;
806
+ const handler: DriftHandler<Extract<DriftRecord, { kind: "stale-render" }>> = {
807
+ kind: "stale-render",
808
+ detect: () => [drift],
809
+ blocker: staleRenderHandler.blocker!,
810
+ repair: () => {
811
+ repairCalled = true;
812
+ },
813
+ };
814
+
815
+ const validationBlocker = [
816
+ "Milestone M001 is blocked because milestone validation returned needs-attention.",
817
+ "Fix options:",
818
+ "1. Review the validation details: `/gsd status`",
819
+ ].join("\n");
820
+
821
+ const result = await reconcileBeforeDispatch("/repo", {
822
+ invalidateStateCache: () => {},
823
+ deriveState: async () =>
824
+ makeState({
825
+ phase: "blocked",
826
+ blockers: [validationBlocker],
827
+ nextAction: "Resolve M001 validation attention before proceeding.",
828
+ }),
829
+ registry: [handler],
830
+ });
831
+
832
+ assert.equal(result.ok, true);
833
+ assert.equal(repairCalled, false, "validation-blocked milestone summary drift should not attempt repair");
834
+ assert.ok(
835
+ result.blockers.some((blocker) => blocker.includes("milestone validation returned needs-attention")),
836
+ "validation blocker should be returned to the caller",
837
+ );
838
+ assert.ok(
839
+ result.blockers.some((blocker) => blocker.includes("Stale milestone summary render")),
840
+ "stale-render blocker should explain why repair did not run",
841
+ );
842
+ });
843
+
699
844
  test("ADR-017 (#5702): stale-render detector reason strings match repair contract", (t) => {
700
845
  t.skip("TODO(flat-phase): stale-render detection temporarily disabled during layout transition"); return;
701
846
  const base = mkdtempSync(join(tmpdir(), "gsd-adr017-render-reasons-"));
@@ -1811,3 +1956,49 @@ test("ADR-017 (#5700): cascading drift triggers second pass within cap", async (
1811
1956
  assert.equal(result.repaired.length, 2, "both passes' repairs collected");
1812
1957
  assert.equal(repaired.length, 2);
1813
1958
  });
1959
+
1960
+ test("deriveState is pure: stale sketch healed only via reconcileBeforeDispatch", async (t) => {
1961
+ const base = makeFixtureBase();
1962
+ t.after(() => cleanup(base));
1963
+
1964
+ openDatabase(join(base, ".gsd", "gsd.db"));
1965
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
1966
+ insertSlice({
1967
+ id: "S02",
1968
+ milestoneId: "M001",
1969
+ title: "Feature",
1970
+ status: "pending",
1971
+ risk: "medium",
1972
+ depends: [],
1973
+ demo: "S02 demo.",
1974
+ sequence: 1,
1975
+ isSketch: true,
1976
+ sketchScope: "limited",
1977
+ });
1978
+ writeFileSync(
1979
+ join(base, ".gsd", "phases", "01-test", "01-02-PLAN.md"),
1980
+ "# S02 Plan\n",
1981
+ );
1982
+
1983
+ const { deriveState } = await import("../state.ts");
1984
+ invalidateStateCache();
1985
+
1986
+ const beforeReconcile = await deriveState(base);
1987
+ assert.equal(beforeReconcile.phase, "refining", "derive alone must not heal stale sketch flag");
1988
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 1, "DB flag unchanged before reconcile");
1989
+
1990
+ const result = await reconcileBeforeDispatch(base);
1991
+ assert.equal(result.ok, true);
1992
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "reconcile clears sketch flag");
1993
+
1994
+ invalidateStateCache();
1995
+ const afterReconcile = await deriveState(base);
1996
+ assert.notEqual(afterReconcile.phase, "refining", "derive after reconcile advances past sketch gate");
1997
+ });
1998
+
1999
+ test("reconciliation repair phases: external edits precede re-project handlers", () => {
2000
+ assert.equal(RECONCILIATION_REPAIR_PHASES.length, 3);
2001
+ assert.ok(handlerPhaseIndex("external-markdown-edit") < handlerPhaseIndex("stale-render"));
2002
+ assert.ok(handlerPhaseIndex("external-planning-edit") < handlerPhaseIndex("roadmap-divergence"));
2003
+ assert.ok(handlerPhaseIndex("stale-sketch-flag") < handlerPhaseIndex("stale-render"));
2004
+ });
@@ -10,6 +10,7 @@ import {
10
10
  resetToolCallLoopGuard,
11
11
  disableToolCallLoopGuard,
12
12
  getToolCallLoopCount,
13
+ getToolCallCountForTool,
13
14
  } from '../bootstrap/tool-call-loop-guard.ts';
14
15
 
15
16
 
@@ -177,3 +178,153 @@ console.log('\n── Loop guard: nested key order is normalized ──');
177
178
  }
178
179
 
179
180
  // ═══════════════════════════════════════════════════════════════════════════
181
+ // Per-tool-name cap (#783 Brief C) — catches improvisation loops with varied args
182
+ // ═══════════════════════════════════════════════════════════════════════════
183
+
184
+ console.log('\n── Loop guard: per-tool cap blocks varied-args improvisation (#783) ──');
185
+
186
+ {
187
+ resetToolCallLoopGuard();
188
+
189
+ // A one-shot workflow tool called with DIFFERENT args each time (the reported
190
+ // improvisation pattern). The identical-signature streak alone would reset
191
+ // every call; the per-tool cap must catch it.
192
+ for (let i = 1; i <= 6; i++) {
193
+ const result = checkToolCallLoop('gsd_complete_milestone', { milestone: `M${i}` });
194
+ assert.ok(result.block === false, `one-shot call ${i} (varied args) should be allowed`);
195
+ assert.deepStrictEqual(getToolCallCountForTool('gsd_complete_milestone'), i, `per-tool count should be ${i}`);
196
+ }
197
+ // 7th call (cap 6 + 1) must be blocked by the per-tool guard.
198
+ const blocked = checkToolCallLoop('gsd_complete_milestone', { milestone: 'M7' });
199
+ assert.ok(blocked.block === true, '7th one-shot call (varied args) should be blocked by per-tool cap');
200
+ assert.ok(blocked.reason!.includes('repeated tool'), 'reason should identify the per-tool guard');
201
+ assert.ok(blocked.reason!.includes('gsd_complete_milestone'), 'reason should name the tool');
202
+ assert.ok(blocked.reason!.includes('7'), 'reason should mention the count');
203
+ }
204
+
205
+ // ═══════════════════════════════════════════════════════════════════════════
206
+ // Repeatable tools get the higher cap
207
+ // ═══════════════════════════════════════════════════════════════════════════
208
+
209
+ console.log('\n── Loop guard: repeatable tools get the higher cap (#783) ──');
210
+
211
+ {
212
+ resetToolCallLoopGuard();
213
+
214
+ // bash is repeatable: varied commands are legitimate up to the higher cap.
215
+ for (let i = 1; i <= 15; i++) {
216
+ const result = checkToolCallLoop('bash', { command: `echo ${i}` });
217
+ assert.ok(result.block === false, `bash call ${i} (varied args) should be allowed`);
218
+ }
219
+ // 16th call (cap 15 + 1) is blocked by the per-tool guard — this is the
220
+ // improvisation-through-bash case from the forensics (~51 calls).
221
+ const blocked = checkToolCallLoop('bash', { command: 'echo 16' });
222
+ assert.ok(blocked.block === true, '16th bash call (varied args) should be blocked by per-tool cap');
223
+ assert.ok(blocked.reason!.includes('cap 15'), 'reason should mention the repeatable cap');
224
+ }
225
+
226
+ // ═══════════════════════════════════════════════════════════════════════════
227
+ // Per-tool counts are independent per tool and reset together
228
+ // ═══════════════════════════════════════════════════════════════════════════
229
+
230
+ console.log('\n── Loop guard: per-tool counts are independent and reset together (#783) ──');
231
+
232
+ {
233
+ resetToolCallLoopGuard();
234
+
235
+ // Two different tools tracked separately.
236
+ for (let i = 0; i < 3; i++) checkToolCallLoop('read', { path: `f${i}` });
237
+ for (let i = 0; i < 3; i++) checkToolCallLoop('write', { path: `g${i}` });
238
+ assert.deepStrictEqual(getToolCallCountForTool('read'), 3, 'read tracked separately');
239
+ assert.deepStrictEqual(getToolCallCountForTool('write'), 3, 'write tracked separately');
240
+ assert.deepStrictEqual(getToolCallCountForTool('edit'), 0, 'uncalled tool reports 0');
241
+
242
+ resetToolCallLoopGuard();
243
+ assert.deepStrictEqual(getToolCallCountForTool('read'), 0, 'per-tool counts cleared on reset');
244
+ assert.deepStrictEqual(getToolCallCountForTool('write'), 0, 'per-tool counts cleared on reset');
245
+ }
246
+
247
+ // ═══════════════════════════════════════════════════════════════════════════
248
+ // Newly-repeatable tools from core-session-tools.ts get the higher cap.
249
+ // bg_shell, find, ls, search_and_read were added to INHERENTLY_REPEATABLE_TOOL_SET
250
+ // in the code-quality consolidation PR (previously only bash/read/write/etc).
251
+ // ═══════════════════════════════════════════════════════════════════════════
252
+
253
+ console.log('\n── Loop guard: new repeatable tools (bg_shell, find, ls, search_and_read) get high cap ──');
254
+
255
+ {
256
+ for (const toolName of ['bg_shell', 'find', 'ls', 'search_and_read']) {
257
+ resetToolCallLoopGuard();
258
+
259
+ // Should allow up to 15 varied calls without blocking.
260
+ for (let i = 1; i <= 15; i++) {
261
+ const result = checkToolCallLoop(toolName, { arg: `v${i}` });
262
+ assert.ok(result.block === false, `${toolName} call ${i} (varied args) should be allowed`);
263
+ }
264
+ // 16th call with varied args must be blocked by the per-tool repeatable cap.
265
+ const blocked = checkToolCallLoop(toolName, { arg: 'v16' });
266
+ assert.ok(blocked.block === true, `${toolName}: 16th call must be blocked by repeatable cap`);
267
+ assert.ok(blocked.reason?.includes('cap 15'), `${toolName}: reason must mention cap 15`);
268
+ }
269
+ }
270
+
271
+ // ═══════════════════════════════════════════════════════════════════════════
272
+ // Non-repeatable tools excluded from INHERENTLY_REPEATABLE_TOOL_SET use the
273
+ // default cap (6), not the repeatable cap (15).
274
+ // ═══════════════════════════════════════════════════════════════════════════
275
+
276
+ console.log('\n── Loop guard: ToolSearch (excluded from repeatable set) hits default cap ──');
277
+
278
+ {
279
+ // ToolSearch is in MINIMAL_AUTO_BASE but explicitly excluded from repeatable.
280
+ // It should hit the default cap of 6, not 15.
281
+ resetToolCallLoopGuard();
282
+ for (let i = 1; i <= 6; i++) {
283
+ const result = checkToolCallLoop('ToolSearch', { query: `q${i}` });
284
+ assert.ok(result.block === false, `ToolSearch call ${i} should be allowed`);
285
+ }
286
+ const blocked = checkToolCallLoop('ToolSearch', { query: 'q7' });
287
+ assert.ok(blocked.block === true, 'ToolSearch: 7th varied call must be blocked by default cap');
288
+ assert.ok(!blocked.reason?.includes('cap 15'), 'ToolSearch must NOT use the repeatable cap');
289
+ }
290
+
291
+ // ═══════════════════════════════════════════════════════════════════════════
292
+ // Block reasons instruct the model to stop tooling this turn
293
+ // ═══════════════════════════════════════════════════════════════════════════
294
+
295
+ console.log('\n── Loop guard: block reasons tell model to respond in text ──');
296
+
297
+ {
298
+ resetToolCallLoopGuard();
299
+
300
+ for (let i = 0; i < 4; i++) {
301
+ checkToolCallLoop('web_search', { query: 'same query' });
302
+ }
303
+ const identicalBlocked = checkToolCallLoop('web_search', { query: 'same query' });
304
+ assert.ok(identicalBlocked.block === true, 'identical-args guard should block');
305
+ assert.ok(
306
+ identicalBlocked.reason!.includes('respond to the user in text'),
307
+ 'identical-args reason should tell the model to stop tooling',
308
+ );
309
+ assert.ok(
310
+ !identicalBlocked.reason!.includes('Try a different approach'),
311
+ 'identical-args reason should not suggest retrying another tool',
312
+ );
313
+
314
+ resetToolCallLoopGuard();
315
+ for (let i = 0; i < 6; i++) {
316
+ checkToolCallLoop('gsd_complete_milestone', { milestone: `M${i}` });
317
+ }
318
+ const perToolBlocked = checkToolCallLoop('gsd_complete_milestone', { milestone: 'M7' });
319
+ assert.ok(perToolBlocked.block === true, 'per-tool guard should block');
320
+ assert.ok(
321
+ perToolBlocked.reason!.includes('respond to the user in text'),
322
+ 'per-tool reason should tell the model to stop tooling',
323
+ );
324
+ assert.ok(
325
+ perToolBlocked.reason!.includes('Do not retry this tool'),
326
+ 'per-tool reason should forbid retrying the blocked tool',
327
+ );
328
+ }
329
+
330
+ // ═══════════════════════════════════════════════════════════════════════════
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { test } from "node:test";
15
15
  import assert from "node:assert/strict";
16
+ import { SUMMARY_SAVE_CONTENT_MAX_LENGTH } from "@opengsd/contracts";
16
17
  import { registerDbTools } from "../bootstrap/db-tools.ts";
17
18
  import AjvModule from "ajv";
18
19
 
@@ -86,6 +87,31 @@ test("gsd_summary_save — validates UAT assessment params", () => {
86
87
  assert.strictEqual(valid, true, `UAT assessment params should validate but got errors: ${JSON.stringify(validate.errors)}`);
87
88
  });
88
89
 
90
+ test("gsd_summary_save — content has a provider-safe maxLength", () => {
91
+ const tool = getTool("gsd_summary_save");
92
+ assert.ok(tool, "gsd_summary_save must be registered");
93
+
94
+ const contentSchema = tool.parameters.properties.content;
95
+ assert.strictEqual(contentSchema.maxLength, SUMMARY_SAVE_CONTENT_MAX_LENGTH);
96
+
97
+ const validAtLimit = validateSchema(tool, {
98
+ milestone_id: "M001",
99
+ artifact_type: "CONTEXT-DRAFT",
100
+ content: "x".repeat(SUMMARY_SAVE_CONTENT_MAX_LENGTH),
101
+ });
102
+ assert.deepEqual(validAtLimit, []);
103
+
104
+ const overLimit = validateSchema(tool, {
105
+ milestone_id: "M001",
106
+ artifact_type: "CONTEXT-DRAFT",
107
+ content: "x".repeat(SUMMARY_SAVE_CONTENT_MAX_LENGTH + 1),
108
+ });
109
+ assert.ok(
110
+ overLimit.some((error) => error.includes(`must NOT have more than ${SUMMARY_SAVE_CONTENT_MAX_LENGTH} characters`)),
111
+ `expected maxLength validation error, got: ${overLimit.join("; ")}`,
112
+ );
113
+ });
114
+
89
115
  // ─── gsd_slice_complete: enrichment arrays must be optional ──────────────────
90
116
 
91
117
  test("gsd_slice_complete — enrichment arrays are optional", () => {