@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
@@ -1671,19 +1671,25 @@ export function serializePreferencesToFrontmatter(prefs) {
1671
1671
  const entries = Object.entries(item);
1672
1672
  if (entries.length > 0) {
1673
1673
  const [firstKey, firstVal] = entries[0];
1674
- lines.push(`${prefix} - ${firstKey}: ${yamlSafeString(firstVal)}`);
1675
- for (let i = 1; i < entries.length; i++) {
1676
- const [k, v] = entries[i];
1677
- if (Array.isArray(v)) {
1678
- lines.push(`${prefix} ${k}:`);
1679
- for (const arrItem of v) {
1680
- lines.push(`${prefix} - ${yamlSafeString(arrItem)}`);
1681
- }
1674
+ if (Array.isArray(firstVal)) {
1675
+ lines.push(`${prefix} - ${firstKey}:`);
1676
+ for (const arrItem of firstVal) {
1677
+ lines.push(`${prefix} - ${yamlSafeString(arrItem)}`);
1682
1678
  }
1683
- else {
1684
- lines.push(`${prefix} ${k}: ${yamlSafeString(v)}`);
1679
+ }
1680
+ else if (typeof firstVal === "object" && firstVal !== null) {
1681
+ lines.push(`${prefix} - ${firstKey}:`);
1682
+ for (const [k, v] of Object.entries(firstVal)) {
1683
+ serializeValue(k, v, indent + 3);
1685
1684
  }
1686
1685
  }
1686
+ else {
1687
+ lines.push(`${prefix} - ${firstKey}: ${yamlSafeString(firstVal)}`);
1688
+ }
1689
+ for (let i = 1; i < entries.length; i++) {
1690
+ const [k, v] = entries[i];
1691
+ serializeValue(k, v, indent + 2);
1692
+ }
1687
1693
  }
1688
1694
  }
1689
1695
  else {
@@ -12,9 +12,9 @@ import { inferCommitType } from "./git-service.js";
12
12
  import { autoCommitCurrentBranch } from "./worktree.js";
13
13
  import { GSDError, GSD_GIT_ERROR } from "./errors.js";
14
14
  // ─── Status helper ─────────────────────────────────────────────────────────
15
- function getStatus(basePath, name, wtPath) {
16
- const diff = diffWorktreeAll(basePath, name);
17
- const numstat = diffWorktreeNumstat(basePath, name);
15
+ function getStatus(basePath, name, wtPath, mainBranch) {
16
+ const diff = diffWorktreeAll(basePath, name, undefined, mainBranch);
17
+ const numstat = diffWorktreeNumstat(basePath, name, undefined, mainBranch);
18
18
  const filesChanged = diff.added.length + diff.modified.length + diff.removed.length;
19
19
  let linesAdded = 0;
20
20
  let linesRemoved = 0;
@@ -31,8 +31,7 @@ function getStatus(basePath, name, wtPath) {
31
31
  }
32
32
  let commits = 0;
33
33
  try {
34
- const main = nativeDetectMainBranch(basePath);
35
- commits = nativeCommitCountBetween(basePath, main, worktreeBranchName(name));
34
+ commits = nativeCommitCountBetween(basePath, mainBranch, worktreeBranchName(name));
36
35
  }
37
36
  catch {
38
37
  // commit count unavailable → leave at 0
@@ -88,7 +87,8 @@ export function formatCleanKeepReason(status) {
88
87
  async function handleList(ctx) {
89
88
  const basePath = projectRoot();
90
89
  const worktrees = listWorktrees(basePath);
91
- const statuses = worktrees.map((wt) => getStatus(basePath, wt.name, wt.path));
90
+ const mainBranch = worktrees.length > 0 ? nativeDetectMainBranch(basePath) : "";
91
+ const statuses = worktrees.map((wt) => getStatus(basePath, wt.name, wt.path, mainBranch));
92
92
  ctx.ui.notify(formatWorktreeList(statuses), "info");
93
93
  }
94
94
  // ─── Subcommand: merge ──────────────────────────────────────────────────────
@@ -117,7 +117,8 @@ async function handleMerge(args, ctx) {
117
117
  ctx.ui.notify(`Worktree "${target}" not found.\n\nAvailable: ${available}`, "error");
118
118
  return;
119
119
  }
120
- const status = getStatus(basePath, target, wt.path);
120
+ const mainBranch = nativeDetectMainBranch(basePath);
121
+ const status = getStatus(basePath, target, wt.path, mainBranch);
121
122
  if (status.filesChanged === 0 && !status.uncommitted) {
122
123
  try {
123
124
  removeWorktree(basePath, target, { deleteBranch: true });
@@ -144,7 +145,6 @@ async function handleMerge(args, ctx) {
144
145
  }
145
146
  }
146
147
  const commitType = inferCommitType(target);
147
- const mainBranch = nativeDetectMainBranch(basePath);
148
148
  const commitMessage = `${commitType}: merge worktree ${target}\n\nGSD-Worktree: ${target}`;
149
149
  try {
150
150
  mergeWorktreeToMain(basePath, target, commitMessage);
@@ -191,8 +191,9 @@ async function handleClean(ctx) {
191
191
  }
192
192
  const removed = [];
193
193
  const kept = [];
194
+ const mainBranch = nativeDetectMainBranch(basePath);
194
195
  for (const wt of worktrees) {
195
- const status = getStatus(basePath, wt.name, wt.path);
196
+ const status = getStatus(basePath, wt.name, wt.path, mainBranch);
196
197
  if (status.filesChanged === 0 && !status.uncommitted) {
197
198
  try {
198
199
  removeWorktree(basePath, wt.name, { deleteBranch: true });
@@ -238,7 +239,8 @@ async function handleRemove(args, ctx) {
238
239
  ctx.ui.notify(`Worktree "${name}" not found.\n\nAvailable: ${available}`, "error");
239
240
  return;
240
241
  }
241
- const status = getStatus(basePath, name, wt.path);
242
+ const mainBranch = nativeDetectMainBranch(basePath);
243
+ const status = getStatus(basePath, name, wt.path, mainBranch);
242
244
  if ((status.filesChanged > 0 || status.uncommitted) && !force) {
243
245
  ctx.ui.notify([
244
246
  `Worktree "${name}" has pending changes (${formatCleanKeepReason(status)}).`,
@@ -21,7 +21,7 @@ import { getWorkerBatches, hasActiveWorkers } from "../subagent/worker-registry.
21
21
  import { formatDuration, padRight, joinColumns, centerLine, fitColumns, STATUS_GLYPH, STATUS_COLOR } from "../shared/mod.js";
22
22
  import { estimateTimeRemaining } from "./auto-dashboard.js";
23
23
  import { computeProgressScore } from "./progress-score.js";
24
- import { runEnvironmentChecks } from "./doctor-environment.js";
24
+ import { runEnvironmentChecksAsync } from "./doctor-environment.js";
25
25
  import { formattedShortcutPair } from "./shortcut-defs.js";
26
26
  import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
27
27
  export function unitLabel(type) {
@@ -62,6 +62,9 @@ export class GSDDashboardOverlay {
62
62
  loading = true;
63
63
  loadedDashboardIdentity;
64
64
  refreshInFlight = null;
65
+ envRefreshInFlight = null;
66
+ cachedEnvBasePath;
67
+ cachedEnvIssues = [];
65
68
  disposed = false;
66
69
  resizeHandler = null;
67
70
  cachedMetrics = null;
@@ -155,11 +158,38 @@ export class GSDDashboardOverlay {
155
158
  if (initial) {
156
159
  this.loading = false;
157
160
  }
161
+ this.scheduleEnvironmentRefresh(this.dashData.basePath || process.cwd());
158
162
  if (identityChanged) {
159
163
  this.invalidate();
160
164
  }
161
165
  this.tui.requestRender();
162
166
  }
167
+ scheduleEnvironmentRefresh(basePath) {
168
+ if (this.cachedEnvBasePath !== basePath) {
169
+ this.cachedEnvBasePath = basePath;
170
+ this.cachedEnvIssues = [];
171
+ this.invalidate();
172
+ }
173
+ if (this.envRefreshInFlight || this.disposed)
174
+ return;
175
+ this.envRefreshInFlight = this.refreshEnvironmentHealth(basePath)
176
+ .finally(() => {
177
+ this.envRefreshInFlight = null;
178
+ });
179
+ }
180
+ async refreshEnvironmentHealth(basePath) {
181
+ try {
182
+ const envResults = await runEnvironmentChecksAsync(basePath);
183
+ if (this.disposed || this.cachedEnvBasePath !== basePath)
184
+ return;
185
+ this.cachedEnvIssues = envResults.filter(r => r.status !== "ok");
186
+ this.invalidate();
187
+ this.tui.requestRender();
188
+ }
189
+ catch {
190
+ // Non-fatal — keep last known environment issues
191
+ }
192
+ }
163
193
  async loadData() {
164
194
  const base = this.dashData.basePath || process.cwd();
165
195
  try {
@@ -540,8 +570,7 @@ export class GSDDashboardOverlay {
540
570
  }
541
571
  }
542
572
  // Environment health section (#1221) — only show issues
543
- const envResults = runEnvironmentChecks(this.dashData.basePath || process.cwd());
544
- const envIssues = envResults.filter(r => r.status !== "ok");
573
+ const envIssues = this.cachedEnvIssues;
545
574
  if (envIssues.length > 0) {
546
575
  lines.push(blank());
547
576
  lines.push(hr());
@@ -12,6 +12,7 @@ import { rowToActiveDecision, rowToActiveRequirement, rowToDecision, rowToRequir
12
12
  import { rowToGate } from "../db-gate-rows.js";
13
13
  import { rowToArtifact, rowToMilestone } from "../db-milestone-artifact-rows.js";
14
14
  import { rowToSlice, rowToTask } from "../db-task-slice-rows.js";
15
+ import { TERMINAL_STATUS_SQL } from "./sql-constants.js";
15
16
  function parseStringArrayColumn(raw) {
16
17
  if (Array.isArray(raw))
17
18
  return raw.filter((entry) => typeof entry === "string");
@@ -35,6 +36,44 @@ function parseStringArrayColumn(raw) {
35
36
  function normalizeRepoPath(file) {
36
37
  return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
37
38
  }
39
+ function numberColumn(row, column) {
40
+ const value = row?.[column];
41
+ if (typeof value === "number")
42
+ return value;
43
+ if (typeof value === "bigint")
44
+ return Number(value);
45
+ if (typeof value === "string") {
46
+ const parsed = Number(value);
47
+ return Number.isFinite(parsed) ? parsed : 0;
48
+ }
49
+ return 0;
50
+ }
51
+ function getCompletionCount(table) {
52
+ const row = getDbOrNull().prepare(`SELECT
53
+ COUNT(*) AS total,
54
+ COALESCE(SUM(CASE WHEN status IN (${TERMINAL_STATUS_SQL}) THEN 1 ELSE 0 END), 0) AS completed
55
+ FROM ${table}`).get();
56
+ return {
57
+ completed: numberColumn(row, "completed"),
58
+ total: numberColumn(row, "total"),
59
+ };
60
+ }
61
+ export function getHierarchyCompletionCounts() {
62
+ if (!getDbOrNull()) {
63
+ return { milestones: 0, milestonesTotal: 0, slices: 0, slicesTotal: 0, tasks: 0, tasksTotal: 0 };
64
+ }
65
+ const milestones = getCompletionCount("milestones");
66
+ const slices = getCompletionCount("slices");
67
+ const tasks = getCompletionCount("tasks");
68
+ return {
69
+ milestones: milestones.completed,
70
+ milestonesTotal: milestones.total,
71
+ slices: slices.completed,
72
+ slicesTotal: slices.total,
73
+ tasks: tasks.completed,
74
+ tasksTotal: tasks.total,
75
+ };
76
+ }
38
77
  export function getDecisionById(id) {
39
78
  if (!getDbOrNull())
40
79
  return null;
@@ -391,6 +430,27 @@ export function getAssessment(path) {
391
430
  const row = getDbOrNull().prepare(`SELECT * FROM assessments WHERE path = :path`).get({ ":path": path });
392
431
  return row ?? null;
393
432
  }
433
+ /**
434
+ * Look up a slice's `run-uat` assessment by (milestoneId, sliceId) identity,
435
+ * independent of the artifact `path`. Used as a DB fallback by the UAT
436
+ * closeout gate when a path migration orphans the ASSESSMENT markdown from its
437
+ * canonical expected path (ADR-017: DB-authoritative UAT sign-off).
438
+ *
439
+ * `status` holds the normalized verdict (`pass`/`fail`/…) written by
440
+ * `executeUatResultSave`; `fullContent` carries the ASSESSMENT body so callers
441
+ * can derive `uatType` without re-reading a file that may not exist.
442
+ */
443
+ export function getSliceRunUatAssessment(milestoneId, sliceId) {
444
+ if (!getDbOrNull())
445
+ return null;
446
+ const row = getDbOrNull().prepare(`SELECT status, full_content AS fullContent FROM assessments
447
+ WHERE milestone_id = :mid AND slice_id = :sid AND scope = 'run-uat'
448
+ ORDER BY created_at DESC, ROWID DESC
449
+ LIMIT 1`).get({ ":mid": milestoneId, ":sid": sliceId });
450
+ if (!row)
451
+ return null;
452
+ return { status: String(row["status"] ?? ""), fullContent: String(row["fullContent"] ?? "") };
453
+ }
394
454
  export function getLatestAssessmentByScope(milestoneId, scope) {
395
455
  if (!getDbOrNull())
396
456
  return null;
@@ -1,10 +1,24 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Workspace-facing Interface for opening and maintaining the workflow database.
3
3
  import { existsSync } from "node:fs";
4
- import { dirname } from "node:path";
4
+ import { dirname, join } from "node:path";
5
5
  import { backupDatabaseSnapshot, checkpointDatabase, closeAllDatabases, closeDatabase, closeDatabaseByWorkspace, getDbPath, getDbStatus, getDbProvider, isDbAvailable, openDatabase, openDatabaseByScope, openDatabaseByWorkspace, openIsolatedDatabase, refreshOpenDatabaseFromDisk, vacuumDatabase, wasDbOpenAttempted, } from "./gsd-db.js";
6
- import { resolveGsdPathContract } from "./paths.js";
7
- import { setLogBasePath } from "./workflow-logger.js";
6
+ import { resolveGsdPathContract, gsdRoot } from "./paths.js";
7
+ import { logWarning, setLogBasePath } from "./workflow-logger.js";
8
+ /**
9
+ * Global SQLite handle invariants:
10
+ *
11
+ * - `openWorkflowDatabase` / `openDatabase` switch the process-global handle consumed by
12
+ * deriveState, dispatch, reconciliation repairs, and domain writers. Only one active
13
+ * project database should own the global handle at a time.
14
+ * - `openWorkflowDatabaseIsolated` opens a caller-owned connection that does not clobber
15
+ * the global handle. Use for read-only observers (parallel monitor) and other background
16
+ * probes that must not disturb the active workflow session.
17
+ * - Reconciliation repairs that write markdown/DB state must use `ensureWorkflowDbForBase`
18
+ * so repairs target the correct project; those paths intentionally re-open the global handle.
19
+ * - Pair ad-hoc project switches with `closeWorkflowDatabase()` or restore via
20
+ * `ensureWorkflowDbForBase(..., { refresh: true })` before returning to derive/dispatch.
21
+ */
8
22
  export function resolveWorkflowDatabaseLocation(basePath) {
9
23
  const contract = resolveGsdPathContract(basePath);
10
24
  return {
@@ -104,6 +118,44 @@ export function getWorkflowDatabasePath() {
104
118
  export function refreshWorkflowDatabaseFromDisk() {
105
119
  return refreshOpenDatabaseFromDisk();
106
120
  }
121
+ export function expectedWorkflowDbPathForBase(basePath) {
122
+ return join(gsdRoot(basePath), "gsd.db");
123
+ }
124
+ export function ensureWorkflowDbAtPath(dbPath) {
125
+ if (!dbPath || dbPath === ":memory:")
126
+ return isDbAvailable();
127
+ if (isDbAvailable() && getWorkflowDatabasePath() === dbPath)
128
+ return true;
129
+ if (!existsSync(dbPath))
130
+ return false;
131
+ try {
132
+ return openWorkflowDatabasePath(dbPath);
133
+ }
134
+ catch (err) {
135
+ logWarning("reconcile", `ensureWorkflowDbAtPath could not reopen DB: ${err.message}`);
136
+ return false;
137
+ }
138
+ }
139
+ export function ensureWorkflowDbForBase(basePath, options = {}) {
140
+ const dbPath = expectedWorkflowDbPathForBase(basePath);
141
+ if (!existsSync(dbPath))
142
+ return false;
143
+ try {
144
+ if (options.refresh) {
145
+ if (isDbAvailable() && getWorkflowDatabasePath() === dbPath && refreshWorkflowDatabaseFromDisk()) {
146
+ return true;
147
+ }
148
+ return openWorkflowDatabasePath(dbPath);
149
+ }
150
+ if (isDbAvailable() && getWorkflowDatabasePath() === dbPath)
151
+ return true;
152
+ return openWorkflowDatabasePath(dbPath);
153
+ }
154
+ catch (err) {
155
+ logWarning("reconcile", `ensureWorkflowDbForBase could not reopen DB: ${err.message}`);
156
+ return false;
157
+ }
158
+ }
107
159
  export function checkpointWorkflowDatabase() {
108
160
  checkpointDatabase();
109
161
  }
@@ -11,6 +11,7 @@
11
11
  * - Optional search/tool integrations (Brave, Tavily, Jina, Context7)
12
12
  */
13
13
  import { existsSync, readFileSync } from "node:fs";
14
+ import { access, readFile } from "node:fs/promises";
14
15
  import { delimiter, join } from "node:path";
15
16
  import { AuthStorage } from "@gsd/pi-coding-agent";
16
17
  import { getEnvApiKey } from "@gsd/pi-ai";
@@ -133,15 +134,11 @@ const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
133
134
  "google-gemini-cli",
134
135
  "google-antigravity",
135
136
  ]);
136
- /**
137
- * Check if a CLI provider's binary exists anywhere in PATH.
138
- * Fast filesystem scan — no subprocess, no network, sub-1ms.
139
- */
140
- function isCliBinaryInPath(providerId) {
137
+ let asyncCliBinaryPathCache = null;
138
+ function cliExecutableNames(providerId) {
141
139
  const binaries = CLI_BINARY_MAP[providerId];
142
140
  if (!binaries)
143
- return false;
144
- const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
141
+ return [];
145
142
  // On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
146
143
  // Scan PATHEXT candidates in addition to the bare binary name.
147
144
  const executableNames = [...binaries];
@@ -160,8 +157,45 @@ function isCliBinaryInPath(providerId) {
160
157
  }
161
158
  }
162
159
  }
160
+ return executableNames;
161
+ }
162
+ /**
163
+ * Check if a CLI provider's binary exists anywhere in PATH.
164
+ * Fast filesystem scan — no subprocess, no network, sub-1ms.
165
+ */
166
+ function isCliBinaryInPath(providerId) {
167
+ const cached = asyncCliBinaryPathCache?.get(providerId);
168
+ if (cached !== undefined)
169
+ return cached;
170
+ const executableNames = cliExecutableNames(providerId);
171
+ if (executableNames.length === 0)
172
+ return false;
173
+ const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
163
174
  return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
164
175
  }
176
+ async function isCliBinaryInPathAsync(providerId) {
177
+ const executableNames = cliExecutableNames(providerId);
178
+ if (executableNames.length === 0)
179
+ return false;
180
+ const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
181
+ const candidates = pathDirs.flatMap(dir => executableNames.map(name => join(dir, name)));
182
+ if (candidates.length === 0)
183
+ return false;
184
+ try {
185
+ await Promise.any(candidates.map(candidate => access(candidate)));
186
+ return true;
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ }
192
+ async function loadCliBinaryPathCache() {
193
+ const entries = await Promise.all(Object.keys(CLI_BINARY_MAP).map(async (providerId) => [
194
+ providerId,
195
+ await isCliBinaryInPathAsync(providerId),
196
+ ]));
197
+ return new Map(entries);
198
+ }
165
199
  function modelsJsonPaths() {
166
200
  const home = homedir();
167
201
  return [
@@ -170,7 +204,11 @@ function modelsJsonPaths() {
170
204
  join(home, ".pi", "agent", "models.json"),
171
205
  ];
172
206
  }
207
+ let asyncModelsJsonApiKeyCache = null;
173
208
  function hasModelsJsonApiKey(providerId) {
209
+ if (asyncModelsJsonApiKeyCache) {
210
+ return asyncModelsJsonApiKeyCache.has(providerId);
211
+ }
174
212
  for (const path of modelsJsonPaths()) {
175
213
  if (!existsSync(path))
176
214
  continue;
@@ -187,7 +225,25 @@ function hasModelsJsonApiKey(providerId) {
187
225
  }
188
226
  return false;
189
227
  }
190
- function resolveKey(providerId) {
228
+ async function loadModelsJsonApiKeyCache() {
229
+ const providersWithKeys = new Set();
230
+ for (const path of modelsJsonPaths()) {
231
+ try {
232
+ const parsed = JSON.parse(await readFile(path, "utf-8"));
233
+ for (const [providerId, provider] of Object.entries(parsed.providers ?? {})) {
234
+ const apiKey = provider.apiKey;
235
+ if (typeof apiKey === "string" && apiKey.trim().length > 0) {
236
+ providersWithKeys.add(providerId);
237
+ }
238
+ }
239
+ }
240
+ catch {
241
+ // Missing or malformed models.json should not break dashboard health checks.
242
+ }
243
+ }
244
+ return providersWithKeys;
245
+ }
246
+ function resolveKeyFromAuthOrEnv(providerId) {
191
247
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
192
248
  if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
193
249
  return { found: true, source: "env", backedOff: false };
@@ -234,6 +290,12 @@ function resolveKey(providerId) {
234
290
  if (info?.envVar && process.env[info.envVar]) {
235
291
  return { found: true, source: "env", backedOff: false };
236
292
  }
293
+ return null;
294
+ }
295
+ function resolveKey(providerId) {
296
+ const direct = resolveKeyFromAuthOrEnv(providerId);
297
+ if (direct)
298
+ return direct;
237
299
  if (hasModelsJsonApiKey(providerId)) {
238
300
  return { found: true, source: "models.json", backedOff: false };
239
301
  }
@@ -432,6 +494,28 @@ export function runProviderChecks() {
432
494
  results.push(...checkOptionalProviders());
433
495
  return results;
434
496
  }
497
+ /**
498
+ * Non-blocking equivalent of `runProviderChecks` for the health-widget
499
+ * background refresh. PATH checks and custom models.json discovery use async
500
+ * filesystem APIs so periodic widget refreshes do not stall the input loop.
501
+ */
502
+ export async function runProviderChecksAsync() {
503
+ const [cliCache, modelsJsonCache] = await Promise.all([
504
+ loadCliBinaryPathCache(),
505
+ loadModelsJsonApiKeyCache(),
506
+ ]);
507
+ const previousCliCache = asyncCliBinaryPathCache;
508
+ const previousModelsJsonCache = asyncModelsJsonApiKeyCache;
509
+ asyncCliBinaryPathCache = cliCache;
510
+ asyncModelsJsonApiKeyCache = modelsJsonCache;
511
+ try {
512
+ return runProviderChecks();
513
+ }
514
+ finally {
515
+ asyncCliBinaryPathCache = previousCliCache;
516
+ asyncModelsJsonApiKeyCache = previousModelsJsonCache;
517
+ }
518
+ }
435
519
  /**
436
520
  * Format provider check results as a human-readable report string.
437
521
  */
@@ -180,10 +180,22 @@ export function runExecSandbox(request, opts) {
180
180
  const effectiveGraceMs = opts.kill_grace_ms ?? SIGKILL_GRACE_MS;
181
181
  const effectiveForceResolveDelay = opts.force_resolve_delay_ms ?? (effectiveGraceMs + HARD_DEADLINE_MS);
182
182
  let timedOut = false;
183
+ let aborted = false;
183
184
  let settled = false;
185
+ let killInitiated = false;
186
+ let timer;
184
187
  let forceResolveTimer;
185
- const timer = setTimeout(() => {
186
- timedOut = true;
188
+ let abortListener;
189
+ const removeAbortListener = () => {
190
+ if (opts.signal && abortListener) {
191
+ opts.signal.removeEventListener("abort", abortListener);
192
+ abortListener = undefined;
193
+ }
194
+ };
195
+ const initiateKill = () => {
196
+ if (killInitiated)
197
+ return;
198
+ killInitiated = true;
187
199
  // killProcessTree handles both platforms and kills the whole tree: on Unix
188
200
  // it signals the process group (SIGTERM -> grace -> SIGKILL); on Windows it
189
201
  // force-kills the tree via taskkill /F /T. Using child.kill("SIGTERM") here
@@ -201,14 +213,14 @@ export function runExecSandbox(request, opts) {
201
213
  finalize(null, "SIGKILL", true);
202
214
  }, effectiveForceResolveDelay);
203
215
  forceResolveTimer.unref?.();
204
- }, timeoutMs);
205
- timer.unref?.();
216
+ };
206
217
  const finalize = (exitCode, signal, forceResolved = false) => {
207
218
  if (settled)
208
219
  return;
209
220
  settled = true;
210
221
  clearTimeout(timer);
211
222
  clearTimeout(forceResolveTimer);
223
+ removeAbortListener();
212
224
  const duration = Date.now() - started;
213
225
  const stdoutBuf = Buffer.concat(stdoutChunks);
214
226
  const stderrBuf = Buffer.concat(stderrChunks);
@@ -219,17 +231,20 @@ export function runExecSandbox(request, opts) {
219
231
  const digestBody = tail(stdoutBuf, opts.digest_chars);
220
232
  const digest = digestBody.length > 0
221
233
  ? digestBody
222
- : timedOut
223
- ? "[no stdout — timed out]"
224
- : stderrBuf.length > 0
225
- ? `[no stdout — tail of stderr]\n${tail(stderrBuf, opts.digest_chars)}`
226
- : "[no output]";
234
+ : aborted
235
+ ? "[no stdout — aborted]"
236
+ : timedOut
237
+ ? "[no stdout — timed out]"
238
+ : stderrBuf.length > 0
239
+ ? `[no stdout — tail of stderr]\n${tail(stderrBuf, opts.digest_chars)}`
240
+ : "[no output]";
227
241
  const result = {
228
242
  id,
229
243
  runtime: request.runtime,
230
244
  exit_code: exitCode,
231
245
  signal,
232
246
  timed_out: timedOut,
247
+ aborted,
233
248
  force_resolved: forceResolved,
234
249
  duration_ms: duration,
235
250
  stdout_bytes: stdoutBytes,
@@ -244,6 +259,26 @@ export function runExecSandbox(request, opts) {
244
259
  writeMeta(metaPath, result, request, now);
245
260
  resolveP(result);
246
261
  };
262
+ timer = setTimeout(() => {
263
+ timedOut = true;
264
+ initiateKill();
265
+ }, timeoutMs);
266
+ timer.unref?.();
267
+ if (opts.signal) {
268
+ abortListener = () => {
269
+ if (settled || timedOut)
270
+ return;
271
+ aborted = true;
272
+ clearTimeout(timer);
273
+ initiateKill();
274
+ };
275
+ if (opts.signal.aborted) {
276
+ abortListener();
277
+ }
278
+ else {
279
+ opts.signal.addEventListener("abort", abortListener, { once: true });
280
+ }
281
+ }
247
282
  child.on("error", (err) => {
248
283
  const message = err instanceof Error ? err.message : String(err);
249
284
  const line = `child error: ${message}\n`;
@@ -274,6 +309,7 @@ function writeMeta(path, result, request, now) {
274
309
  exit_code: result.exit_code,
275
310
  signal: result.signal,
276
311
  timed_out: result.timed_out,
312
+ aborted: result.aborted === true,
277
313
  force_resolved: result.force_resolved,
278
314
  duration_ms: result.duration_ms,
279
315
  stdout_bytes: result.stdout_bytes,
@@ -22,8 +22,7 @@ import { deriveState } from "./state.js";
22
22
  import { isAutoActive } from "./auto.js";
23
23
  import { loadPrompt } from "./prompt-loader.js";
24
24
  import { gsdRoot } from "./paths.js";
25
- import { isDbAvailable, getAllMilestones, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
26
- import { isClosedStatus } from "./status-guards.js";
25
+ import { isDbAvailable, getHierarchyCompletionCounts } from "./gsd-db.js";
27
26
  import { formatDuration } from "../shared/format-utils.js";
28
27
  import { getAutoWorktreePath } from "./auto-worktree.js";
29
28
  import { clearGSDPreferencesCache, loadEffectiveGSDPreferences, loadGlobalGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
@@ -593,36 +592,7 @@ function loadCompletedKeys(basePath) {
593
592
  function getDbCompletionCounts() {
594
593
  if (!isDbAvailable())
595
594
  return null;
596
- const milestones = getAllMilestones();
597
- let completedMilestones = 0;
598
- let totalSlices = 0;
599
- let completedSlices = 0;
600
- let totalTasks = 0;
601
- let completedTasks = 0;
602
- for (const m of milestones) {
603
- if (isClosedStatus(m.status))
604
- completedMilestones++;
605
- const slices = getMilestoneSlices(m.id);
606
- for (const s of slices) {
607
- totalSlices++;
608
- if (isClosedStatus(s.status))
609
- completedSlices++;
610
- const tasks = getSliceTasks(m.id, s.id);
611
- for (const t of tasks) {
612
- totalTasks++;
613
- if (isClosedStatus(t.status))
614
- completedTasks++;
615
- }
616
- }
617
- }
618
- return {
619
- milestones: completedMilestones,
620
- milestonesTotal: milestones.length,
621
- slices: completedSlices,
622
- slicesTotal: totalSlices,
623
- tasks: completedTasks,
624
- tasksTotal: totalTasks,
625
- };
595
+ return getHierarchyCompletionCounts();
626
596
  }
627
597
  // ─── Anomaly Detectors ───────────────────────────────────────────────────────
628
598
  /**
@@ -225,6 +225,7 @@ export const RUNTIME_EXCLUSION_PATHS = [
225
225
  ".gsd/event-log.jsonl",
226
226
  ".gsd/DISCUSSION-MANIFEST.json",
227
227
  ];
228
+ const runtimeFilesCleanedUpRepos = new Set();
228
229
  // ─── Integration Branch Metadata ───────────────────────────────────────────
229
230
  /**
230
231
  * Path to the milestone metadata file that stores the integration branch.
@@ -565,7 +566,8 @@ export class GitServiceImpl {
565
566
  // and the worktree is torn down. This prevents a mid-execution behavioral
566
567
  // discontinuity where the first half of a milestone has .gsd/ artifacts
567
568
  // committed but the second half doesn't (#1326).
568
- if (!this._runtimeFilesCleanedUp) {
569
+ const cleanupRepoKey = resolve(this.basePath);
570
+ if (!runtimeFilesCleanedUpRepos.has(cleanupRepoKey)) {
569
571
  let cleaned = false;
570
572
  for (const exclusion of RUNTIME_EXCLUSION_PATHS) {
571
573
  const removed = nativeRmCached(this.basePath, [exclusion]);
@@ -575,7 +577,7 @@ export class GitServiceImpl {
575
577
  if (cleaned) {
576
578
  nativeCommit(this.basePath, "chore: untrack .gsd/ runtime files from git index", { allowEmpty: false });
577
579
  }
578
- this._runtimeFilesCleanedUp = true;
580
+ runtimeFilesCleanedUpRepos.add(cleanupRepoKey);
579
581
  }
580
582
  // Stage everything using pathspec exclusions so excluded paths are never
581
583
  // hashed by git. The old approach of `git add -A` followed by unstaging
@@ -683,8 +685,6 @@ export class GitServiceImpl {
683
685
  return false;
684
686
  }
685
687
  }
686
- /** Tracks whether runtime file cleanup has run this session. */
687
- _runtimeFilesCleanedUp = false;
688
688
  /**
689
689
  * Stage files (smart staging) and commit.
690
690
  * Returns the commit message string on success, or null if nothing to commit.