gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714

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 (429) hide show
  1. package/README.md +1 -1
  2. package/dist/claude-cli-check.js +5 -1
  3. package/dist/headless.js +49 -4
  4. package/dist/resource-loader.d.ts +40 -0
  5. package/dist/resource-loader.js +32 -13
  6. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  7. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  8. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  9. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  10. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  11. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +5 -1
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/gsd/auto/loop.js +43 -0
  15. package/dist/resources/extensions/gsd/auto/phases.js +15 -21
  16. package/dist/resources/extensions/gsd/auto/session.js +0 -2
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
  18. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  19. package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
  20. package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
  21. package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
  22. package/dist/resources/extensions/gsd/auto-start.js +34 -24
  23. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  24. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  25. package/dist/resources/extensions/gsd/auto.js +31 -20
  26. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  28. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  29. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  30. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  31. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  32. package/dist/resources/extensions/gsd/component-types.js +69 -0
  33. package/dist/resources/extensions/gsd/detection.js +49 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  35. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  36. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  37. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  38. package/dist/resources/extensions/gsd/git-service.js +126 -2
  39. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  40. package/dist/resources/extensions/gsd/guided-flow.js +17 -5
  41. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  42. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  43. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  44. package/dist/resources/extensions/gsd/model-router.js +6 -0
  45. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  46. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  47. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  48. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  49. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  50. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  51. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  52. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  53. package/dist/resources/extensions/gsd/state.js +44 -33
  54. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  55. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  56. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  57. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  59. package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
  60. package/dist/resources/extensions/mcp-client/index.js +3 -1
  61. package/dist/resources/extensions/ollama/index.js +5 -1
  62. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  63. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  64. package/dist/web/standalone/.next/BUILD_ID +1 -1
  65. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  66. package/dist/web/standalone/.next/build-manifest.json +2 -2
  67. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/index.html +1 -1
  86. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  93. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  94. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  97. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  98. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  99. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  100. package/package.json +2 -3
  101. package/packages/daemon/src/logger.ts +4 -3
  102. package/packages/mcp-server/dist/server.d.ts +24 -0
  103. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  104. package/packages/mcp-server/dist/server.js +88 -87
  105. package/packages/mcp-server/dist/server.js.map +1 -1
  106. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  107. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  108. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  109. package/packages/mcp-server/src/server.ts +131 -105
  110. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  111. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  112. package/packages/native/package.json +1 -1
  113. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  114. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  115. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  116. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  117. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  118. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  119. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  120. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  121. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  123. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  124. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  125. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  127. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  128. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  129. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  130. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  131. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  133. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  135. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  136. package/packages/pi-ai/dist/models.test.js +36 -11
  137. package/packages/pi-ai/dist/models.test.js.map +1 -1
  138. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  139. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  140. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  141. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  142. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  143. package/packages/pi-ai/src/models.test.ts +48 -11
  144. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  145. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  146. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  148. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  150. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  152. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  154. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  156. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  158. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  160. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  163. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  165. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  167. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  169. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  170. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  171. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  173. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  175. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/index.js +1 -0
  177. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  191. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  195. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  196. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  197. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  198. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  199. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  200. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  201. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  202. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  203. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  204. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  205. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  206. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  207. package/packages/pi-coding-agent/src/index.ts +1 -0
  208. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  213. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  214. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  215. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  216. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  217. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  218. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  219. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  220. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  221. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  222. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  223. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  224. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  225. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  226. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  227. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  228. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  229. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  230. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  231. package/packages/pi-tui/dist/components/editor.js +19 -0
  232. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  233. package/packages/pi-tui/dist/components/image.test.js +6 -5
  234. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  235. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  236. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  237. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  239. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  240. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  241. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  242. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  243. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  244. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  245. package/packages/pi-tui/src/components/editor.ts +22 -0
  246. package/packages/pi-tui/src/components/image.test.ts +10 -5
  247. package/packages/pi-tui/src/editor-component.ts +3 -0
  248. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  249. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  250. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  251. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  252. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  253. package/scripts/install.js +15 -1
  254. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  255. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  256. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  257. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  258. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  259. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  260. package/src/resources/extensions/claude-code-cli/readiness.ts +5 -1
  261. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  262. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  263. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  264. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  265. package/src/resources/extensions/gsd/auto/loop.ts +47 -0
  266. package/src/resources/extensions/gsd/auto/phases.ts +16 -20
  267. package/src/resources/extensions/gsd/auto/session.ts +0 -2
  268. package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
  269. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  270. package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
  271. package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
  272. package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
  273. package/src/resources/extensions/gsd/auto-start.ts +54 -6
  274. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  275. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  276. package/src/resources/extensions/gsd/auto.ts +43 -22
  277. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  278. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  279. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  280. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  281. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  282. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  283. package/src/resources/extensions/gsd/component-types.ts +362 -0
  284. package/src/resources/extensions/gsd/detection.ts +58 -1
  285. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  286. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  287. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  288. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  289. package/src/resources/extensions/gsd/git-service.ts +133 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  291. package/src/resources/extensions/gsd/guided-flow.ts +20 -5
  292. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  293. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  294. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  295. package/src/resources/extensions/gsd/model-router.ts +6 -0
  296. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  297. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  299. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  300. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  301. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  302. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  303. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  304. package/src/resources/extensions/gsd/state.ts +49 -44
  305. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  306. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  307. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  308. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  309. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +94 -289
  310. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  311. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  312. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  313. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  314. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -197
  315. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  316. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  317. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  318. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  319. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  320. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  321. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  322. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  323. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  324. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  325. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  326. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  327. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -3
  328. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  329. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  330. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
  331. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  332. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  334. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -57
  335. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  336. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  337. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  338. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  339. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  340. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  341. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  342. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  343. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  344. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -62
  345. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  346. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  347. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  348. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -49
  349. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  350. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
  351. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  352. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  353. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  354. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  355. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  356. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  357. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  358. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  359. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  360. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  361. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  363. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  364. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  365. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  366. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  367. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  368. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
  369. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  370. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  371. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  372. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +12 -61
  373. package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
  374. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  375. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  376. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  377. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  378. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  379. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  380. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  381. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -81
  382. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  383. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  384. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  385. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  386. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  387. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  388. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  389. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  390. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  391. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  392. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  393. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  394. package/src/resources/extensions/gsd/types.ts +3 -3
  395. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  396. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  397. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  398. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  399. package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
  400. package/src/resources/extensions/mcp-client/index.ts +3 -1
  401. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  402. package/src/resources/extensions/ollama/index.ts +5 -1
  403. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  404. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  405. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  406. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  407. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  408. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  409. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  410. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  411. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  412. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  413. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  414. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -144
  415. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  416. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  417. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  418. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  419. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  420. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  421. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -75
  422. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  423. package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
  424. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  425. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  426. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -130
  427. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
  428. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
  429. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
