@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.738160b9

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 (488) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +17 -4
  3. package/dist/headless-answers.js +1 -1
  4. package/dist/headless-ui.d.ts +1 -1
  5. package/dist/headless-ui.js +1 -1
  6. package/dist/headless.js +1 -1
  7. package/dist/resources/.managed-resources-content-hash +1 -1
  8. package/dist/resources/extensions/claude-code-cli/models.js +9 -0
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
  10. package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
  11. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  12. package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
  13. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  14. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
  15. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  16. package/dist/resources/extensions/gsd/auto-budget.js +5 -1
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +34 -20
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
  19. package/dist/resources/extensions/gsd/auto-prompts.js +29 -14
  20. package/dist/resources/extensions/gsd/auto-recovery.js +9 -510
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  23. package/dist/resources/extensions/gsd/auto-verification.js +5 -2
  24. package/dist/resources/extensions/gsd/auto.js +19 -10
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
  26. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +39 -0
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -5
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
  29. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  30. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +41 -21
  31. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
  32. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +96 -10
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
  35. package/dist/resources/extensions/gsd/commands/handlers/auto.js +1 -1
  36. package/dist/resources/extensions/gsd/commands-context.js +19 -1
  37. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +17 -11
  38. package/dist/resources/extensions/gsd/commands-usage.js +1 -1
  39. package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
  40. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
  41. package/dist/resources/extensions/gsd/db/engine.js +4 -1
  42. package/dist/resources/extensions/gsd/db/queries.js +60 -0
  43. package/dist/resources/extensions/gsd/db-memory-fts-schema.js +34 -0
  44. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  45. package/dist/resources/extensions/gsd/delegation-policy.js +1 -1
  46. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  47. package/dist/resources/extensions/gsd/doctor-engine-checks.js +192 -4
  48. package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
  49. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
  50. package/dist/resources/extensions/gsd/exec-sandbox.js +53 -11
  51. package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
  52. package/dist/resources/extensions/gsd/forensics.js +2 -32
  53. package/dist/resources/extensions/gsd/git-service.js +61 -4
  54. package/dist/resources/extensions/gsd/gitignore.js +4 -1
  55. package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
  56. package/dist/resources/extensions/gsd/health-widget.js +55 -29
  57. package/dist/resources/extensions/gsd/json-persistence.js +20 -0
  58. package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
  59. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  60. package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
  61. package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
  62. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
  63. package/dist/resources/extensions/gsd/metrics.js +4 -4
  64. package/dist/resources/extensions/gsd/migrate-external.js +11 -2
  65. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  66. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  67. package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
  68. package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
  69. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  70. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  71. package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  72. package/dist/resources/extensions/gsd/quick.js +45 -2
  73. package/dist/resources/extensions/gsd/root-write-leak-guard.js +7 -1
  74. package/dist/resources/extensions/gsd/safety/destructive-guard.js +8 -1
  75. package/dist/resources/extensions/gsd/safety/evidence-collector.js +16 -1
  76. package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
  77. package/dist/resources/extensions/gsd/session-forensics.js +11 -1
  78. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  79. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  80. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  81. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  82. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  83. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  84. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
  85. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  86. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  87. package/dist/resources/extensions/gsd/state.js +12 -611
  88. package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
  89. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  90. package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
  91. package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
  92. package/dist/resources/extensions/gsd/tools/plan-slice.js +59 -44
  93. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -5
  94. package/dist/resources/extensions/gsd/undo.js +43 -6
  95. package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
  96. package/dist/resources/extensions/gsd/unit-registry.js +36 -6
  97. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
  98. package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
  99. package/dist/resources/extensions/gsd/verification-gate.js +7 -0
  100. package/dist/resources/extensions/gsd/workflow-manifest.js +126 -13
  101. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  102. package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
  103. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  104. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  105. package/dist/web/standalone/.next/BUILD_ID +1 -1
  106. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  107. package/dist/web/standalone/.next/build-manifest.json +3 -3
  108. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  109. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  110. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/index.html +1 -1
  130. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  131. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  132. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  133. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  138. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  139. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  142. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  143. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  144. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  145. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  146. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  147. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  148. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  149. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  151. package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
  152. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  153. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  154. package/package.json +1 -1
  155. package/packages/cloud-mcp-gateway/package.json +2 -2
  156. package/packages/contracts/dist/workflow.d.ts +1 -0
  157. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  158. package/packages/contracts/dist/workflow.js +2 -0
  159. package/packages/contracts/dist/workflow.js.map +1 -1
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  163. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  165. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  166. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  167. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  168. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  169. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  170. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  171. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  172. package/packages/gsd-agent-core/dist/index.js +2 -0
  173. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  174. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  175. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  176. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  177. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  178. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  179. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  180. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  181. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  182. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  183. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  184. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  185. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  186. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  187. package/packages/gsd-agent-core/package.json +5 -5
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  193. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  194. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  195. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  197. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  198. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
  199. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  202. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  203. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  204. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  205. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  206. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  207. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  208. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  209. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  210. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  211. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  212. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  213. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  214. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  215. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  222. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  223. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  224. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  225. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  226. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  227. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  228. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  229. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  230. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  231. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  232. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  233. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  234. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  235. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  236. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts +2 -0
  237. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  238. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +23 -5
  239. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  240. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  242. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  243. package/packages/gsd-agent-modes/package.json +7 -7
  244. package/packages/mcp-server/README.md +1 -1
  245. package/packages/mcp-server/dist/server.d.ts +1 -1
  246. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  247. package/packages/mcp-server/dist/server.js +3 -3
  248. package/packages/mcp-server/dist/server.js.map +1 -1
  249. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  250. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  251. package/packages/mcp-server/dist/workflow-tools.js +35 -21
  252. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  253. package/packages/mcp-server/package.json +4 -4
  254. package/packages/native/package.json +1 -1
  255. package/packages/pi-agent-core/dist/agent-loop.js +3 -2
  256. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  257. package/packages/pi-agent-core/package.json +1 -1
  258. package/packages/pi-ai/dist/models.generated.d.ts +46 -0
  259. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  260. package/packages/pi-ai/dist/models.generated.js +38 -0
  261. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  262. package/packages/pi-ai/package.json +1 -1
  263. package/packages/pi-coding-agent/README.md +3 -2
  264. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  265. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  266. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  267. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  268. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  269. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  270. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  271. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  272. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  273. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  274. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  275. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  276. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  277. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  278. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  279. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  280. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  281. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  283. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  284. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  285. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  286. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  287. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  288. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  289. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  290. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  291. package/packages/pi-coding-agent/package.json +7 -7
  292. package/packages/pi-tui/package.json +2 -2
  293. package/packages/rpc-client/package.json +2 -2
  294. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  295. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  296. package/pkg/dist/theme/theme-schema.js +1 -1
  297. package/pkg/dist/theme/theme-schema.js.map +1 -1
  298. package/pkg/dist/theme/theme.d.ts.map +1 -1
  299. package/pkg/dist/theme/theme.js +11 -7
  300. package/pkg/dist/theme/theme.js.map +1 -1
  301. package/pkg/package.json +1 -1
  302. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  303. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
  304. package/src/resources/extensions/claude-code-cli/tests/index.test.ts +14 -0
  305. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
  306. package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
  307. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  308. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  309. package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
  310. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  311. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
  312. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  313. package/src/resources/extensions/gsd/auto-budget.ts +12 -1
  314. package/src/resources/extensions/gsd/auto-dispatch.ts +35 -24
  315. package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
  316. package/src/resources/extensions/gsd/auto-prompts.ts +41 -15
  317. package/src/resources/extensions/gsd/auto-recovery.ts +13 -511
  318. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  319. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  320. package/src/resources/extensions/gsd/auto-verification.ts +5 -1
  321. package/src/resources/extensions/gsd/auto.ts +23 -8
  322. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
  323. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +44 -0
  324. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +11 -5
  325. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
  326. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  327. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +52 -21
  328. package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
  329. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +104 -10
  330. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  331. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
  332. package/src/resources/extensions/gsd/commands/handlers/auto.ts +1 -1
  333. package/src/resources/extensions/gsd/commands-context.ts +18 -1
  334. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -10
  335. package/src/resources/extensions/gsd/commands-usage.ts +1 -1
  336. package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
  337. package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
  338. package/src/resources/extensions/gsd/db/engine.ts +8 -1
  339. package/src/resources/extensions/gsd/db/queries.ts +79 -0
  340. package/src/resources/extensions/gsd/db-memory-fts-schema.ts +45 -0
  341. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  342. package/src/resources/extensions/gsd/delegation-policy.ts +1 -1
  343. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  344. package/src/resources/extensions/gsd/doctor-engine-checks.ts +233 -5
  345. package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
  346. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
  347. package/src/resources/extensions/gsd/doctor-types.ts +3 -0
  348. package/src/resources/extensions/gsd/exec-sandbox.ts +56 -11
  349. package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
  350. package/src/resources/extensions/gsd/forensics.ts +2 -33
  351. package/src/resources/extensions/gsd/git-service.ts +69 -5
  352. package/src/resources/extensions/gsd/gitignore.ts +4 -1
  353. package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
  354. package/src/resources/extensions/gsd/health-widget.ts +69 -32
  355. package/src/resources/extensions/gsd/json-persistence.ts +20 -0
  356. package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
  357. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  358. package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
  359. package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
  360. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
  361. package/src/resources/extensions/gsd/metrics.ts +4 -4
  362. package/src/resources/extensions/gsd/migrate-external.ts +8 -2
  363. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  364. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  365. package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
  366. package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
  367. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  368. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  369. package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  370. package/src/resources/extensions/gsd/quick.ts +43 -2
  371. package/src/resources/extensions/gsd/root-write-leak-guard.ts +13 -1
  372. package/src/resources/extensions/gsd/safety/destructive-guard.ts +9 -1
  373. package/src/resources/extensions/gsd/safety/evidence-collector.ts +16 -1
  374. package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
  375. package/src/resources/extensions/gsd/session-forensics.ts +11 -1
  376. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  377. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  378. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  379. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  380. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  381. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  382. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
  383. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  384. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  385. package/src/resources/extensions/gsd/state.ts +32 -732
  386. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
  387. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  388. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +14 -5
  389. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +36 -0
  390. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +182 -4
  391. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +146 -1
  392. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +30 -0
  393. package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
  394. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  395. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
  396. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
  397. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
  398. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
  399. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
  400. package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
  401. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
  402. package/src/resources/extensions/gsd/tests/db-memory-fts-schema.test.ts +58 -3
  403. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
  404. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +44 -0
  405. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +143 -0
  406. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
  407. package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
  408. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
  409. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +239 -2
  410. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
  411. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +16 -0
  412. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
  413. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
  414. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
  415. package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
  416. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
  417. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
  418. package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
  419. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +130 -3
  420. package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
  421. package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
  422. package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
  423. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
  424. package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
  425. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
  426. package/src/resources/extensions/gsd/tests/memory-store.test.ts +48 -0
  427. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
  428. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
  429. package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
  430. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +57 -0
  431. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  432. package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
  433. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
  434. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
  435. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -1
  436. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
  437. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
  438. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +103 -4
  439. package/src/resources/extensions/gsd/tests/plan-task.test.ts +44 -1
  440. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +4 -2
  441. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
  442. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  443. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +58 -0
  444. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -6
  445. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
  446. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
  447. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
  448. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +103 -0
  449. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +20 -1
  450. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +15 -3
  451. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  452. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  453. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +208 -0
  454. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +32 -3
  455. package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
  456. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
  457. package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
  458. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +38 -0
  459. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
  460. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
  461. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
  462. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +96 -15
  463. package/src/resources/extensions/gsd/tests/workflow-mcp-preflight.test.ts +1 -0
  464. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +8 -4
  465. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +6 -5
  466. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
  467. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  468. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  469. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  470. package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
  471. package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
  472. package/src/resources/extensions/gsd/tools/plan-slice.ts +60 -47
  473. package/src/resources/extensions/gsd/tools/plan-task.ts +30 -5
  474. package/src/resources/extensions/gsd/undo.ts +47 -6
  475. package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
  476. package/src/resources/extensions/gsd/unit-registry.ts +36 -6
  477. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
  478. package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
  479. package/src/resources/extensions/gsd/verification-gate.ts +8 -0
  480. package/src/resources/extensions/gsd/workflow-manifest.ts +141 -7
  481. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  482. package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
  483. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  484. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  485. package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
  486. package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
  487. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → 2A_WqqCS-EoYZxN9_v3d2}/_buildManifest.js +0 -0
  488. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → 2A_WqqCS-EoYZxN9_v3d2}/_ssgManifest.js +0 -0
