gsd-pi 2.77.0-dev.eaa4973bc → 2.78.0-dev.aeeb2ca00

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 (545) hide show
  1. package/README.md +53 -17
  2. package/dist/claude-cli-check.js +46 -10
  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 +72 -16
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/github-sync/templates.js +103 -0
  15. package/dist/resources/extensions/google-search/index.js +3 -2
  16. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  17. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  18. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  19. package/dist/resources/extensions/gsd/auto-dispatch.js +142 -29
  20. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +150 -64
  22. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  23. package/dist/resources/extensions/gsd/auto-recovery.js +197 -48
  24. package/dist/resources/extensions/gsd/auto-start.js +107 -29
  25. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  26. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  27. package/dist/resources/extensions/gsd/auto.js +76 -21
  28. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  30. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  31. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  33. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  34. package/dist/resources/extensions/gsd/component-types.js +69 -0
  35. package/dist/resources/extensions/gsd/context-store.js +23 -7
  36. package/dist/resources/extensions/gsd/detection.js +49 -1
  37. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -17
  38. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  39. package/dist/resources/extensions/gsd/forensics.js +106 -0
  40. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  41. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  42. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  43. package/dist/resources/extensions/gsd/git-service.js +126 -2
  44. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  45. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  46. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  47. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  48. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  49. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  50. package/dist/resources/extensions/gsd/model-router.js +6 -0
  51. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  53. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  54. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  55. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  56. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  58. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  59. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  60. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  61. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  62. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  64. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  65. package/dist/resources/extensions/gsd/state.js +69 -58
  66. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  67. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  68. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  69. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  70. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  71. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  72. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  73. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  74. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  75. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  76. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  77. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  79. package/dist/resources/extensions/gsd/worktree-manager.js +85 -8
  80. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  81. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  82. package/dist/resources/extensions/mcp-client/index.js +3 -1
  83. package/dist/resources/extensions/ollama/index.js +5 -1
  84. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  85. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  88. package/dist/web/standalone/.next/build-manifest.json +2 -2
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  115. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  116. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  117. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  119. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  120. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  121. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  122. package/package.json +2 -3
  123. package/packages/daemon/package.json +2 -2
  124. package/packages/daemon/src/logger.ts +4 -3
  125. package/packages/mcp-server/dist/server.d.ts +24 -0
  126. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  127. package/packages/mcp-server/dist/server.js +88 -87
  128. package/packages/mcp-server/dist/server.js.map +1 -1
  129. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  130. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  131. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  132. package/packages/mcp-server/package.json +2 -2
  133. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  134. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  135. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  136. package/packages/mcp-server/src/server.ts +131 -105
  137. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  138. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  139. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  140. package/packages/native/package.json +2 -2
  141. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  142. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  143. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  144. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  145. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  146. package/packages/pi-agent-core/package.json +1 -1
  147. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  148. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  149. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  150. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  151. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  152. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  153. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  154. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  155. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  156. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  158. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  159. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  160. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  161. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  162. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  163. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  164. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  165. package/packages/pi-ai/dist/models.test.js +36 -11
  166. package/packages/pi-ai/dist/models.test.js.map +1 -1
  167. package/packages/pi-ai/package.json +1 -1
  168. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  169. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  170. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  171. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  172. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  173. package/packages/pi-ai/src/models.test.ts +48 -11
  174. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  175. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  176. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  178. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  180. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  182. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  184. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  186. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  190. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  193. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  195. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  197. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  199. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  201. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  203. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  205. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/index.js +1 -0
  207. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  217. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  226. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  227. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  228. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  229. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  230. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  231. package/packages/pi-coding-agent/package.json +1 -1
  232. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  233. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  234. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  235. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  236. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  237. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  238. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  239. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  240. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  241. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  242. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  243. package/packages/pi-coding-agent/src/index.ts +1 -0
  244. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  245. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  246. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  247. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  248. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  249. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  250. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  251. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  252. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  253. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  254. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  255. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  256. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  257. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +37 -11
  258. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  259. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  260. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  261. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  262. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  263. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  264. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  265. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  266. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  267. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  268. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  269. package/packages/pi-tui/dist/components/editor.js +19 -0
  270. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  271. package/packages/pi-tui/dist/components/image.test.js +6 -5
  272. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  273. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  274. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  275. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  276. package/packages/pi-tui/package.json +1 -1
  277. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  278. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  279. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +42 -11
  280. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  281. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  282. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  283. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  284. package/packages/pi-tui/src/components/editor.ts +22 -0
  285. package/packages/pi-tui/src/components/image.test.ts +10 -5
  286. package/packages/pi-tui/src/editor-component.ts +3 -0
  287. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  288. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  289. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  290. package/packages/rpc-client/package.json +1 -1
  291. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  292. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  293. package/pkg/package.json +1 -1
  294. package/scripts/install.js +15 -1
  295. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  296. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  297. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  298. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  299. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  300. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  301. package/src/resources/extensions/claude-code-cli/readiness.ts +75 -16
  302. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  303. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  304. package/src/resources/extensions/github-sync/templates.ts +151 -0
  305. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  306. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  307. package/src/resources/extensions/google-search/index.ts +3 -2
  308. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  309. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  310. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  311. package/src/resources/extensions/gsd/auto-dispatch.ts +156 -29
  312. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  313. package/src/resources/extensions/gsd/auto-post-unit.ts +163 -73
  314. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  315. package/src/resources/extensions/gsd/auto-recovery.ts +230 -51
  316. package/src/resources/extensions/gsd/auto-start.ts +127 -9
  317. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  318. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  319. package/src/resources/extensions/gsd/auto.ts +90 -23
  320. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  321. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  322. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  323. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  324. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  325. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  326. package/src/resources/extensions/gsd/component-types.ts +362 -0
  327. package/src/resources/extensions/gsd/context-store.ts +25 -8
  328. package/src/resources/extensions/gsd/detection.ts +58 -1
  329. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -20
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  331. package/src/resources/extensions/gsd/forensics.ts +118 -1
  332. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  333. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  334. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  335. package/src/resources/extensions/gsd/git-service.ts +149 -2
  336. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  337. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  338. package/src/resources/extensions/gsd/journal.ts +11 -1
  339. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  340. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  341. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  342. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  343. package/src/resources/extensions/gsd/model-router.ts +6 -0
  344. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  345. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  346. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  347. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  348. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  349. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  350. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  351. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  352. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  353. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  354. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  355. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  356. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  357. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  358. package/src/resources/extensions/gsd/state.ts +76 -66
  359. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  360. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  361. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  362. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  363. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  364. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +133 -292
  365. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  366. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  367. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  368. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  369. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  370. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  371. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  372. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  373. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  374. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  375. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  376. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  377. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  378. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  379. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  380. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  381. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  382. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  383. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  384. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  385. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  386. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  387. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  388. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  389. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  390. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  391. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  392. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  393. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  394. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  395. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  396. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  397. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  398. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +5 -0
  399. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  400. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  401. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  402. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  403. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  404. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  405. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  406. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  407. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  408. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +14 -4
  410. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  411. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  412. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  413. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  414. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  415. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  416. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  417. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  418. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  419. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  420. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  421. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  422. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  423. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  424. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  425. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  426. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  427. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  428. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  429. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  430. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  431. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  432. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  433. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  434. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  435. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  436. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  437. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  438. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  439. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  440. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  442. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  443. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  444. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  445. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  446. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  447. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  448. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  449. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  450. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  451. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  452. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  453. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  454. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  455. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  456. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  457. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  458. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  459. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  460. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  461. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  462. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  463. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  464. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  465. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  466. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  467. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  468. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  469. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  470. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  471. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  472. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  473. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  474. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  475. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  476. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  477. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  478. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  479. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  480. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  481. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  482. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  483. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  484. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  485. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -0
  486. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  487. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  488. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  489. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  490. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  491. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  492. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  493. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  494. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  495. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  496. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  497. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  498. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  499. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  500. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  501. package/src/resources/extensions/gsd/types.ts +3 -3
  502. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  503. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  504. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  505. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  506. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  507. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  508. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  509. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  510. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  511. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  512. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  513. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  514. package/src/resources/extensions/gsd/worktree-manager.ts +108 -7
  515. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  516. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  517. package/src/resources/extensions/mcp-client/index.ts +3 -1
  518. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  519. package/src/resources/extensions/ollama/index.ts +5 -1
  520. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  521. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  522. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  523. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  524. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  525. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  526. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  527. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  528. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  529. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  530. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  531. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  532. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  533. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  534. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  535. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  536. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  537. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  538. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  539. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  540. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  541. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  542. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  543. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  544. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_buildManifest.js +0 -0
  545. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_ssgManifest.js +0 -0