@@ -188,7 +188,7 @@ export async function deriveState(basePath) {
188
188
  let synced = false;
189
189
  for (const diskId of diskIds) {
190
190
  if (!isGhostMilestone(basePath, diskId)) {
191
- insertMilestone({ id: diskId, status: 'active' });
191
+ insertMilestone(diskMilestoneInsert(basePath, diskId));
192
192
  synced = true;
193
193
  }
194
194
  }
@@ -246,6 +246,36 @@ function extractContextTitle(content, fallback) {
246
246
  // isStatusDone replaced by isClosedStatus from status-guards.ts (single source of truth).
247
247
  // Alias kept for backward compatibility within this file.
248
248
  const isStatusDone = isClosedStatus;
249
+ function loadSync(path) {
250
+ if (!path)
251
+ return null;
252
+ try {
253
+ return readFileSync(path, "utf-8");
254
+ }
255
+ catch {
256
+ return null;
257
+ }
258
+ }
259
+ function diskMilestoneInsert(basePath, mid) {
260
+ const contextContent = loadSync(resolveMilestoneFile(basePath, mid, "CONTEXT"));
261
+ const draftContent = !contextContent ? loadSync(resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT")) : null;
262
+ const roadmapContent = loadSync(resolveMilestoneFile(basePath, mid, "ROADMAP"));
263
+ const summaryContent = loadSync(resolveMilestoneFile(basePath, mid, "SUMMARY"));
264
+ const roadmap = roadmapContent ? parseRoadmap(roadmapContent) : null;
265
+ const summary = summaryContent ? parseSummary(summaryContent) : null;
266
+ const summaryTerminal = summaryContent != null && isTerminalMilestoneSummaryContent(summaryContent);
267
+ const parked = resolveMilestoneFile(basePath, mid, "PARKED") !== null;
268
+ return {
269
+ id: mid,
270
+ title: roadmap
271
+ ? stripMilestonePrefix(roadmap.title)
272
+ : (contextContent || draftContent)
273
+ ? extractContextTitle(contextContent || draftContent, mid)
274
+ : (summary?.title || mid),
275
+ status: parked ? "parked" : summaryTerminal ? "complete" : "active",
276
+ depends_on: parseContextDependsOn(contextContent ?? draftContent),
277
+ };
278
+ }
249
279
  /**
250
280
  * Derive GSD state from the milestones/slices/tasks DB tables.
251
281
  * Flag files (PARKED, VALIDATION, CONTINUE, REPLAN, REPLAN-TRIGGER, CONTEXT-DRAFT)
@@ -261,7 +291,7 @@ function reconcileDiskToDb(basePath) {
261
291
  let synced = false;
262
292
  for (const diskId of diskIds) {
263
293
  if (!dbIdSet.has(diskId) && !isGhostMilestone(basePath, diskId)) {
264
- insertMilestone({ id: diskId, status: 'active' });
294
+ insertMilestone(diskMilestoneInsert(basePath, diskId));
265
295
  synced = true;
266
296
  }
267
297
  }
@@ -556,8 +586,6 @@ function resolveSliceDependencies(activeMilestoneSlices) {
556
586
  return { activeSlice: null, activeSliceRow: null };
557
587
  }
558
588
  }
559
- let bestFallback = null;
560
- let bestFallbackSatisfied = -1;
561
589
  for (const s of activeMilestoneSlices) {
562
590
  if (isStatusDone(s.status))
563
591
  continue;
@@ -566,31 +594,25 @@ function resolveSliceDependencies(activeMilestoneSlices) {
566
594
  if (s.depends.every(dep => doneSliceIds.has(dep))) {
567
595
  return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
568
596
  }
569
- const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
570
- if (satisfied > bestFallbackSatisfied || (satisfied === bestFallbackSatisfied && !bestFallback)) {
571
- bestFallback = s;
572
- bestFallbackSatisfied = satisfied;
573
- }
574
- }
575
- if (bestFallback) {
576
- const unmet = bestFallback.depends.filter(dep => !doneSliceIds.has(dep));
577
- logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallback.id} ` +
578
- `(${bestFallbackSatisfied}/${bestFallback.depends.length} deps met, ` +
579
- `unmet: ${unmet.join(", ")})`, { mid: activeMilestoneSlices[0]?.milestone_id, sid: bestFallback.id });
580
- return { activeSlice: { id: bestFallback.id, title: bestFallback.title }, activeSliceRow: bestFallback };
581
597
  }
582
598
  return { activeSlice: null, activeSliceRow: null };
583
599
  }
584
600
  async function reconcileSliceTasks(basePath, milestoneId, sliceId, planFile) {
585
601
  let tasks = getSliceTasks(milestoneId, sliceId);
586
- if (tasks.length === 0 && planFile) {
602
+ // #3600/#4974: import missing plan-file tasks even when the DB already has
603
+ // a partial task set. Existing DB task statuses stay authoritative.
604
+ if (planFile) {
587
605
  try {
588
606
  const planContent = await loadFile(planFile);
589
607
  if (planContent) {
590
608
  const diskPlan = parsePlan(planContent);
591
609
  if (diskPlan.tasks.length > 0) {
610
+ const dbTaskIds = new Set(tasks.map(t => t.id));
611
+ let inserted = 0;
592
612
  for (let i = 0; i < diskPlan.tasks.length; i++) {
593
613
  const t = diskPlan.tasks[i];
614
+ if (dbTaskIds.has(t.id))
615
+ continue;
594
616
  try {
595
617
  insertTask({
596
618
  id: t.id,
@@ -600,13 +622,16 @@ async function reconcileSliceTasks(basePath, milestoneId, sliceId, planFile) {
600
622
  status: t.done ? 'complete' : 'pending',
601
623
  sequence: i + 1,
602
624
  });
625
+ inserted++;
603
626
  }
604
627
  catch (insertErr) {
605
628
  logWarning("reconcile", `failed to insert task ${t.id} from plan file: ${insertErr instanceof Error ? insertErr.message : String(insertErr)}`);
606
629
  }
607
630
  }
608
- tasks = getSliceTasks(milestoneId, sliceId);
609
- logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${milestoneId}/${sliceId} — DB was empty (#3600)`, { mid: milestoneId, sid: sliceId });
631
+ if (inserted > 0) {
632
+ tasks = getSliceTasks(milestoneId, sliceId);
633
+ logWarning("reconcile", `imported ${inserted} missing task(s) from plan file for ${milestoneId}/${sliceId}`, { mid: milestoneId, sid: sliceId });
634
+ }
610
635
  }
611
636
  }
612
637
  }
@@ -1389,8 +1414,6 @@ export async function _deriveStateImpl(basePath) {
1389
1414
  }
1390
1415
  }
1391
1416
  else {
1392
- let bestFallbackLegacy = null;
1393
- let bestFallbackLegacySatisfied = -1;
1394
1417
  for (const s of activeRoadmap.slices) {
1395
1418
  if (s.done)
1396
1419
  continue;
@@ -1398,18 +1421,6 @@ export async function _deriveStateImpl(basePath) {
1398
1421
  activeSlice = { id: s.id, title: s.title };
1399
1422
  break;
1400
1423
  }
1401
- const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
1402
- if (satisfied > bestFallbackLegacySatisfied) {
1403
- bestFallbackLegacy = s;
1404
- bestFallbackLegacySatisfied = satisfied;
1405
- }
1406
- }
1407
- if (!activeSlice && bestFallbackLegacy) {
1408
- const unmet = bestFallbackLegacy.depends.filter(dep => !doneSliceIds.has(dep));
1409
- logWarning("state", `No slice has all deps satisfied — falling back to ${bestFallbackLegacy.id} ` +
1410
- `(${bestFallbackLegacySatisfied}/${bestFallbackLegacy.depends.length} deps met, ` +
1411
- `unmet: ${unmet.join(", ")})`);
1412
- activeSlice = { id: bestFallbackLegacy.id, title: bestFallbackLegacy.title };
1413
1424
  }
1414
1425
  }
1415
1426
  if (!activeSlice) {
@@ -1,10 +1,9 @@
1
1
  // GSD Extension — Advisory Sync Lock
2
2
  // Prevents concurrent worktree syncs from colliding via a simple file lock.
3
- // Stale locks (mtime > 60s) are auto-overridden. Lock acquisition waits up
4
- // to 5 seconds then skips non-fatally.
5
- import { existsSync, statSync, unlinkSync } from "node:fs";
6
- import { join } from "node:path";
7
- import { atomicWriteSync } from "./atomic-write.js";
3
+ // Stale locks (mtime > 60s, owner PID confirmed dead) are overridden. Lock
4
+ // acquisition waits up to 5 seconds then skips non-fatally.
5
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync } from "node:fs";
6
+ import { dirname, join } from "node:path";
8
7
  const STALE_THRESHOLD_MS = 60_000; // 60 seconds
9
8
  const DEFAULT_TIMEOUT_MS = 5_000; // 5 seconds
10
9
  const SPIN_INTERVAL_MS = 100; // 100ms polling interval
@@ -17,60 +16,117 @@ function lockFilePath(basePath) {
17
16
  function sleepSync(ms) {
18
17
  Atomics.wait(SLEEP_VIEW, 0, 0, ms);
19
18
  }
19
+ /** True if the given PID is alive in the current process namespace. */
20
+ function isPidAlive(pid) {
21
+ try {
22
+ process.kill(pid, 0);
23
+ return true;
24
+ }
25
+ catch (err) {
26
+ if (err.code === "EPERM")
27
+ return true;
28
+ return false;
29
+ }
30
+ }
31
+ /**
32
+ * Atomically create the lock file with `O_EXCL` semantics. Returns true on
33
+ * exclusive create, false if the file already existed. Any other error
34
+ * propagates.
35
+ */
36
+ function tryCreateLockFile(lp, payload) {
37
+ // Ensure parent dir exists (`atomicWriteSync` previously did this implicitly).
38
+ try {
39
+ mkdirSync(dirname(lp), { recursive: true });
40
+ }
41
+ catch {
42
+ /* best-effort */
43
+ }
44
+ let fd;
45
+ try {
46
+ // "wx" → O_WRONLY | O_CREAT | O_EXCL — atomic create-if-not-exists on POSIX.
47
+ fd = openSync(lp, "wx");
48
+ }
49
+ catch (err) {
50
+ const code = err.code;
51
+ if (code === "EEXIST")
52
+ return false;
53
+ throw err;
54
+ }
55
+ try {
56
+ writeSync(fd, payload);
57
+ }
58
+ finally {
59
+ closeSync(fd);
60
+ }
61
+ return true;
62
+ }
20
63
  /**
21
64
  * Acquire an advisory sync lock for the given basePath.
22
65
  * Returns { acquired: true } on success, { acquired: false } after timeout.
23
66
  *
24
- * - Creates lock file at {basePath}/.gsd/sync.lock with JSON { pid, acquired_at }
25
- * - If lock exists and mtime > 60s (stale), overrides it
26
- * - If lock exists and not stale, spins up to timeoutMs before giving up
67
+ * Replaces a non-atomic `existsSync` + `atomicWriteSync` (write-temp+rename,
68
+ * which is not exclusive-create) sequence that allowed two callers to both
69
+ * believe they had acquired the lock, corrupting the event log.
70
+ * (Issue #4980 CRIT-4)
71
+ *
72
+ * Stale-lock override now also verifies the recorded owner PID is dead
73
+ * before stealing — prevents a slow event-loop pause (>60s under heavy I/O)
74
+ * from making a legitimately-held lock appear stale and get stolen.
75
+ * (Issue #4980 M-concurrency-3)
27
76
  */
28
77
  export function acquireSyncLock(basePath, timeoutMs = DEFAULT_TIMEOUT_MS) {
29
78
  const lp = lockFilePath(basePath);
30
79
  const deadline = Date.now() + timeoutMs;
80
+ const lockData = JSON.stringify({ pid: process.pid, acquired_at: new Date().toISOString() }, null, 2);
31
81
  while (true) {
32
- // Check if lock file exists
33
- if (existsSync(lp)) {
34
- // Check staleness
35
- try {
36
- const stat = statSync(lp);
37
- const age = Date.now() - stat.mtimeMs;
38
- if (age > STALE_THRESHOLD_MS) {
39
- // Stale lock override it
40
- try {
41
- unlinkSync(lp);
82
+ // First try the fast path: atomic create. No check-then-write race.
83
+ try {
84
+ if (tryCreateLockFile(lp, lockData)) {
85
+ return { acquired: true };
86
+ }
87
+ }
88
+ catch {
89
+ /* unexpectedfall through to retry */
90
+ }
91
+ // File exists. Decide whether to steal (stale + owner dead) or wait.
92
+ let canSteal = false;
93
+ try {
94
+ const stat = statSync(lp);
95
+ const age = Date.now() - stat.mtimeMs;
96
+ if (age > STALE_THRESHOLD_MS) {
97
+ // Verify the recorded owner PID is dead before stealing.
98
+ let ownerAlive = false;
99
+ try {
100
+ const data = JSON.parse(readFileSync(lp, "utf-8"));
101
+ if (typeof data.pid === "number" && data.pid !== process.pid) {
102
+ ownerAlive = isPidAlive(data.pid);
42
103
  }
43
- catch { /* race: already removed */ }
44
104
  }
45
- else {
46
- // Lock is held and not stale wait or give up
47
- if (Date.now() >= deadline) {
48
- return { acquired: false };
49
- }
50
- sleepSync(SPIN_INTERVAL_MS);
51
- continue;
105
+ catch {
106
+ // Lock contents unreadable be conservative and steal only on age.
107
+ // A garbage lock file that we cannot parse is safer to remove than
108
+ // to leave wedging the lock indefinitely.
52
109
  }
110
+ canSteal = !ownerAlive;
53
111
  }
54
- catch {
55
- // stat failed (file removed between exists check and stat) — try to acquire
56
- }
57
- }
58
- // Lock file does not exist (or was just removed) — try to write it
59
- try {
60
- const lockData = {
61
- pid: process.pid,
62
- acquired_at: new Date().toISOString(),
63
- };
64
- atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
65
- return { acquired: true };
66
112
  }
67
113
  catch {
68
- // Write failed (race condition with another process) — retry or give up
69
- if (Date.now() >= deadline) {
70
- return { acquired: false };
114
+ // stat failed (file removed between exists and stat) — retry create.
115
+ continue;
116
+ }
117
+ if (canSteal) {
118
+ try {
119
+ unlinkSync(lp);
71
120
  }
72
- sleepSync(SPIN_INTERVAL_MS);
121
+ catch { /* race: already removed */ }
122
+ // Loop back to retry create.
123
+ continue;
124
+ }
125
+ // Lock is held and not stale (or owner is alive) — wait or give up.
126
+ if (Date.now() >= deadline) {
127
+ return { acquired: false };
73
128
  }
129
+ sleepSync(SPIN_INTERVAL_MS);
74
130
  }
75
131
  }
76
132
  /**
@@ -0,0 +1,147 @@
1
+ // GSD-2 — UnitContextComposer (#4782 phase 2).
2
+ //
3
+ // Reads a unit type's manifest and orchestrates artifact inlining through
4
+ // a caller-provided resolver. Returns a joined context block suitable for
5
+ // substitution into the unit's prompt template.
6
+ //
7
+ // Design rationale:
8
+ // - Pure dependency on the manifest module — no circular import with
9
+ // `auto-prompts.ts` where the per-artifact-key resolver lives.
10
+ // - Caller-supplied resolver means the composer can be unit-tested with
11
+ // trivial mocks; production wiring in `auto-prompts.ts` dispatches to
12
+ // the existing `inlineFile` / `inline*FromDb` helpers.
13
+ // - Null-returning resolvers are skipped silently: they model the
14
+ // "artifact is optional / missing / not applicable to this milestone"
15
+ // case. The composer never errors on a missing artifact.
16
+ //
17
+ // Scope: phase 2 pilot shipped `composeInlinedContext` for static-key
18
+ // inlining. Phase 3.5 (#4924) adds the v2 surface — `composeUnitContext`
19
+ // — which also handles excerpts, computed artifacts, and prepended blocks.
20
+ // `composeInlinedContext` stays for backward compatibility with the
21
+ // already-migrated simple builders.
22
+ //
23
+ // ─── Composer boundary invariant (#4924) ─────────────────────────────────
24
+ //
25
+ // The composer is allowed to:
26
+ // - order named sections per the manifest's declared sequence
27
+ // - resolve registered artifacts (static / computed / excerpt / on-demand)
28
+ // - apply typed policies (knowledge / memory / codebase-map / preferences)
29
+ //
30
+ // The composer must NOT grow:
31
+ // - arbitrary conditionals on unit state
32
+ // - loops over caller-supplied data
33
+ // - string templating beyond section composition (join + separator)
34
+ //
35
+ // Logic that needs those belongs in a typed computed-artifact builder
36
+ // owned by the unit, not in the composer. Reviews must enforce this — it
37
+ // is the difference between an orchestrator and a runaway DSL.
38
+ import { resolveManifest, } from "./unit-context-manifest.js";
39
+ /**
40
+ * Produce the inlined-context portion of a unit's system prompt by
41
+ * walking the manifest's `artifacts.inline` list in order and calling
42
+ * the provided resolver for each key.
43
+ *
44
+ * Returns an empty string when the unit type has no manifest registered,
45
+ * so callers can guard their wiring with a simple truthy check. Unknown
46
+ * unit types do not error — this mirrors `resolveManifest`'s contract.
47
+ *
48
+ * The separator between inlined blocks matches the in-tree convention
49
+ * (`\n\n---\n\n`) so composer output slots into existing prompt templates
50
+ * without visible diff.
51
+ */
52
+ export async function composeInlinedContext(unitType, resolveArtifact) {
53
+ const manifest = resolveManifest(unitType);
54
+ if (!manifest)
55
+ return "";
56
+ const blocks = [];
57
+ for (const key of manifest.artifacts.inline) {
58
+ const body = await resolveArtifact(key);
59
+ if (body !== null && body.length > 0) {
60
+ blocks.push(body);
61
+ }
62
+ }
63
+ return blocks.join("\n\n---\n\n");
64
+ }
65
+ /**
66
+ * Convenience helper returning the manifest's declared budget so callers
67
+ * can telemetry a mismatch between actual prompt size and declared budget.
68
+ * Returns null for unknown unit types.
69
+ */
70
+ export function manifestBudgetChars(unitType) {
71
+ const manifest = resolveManifest(unitType);
72
+ return manifest ? manifest.maxSystemPromptChars : null;
73
+ }
74
+ const SECTION_SEPARATOR = "\n\n---\n\n";
75
+ /**
76
+ * Compose all manifest-declared context for a unit type using the v2
77
+ * surface. Walks `prepend` first (computed-only), then the `inline` list
78
+ * (static keys via `resolveArtifact`), then `excerpt` (via `resolveExcerpt`),
79
+ * then `artifacts.computed` (via the typed registry). Order within each
80
+ * section follows the manifest's declared sequence.
81
+ *
82
+ * Unknown unit types return empty strings for both sections — callers can
83
+ * fall back to existing imperative wiring without a special case.
84
+ *
85
+ * Resolver / registry omissions: if the manifest declares an entry but no
86
+ * resolver / registry entry is provided, the composer skips it silently.
87
+ * This matches the v1 contract where a null body is a no-op, and lets
88
+ * partial migrations land without forcing every consumer to register
89
+ * every artifact class up-front.
90
+ */
91
+ export async function composeUnitContext(unitType, opts) {
92
+ const manifest = resolveManifest(unitType);
93
+ if (!manifest)
94
+ return { prepend: "", inline: "" };
95
+ // Single-source `unitType`: the manifest is resolved against the
96
+ // function arg, but computed builders read it from `base.unitType`.
97
+ // If those ever diverge (caller passes one type to composeUnitContext
98
+ // but a different one in opts.base), the composer would silently
99
+ // mix one unit's manifest with another unit's computed context.
100
+ // Normalize here so the composer dispatches a consistent identity
101
+ // through to every builder.
102
+ const normalizedOpts = {
103
+ ...opts,
104
+ base: { ...opts.base, unitType },
105
+ };
106
+ const prependBlocks = await runComputed(manifest.prepend ?? [], normalizedOpts);
107
+ const inlineBlocks = [];
108
+ for (const key of manifest.artifacts.inline) {
109
+ if (!normalizedOpts.resolveArtifact)
110
+ break;
111
+ const body = await normalizedOpts.resolveArtifact(key);
112
+ if (body && body.length > 0)
113
+ inlineBlocks.push(body);
114
+ }
115
+ for (const key of manifest.artifacts.excerpt) {
116
+ if (!normalizedOpts.resolveExcerpt)
117
+ break;
118
+ const body = await normalizedOpts.resolveExcerpt(key);
119
+ if (body && body.length > 0)
120
+ inlineBlocks.push(body);
121
+ }
122
+ inlineBlocks.push(...await runComputed(manifest.artifacts.computed ?? [], normalizedOpts));
123
+ return {
124
+ prepend: prependBlocks.join(SECTION_SEPARATOR),
125
+ inline: inlineBlocks.join(SECTION_SEPARATOR),
126
+ };
127
+ }
128
+ /**
129
+ * Invoke the registered builder for each declared computed id, in order.
130
+ * Missing registry entries (manifest declares the id but caller didn't
131
+ * register it) are skipped silently — see composeUnitContext rationale.
132
+ */
133
+ async function runComputed(ids, opts) {
134
+ if (ids.length === 0 || !opts.computed)
135
+ return [];
136
+ const registry = opts.computed;
137
+ const out = [];
138
+ for (const id of ids) {
139
+ const entry = registry[id];
140
+ if (!entry)
141
+ continue;
142
+ const body = await entry.build(entry.inputs, opts.base);
143
+ if (body && body.length > 0)
144
+ out.push(body);
145
+ }
146
+ return out;
147
+ }