@@ -28,9 +28,10 @@ const DEFAULT_CODEBASE_MAX_CHARS = 8_000;
28
28
  const MIN_CONTEXT_MESSAGE_MAX_CHARS = 1_000;
29
29
  const MIN_KNOWLEDGE_MAX_CHARS = 1_000;
30
30
 
31
- const contextMaintenanceCompletedForBasePath = new Set<string>();
32
- const contextMaintenanceInFlightByBasePath = new Map<string, Promise<boolean>>();
33
- const deferredContextMaintenanceByBasePath = new Map<string, Promise<void>>();
31
+ const CONTEXT_MAINTENANCE_KEY_SEPARATOR = "\0";
32
+ const contextMaintenanceCompletedForSession = new Set<string>();
33
+ const contextMaintenanceInFlightBySession = new Map<string, Promise<boolean>>();
34
+ const deferredContextMaintenanceBySession = new Map<string, Promise<void>>();
34
35
 
35
36
  /**
36
37
  * Bundled skill triggers — resolved dynamically at runtime instead of
@@ -114,7 +115,8 @@ async function runSessionStartupMaintenanceOnce(
114
115
  basePath: string,
115
116
  ctx: ExtensionContext,
116
117
  ): Promise<boolean> {
117
- if (contextMaintenanceCompletedForBasePath.has(basePath)) {
118
+ const maintenanceKey = getContextMaintenanceKey(basePath, ctx);
119
+ if (contextMaintenanceCompletedForSession.has(maintenanceKey)) {
118
120
  // Backfills are session-once, but memory queries and other DB-backed
119
121
  // prompt assembly still need an active adapter on every turn.
120
122
  try {
@@ -126,16 +128,16 @@ async function runSessionStartupMaintenanceOnce(
126
128
  return false;
127
129
  }
128
130
 
129
- const existing = contextMaintenanceInFlightByBasePath.get(basePath);
131
+ const existing = contextMaintenanceInFlightBySession.get(maintenanceKey);
130
132
  const isInitiator = !existing;
131
133
  // Use a definite Promise<boolean> so `await inFlight` has a known return type.
132
134
  let inFlight: Promise<boolean>;
133
135
  if (isInitiator) {
134
- inFlight = performSessionStartupMaintenance(basePath, ctx);
135
- contextMaintenanceInFlightByBasePath.set(basePath, inFlight);
136
+ inFlight = performSessionStartupMaintenance(basePath, ctx, maintenanceKey);
137
+ contextMaintenanceInFlightBySession.set(maintenanceKey, inFlight);
136
138
  void inFlight.finally(() => {
137
- if (contextMaintenanceInFlightByBasePath.get(basePath) === inFlight) {
138
- contextMaintenanceInFlightByBasePath.delete(basePath);
139
+ if (contextMaintenanceInFlightBySession.get(maintenanceKey) === inFlight) {
140
+ contextMaintenanceInFlightBySession.delete(maintenanceKey);
139
141
  }
140
142
  });
141
143
  } else {
@@ -149,6 +151,7 @@ async function runSessionStartupMaintenanceOnce(
149
151
  async function performSessionStartupMaintenance(
150
152
  basePath: string,
151
153
  ctx: ExtensionContext,
154
+ maintenanceKey: string,
152
155
  ): Promise<boolean> {
153
156
  // DB-backed memory backfills run below. On a cold session the database file
154
157
  // may exist without an active in-process adapter, so open the canonical
@@ -172,11 +175,40 @@ async function performSessionStartupMaintenance(
172
175
 
173
176
  // Mark session complete before scheduling deferred work so any concurrent
174
177
  // caller that observes the completed state does not re-enter maintenance.
175
- contextMaintenanceCompletedForBasePath.add(basePath);
176
- scheduleDeferredContextMaintenance(basePath);
178
+ contextMaintenanceCompletedForSession.add(maintenanceKey);
179
+ scheduleDeferredContextMaintenance(basePath, maintenanceKey);
177
180
  return true;
178
181
  }
179
182
 
183
+ function getContextMaintenanceKey(basePath: string, ctx: ExtensionContext): string {
184
+ return `${basePath}${CONTEXT_MAINTENANCE_KEY_SEPARATOR}${getContextSessionPart(ctx)}`;
185
+ }
186
+
187
+ function getContextSessionPart(ctx: ExtensionContext): string {
188
+ const sessionManager = (ctx as {
189
+ sessionManager?: {
190
+ getSessionId?: () => string | undefined;
191
+ getSessionFile?: () => string | undefined;
192
+ };
193
+ }).sessionManager;
194
+
195
+ try {
196
+ const sessionId = sessionManager?.getSessionId?.();
197
+ if (typeof sessionId === "string" && sessionId.length > 0) return `id:${sessionId}`;
198
+ } catch (e) {
199
+ logWarning("bootstrap", `session-id fetch failed: ${(e as Error).message}`);
200
+ }
201
+
202
+ try {
203
+ const sessionFile = sessionManager?.getSessionFile?.();
204
+ if (typeof sessionFile === "string" && sessionFile.length > 0) return `file:${sessionFile}`;
205
+ } catch (e) {
206
+ logWarning("bootstrap", `session-file fetch failed: ${(e as Error).message}`);
207
+ }
208
+
209
+ return "process";
210
+ }
211
+
180
212
  async function runDecisionsMemoryBackfill(ctx: ExtensionContext): Promise<void> {
181
213
  // ADR-013 step 5: opportunistic decisions->memories backfill. Idempotent
182
214
  // and best-effort — first run absorbs the existing decisions table into
@@ -210,8 +242,8 @@ async function runKnowledgeMemoryBackfill(
210
242
  }
211
243
  }
212
244
 
213
- function scheduleDeferredContextMaintenance(basePath: string): void {
214
- if (deferredContextMaintenanceByBasePath.has(basePath)) return;
245
+ function scheduleDeferredContextMaintenance(basePath: string, maintenanceKey: string): void {
246
+ if (deferredContextMaintenanceBySession.has(maintenanceKey)) return;
215
247
 
216
248
  const task = new Promise<void>((resolve) => {
217
249
  setTimeout(() => {
@@ -219,10 +251,10 @@ function scheduleDeferredContextMaintenance(basePath: string): void {
219
251
  }, 0);
220
252
  });
221
253
 
222
- deferredContextMaintenanceByBasePath.set(basePath, task);
254
+ deferredContextMaintenanceBySession.set(maintenanceKey, task);
223
255
  void task.finally(() => {
224
- if (deferredContextMaintenanceByBasePath.get(basePath) === task) {
225
- deferredContextMaintenanceByBasePath.delete(basePath);
256
+ if (deferredContextMaintenanceBySession.get(maintenanceKey) === task) {
257
+ deferredContextMaintenanceBySession.delete(maintenanceKey);
226
258
  }
227
259
  });
228
260
  }
@@ -257,8 +289,10 @@ async function reportConsolidationGapsDeferred(basePath: string): Promise<void>
257
289
 
258
290
  export async function _flushDeferredContextMaintenanceForTest(basePath?: string): Promise<void> {
259
291
  const tasks = basePath
260
- ? [deferredContextMaintenanceByBasePath.get(basePath)].filter((task): task is Promise<void> => Boolean(task))
261
- : [...deferredContextMaintenanceByBasePath.values()];
292
+ ? [...deferredContextMaintenanceBySession.entries()]
293
+ .filter(([key]) => key.startsWith(`${basePath}${CONTEXT_MAINTENANCE_KEY_SEPARATOR}`))
294
+ .map(([, task]) => task)
295
+ : [...deferredContextMaintenanceBySession.values()];
262
296
  await Promise.allSettled(tasks);
263
297
  }
264
298
 
@@ -1,18 +1,30 @@
1
1
  /**
2
2
  * Tool-call loop guard.
3
3
  *
4
- * Detects when a model calls the same tool with identical arguments
5
- * repeatedly within a single agent turn. Works in both auto-mode and
6
- * interactive sessions by hooking into the `tool_call` event, which
7
- * fires before execution and can block the call.
4
+ * Detects when a model repeats tool calls within a single Agent Turn.
5
+ * Works in both auto-mode and interactive sessions by hooking into the
6
+ * native engine's `tool_call` event, which fires before execution and can
7
+ * block the call.
8
8
  *
9
- * The guard uses a sliding window: it tracks the last N tool signatures
10
- * and blocks when the same signature appears more than MAX_CONSECUTIVE
11
- * times in a row. Resets on each agent turn (session_start, agent_end)
12
- * and when a different tool call breaks the streak.
9
+ * The guard has two independent checks: a sliding window for identical
10
+ * tool signatures, and a per-tool-name cap for repeated calls with varied
11
+ * arguments. State resets at Agent Turn boundaries (session_start,
12
+ * agent_end) and the identical-signature streak also resets when a
13
+ * different tool call breaks the streak. Block messages instruct the model
14
+ * to stop tooling for the rest of that turn and answer in text.
15
+ *
16
+ * The per-tool-name check (#783 Brief C) tracks call counts within a
17
+ * turn regardless of args, decaying after successful state-mutating tools.
18
+ * This catches improvisation
19
+ * loops where the model attempts the same missing workflow tool through
20
+ * varied surfaces (bash → `node -e` → CLI), each with a different
21
+ * signature, so the identical-args streak never trips, while allowing
22
+ * tool-heavy turns that are making file-mutation progress. Whichever guard
23
+ * trips first blocks.
13
24
  */
