@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
@@ -11,6 +11,7 @@ import { formatPercent, formatTokenCount } from "./metrics.js";
11
11
  import { countTokensSync, type TokenProvider } from "./token-counter.js";
12
12
  import { writeContextChartHtml } from "./context-chart-html.js";
13
13
  import { openInBrowser } from "./export.js";
14
+ import { truncateWithEllipsis } from "../shared/format-utils.js";
14
15
 
15
16
  export interface ContextSectionBreakdown {
16
17
  label: string;
@@ -34,6 +35,7 @@ export interface ContextBreakdownReport {
34
35
  subagentSpawns: number;
35
36
  }
36
37
 
38
+ const REDACTED_TOOL_ARGUMENT_KEYS = new Set(["content", "oldText", "newText"]);
37
39
 
38
40
  function resolveProvider(provider: string | undefined): TokenProvider {
39
41
  const normalized = (provider ?? "unknown").toLowerCase();
@@ -206,6 +208,21 @@ export function parseSystemPromptSections(systemPrompt: string, provider: TokenP
206
208
  return sections;
207
209
  }
208
210
 
211
+ function redactToolCallArguments(value: unknown): unknown {
212
+ if (Array.isArray(value)) return value.map(redactToolCallArguments);
213
+ if (!value || typeof value !== "object") return value;
214
+
215
+ const safe: Record<string, unknown> = {};
216
+ for (const [key, child] of Object.entries(value)) {
217
+ if (REDACTED_TOOL_ARGUMENT_KEYS.has(key)) {
218
+ safe[key] = typeof child === "string" ? truncateWithEllipsis(child, 101) : "[redacted]";
219
+ } else {
220
+ safe[key] = redactToolCallArguments(child);
221
+ }
222
+ }
223
+ return safe;
224
+ }
225
+
209
226
  function messageToText(message: SessionMessageEntry["message"]): string {
210
227
  const role = message.role;
211
228
 
@@ -220,7 +237,7 @@ function messageToText(message: SessionMessageEntry["message"]): string {
220
237
  if (typed.type === "thinking" && typed.thinking) parts.push(typed.thinking);
221
238
  if (typed.type === "toolCall") {
222
239
  parts.push(typed.name ?? "tool");
223
- parts.push(JSON.stringify(typed.arguments ?? {}));
240
+ parts.push(JSON.stringify(redactToolCallArguments(typed.arguments ?? {})));
224
241
  }
225
242
  }
226
243
  }
@@ -1724,17 +1724,22 @@ export function serializePreferencesToFrontmatter(prefs: Record<string, unknown>
1724
1724
  const entries = Object.entries(item as Record<string, unknown>);
1725
1725
  if (entries.length > 0) {
1726
1726
  const [firstKey, firstVal] = entries[0];
1727
- lines.push(`${prefix} - ${firstKey}: ${yamlSafeString(firstVal)}`);
1727
+ if (Array.isArray(firstVal)) {
1728
+ lines.push(`${prefix} - ${firstKey}:`);
1729
+ for (const arrItem of firstVal) {
1730
+ lines.push(`${prefix} - ${yamlSafeString(arrItem)}`);
1731
+ }
1732
+ } else if (typeof firstVal === "object" && firstVal !== null) {
1733
+ lines.push(`${prefix} - ${firstKey}:`);
1734
+ for (const [k, v] of Object.entries(firstVal as Record<string, unknown>)) {
1735
+ serializeValue(k, v, indent + 3);
1736
+ }
1737
+ } else {
1738
+ lines.push(`${prefix} - ${firstKey}: ${yamlSafeString(firstVal)}`);
1739
+ }
1728
1740
  for (let i = 1; i < entries.length; i++) {
1729
1741
  const [k, v] = entries[i];
1730
- if (Array.isArray(v)) {
1731
- lines.push(`${prefix} ${k}:`);
1732
- for (const arrItem of v) {
1733
- lines.push(`${prefix} - ${yamlSafeString(arrItem)}`);
1734
- }
1735
- } else {
1736
- lines.push(`${prefix} ${k}: ${yamlSafeString(v)}`);
1737
- }
1742
+ serializeValue(k, v, indent + 2);
1738
1743
  }
1739
1744
  }
1740
1745
  } else {
@@ -42,9 +42,9 @@ export interface WorktreeStatus {
42
42
 
43
43
  // ─── Status helper ─────────────────────────────────────────────────────────
44
44
 
45
- function getStatus(basePath: string, name: string, wtPath: string): WorktreeStatus {
46
- const diff = diffWorktreeAll(basePath, name);
47
- const numstat = diffWorktreeNumstat(basePath, name);
45
+ function getStatus(basePath: string, name: string, wtPath: string, mainBranch: string): WorktreeStatus {
46
+ const diff = diffWorktreeAll(basePath, name, undefined, mainBranch);
47
+ const numstat = diffWorktreeNumstat(basePath, name, undefined, mainBranch);
48
48
  const filesChanged = diff.added.length + diff.modified.length + diff.removed.length;
49
49
  let linesAdded = 0;
50
50
  let linesRemoved = 0;
@@ -62,8 +62,7 @@ function getStatus(basePath: string, name: string, wtPath: string): WorktreeStat
62
62
 
63
63
  let commits = 0;
64
64
  try {
65
- const main = nativeDetectMainBranch(basePath);
66
- commits = nativeCommitCountBetween(basePath, main, worktreeBranchName(name));
65
+ commits = nativeCommitCountBetween(basePath, mainBranch, worktreeBranchName(name));
67
66
  } catch {
68
67
  // commit count unavailable → leave at 0
69
68
  }
@@ -129,7 +128,8 @@ export function formatCleanKeepReason(status: WorktreeStatus): string {
129
128
  async function handleList(ctx: ExtensionCommandContext): Promise<void> {
130
129
  const basePath = projectRoot();
131
130
  const worktrees = listWorktrees(basePath);
132
- const statuses = worktrees.map((wt) => getStatus(basePath, wt.name, wt.path));
131
+ const mainBranch = worktrees.length > 0 ? nativeDetectMainBranch(basePath) : "";
132
+ const statuses = worktrees.map((wt) => getStatus(basePath, wt.name, wt.path, mainBranch));
133
133
  ctx.ui.notify(formatWorktreeList(statuses), "info");
134
134
  }
135
135
 
@@ -161,7 +161,8 @@ async function handleMerge(args: string, ctx: ExtensionCommandContext): Promise<
161
161
  return;
162
162
  }
163
163
 
164
- const status = getStatus(basePath, target, wt.path);
164
+ const mainBranch = nativeDetectMainBranch(basePath);
165
+ const status = getStatus(basePath, target, wt.path, mainBranch);
165
166
  if (status.filesChanged === 0 && !status.uncommitted) {
166
167
  try {
167
168
  removeWorktree(basePath, target, { deleteBranch: true });
@@ -194,7 +195,6 @@ async function handleMerge(args: string, ctx: ExtensionCommandContext): Promise<
194
195
  }
195
196
 
196
197
  const commitType = inferCommitType(target);
197
- const mainBranch = nativeDetectMainBranch(basePath);
198
198
  const commitMessage = `${commitType}: merge worktree ${target}\n\nGSD-Worktree: ${target}`;
199
199
 
200
200
  try {
@@ -250,8 +250,9 @@ async function handleClean(ctx: ExtensionCommandContext): Promise<void> {
250
250
 
251
251
  const removed: string[] = [];
252
252
  const kept: string[] = [];
253
+ const mainBranch = nativeDetectMainBranch(basePath);
253
254
  for (const wt of worktrees) {
254
- const status = getStatus(basePath, wt.name, wt.path);
255
+ const status = getStatus(basePath, wt.name, wt.path, mainBranch);
255
256
  if (status.filesChanged === 0 && !status.uncommitted) {
256
257
  try {
257
258
  removeWorktree(basePath, wt.name, { deleteBranch: true });
@@ -298,7 +299,8 @@ async function handleRemove(args: string, ctx: ExtensionCommandContext): Promise
298
299
  return;
299
300
  }
300
301
 
301
- const status = getStatus(basePath, name, wt.path);
302
+ const mainBranch = nativeDetectMainBranch(basePath);
303
+ const status = getStatus(basePath, name, wt.path, mainBranch);
302
304
  if ((status.filesChanged > 0 || status.uncommitted) && !force) {
303
305
  ctx.ui.notify(
304
306
  [
@@ -29,7 +29,7 @@ import { getWorkerBatches, hasActiveWorkers, type WorkerEntry } from "../subagen
29
29
  import { formatDuration, padRight, joinColumns, centerLine, fitColumns, STATUS_GLYPH, STATUS_COLOR } from "../shared/mod.js";
30
30
  import { estimateTimeRemaining } from "./auto-dashboard.js";
31
31
  import { computeProgressScore, formatProgressLine } from "./progress-score.js";
32
- import { runEnvironmentChecks, type EnvironmentCheckResult } from "./doctor-environment.js";
32
+ import { runEnvironmentChecksAsync, type EnvironmentCheckResult } from "./doctor-environment.js";
33
33
  import { formattedShortcutPair } from "./shortcut-defs.js";
34
34
  import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
35
35
 
@@ -71,6 +71,9 @@ export class GSDDashboardOverlay {
71
71
  private loading = true;
72
72
  private loadedDashboardIdentity?: string;
73
73
  private refreshInFlight: Promise<void> | null = null;
74
+ private envRefreshInFlight: Promise<void> | null = null;
75
+ private cachedEnvBasePath?: string;
76
+ private cachedEnvIssues: EnvironmentCheckResult[] = [];
74
77
  private disposed = false;
75
78
  private resizeHandler: (() => void) | null = null;
76
79
  private cachedMetrics: {
@@ -181,12 +184,39 @@ export class GSDDashboardOverlay {
181
184
  this.loading = false;
182
185
  }
183
186
 
187
+ this.scheduleEnvironmentRefresh(this.dashData.basePath || process.cwd());
188
+
184
189
  if (identityChanged) {
185
190
  this.invalidate();
186
191
  }
187
192
  this.tui.requestRender();
188
193
  }
189
194
 
195
+ private scheduleEnvironmentRefresh(basePath: string): void {
196
+ if (this.cachedEnvBasePath !== basePath) {
197
+ this.cachedEnvBasePath = basePath;
198
+ this.cachedEnvIssues = [];
199
+ this.invalidate();
200
+ }
201
+ if (this.envRefreshInFlight || this.disposed) return;
202
+ this.envRefreshInFlight = this.refreshEnvironmentHealth(basePath)
203
+ .finally(() => {
204
+ this.envRefreshInFlight = null;
205
+ });
206
+ }
207
+
208
+ private async refreshEnvironmentHealth(basePath: string): Promise<void> {
209
+ try {
210
+ const envResults = await runEnvironmentChecksAsync(basePath);
211
+ if (this.disposed || this.cachedEnvBasePath !== basePath) return;
212
+ this.cachedEnvIssues = envResults.filter(r => r.status !== "ok");
213
+ this.invalidate();
214
+ this.tui.requestRender();
215
+ } catch {
216
+ // Non-fatal — keep last known environment issues
217
+ }
218
+ }
219
+
190
220
  private async loadData(): Promise<boolean> {
191
221
  const base = this.dashData.basePath || process.cwd();
192
222
  try {
@@ -629,8 +659,7 @@ export class GSDDashboardOverlay {
629
659
  }
630
660
 
631
661
  // Environment health section (#1221) — only show issues
632
- const envResults = runEnvironmentChecks(this.dashData.basePath || process.cwd());
633
- const envIssues = envResults.filter(r => r.status !== "ok");
662
+ const envIssues = this.cachedEnvIssues;
634
663
  if (envIssues.length > 0) {
635
664
  lines.push(blank());
636
665
  lines.push(hr());
@@ -28,6 +28,7 @@ import {
28
28
  import { rowToGate } from "../db-gate-rows.js";
29
29
  import { rowToArtifact, rowToMilestone, type ArtifactRow, type MilestoneRow } from "../db-milestone-artifact-rows.js";
30
30
  import { rowToSlice, rowToTask, type SliceRow, type TaskRow } from "../db-task-slice-rows.js";
31
+ import { TERMINAL_STATUS_SQL } from "./sql-constants.js";
31
32
 
32
33
 
33
34
  function parseStringArrayColumn(raw: unknown): string[] {
@@ -49,6 +50,59 @@ function normalizeRepoPath(file: string): string {
49
50
  return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
50
51
  }
51
52
 
53
+ export interface HierarchyCompletionCounts {
54
+ milestones: number;
55
+ milestonesTotal: number;
56
+ slices: number;
57
+ slicesTotal: number;
58
+ tasks: number;
59
+ tasksTotal: number;
60
+ }
61
+
62
+ function numberColumn(row: Record<string, unknown> | undefined, column: string): number {
63
+ const value = row?.[column];
64
+ if (typeof value === "number") return value;
65
+ if (typeof value === "bigint") return Number(value);
66
+ if (typeof value === "string") {
67
+ const parsed = Number(value);
68
+ return Number.isFinite(parsed) ? parsed : 0;
69
+ }
70
+ return 0;
71
+ }
72
+
73
+ function getCompletionCount(table: "milestones" | "slices" | "tasks"): { completed: number; total: number } {
74
+ const row = getDbOrNull()!.prepare(
75
+ `SELECT
76
+ COUNT(*) AS total,
77
+ COALESCE(SUM(CASE WHEN status IN (${TERMINAL_STATUS_SQL}) THEN 1 ELSE 0 END), 0) AS completed
78
+ FROM ${table}`,
79
+ ).get();
80
+
81
+ return {
82
+ completed: numberColumn(row, "completed"),
83
+ total: numberColumn(row, "total"),
84
+ };
85
+ }
86
+
87
+ export function getHierarchyCompletionCounts(): HierarchyCompletionCounts {
88
+ if (!getDbOrNull()!) {
89
+ return { milestones: 0, milestonesTotal: 0, slices: 0, slicesTotal: 0, tasks: 0, tasksTotal: 0 };
90
+ }
91
+
92
+ const milestones = getCompletionCount("milestones");
93
+ const slices = getCompletionCount("slices");
94
+ const tasks = getCompletionCount("tasks");
95
+
96
+ return {
97
+ milestones: milestones.completed,
98
+ milestonesTotal: milestones.total,
99
+ slices: slices.completed,
100
+ slicesTotal: slices.total,
101
+ tasks: tasks.completed,
102
+ tasksTotal: tasks.total,
103
+ };
104
+ }
105
+
52
106
  export function getDecisionById(id: string): Decision | null {
53
107
  if (!getDbOrNull()!) return null;
54
108
  const row = getDbOrNull()!.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
@@ -490,6 +544,31 @@ export function getAssessment(path: string): Record<string, unknown> | null {
490
544
  return row ?? null;
491
545
  }
492
546
 
547
+ /**
548
+ * Look up a slice's `run-uat` assessment by (milestoneId, sliceId) identity,
549
+ * independent of the artifact `path`. Used as a DB fallback by the UAT
550
+ * closeout gate when a path migration orphans the ASSESSMENT markdown from its
551
+ * canonical expected path (ADR-017: DB-authoritative UAT sign-off).
552
+ *
553
+ * `status` holds the normalized verdict (`pass`/`fail`/…) written by
554
+ * `executeUatResultSave`; `fullContent` carries the ASSESSMENT body so callers
555
+ * can derive `uatType` without re-reading a file that may not exist.
556
+ */
557
+ export function getSliceRunUatAssessment(
558
+ milestoneId: string,
559
+ sliceId: string,
560
+ ): { status: string; fullContent: string } | null {
561
+ if (!getDbOrNull()!) return null;
562
+ const row = getDbOrNull()!.prepare(
563
+ `SELECT status, full_content AS fullContent FROM assessments
564
+ WHERE milestone_id = :mid AND slice_id = :sid AND scope = 'run-uat'
565
+ ORDER BY created_at DESC, ROWID DESC
566
+ LIMIT 1`,
567
+ ).get({ ":mid": milestoneId, ":sid": sliceId });
568
+ if (!row) return null;
569
+ return { status: String(row["status"] ?? ""), fullContent: String(row["fullContent"] ?? "") };
570
+ }
571
+
493
572
  export function getLatestAssessmentByScope(
494
573
  milestoneId: string,
495
574
  scope: string,
@@ -2,7 +2,7 @@
2
2
  // File Purpose: Workspace-facing Interface for opening and maintaining the workflow database.
3
3
 
4
4
  import { existsSync } from "node:fs";
5
- import { dirname } from "node:path";
5
+ import { dirname, join } from "node:path";
6
6
 
7
7
  import type { GsdWorkspace, MilestoneScope } from "./workspace.js";
8
8
  import type { DbAdapter } from "./db-adapter.js";
@@ -24,8 +24,8 @@ import {
24
24
  vacuumDatabase,
25
25
  wasDbOpenAttempted,
26
26
  } from "./gsd-db.js";
27
- import { resolveGsdPathContract } from "./paths.js";
28
- import { setLogBasePath } from "./workflow-logger.js";
27
+ import { resolveGsdPathContract, gsdRoot } from "./paths.js";
28
+ import { logWarning, setLogBasePath } from "./workflow-logger.js";
29
29
 
30
30
  export interface WorkflowDatabaseLocation {
31
31
  projectRoot: string;
@@ -56,6 +56,20 @@ export type WorkflowDatabaseOpenResult =
56
56
  export type WorkflowDatabaseStatus = ReturnType<typeof getDbStatus>;
57
57
  export type WorkflowDatabaseProvider = ReturnType<typeof getDbProvider>;
58
58
 
59
+ /**
60
+ * Global SQLite handle invariants:
61
+ *
62
+ * - `openWorkflowDatabase` / `openDatabase` switch the process-global handle consumed by
63
+ * deriveState, dispatch, reconciliation repairs, and domain writers. Only one active
64
+ * project database should own the global handle at a time.
65
+ * - `openWorkflowDatabaseIsolated` opens a caller-owned connection that does not clobber
66
+ * the global handle. Use for read-only observers (parallel monitor) and other background
67
+ * probes that must not disturb the active workflow session.
68
+ * - Reconciliation repairs that write markdown/DB state must use `ensureWorkflowDbForBase`
69
+ * so repairs target the correct project; those paths intentionally re-open the global handle.
70
+ * - Pair ad-hoc project switches with `closeWorkflowDatabase()` or restore via
71
+ * `ensureWorkflowDbForBase(..., { refresh: true })` before returning to derive/dispatch.
72
+ */
59
73
  export function resolveWorkflowDatabaseLocation(basePath: string): WorkflowDatabaseLocation {
60
74
  const contract = resolveGsdPathContract(basePath);
61
75
  return {
@@ -172,6 +186,50 @@ export function refreshWorkflowDatabaseFromDisk(): boolean {
172
186
  return refreshOpenDatabaseFromDisk();
173
187
  }
174
188
 
189
+ export function expectedWorkflowDbPathForBase(basePath: string): string {
190
+ return join(gsdRoot(basePath), "gsd.db");
191
+ }
192
+
193
+ export interface EnsureWorkflowDbOptions {
194
+ /** When true, refresh from disk before reopening if already open on the correct path. */
195
+ refresh?: boolean;
196
+ }
197
+
198
+ export function ensureWorkflowDbAtPath(dbPath: string | null): boolean {
199
+ if (!dbPath || dbPath === ":memory:") return isDbAvailable();
200
+ if (isDbAvailable() && getWorkflowDatabasePath() === dbPath) return true;
201
+ if (!existsSync(dbPath)) return false;
202
+ try {
203
+ return openWorkflowDatabasePath(dbPath);
204
+ } catch (err) {
205
+ logWarning("reconcile", `ensureWorkflowDbAtPath could not reopen DB: ${(err as Error).message}`);
206
+ return false;
207
+ }
208
+ }
209
+
210
+ export function ensureWorkflowDbForBase(
211
+ basePath: string,
212
+ options: EnsureWorkflowDbOptions = {},
213
+ ): boolean {
214
+ const dbPath = expectedWorkflowDbPathForBase(basePath);
215
+ if (!existsSync(dbPath)) return false;
216
+
217
+ try {
218
+ if (options.refresh) {
219
+ if (isDbAvailable() && getWorkflowDatabasePath() === dbPath && refreshWorkflowDatabaseFromDisk()) {
220
+ return true;
221
+ }
222
+ return openWorkflowDatabasePath(dbPath);
223
+ }
224
+
225
+ if (isDbAvailable() && getWorkflowDatabasePath() === dbPath) return true;
226
+ return openWorkflowDatabasePath(dbPath);
227
+ } catch (err) {
228
+ logWarning("reconcile", `ensureWorkflowDbForBase could not reopen DB: ${(err as Error).message}`);
229
+ return false;
230
+ }
231
+ }
232
+
175
233
  export function checkpointWorkflowDatabase(): void {
176
234
  checkpointDatabase();
177
235
  }
@@ -12,6 +12,7 @@
12
12
  */
13
13
 
14
14
  import { existsSync, readFileSync } from "node:fs";
15
+ import { access, readFile } from "node:fs/promises";
15
16
  import { delimiter, join } from "node:path";
16
17
  import { AuthStorage } from "@gsd/pi-coding-agent";
17
18
  import { getEnvApiKey } from "@gsd/pi-ai";
@@ -166,15 +167,11 @@ const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
166
167
  "google-antigravity",
167
168
  ]);
168
169
 
169
- /**
170
- * Check if a CLI provider's binary exists anywhere in PATH.
171
- * Fast filesystem scan — no subprocess, no network, sub-1ms.
172
- */
173
- function isCliBinaryInPath(providerId: string): boolean {
174
- const binaries = CLI_BINARY_MAP[providerId];
175
- if (!binaries) return false;
170
+ let asyncCliBinaryPathCache: Map<string, boolean> | null = null;
176
171
 
177
- const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
172
+ function cliExecutableNames(providerId: string): string[] {
173
+ const binaries = CLI_BINARY_MAP[providerId];
174
+ if (!binaries) return [];
178
175
 
179
176
  // On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
180
177
  // Scan PATHEXT candidates in addition to the bare binary name.
@@ -196,9 +193,51 @@ function isCliBinaryInPath(providerId: string): boolean {
196
193
  }
197
194
  }
198
195
 
196
+ return executableNames;
197
+ }
198
+
199
+ /**
200
+ * Check if a CLI provider's binary exists anywhere in PATH.
201
+ * Fast filesystem scan — no subprocess, no network, sub-1ms.
202
+ */
203
+ function isCliBinaryInPath(providerId: string): boolean {
204
+ const cached = asyncCliBinaryPathCache?.get(providerId);
205
+ if (cached !== undefined) return cached;
206
+
207
+ const executableNames = cliExecutableNames(providerId);
208
+ if (executableNames.length === 0) return false;
209
+
210
+ const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
211
+
199
212
  return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
200
213
  }
201
214
 
215
+ async function isCliBinaryInPathAsync(providerId: string): Promise<boolean> {
216
+ const executableNames = cliExecutableNames(providerId);
217
+ if (executableNames.length === 0) return false;
218
+
219
+ const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
220
+ const candidates = pathDirs.flatMap(dir => executableNames.map(name => join(dir, name)));
221
+ if (candidates.length === 0) return false;
222
+
223
+ try {
224
+ await Promise.any(candidates.map(candidate => access(candidate)));
225
+ return true;
226
+ } catch {
227
+ return false;
228
+ }
229
+ }
230
+
231
+ async function loadCliBinaryPathCache(): Promise<Map<string, boolean>> {
232
+ const entries = await Promise.all(
233
+ Object.keys(CLI_BINARY_MAP).map(async providerId => [
234
+ providerId,
235
+ await isCliBinaryInPathAsync(providerId),
236
+ ] as const),
237
+ );
238
+ return new Map(entries);
239
+ }
240
+
202
241
  function modelsJsonPaths(): string[] {
203
242
  const home = homedir();
204
243
  return [
@@ -208,7 +247,13 @@ function modelsJsonPaths(): string[] {
208
247
  ];
209
248
  }
210
249
 
250
+ let asyncModelsJsonApiKeyCache: Set<string> | null = null;
251
+
211
252
  function hasModelsJsonApiKey(providerId: string): boolean {
253
+ if (asyncModelsJsonApiKeyCache) {
254
+ return asyncModelsJsonApiKeyCache.has(providerId);
255
+ }
256
+
212
257
  for (const path of modelsJsonPaths()) {
213
258
  if (!existsSync(path)) continue;
214
259
  try {
@@ -226,7 +271,27 @@ function hasModelsJsonApiKey(providerId: string): boolean {
226
271
  return false;
227
272
  }
228
273
 
229
- function resolveKey(providerId: string): KeyLookup {
274
+ async function loadModelsJsonApiKeyCache(): Promise<Set<string>> {
275
+ const providersWithKeys = new Set<string>();
276
+ for (const path of modelsJsonPaths()) {
277
+ try {
278
+ const parsed = JSON.parse(await readFile(path, "utf-8")) as {
279
+ providers?: Record<string, { apiKey?: unknown }>;
280
+ };
281
+ for (const [providerId, provider] of Object.entries(parsed.providers ?? {})) {
282
+ const apiKey = provider.apiKey;
283
+ if (typeof apiKey === "string" && apiKey.trim().length > 0) {
284
+ providersWithKeys.add(providerId);
285
+ }
286
+ }
287
+ } catch {
288
+ // Missing or malformed models.json should not break dashboard health checks.
289
+ }
290
+ }
291
+ return providersWithKeys;
292
+ }
293
+
294
+ function resolveKeyFromAuthOrEnv(providerId: string): KeyLookup | null {
230
295
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
231
296
 
232
297
  if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
@@ -276,6 +341,13 @@ function resolveKey(providerId: string): KeyLookup {
276
341
  return { found: true, source: "env", backedOff: false };
277
342
  }
278
343
 
344
+ return null;
345
+ }
346
+
347
+ function resolveKey(providerId: string): KeyLookup {
348
+ const direct = resolveKeyFromAuthOrEnv(providerId);
349
+ if (direct) return direct;
350
+
279
351
  if (hasModelsJsonApiKey(providerId)) {
280
352
  return { found: true, source: "models.json", backedOff: false };
281
353
  }
@@ -495,6 +567,28 @@ export function runProviderChecks(): ProviderCheckResult[] {
495
567
  return results;
496
568
  }
497
569
 
570
+ /**
571
+ * Non-blocking equivalent of `runProviderChecks` for the health-widget
572
+ * background refresh. PATH checks and custom models.json discovery use async
573
+ * filesystem APIs so periodic widget refreshes do not stall the input loop.
574
+ */
575
+ export async function runProviderChecksAsync(): Promise<ProviderCheckResult[]> {
576
+ const [cliCache, modelsJsonCache] = await Promise.all([
577
+ loadCliBinaryPathCache(),
578
+ loadModelsJsonApiKeyCache(),
579
+ ]);
580
+ const previousCliCache = asyncCliBinaryPathCache;
581
+ const previousModelsJsonCache = asyncModelsJsonApiKeyCache;
582
+ asyncCliBinaryPathCache = cliCache;
583
+ asyncModelsJsonApiKeyCache = modelsJsonCache;
584
+ try {
585
+ return runProviderChecks();
586
+ } finally {
587
+ asyncCliBinaryPathCache = previousCliCache;
588
+ asyncModelsJsonApiKeyCache = previousModelsJsonCache;
589
+ }
590
+ }
591
+
498
592
  /**
499
593
  * Format provider check results as a human-readable report string.
500
594
  */