gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6

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 (368) hide show
  1. package/dist/headless.js +25 -4
  2. package/dist/resource-loader.d.ts +40 -0
  3. package/dist/resource-loader.js +32 -13
  4. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  5. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  6. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  7. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  8. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  9. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +5 -18
  11. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
  14. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  15. package/dist/resources/extensions/gsd/auto-start.js +75 -24
  16. package/dist/resources/extensions/gsd/auto.js +34 -0
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  18. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
  19. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  20. package/dist/resources/extensions/gsd/component-types.js +69 -0
  21. package/dist/resources/extensions/gsd/context-store.js +23 -7
  22. package/dist/resources/extensions/gsd/detection.js +49 -1
  23. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  24. package/dist/resources/extensions/gsd/forensics.js +106 -0
  25. package/dist/resources/extensions/gsd/gsd-db.js +1 -1
  26. package/dist/resources/extensions/gsd/guided-flow.js +2 -4
  27. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  28. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  29. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  30. package/dist/resources/extensions/gsd/model-router.js +6 -0
  31. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  32. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  33. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  34. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  35. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  36. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  37. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  38. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  39. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  40. package/dist/resources/extensions/gsd/unit-context-manifest.js +334 -0
  41. package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
  42. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  43. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  44. package/dist/resources/extensions/mcp-client/index.js +3 -1
  45. package/dist/resources/extensions/ollama/index.js +5 -1
  46. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  47. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  48. package/dist/web/standalone/.next/BUILD_ID +1 -1
  49. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  50. package/dist/web/standalone/.next/build-manifest.json +2 -2
  51. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  76. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  77. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  79. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  80. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  81. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  82. package/package.json +1 -3
  83. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  84. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  85. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  86. package/packages/native/package.json +1 -1
  87. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  88. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  89. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  90. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  91. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  92. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  93. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  94. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  95. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  96. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  98. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  99. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  100. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  101. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  102. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  103. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  104. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  105. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  106. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  107. package/packages/pi-ai/dist/models.test.js +29 -11
  108. package/packages/pi-ai/dist/models.test.js.map +1 -1
  109. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  110. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  111. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  112. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  113. package/packages/pi-ai/src/models.test.ts +39 -11
  114. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  115. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  116. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  118. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  120. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  122. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  124. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  126. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  128. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  130. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  133. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  135. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  137. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  139. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  140. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  141. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  143. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/index.js +1 -0
  147. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  156. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  157. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  158. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  159. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  160. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  161. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  163. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  164. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  165. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  166. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  167. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  168. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  169. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  170. package/packages/pi-coding-agent/src/index.ts +1 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  172. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  173. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  174. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  175. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  176. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  177. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  178. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  179. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  180. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  181. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  182. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  183. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  184. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  185. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  186. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  187. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  188. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  189. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  190. package/packages/pi-tui/dist/components/image.test.js +6 -5
  191. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  192. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  193. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  194. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  195. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  196. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  197. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  198. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  199. package/packages/pi-tui/src/components/image.test.ts +10 -5
  200. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  201. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  202. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  203. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  204. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  205. package/scripts/install.js +15 -1
  206. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  207. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  208. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  209. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  210. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  211. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  212. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -72
  213. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  214. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  215. package/src/resources/extensions/gsd/auto/phases.ts +6 -17
  216. package/src/resources/extensions/gsd/auto/session.ts +7 -0
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
  218. package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
  219. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  220. package/src/resources/extensions/gsd/auto-start.ts +97 -4
  221. package/src/resources/extensions/gsd/auto.ts +37 -0
  222. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  223. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
  224. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  225. package/src/resources/extensions/gsd/component-types.ts +362 -0
  226. package/src/resources/extensions/gsd/context-store.ts +25 -8
  227. package/src/resources/extensions/gsd/detection.ts +58 -1
  228. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  229. package/src/resources/extensions/gsd/forensics.ts +118 -1
  230. package/src/resources/extensions/gsd/git-service.ts +16 -0
  231. package/src/resources/extensions/gsd/gsd-db.ts +1 -1
  232. package/src/resources/extensions/gsd/guided-flow.ts +2 -4
  233. package/src/resources/extensions/gsd/journal.ts +11 -1
  234. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  235. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  236. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  237. package/src/resources/extensions/gsd/model-router.ts +6 -0
  238. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  239. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  240. package/src/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  241. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  242. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  243. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  244. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  245. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
  247. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  248. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  249. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  250. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  251. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  252. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  253. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  254. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  255. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  256. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  257. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  258. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  259. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  260. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  261. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -4
  262. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  263. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  264. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  265. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  266. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  267. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  268. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  269. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  270. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  271. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  272. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  273. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  274. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  275. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  276. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1 -1
  277. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  278. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  279. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  280. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  281. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  282. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  283. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  284. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  285. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  287. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  288. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  289. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  290. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  291. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  292. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  294. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +22 -16
  295. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  296. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  297. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  298. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  299. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  300. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  301. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  303. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  304. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  305. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  306. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  307. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  308. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  309. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  310. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  311. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  312. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  313. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  314. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  315. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  316. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  317. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  318. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  319. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  320. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -0
  321. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  322. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  323. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  324. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  325. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  326. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  327. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  328. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  329. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  330. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  331. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  332. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  333. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  334. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  335. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  336. package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
  337. package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
  338. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  339. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  340. package/src/resources/extensions/mcp-client/index.ts +3 -1
  341. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  342. package/src/resources/extensions/ollama/index.ts +5 -1
  343. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  344. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  345. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  346. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  347. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  348. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  349. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  350. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  351. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  352. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  353. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  354. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  355. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  356. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  357. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  358. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  359. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  360. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  361. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  362. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  363. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  364. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  365. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  366. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  367. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
  368. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