14
25
 
15
26
  import { createHash } from "node:crypto";
27
+ import { INHERENTLY_REPEATABLE_TOOL_SET } from "./core-session-tools.js";
16
28
 
17
29
  const MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
18
30
 
@@ -20,11 +32,31 @@ const MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
20
32
  const STRICT_LOOP_TOOLS = new Set(["ask_user_questions"]);
21
33
  const MAX_CONSECUTIVE_STRICT = 1;
22
34
 
35
+ /**
36
+ * Per-turn cap on calls to the SAME tool name, regardless of args (#783).
37
+ *
38
+ * General-purpose execution tools are routinely called many times per turn
39
+ * (touching multiple files, running several commands), so they get a higher
40
+ * ceiling. Everything else — workflow one-shot tools (e.g. gsd_complete_milestone)
41
+ * and any non-allowlisted tool — gets the default cap. The default is generous
42
+ * enough to absorb legitimate retries but catches the reported improvisation
43
+ * loop (~51 calls) well before a cost spike.
44
+ */
45
+ const PER_TOOL_DEFAULT_CAP = 6;
46
+ const PER_TOOL_REPEATABLE_CAP = 15;
47
+ const PER_TOOL_CAP_EXEMPT_TOOLS = new Set(["find", "glob", "grep", "ls", "read", "search_and_read"]);
48
+ const STATE_MUTATING_TOOL_SET = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
49
+
23
50
  let consecutiveCount = 0;