@@ -15,6 +15,7 @@ import { logWarning, logError } from './workflow-logger.js';
15
15
  import { extractVerdict } from './verdict-parser.js';
16
16
  import { loadEffectiveGSDPreferences } from './preferences.js';
17
17
  import { detectPendingEscalation } from './escalation.js';
18
+ import { isTerminalMilestoneSummaryContent } from './milestone-summary-classifier.js';
18
19
  import { isDbAvailable, wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateSliceStatus, updateTaskStatus, getPendingGateCountForTurn, autoHealSketchFlags, } from './gsd-db.js';
19
20
  /**
20
21
  * A "ghost" milestone directory contains only META.json (and no substantive
@@ -81,6 +82,10 @@ export function isMilestoneComplete(roadmap) {
81
82
  export function isValidationTerminal(validationContent) {
82
83
  return extractVerdict(validationContent) != null;
83
84
  }
85
+ async function isTerminalMilestoneSummaryFile(path, loader) {
86
+ const content = await loader(path);
87
+ return content != null && isTerminalMilestoneSummaryContent(content);
88
+ }
84
89
  const CACHE_TTL_MS = 100;
85
90
  let _stateCache = null;
86
91
  // ── Telemetry counters for derive-path observability ────────────────────────
@@ -138,18 +143,19 @@ export async function getActiveMilestoneId(basePath) {
138
143
  const content = roadmapFile ? await loadFile(roadmapFile) : null;
139
144
  if (!content) {
140
145
  const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
141
- if (summaryFile)
146
+ if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, loadFile))
142
147
  continue;
143
148
  if (isGhostMilestone(basePath, mid))
144
149
  continue;
145
150
  return mid;
146
151
  }
147
152
  const roadmap = parseRoadmap(content);
148
- if (!isMilestoneComplete(roadmap)) {
149
- const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
150
- if (!summaryFile)
151
- return mid;
152
- }
153
+ const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
154
+ if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, loadFile))
155
+ continue;
156
+ if (!isMilestoneComplete(roadmap))
157
+ return mid;
158
+ return mid;
153
159
  }
154
160
  return null;
155
161
  }
@@ -182,7 +188,7 @@ export async function deriveState(basePath) {
182
188
  let synced = false;
183
189
  for (const diskId of diskIds) {
184
190
  if (!isGhostMilestone(basePath, diskId)) {
185
- insertMilestone({ id: diskId, status: 'active' });
191
+ insertMilestone(diskMilestoneInsert(basePath, diskId));
186
192
  synced = true;
187
193
  }
188
194
  }
@@ -240,6 +246,36 @@ function extractContextTitle(content, fallback) {
240
246
  // isStatusDone replaced by isClosedStatus from status-guards.ts (single source of truth).
241
247
  // Alias kept for backward compatibility within this file.
242
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
+ }
243
279
  /**
244
280
  * Derive GSD state from the milestones/slices/tasks DB tables.
245
281
  * Flag files (PARKED, VALIDATION, CONTINUE, REPLAN, REPLAN-TRIGGER, CONTEXT-DRAFT)
@@ -255,7 +291,7 @@ function reconcileDiskToDb(basePath) {
255
291
  let synced = false;
256
292
  for (const diskId of diskIds) {
257
293
  if (!dbIdSet.has(diskId) && !isGhostMilestone(basePath, diskId)) {
258
- insertMilestone({ id: diskId, status: 'active' });
294
+ insertMilestone(diskMilestoneInsert(basePath, diskId));
259
295
  synced = true;
260
296
  }
261
297
  }
@@ -550,9 +586,6 @@ function resolveSliceDependencies(activeMilestoneSlices) {
550
586
  return { activeSlice: null, activeSliceRow: null };
551
587
  }
552
588
  }
553
- // First pass: find a slice with ALL dependencies satisfied (strict)
554
- let bestFallback = null;
555
- let bestFallbackSatisfied = -1;
556
589
  for (const s of activeMilestoneSlices) {
557
590
  if (isStatusDone(s.status))
558
591
  continue;
@@ -561,36 +594,25 @@ function resolveSliceDependencies(activeMilestoneSlices) {
561
594
  if (s.depends.every(dep => doneSliceIds.has(dep))) {
562
595
  return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
563
596
  }
564
- // Track the slice with the most satisfied dependencies as fallback
565
- const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
566
- if (satisfied > bestFallbackSatisfied || (satisfied === bestFallbackSatisfied && !bestFallback)) {
567
- bestFallback = s;
568
- bestFallbackSatisfied = satisfied;
569
- }
570
- }
571
- // Fallback: if no slice has all deps met but there ARE incomplete non-deferred
572
- // slices, pick the one with the most deps satisfied. This prevents hard-blocking
573
- // when dependency metadata is stale (e.g. after reassessment added/removed slices)
574
- // or when deps reference slices from previous milestones.
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
  }
@@ -988,7 +1013,7 @@ export async function _deriveStateImpl(basePath) {
988
1013
  const rc = rf ? await cachedLoadFile(rf) : null;
989
1014
  if (!rc) {
990
1015
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
991
- if (sf)
1016
+ if (sf && await isTerminalMilestoneSummaryFile(sf, cachedLoadFile))
992
1017
  completeMilestoneIds.add(mid);
993
1018
  continue;
994
1019
  }
@@ -998,12 +1023,12 @@ export async function _deriveStateImpl(basePath) {
998
1023
  // Summary is the terminal artifact — if it exists, the milestone is
999
1024
  // complete even when roadmap checkboxes weren't ticked (#864).
1000
1025
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
1001
- if (sf)
1026
+ if (sf && await isTerminalMilestoneSummaryFile(sf, cachedLoadFile))
1002
1027
  completeMilestoneIds.add(mid);
1003
1028
  continue;
1004
1029
  }
1005
1030
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
1006
- if (sf)
1031
+ if (sf && await isTerminalMilestoneSummaryFile(sf, cachedLoadFile))
1007
1032
  completeMilestoneIds.add(mid);
1008
1033
  }
1009
1034
  // Phase 2: Build registry using cached roadmaps (no re-parsing or re-reading)
@@ -1028,12 +1053,14 @@ export async function _deriveStateImpl(basePath) {
1028
1053
  const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
1029
1054
  if (summaryFile) {
1030
1055
  const summaryContent = await cachedLoadFile(summaryFile);
1031
- const summaryTitle = summaryContent
1032
- ? (parseSummary(summaryContent).title || mid)
1033
- : mid;
1034
- registry.push({ id: mid, title: summaryTitle, status: 'complete' });
1035
- completeMilestoneIds.add(mid);
1036
- continue;
1056
+ if (summaryContent != null && isTerminalMilestoneSummaryContent(summaryContent)) {
1057
+ const summaryTitle = summaryContent
1058
+ ? (parseSummary(summaryContent).title || mid)
1059
+ : mid;
1060
+ registry.push({ id: mid, title: summaryTitle, status: 'complete' });
1061
+ completeMilestoneIds.add(mid);
1062
+ continue;
1063
+ }
1037
1064
  }
1038
1065
  // Ghost milestone (only META.json, no CONTEXT/ROADMAP/SUMMARY) — skip entirely
1039
1066
  if (isGhostMilestone(basePath, mid))
@@ -1087,7 +1114,7 @@ export async function _deriveStateImpl(basePath) {
1087
1114
  const verdict = validationContent ? extractVerdict(validationContent) : undefined;
1088
1115
  // needs-remediation is terminal but requires re-validation (#3596)
1089
1116
  const needsRevalidation = !validationTerminal || verdict === 'needs-remediation';
1090
- if (summaryFile) {
1117
+ if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, cachedLoadFile)) {
1091
1118
  // Summary exists → milestone is complete regardless of validation state.
1092
1119
  // The summary is the terminal artifact (#864).
1093
1120
  registry.push({ id: mid, title, status: 'complete' });
@@ -1118,7 +1145,7 @@ export async function _deriveStateImpl(basePath) {
1118
1145
  // Roadmap slices not all checked — but if a summary exists, the milestone
1119
1146
  // is still complete. The summary is the terminal artifact (#864).
1120
1147
  const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
1121
- if (summaryFile) {
1148
+ if (summaryFile && await isTerminalMilestoneSummaryFile(summaryFile, cachedLoadFile)) {
1122
1149
  registry.push({ id: mid, title, status: 'complete' });
1123
1150
  }
1124
1151
  else if (!activeMilestoneFound) {
@@ -1387,8 +1414,6 @@ export async function _deriveStateImpl(basePath) {
1387
1414
  }
1388
1415
  }
1389
1416
  else {
1390
- let bestFallbackLegacy = null;
1391
- let bestFallbackLegacySatisfied = -1;
1392
1417
  for (const s of activeRoadmap.slices) {
1393
1418
  if (s.done)
1394
1419
  continue;
@@ -1396,20 +1421,6 @@ export async function _deriveStateImpl(basePath) {
1396
1421
  activeSlice = { id: s.id, title: s.title };
1397
1422
  break;
1398
1423
  }
1399
- // Track best fallback
1400
- const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
1401
- if (satisfied > bestFallbackLegacySatisfied) {
1402
- bestFallbackLegacy = s;
1403
- bestFallbackLegacySatisfied = satisfied;
1404
- }
1405
- }
1406
- // Fallback: if no slice has all deps met, pick the one with the most deps satisfied
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
  /**
@@ -11,6 +11,7 @@
11
11
  import { join } from "node:path";
12
12
  import { transaction, insertAssessment, deleteAssessmentByScope, getMilestoneSlices, } from "../gsd-db.js";
13
13
  import { resolveMilestonePath, clearPathCache } from "../paths.js";
14
+ import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
14
15
  import { saveFile, clearParseCache } from "../files.js";
15
16
  import { invalidateStateCache } from "../state.js";
16
17
  import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
@@ -59,14 +60,18 @@ export async function handleValidateMilestone(params, basePath, opts) {
59
60
  return { error: `verdict must be one of: ${VALIDATION_VERDICTS.join(", ")}` };
60
61
  }
61
62
  // ── Resolve paths and render markdown ────────────────────────────────
63
+ // #4761: route through the canonical-root resolver so that when a live
64
+ // worktree exists for this milestone, validation reads/writes the
65
+ // worktree's artifacts instead of stale project-root state.
62
66
  const validationMd = renderValidationMarkdown(params);
67
+ const canonicalBase = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
63
68
  let validationPath;
64
- const milestoneDir = resolveMilestonePath(basePath, params.milestoneId);
69
+ const milestoneDir = resolveMilestonePath(canonicalBase, params.milestoneId);
65
70
  if (milestoneDir) {
66
71
  validationPath = join(milestoneDir, `${params.milestoneId}-VALIDATION.md`);
67
72
  }
68
73
  else {
69
- const gsdDir = join(basePath, ".gsd");
74
+ const gsdDir = join(canonicalBase, ".gsd");
70
75
  const manualDir = join(gsdDir, "milestones", params.milestoneId);
71
76
  validationPath = join(manualDir, `${params.milestoneId}-VALIDATION.md`);
72
77
  }
@@ -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
+ }