@@ -1,11 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { readFileSync } from "node:fs";
4
- import { join, dirname } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
- const gsdDir = join(__dirname, "..");
9
3
 
10
4
  /**
11
5
  * Regression tests for #2826: hook/* completed-unit keys were parsed
@@ -15,17 +9,13 @@ const gsdDir = join(__dirname, "..");
15
9
  * The root cause: `key.indexOf("/")` splits "hook/telegram-progress/M007/S01"
16
10
  * into unitType="hook" + unitId="telegram-progress/M007/S01" instead of
17
11
  * unitType="hook/telegram-progress" + unitId="M007/S01".
12
+ *
13
+ * These tests exercise the real `splitCompletedKey` helper — the previous
14
+ * source-grep "does not use indexOf" blocks were dropped under #4825 as
15
+ * they over-specified implementation shape.
18
16
  */
19
17
 
20
18
  describe("splitCompletedKey (#2826)", () => {
21
- it("is exported from forensics.ts", () => {
22
- const source = readFileSync(join(gsdDir, "forensics.ts"), "utf-8");
23
- assert.ok(
24
- source.includes("export function splitCompletedKey"),
25
- "forensics.ts must export splitCompletedKey helper",
26
- );
27
- });
28
-
29
19
  it("splits simple unit types correctly", async () => {
30
20
  const { splitCompletedKey } = await import("../forensics.ts");
31
21
  const result = splitCompletedKey("execute-task/M007/S01/T01");
@@ -64,44 +54,3 @@ describe("splitCompletedKey (#2826)", () => {
64
54
  assert.strictEqual(splitCompletedKey("hook/someName"), null);
65
55
  });
66
56
  });
67
-
68
- describe("forensics detectMissingArtifacts uses splitCompletedKey (#2826)", () => {
69
- it("does not use indexOf for key splitting", () => {
70
- const source = readFileSync(join(gsdDir, "forensics.ts"), "utf-8");
71
- // Extract only the detectMissingArtifacts function body
72
- const fnStart = source.indexOf("function detectMissingArtifacts");
73
- assert.ok(fnStart !== -1, "detectMissingArtifacts must exist");
74
- const fnBody = source.slice(fnStart, source.indexOf("\n}\n", fnStart) + 3);
75
-
76
- assert.ok(
77
- !fnBody.includes('key.indexOf("/")'),
78
- "detectMissingArtifacts must not use key.indexOf('/') — use splitCompletedKey instead",
79
- );
80
- assert.ok(
81
- fnBody.includes("splitCompletedKey"),
82
- "detectMissingArtifacts must use splitCompletedKey helper",
83
- );
84
- });
85
- });
86
-
87
- describe("doctor-runtime-checks uses splitCompletedKey (#2826)", () => {
88
- it("does not use indexOf for key splitting in orphaned-key check", () => {
89
- const source = readFileSync(
90
- join(gsdDir, "doctor-runtime-checks.ts"),
91
- "utf-8",
92
- );
93
- // Find the orphaned completed-units section
94
- const sectionStart = source.indexOf("Orphaned completed-units");
95
- assert.ok(sectionStart !== -1, "orphaned completed-units section must exist");
96
- const sectionBody = source.slice(sectionStart, source.indexOf("} catch", sectionStart));
97
-
98
- assert.ok(
99
- !sectionBody.includes('key.indexOf("/")'),
100
- "doctor orphaned-key check must not use key.indexOf('/') — use splitCompletedKey instead",
101
- );
102
- assert.ok(
103
- sectionBody.includes("splitCompletedKey"),
104
- "doctor orphaned-key check must use splitCompletedKey helper",
105
- );
106
- });
107
- });
@@ -21,10 +21,9 @@ import {
21
21
  realpathSync,
22
22
  readFileSync,
23
23
  } from "node:fs";
24
- import { join, dirname } from "node:path";
24
+ import { join } from "node:path";
25
25
  import { tmpdir } from "node:os";
26
26
  import { execSync } from "node:child_process";
27
- import { fileURLToPath } from "node:url";
28
27
 
29
28
  import {
30
29
  createAutoWorktree,
@@ -33,8 +32,6 @@ import {
33
32
  mergeMilestoneToMain,
34
33
  } from "../../auto-worktree.ts";
35
34
 
36
- const __dirname = dirname(fileURLToPath(import.meta.url));
37
-
38
35
  function run(command: string, cwd: string): string {
39
36
  return execSync(command, {
40
37
  cwd,
@@ -75,58 +72,12 @@ function createMilestoneArtifacts(dir: string, mid: string): void {
75
72
  writeFileSync(join(msDir, `${mid}-ROADMAP.md`), roadmap);
76
73
  }
77
74
 
78
- // ─── Source-level: verify the merge code exists in the "all complete" path ────
79
-
80
- test("auto-loop 'all milestones complete' path merges before stopping (#962)", () => {
81
- const loopSrc = readFileSync(join(__dirname, "../..", "auto", "phases.ts"), "utf-8");
82
- const resolverSrc = readFileSync(
83
- join(__dirname, "../..", "worktree-resolver.ts"),
84
- "utf-8",
85
- );
86
-
87
- // Find the "incomplete.length === 0" block
88
- const incompleteIdx = loopSrc.indexOf("incomplete.length === 0");
89
- assert.ok(
90
- incompleteIdx > -1,
91
- "auto/phases.ts should have 'incomplete.length === 0' check",
92
- );
93
-
94
- // The merge call must appear BETWEEN the incomplete check and the stopAuto call.
95
- const blockAfterIncomplete = loopSrc.slice(
96
- incompleteIdx,
97
- incompleteIdx + 3000,
98
- );
99
-
100
- assert.ok(
101
- blockAfterIncomplete.includes("deps.resolver.mergeAndExit"),
102
- "auto/phases.ts should call resolver.mergeAndExit in the 'all milestones complete' path",
103
- );
104
-
105
- // The merge should come before stopAuto in this block
106
- const mergePos = blockAfterIncomplete.indexOf("deps.resolver.mergeAndExit");
107
- const stopPos = blockAfterIncomplete.indexOf("stopAuto");
108
- assert.ok(
109
- mergePos < stopPos,
110
- "resolver.mergeAndExit should be called before stopAuto in the 'all complete' path",
111
- );
112
-
113
- const helperIdx = resolverSrc.indexOf("mergeAndExit(milestoneId");
114
- assert.ok(
115
- helperIdx > -1,
116
- "WorktreeResolver.mergeAndExit helper should exist",
117
- );
118
- const helperBlock = resolverSrc.slice(helperIdx, helperIdx + 2600);
119
- assert.ok(
120
- helperBlock.includes('mode === "worktree"') ||
121
- helperBlock.includes('mode: "worktree"'),
122
- "WorktreeResolver.mergeAndExit should handle worktree mode",
123
- );
124
- assert.ok(
125
- helperBlock.includes('mode === "branch"') ||
126
- helperBlock.includes('mode: "branch"'),
127
- "WorktreeResolver.mergeAndExit should handle branch mode",
128
- );
129
- });
75
+ // Note: the prior phases.ts / worktree-resolver.ts source-grep block was
76
+ // removed under #4825 — it asserted `deps.resolver.mergeAndExit` appears
77
+ // before `stopAuto` via indexOf positions in the source text, which broke
78
+ // on any helper refactor without catching a real regression. The two
79
+ // integration tests below exercise the merge-before-stop behaviour end
80
+ // to end through real git worktrees.
130
81
 
131
82
  // ─── Integration: single milestone completes → merged to main ────────────────
132
83
 
@@ -161,11 +161,27 @@ describe('doctor-proactive', async () => {
161
161
  });
162
162
 
163
163
  test('escalation: no double escalation', () => {
164
- // Don't reset should already be escalated from previous test
164
+ // Self-contained: drive the escalated state from scratch in this test.
165
+ // Previously this relied on module-singleton state left over from the
166
+ // preceding 'escalation: at threshold' test, which silently broke under
167
+ // filtered/parallel/reordered runs (the fallback `shouldEscalate: false`
168
+ // path was satisfied by the wrong reason — see #4828).
169
+ resetProactiveHealing();
170
+ for (let i = 0; i < 5; i++) {
171
+ recordHealthSnapshot(0, 0, 0); // older clean snapshots
172
+ }
173
+ for (let i = 0; i < 5; i++) {
174
+ recordHealthSnapshot(2, 1, 0); // recent error snapshots → degrading trend
175
+ }
176
+ // First check: trigger escalation.
177
+ const first = checkHealEscalation(2, [{ code: "test", message: "test error", unitId: "M001/S01" }]);
178
+ assert.deepStrictEqual(first.shouldEscalate, true, "precondition: first call escalates");
179
+
180
+ // Second check: same session, must NOT double-escalate.
165
181
  recordHealthSnapshot(2, 0, 0);
166
182
  const result = checkHealEscalation(2, [{ code: "test", message: "test error", unitId: "M001/S01" }]);
167
183
  assert.deepStrictEqual(result.shouldEscalate, false, "no double escalation in same session");
168
- assert.ok(result.reason.includes("already escalated"), "reason explains why no escalation");
184
+ assert.ok(result.reason.includes("already escalated"), `reason must explain no-re-escalation (got: ${result.reason})`);
169
185
  });
170
186
 
171
187
  test('escalation: deferred when improving', () => {
@@ -140,12 +140,18 @@ for (let i = 1; i <= COMPLETED_COUNT; i++) {
140
140
 
141
141
  // ─── Test: the overall context should be reasonable in size ──────────────
142
142
 
143
- // With 25 completed milestones NOT loading files, the context should be
144
- // significantly smaller than if all files were loaded
143
+ // Invariant (not absolute budget): the per-completed-milestone line
144
+ // contribution should stay small and CONSTANT (not proportional to the
145
+ // size of its CONTEXT.md / SUMMARY.md). With 50 lines of fixture text
146
+ // per completed CONTEXT.md, a naive loader would produce >=50 lines per
147
+ // completed milestone (>1250 lines for 25 milestones). The fix emits a
148
+ // short summary section plus separator per completed milestone, which
149
+ // stays well under 10 lines/milestone regardless of CONTEXT.md size.
145
150
  const contextLines = context.split("\n").length;
151
+ const avgLinesPerCompletedMilestone = contextLines / COMPLETED_COUNT;
146
152
  assertTrue(
147
- contextLines < 200,
148
- `Context should be concise (got ${contextLines} lines); completed milestones should not inflate it`,
153
+ avgLinesPerCompletedMilestone < 10,
154
+ `Completed milestones should not inflate the context: got ${contextLines} lines across ${COMPLETED_COUNT} completed milestones (~${avgLinesPerCompletedMilestone.toFixed(1)}/milestone)`,
149
155
  );
150
156
 
151
157
  // ─── Cleanup ──────────────────────────────────────────────────────────────
@@ -1155,7 +1155,7 @@ describe("dispatch guard integration", () => {
1155
1155
  assert.ok(existsSync(validationPath), "VALIDATION file should be written");
1156
1156
  const content = readFileSync(validationPath, "utf-8");
1157
1157
  assert.ok(content.includes("verdict: pass"), "should contain pass verdict");
1158
- assert.ok(content.includes("skipped by preference"), "should note it was skipped");
1158
+ assert.ok(content.includes("`skip_milestone_validation` preference"), "should note it was skipped via the preference path (#4781)");
1159
1159
  });
1160
1160
 
1161
1161
  test("rewrite-docs circuit breaker: exceeding MAX attempts resolves all overrides", async () => {
@@ -8,6 +8,7 @@ import assert from "node:assert/strict";
8
8
  import { readFileSync } from "node:fs";
9
9
  import { join, dirname } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
+ import { extractSourceRegion } from "./test-helpers.ts";
11
12
 
12
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
14
 
@@ -62,7 +63,7 @@ describe("interactive routing bypass (#3962)", () => {
62
63
  );
63
64
  // The function should check isAutoMode before routing synthesis
64
65
  const fnIdx = modelSelectionSrc.indexOf("function resolvePreferredModelConfig");
65
- const fnBody = modelSelectionSrc.slice(fnIdx, fnIdx + 900);
66
+ const fnBody = extractSourceRegion(modelSelectionSrc, "function resolvePreferredModelConfig");
66
67
  assert.ok(
67
68
  fnBody.includes("isAutoMode"),
68
69
  "resolvePreferredModelConfig should accept isAutoMode parameter",
@@ -137,8 +138,13 @@ describe("model downgrade notifications always visible (#3962)", () => {
137
138
  const escalatedIdx = modelSelectionSrc.indexOf("if (escalated)");
138
139
  assert.ok(escalatedIdx > 0, "escalation block should exist");
139
140
 
140
- // Get the block from "if (escalated)" to the next closing brace pattern
141
- const block = modelSelectionSrc.slice(escalatedIdx, escalatedIdx + 400);
141
+ // Get the block from "if (escalated)" to the next distinctive marker
142
+ // (the capability-override loading that immediately follows).
143
+ const block = extractSourceRegion(
144
+ modelSelectionSrc,
145
+ "if (escalated)",
146
+ "// Load user capability overrides",
147
+ );
142
148
  assert.ok(
143
149
  block.includes("Tier escalation:"),
144
150
  "escalation block should contain the notification",
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { randomUUID } from "node:crypto";
@@ -126,11 +126,8 @@ test("guided-flow stale paused-session scenario is suppressed when no resumable
126
126
  }
127
127
  });
128
128
 
129
- test("guided-flow source uses step-aware resume and clears stale paused metadata without changing discuss handoff semantics", () => {
130
- const source = readFileSync(join(import.meta.dirname, "..", "guided-flow.ts"), "utf-8");
131
- assert.ok(source.includes('const interrupted = await assessInterruptedSession(basePath);'));
132
- assert.ok(source.includes('resumeLabel = interrupted.pausedSession?.stepMode'));
133
- assert.ok(source.includes('step: interrupted.pausedSession?.stepMode ?? false'));
134
- assert.ok(source.includes('unlinkSync(join(gsdRoot(basePath), "runtime", "paused-session.json"))'));
135
- assert.ok(source.includes('pendingAutoStartMap.set(basePath,'));
136
- });
129
+ // Note: the prior source-grep test that scanned guided-flow.ts for five
130
+ // string literals was removed under #4827. The invariants it encoded
131
+ // (step-aware resume + stale paused-session cleanup + pendingAutoStartMap
132
+ // side effect) should be covered by a runtime drive of guided-flow —
133
+ // tracked as a follow-up.
@@ -15,7 +15,7 @@ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, realpathSy
15
15
  import { join } from 'node:path';
16
16
  import { tmpdir } from 'node:os';
17
17
  import { GSD_ROOT_FILES, resolveGsdRootFile } from '../paths.ts';
18
- import { inlineGsdRootFile } from '../auto-prompts.ts';
18
+ import { inlineGsdRootFile, inlineKnowledgeBudgeted } from '../auto-prompts.ts';
19
19
  import { appendKnowledge } from '../files.ts';
20
20
  import { loadKnowledgeBlock } from '../bootstrap/system-context.ts';
21
21
 
@@ -248,3 +248,95 @@ test('loadKnowledgeBlock: reports globalSizeKb above 4KB threshold', () => {
248
248
 
249
249
  rmSync(tmp, { recursive: true, force: true });
250
250
  });
251
+
252
+ // ─── inlineKnowledgeBudgeted — issue #4719 ─────────────────────────────────
253
+ // Milestone-phase prompts must not inject the full KNOWLEDGE.md. The budgeted
254
+ // helper scopes by milestone-level keywords and caps the injected size.
255
+
256
+ test('inlineKnowledgeBudgeted: returns scoped H3 entries for single-H2 file', async () => {
257
+ const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
258
+ const gsdDir = join(tmp, '.gsd');
259
+ mkdirSync(gsdDir, { recursive: true });
260
+
261
+ const content = `# Project Knowledge
262
+
263
+ ## Patterns
264
+
265
+ ### Database: prepared statements
266
+ Always use prepared statements with SQLite.
267
+
268
+ ### API: versioned paths
269
+ Use /v1/resource style versioning.
270
+
271
+ ### Testing: node:test
272
+ Prefer node:test over external frameworks.
273
+ `;
274
+ writeFileSync(join(gsdDir, 'KNOWLEDGE.md'), content);
275
+
276
+ const result = await inlineKnowledgeBudgeted(tmp, ['database']);
277
+ assert.ok(result !== null, 'should return content');
278
+ assert.ok(result!.includes('Database: prepared statements'), 'includes matching H3');
279
+ assert.ok(!result!.includes('API: versioned paths'), 'excludes non-matching H3');
280
+
281
+ rmSync(tmp, { recursive: true, force: true });
282
+ });
283
+
284
+ test('inlineKnowledgeBudgeted: caps payload below budget for large files', async () => {
285
+ const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
286
+ const gsdDir = join(tmp, '.gsd');
287
+ mkdirSync(gsdDir, { recursive: true });
288
+
289
+ // Build a 200KB KNOWLEDGE with 500 H3 entries all matching 'shared'
290
+ const entries = Array.from({ length: 500 }, (_, i) =>
291
+ `### Entry ${i}: shared topic\n${'filler text '.repeat(30)}\n`,
292
+ ).join('\n');
293
+ const content = `# Project Knowledge\n\n## Patterns\n\n${entries}`;
294
+ writeFileSync(join(gsdDir, 'KNOWLEDGE.md'), content);
295
+
296
+ const BUDGET_CHARS = 30_000;
297
+ const result = await inlineKnowledgeBudgeted(tmp, ['shared'], { maxChars: BUDGET_CHARS });
298
+ assert.ok(result !== null, 'should return content');
299
+ // Allow some overhead for header formatting, but must stay close to budget
300
+ assert.ok(
301
+ result!.length <= BUDGET_CHARS + 500,
302
+ `payload ${result!.length} chars should be <= budget ${BUDGET_CHARS} (+overhead)`,
303
+ );
304
+ // Far smaller than the raw file
305
+ assert.ok(
306
+ result!.length < content.length / 4,
307
+ `payload should be much smaller than full content (${content.length} chars)`,
308
+ );
309
+ assert.match(
310
+ result!,
311
+ /\[\.\.\.truncated \d+ chars; rerun with narrower scope if needed\]/,
312
+ 'should include truncation note when budget is exceeded',
313
+ );
314
+
315
+ rmSync(tmp, { recursive: true, force: true });
316
+ });
317
+
318
+ test('inlineKnowledgeBudgeted: returns null when no KNOWLEDGE.md exists', async () => {
319
+ const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
320
+ const gsdDir = join(tmp, '.gsd');
321
+ mkdirSync(gsdDir, { recursive: true });
322
+
323
+ const result = await inlineKnowledgeBudgeted(tmp, ['database']);
324
+ assert.strictEqual(result, null);
325
+
326
+ rmSync(tmp, { recursive: true, force: true });
327
+ });
328
+
329
+ test('inlineKnowledgeBudgeted: returns null when no entries match', async () => {
330
+ const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
331
+ const gsdDir = join(tmp, '.gsd');
332
+ mkdirSync(gsdDir, { recursive: true });
333
+ writeFileSync(
334
+ join(gsdDir, 'KNOWLEDGE.md'),
335
+ '# Project Knowledge\n\n## Patterns\n\n### Database\nuse it\n',
336
+ );
337
+
338
+ const result = await inlineKnowledgeBudgeted(tmp, ['nonexistent']);
339
+ assert.strictEqual(result, null);
340
+
341
+ rmSync(tmp, { recursive: true, force: true });
342
+ });
@@ -1,12 +1,19 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { readFileSync } from "node:fs";
4
3
 
5
4
  import {
6
5
  _buildMcpChildEnvForTest,
7
6
  _buildMcpTrustConfirmOptionsForTest,
8
7
  } from "../../mcp-client/index.ts";
9
8
 
9
+ // Note: four source-grep tests that scanned `mcp-client/index.ts` for
10
+ // Map<> shapes, catch-block structure, and closeAll body were removed
11
+ // under #4827. They encoded implementation shape rather than behaviour —
12
+ // any refactor (extracted helper, different Map key type, rearranged
13
+ // cleanup order) broke the greps without a real regression. Runtime
14
+ // coverage of connectServer/closeAll with a mocked failing transport
15
+ // is tracked as a follow-up.
16
+
10
17
  test("MCP stdio child env only includes safe inherited keys plus explicit config env", () => {
11
18
  const previousSecret = process.env.SECRET_MCP_TEST_TOKEN;
12
19
  const previousPath = process.env.PATH;
@@ -38,39 +45,3 @@ test("MCP stdio trust confirmation is abort-aware", () => {
38
45
  assert.equal(options.timeout, 120_000);
39
46
  assert.equal(options.signal, controller.signal);
40
47
  });
41
-
42
- test("MCP client uses a single in-flight connection per canonical server", () => {
43
- const source = readFileSync(new URL("../../mcp-client/index.ts", import.meta.url), "utf8");
44
-
45
- assert.match(source, /const pendingConnections = new Map<string, Promise<Client>>\(\)/);
46
- assert.match(source, /const pending = pendingConnections\.get\(config\.name\)/);
47
- assert.match(source, /pendingConnections\.set\(config\.name, connectionPromise\)/);
48
- assert.match(source, /pendingConnections\.delete\(config\.name\)/);
49
- assert.match(source, /env: config\.env \?\? \{\}/);
50
- });
51
-
52
- test("MCP stdio trust is persisted only after a successful connection", () => {
53
- const source = readFileSync(new URL("../../mcp-client/index.ts", import.meta.url), "utf8");
54
- const connectIndex = source.indexOf("await client.connect(transport");
55
- const trustIndex = source.indexOf("trustedStdioServers.add(approvedTrustKey)");
56
-
57
- assert.ok(connectIndex > -1, "connectServer should await client.connect");
58
- assert.ok(trustIndex > connectIndex, "trust should be recorded after client.connect succeeds");
59
- assert.doesNotMatch(source, /assertTrustedStdioServer[\s\S]*trustedStdioServers\.add\(trustKey\)/);
60
- });
61
-
62
- test("MCP client closes transports after failed connection attempts", () => {
63
- const source = readFileSync(new URL("../../mcp-client/index.ts", import.meta.url), "utf8");
64
-
65
- assert.match(source, /catch \(err\) \{[\s\S]*await transport\.close\(\)/);
66
- assert.match(source, /catch \(err\) \{[\s\S]*await client\.close\(\)/);
67
- assert.match(source, /catch \(err\) \{[\s\S]*throw err/);
68
- });
69
-
70
- test("MCP client clears process-local trust and closes transports on session cleanup", () => {
71
- const source = readFileSync(new URL("../../mcp-client/index.ts", import.meta.url), "utf8");
72
-
73
- assert.match(source, /async function closeAll\(\)[\s\S]*await conn\.transport\.close\(\)/);
74
- assert.match(source, /async function closeAll\(\)[\s\S]*pendingConnections\.clear\(\)/);
75
- assert.match(source, /async function closeAll\(\)[\s\S]*trustedStdioServers\.clear\(\)/);
76
- });
@@ -1,4 +1,4 @@
1
- import { parseMemoryResponse, _resetExtractionState, buildMemoryLLMCall } from '../memory-extractor.ts';
1
+ import { parseMemoryResponse, buildMemoryLLMCall } from '../memory-extractor.ts';
2
2
  import {
3
3
  openDatabase,
4
4
  closeDatabase,
@@ -159,16 +159,6 @@ test('integration: mixed action lifecycle', () => {
159
159
  closeDatabase();
160
160
  });
161
161
 
162
- // ═══════════════════════════════════════════════════════════════════════════
163
- // memory-extractor: _resetExtractionState
164
- // ═══════════════════════════════════════════════════════════════════════════
165
-
166
- test('memory-extractor: reset extraction state', () => {
167
- // Just verify it doesn't throw
168
- _resetExtractionState();
169
- assert.ok(true, '_resetExtractionState should not throw');
170
- });
171
-
172
162
  // ═══════════════════════════════════════════════════════════════════════════
173
163
  // memory-extractor: buildMemoryLLMCall resolves OAuth API key via modelRegistry
174
164
  // Regression test for #2959 — OAuth users had broken memory extraction
@@ -200,8 +190,8 @@ test('memory-extractor: buildMemoryLLMCall resolves API key from modelRegistry f
200
190
  assert.ok(llmCallFn !== null, 'buildMemoryLLMCall should return a function when models are available');
201
191
 
202
192
  // The function should have resolved the API key eagerly via modelRegistry.getApiKey.
203
- // Give the async getApiKey a tick to resolve.
204
- await new Promise(resolve => setTimeout(resolve, 50));
193
+ // Await the exposed promise deterministically instead of polling via setTimeout.
194
+ await llmCallFn!.apiKeyReady;
205
195
  assert.ok(getApiKeyCalled, 'buildMemoryLLMCall must call modelRegistry.getApiKey() to resolve OAuth tokens');
206
196
  });
207
197
 
@@ -246,8 +236,8 @@ test('memory-extractor: buildMemoryLLMCall prefers haiku model', async () => {
246
236
  const llmCallFn = buildMemoryLLMCall(ctx);
247
237
  assert.ok(llmCallFn !== null, 'should return a function');
248
238
 
249
- // Wait for the async getApiKey to resolve
250
- await new Promise(resolve => setTimeout(resolve, 50));
239
+ // Await the exposed API-key-ready promise deterministically.
240
+ await llmCallFn!.apiKeyReady;
251
241
  assert.strictEqual(resolvedModelId, 'claude-3-5-haiku-20241022',
252
242
  'should resolve API key for haiku model, not sonnet');
253
243
  });