24
51
  let lastSignature = "";
25
52
  let lastToolName = "";
26
53
  let enabled = true;
27
54
 
55
+ /** Per-tool-name call counts within the current turn (#783 Brief C). */
56
+ const perToolCounts = new Map<string, number>();
57
+ let mutationEpoch = 0;
58
+ const perToolLastMutationEpoch = new Map<string, number>();
59
+
28
60
  /** Hash tool name + args into a compact signature for comparison. */
29
61
  function hashToolCall(toolName: string, args: Record<string, unknown>): string {
30
62
  const h = createHash("sha256");
@@ -46,6 +78,13 @@ function hashToolCall(toolName: string, args: Record<string, unknown>): string {
46
78
  *
47
79
  * Returns `{ block: false }` for allowed calls.
48
80
  * Returns `{ block: true, reason }` when the loop threshold is exceeded.
81
+ *
82
+ * Two independent guards run; whichever trips first blocks:
83
+ * 1. Identical-signature streak (MAX_CONSECUTIVE_IDENTICAL_CALLS, strict for
84
+ * ask_user_questions).
85
+ * 2. Per-tool-name cap (PER_TOOL_DEFAULT_CAP / PER_TOOL_REPEATABLE_CAP),
86
+ * independent of args, reset after file-mutation progress — catches
87
+ * improvisation loops (#783).
49
88
  */
50
89
  export function checkToolCallLoop(
51
90
  toolName: string,
@@ -63,6 +102,7 @@ export function checkToolCallLoop(
63
102
  lastToolName = toolName;
64
103
  }
65
104
 
105
+ // ── Guard 1: identical-signature streak ──
66
106
  const threshold = STRICT_LOOP_TOOLS.has(toolName)
67
107
  ? MAX_CONSECUTIVE_STRICT
68
108
  : MAX_CONSECUTIVE_IDENTICAL_CALLS;
@@ -71,22 +111,65 @@ export function checkToolCallLoop(
71
111
  return {
72
112
  block: true,
73
113
  reason:
74
- `Tool loop detected: ${toolName} called ${consecutiveCount} times ` +
114
+ `Tool loop detected (identical args): ${toolName} called ${consecutiveCount} times ` +
75
115
  `with identical arguments. Blocking to prevent infinite loop. ` +
76
- `Try a different approach or modify your arguments.`,
116
+ `Do not retry this tool or call other tools this turn — stop and respond to the user in text.`,
77
117
  count: consecutiveCount,
78
118
  };
79
119
  }
80
120
 
121
+ // ── Guard 2: per-tool-name cap, independent of args (#783 Brief C) ──
122
+ // Catches improvisation loops where the same tool is invoked many times with
123
+ // varied args (e.g. retrying a missing workflow tool via bash/node -e/CLI).
124
+ const priorPerToolCount =
125
+ (perToolLastMutationEpoch.get(toolName) ?? mutationEpoch) < mutationEpoch
126
+ ? 0
127
+ : (perToolCounts.get(toolName) ?? 0);
128
+ const perToolCount = priorPerToolCount + 1;
129
+ perToolCounts.set(toolName, perToolCount);
130
+ perToolLastMutationEpoch.set(toolName, mutationEpoch);
131
+
132
+ // Read-only navigation tools are normal context gathering; Guard 1 still
133
+ // catches true reread loops with identical arguments.
134
+ if (PER_TOOL_CAP_EXEMPT_TOOLS.has(toolName)) {
135
+ return { block: false, count: consecutiveCount };
136
+ }
137
+
138
+ const perToolCap = INHERENTLY_REPEATABLE_TOOL_SET.has(toolName)
139
+ ? PER_TOOL_REPEATABLE_CAP
140
+ : PER_TOOL_DEFAULT_CAP;
141
+
142
+ if (perToolCount > perToolCap) {
143
+ return {
144
+ block: true,
145
+ reason:
146
+ `Tool loop detected (repeated tool): ${toolName} called ${perToolCount} times ` +
147
+ `this turn (cap ${perToolCap}). Blocking to prevent infinite loop. ` +
148
+ `The tool may be unavailable or failing repeatedly. ` +
149
+ `Do not retry this tool or pivot to other tools this turn — stop and respond to the user in text.`,
150
+ count: perToolCount,
151
+ };
152
+ }
153
+
81
154
  return { block: false, count: consecutiveCount };
82
155
  }
83
156
 
157
+ /** Record successful mutation progress so Guard 2 can decay on the next count. */
158
+ export function recordToolCallLoopMutation(toolName: string): void {
159
+ if (!enabled) return;
160
+ if (!STATE_MUTATING_TOOL_SET.has(toolName)) return;
161
+ mutationEpoch++;
162
+ }
163
+
84
164
  /** Reset the guard state. Call at agent turn boundaries. */
85
165
  export function resetToolCallLoopGuard(): void {
86
166
  consecutiveCount = 0;
87
167
  lastSignature = "";
88
168
  lastToolName = "";
89
169
  enabled = true;
170
+ perToolCounts.clear();
171
+ mutationEpoch = 0;
172
+ perToolLastMutationEpoch.clear();
90
173
  }
91
174
 
92
175
  /** Disable the guard (e.g. during shutdown). */
@@ -95,9 +178,20 @@ export function disableToolCallLoopGuard(): void {
95
178
  consecutiveCount = 0;
96
179
  lastSignature = "";
97
180
  lastToolName = "";
181
+ perToolCounts.clear();
182
+ mutationEpoch = 0;
183
+ perToolLastMutationEpoch.clear();
98
184
  }
99
185
 
100
186
  /** Get current consecutive count for diagnostics. */
101
187
  export function getToolCallLoopCount(): number {
102
188
  return consecutiveCount;
103
189
  }
190
+
191
+ /**
192
+ * Get the per-tool-name call count for the current turn (#783 Brief C).
193
+ * Returns 0 for tools not yet called. Diagnostic only.
194
+ */
195
+ export function getToolCallCountForTool(toolName: string): number {
196
+ return perToolCounts.get(toolName) ?? 0;
197
+ }
@@ -180,7 +180,7 @@ function ensureWriteGateSnapshotDirectory(basePath: string): void {
180
180
  mkdirSync(join(gsdPath, "runtime"), { recursive: true });
181
181
  }
182
182
 
183
- function currentWriteGateSnapshot(basePath: string = process.cwd()): WriteGateSnapshot {
183
+ export function currentWriteGateSnapshot(basePath: string = process.cwd()): WriteGateSnapshot {
184
184
  const state = getWriteGateState(basePath);
185
185
  return {
186
186
  verifiedDepthMilestones: [...state.verifiedDepthMilestones].sort(),
@@ -18,7 +18,7 @@ import {
18
18
  getWorkflowDatabasePath,
19
19
  refreshWorkflowDatabaseFromDisk,
20
20
  } from "./db-workspace.js";
21
- import { isClosedStatus } from "./status-guards.js";
21
+ import { isClosedStatus, isDeferredStatus } from "./status-guards.js";
22
22
  import { closeQualityGatesFromEvidence } from "./quality-gate-closure.js";
23
23
  import { insertMilestoneValidationGates } from "./milestone-validation-gates.js";
24
24
  import { relMilestoneFile, resolveSliceFile } from "./paths.js";
@@ -229,6 +229,7 @@ export function checkCloseoutConsistencyGate(
229
229
  }
230
230
 
231
231
  for (const slice of slices) {
232
+ if (isDeferredStatus(slice.status)) continue;
232
233
  if (!isClosedStatus(slice.status)) {
233
234
  return blocked(
234
235
  "slice-open",
@@ -200,7 +200,7 @@ export async function handleAutoCommand(trimmed: string, ctx: ExtensionCommandCo
200
200
  }
201
201
  return true;
202
202
  }
203
- await pauseAuto(ctx, pi);
203
+ await pauseAuto(ctx, pi, undefined, { abortActiveTurn: true });
204
204
  return true;
205
205
  }
206
206
 
@@ -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
  }
@@ -1228,7 +1228,7 @@ async function configureContextCodebase(ctx: ExtensionCommandContext, prefs: Rec
1228
1228
  const maskTurns = await promptInteger(ctx, "Observation mask turns (1–50)", cm.observation_mask_turns, "8");
1229
1229
  if (maskTurns !== undefined && maskTurns !== "clear") cm.observation_mask_turns = maskTurns;
1230
1230
  else if (maskTurns === "clear") delete cm.observation_mask_turns;
1231
- const thresh = await promptNumber(ctx, "Compaction threshold percent (0.5–0.95)", cm.compaction_threshold_percent, "0.60");
1231
+ const thresh = await promptNumber(ctx, "Soft context warning threshold percent (0.5–0.95)", cm.compaction_threshold_percent, "0.60");
1232
1232
  if (thresh !== undefined && thresh !== "clear") cm.compaction_threshold_percent = thresh;
1233
1233
  else if (thresh === "clear") delete cm.compaction_threshold_percent;
1234
1234
  const toolMax = await promptInteger(ctx, "Tool result max chars (200–10000)", cm.tool_result_max_chars, "800");
@@ -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 {
@@ -112,7 +112,7 @@ function formatThresholdLines(): string[] {
112
112
 
113
113
  const compactionThreshold = prefs?.context_management?.compaction_threshold_percent;
114
114
  if (typeof compactionThreshold === "number") {
115
- lines.push(`Compaction: ${Math.round(compactionThreshold * 100)}%`);
115
+ lines.push(`Soft context warning: ${Math.round(compactionThreshold * 100)}%`);
116
116
  }
117
117
 
118
118
  return lines;
@@ -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());
@@ -47,7 +47,11 @@ import {
47
47
  applyMigrationV28MemoryLastHitAt,
48
48
  applyMigrationV29RepositoryTargets,
49
49
  } from "../db-migration-steps.js";
50
- import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "../db-memory-fts-schema.js";
50
+ import {
51
+ isMemoriesFtsAvailableSchema,
52
+ rebuildMemoriesFtsSchemaOnce,
53
+ tryCreateMemoriesFtsSchema,
54
+ } from "../db-memory-fts-schema.js";
51
55
  import { createDbOpenState, type DbOpenPhase } from "../db-open-state.js";
52
56
  import { createRuntimeKvTableV25 } from "../db-runtime-kv-schema.js";
53
57
  import { getCurrentSchemaVersion, recordSchemaVersion } from "../db-schema-metadata.js";
@@ -134,6 +138,9 @@ function initSchema(db: DbAdapter, fileBacked: boolean, dbPath: string | null):
134
138
  }
135
139
 
136
140
  migrateSchema(db, dbPath);
141
+ rebuildMemoriesFtsSchemaOnce(db, {
142
+ onRebuildFailed: (message) => logWarning("db", message),
143
+ });
137
144
  }
138
145
 
139
146
  export function _isLikelyWslDrvFsPathForTest(dbPath: string | null): boolean {