gsd-pi 2.44.0 → 2.45.0-dev.1afbdaa

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 (608) hide show
  1. package/README.md +30 -12
  2. package/dist/help-text.js +1 -1
  3. package/dist/loader.js +34 -0
  4. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  5. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  6. package/dist/resources/extensions/gsd/auto/phases.js +63 -77
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  8. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  9. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  11. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  12. package/dist/resources/extensions/gsd/auto-start.js +33 -5
  13. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  14. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  15. package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
  16. package/dist/resources/extensions/gsd/auto.js +42 -60
  17. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  19. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  20. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  21. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  22. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  23. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  24. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  25. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  26. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  27. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  29. package/dist/resources/extensions/gsd/db-writer.js +40 -22
  30. package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
  31. package/dist/resources/extensions/gsd/doctor.js +13 -3
  32. package/dist/resources/extensions/gsd/git-service.js +8 -3
  33. package/dist/resources/extensions/gsd/gsd-db.js +28 -4
  34. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  35. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  36. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  37. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  38. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  39. package/dist/resources/extensions/gsd/preferences.js +17 -5
  40. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  41. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  42. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  43. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  47. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  48. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  49. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  50. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  51. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  52. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  54. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  55. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  56. package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
  57. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  58. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  59. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  60. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  61. package/dist/resources/extensions/gsd/rethink.js +115 -0
  62. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  63. package/dist/resources/extensions/gsd/state.js +48 -3
  64. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  65. package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
  66. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  67. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  68. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  69. package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
  70. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
  71. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  72. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  73. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  74. package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
  75. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  76. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  77. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  78. package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
  79. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  80. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  81. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  82. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  83. package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
  84. package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
  85. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  86. package/dist/resources/extensions/mcp-client/index.js +14 -0
  87. package/dist/resources/extensions/voice/index.js +11 -16
  88. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  89. package/dist/web/standalone/.next/BUILD_ID +1 -1
  90. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  91. package/dist/web/standalone/.next/build-manifest.json +4 -4
  92. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  93. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  94. package/dist/web/standalone/.next/required-server-files.json +3 -3
  95. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  96. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  98. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  106. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  112. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  115. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  160. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  180. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  182. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  184. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  186. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/index.html +1 -1
  196. package/dist/web/standalone/.next/server/app/index.rsc +6 -6
  197. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  198. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
  199. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  200. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  201. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  202. package/dist/web/standalone/.next/server/app/page.js +2 -2
  203. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  205. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  206. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  207. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/middleware.js +2 -2
  210. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  211. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  212. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  213. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  214. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  215. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  216. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  217. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  218. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  219. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  220. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  222. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  223. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  224. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  225. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  226. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  227. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  228. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  229. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  230. package/dist/web/standalone/server.js +1 -1
  231. package/package.json +2 -1
  232. package/packages/native/dist/stream-process/index.js +2 -2
  233. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  234. package/packages/native/src/stream-process/index.ts +2 -2
  235. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  236. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  237. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  238. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  239. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  240. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  243. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  245. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  247. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  249. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  250. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  251. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  252. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  253. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  254. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  255. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  256. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  257. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  258. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  259. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  260. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  261. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  262. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  263. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  264. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  265. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
  266. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  267. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  268. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  269. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  270. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  272. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  274. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  276. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  278. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  280. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  281. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/main.js +17 -0
  283. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  284. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  285. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  286. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  287. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  288. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  289. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  290. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  291. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  293. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  294. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  295. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  297. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  298. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  299. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  300. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  302. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  304. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  305. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  306. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  307. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  308. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  309. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  310. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  311. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  312. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  313. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  314. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  315. package/packages/pi-coding-agent/package.json +1 -1
  316. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  317. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  318. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  319. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  320. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  321. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  322. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  323. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  324. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  325. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  326. package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
  327. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  328. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  329. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  330. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  331. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  332. package/packages/pi-coding-agent/src/main.ts +19 -0
  333. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  334. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  335. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  336. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  337. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  338. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  339. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  340. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  341. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  342. package/pkg/package.json +1 -1
  343. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  344. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  345. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  346. package/src/resources/extensions/gsd/auto/phases.ts +69 -91
  347. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  348. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  349. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  350. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  351. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  352. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  353. package/src/resources/extensions/gsd/auto-start.ts +40 -5
  354. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  355. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  356. package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
  357. package/src/resources/extensions/gsd/auto.ts +44 -86
  358. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
  359. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  360. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  361. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  362. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  363. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  364. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  365. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  366. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  367. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  368. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  369. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  370. package/src/resources/extensions/gsd/db-writer.ts +41 -27
  371. package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
  372. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  373. package/src/resources/extensions/gsd/doctor.ts +13 -4
  374. package/src/resources/extensions/gsd/git-service.ts +6 -2
  375. package/src/resources/extensions/gsd/gsd-db.ts +32 -4
  376. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  377. package/src/resources/extensions/gsd/journal.ts +6 -1
  378. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  379. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  380. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  381. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  382. package/src/resources/extensions/gsd/preferences.ts +18 -4
  383. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  384. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  385. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  386. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  387. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  388. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  389. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  390. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  391. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  392. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  393. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  394. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  395. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  396. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  397. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  398. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  399. package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
  400. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  401. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  402. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  403. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  404. package/src/resources/extensions/gsd/rethink.ts +154 -0
  405. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  406. package/src/resources/extensions/gsd/state.ts +49 -1
  407. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  408. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  409. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +19 -29
  410. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  411. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  412. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  413. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  414. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  415. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  416. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  417. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  418. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  419. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  420. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  421. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  422. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  423. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  424. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  425. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +134 -59
  426. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +15 -14
  427. package/src/resources/extensions/gsd/tests/complete-task.test.ts +27 -12
  428. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  429. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  430. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  431. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  432. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +26 -40
  433. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  434. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  435. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  436. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  437. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  438. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  439. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  440. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  441. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  442. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  443. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  444. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  445. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  446. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  447. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  448. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  449. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  450. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  451. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  452. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  453. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  454. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  455. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  456. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  457. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  458. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  459. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  460. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  461. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  462. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  463. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  464. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  465. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  466. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  467. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  468. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  469. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  470. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  471. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  472. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  473. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  474. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  475. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +82 -103
  476. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  477. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  478. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  479. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  480. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  481. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  482. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +33 -42
  483. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  484. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  485. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  486. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  487. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  488. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  489. package/src/resources/extensions/gsd/tests/memory-store.test.ts +81 -94
  490. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  491. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  492. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  493. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  494. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  495. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  496. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  497. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  498. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  499. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  500. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +75 -37
  501. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  502. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  503. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +39 -53
  504. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  505. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  506. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +24 -29
  507. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  508. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  509. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  510. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  511. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  512. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  513. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  514. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  515. package/src/resources/extensions/gsd/tests/preferences.test.ts +34 -9
  516. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  517. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
  518. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  519. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  520. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  521. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  522. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  523. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  524. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  525. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  526. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  527. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  528. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  529. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  530. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  531. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  532. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  533. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  534. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  535. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  536. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -47
  537. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  538. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  539. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  540. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  541. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  542. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  543. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  544. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  545. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  546. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  547. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  548. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  549. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  550. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  551. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  552. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  553. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  554. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  555. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  556. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  557. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  558. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  559. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  560. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  561. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  562. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  563. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  564. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  565. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  566. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  567. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  568. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  569. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  570. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  571. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  572. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  573. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  574. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  575. package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
  576. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  577. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  578. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  579. package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
  580. package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
  581. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  582. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  583. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  584. package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
  585. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  586. package/src/resources/extensions/gsd/types.ts +8 -0
  587. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  588. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  589. package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
  590. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  591. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  592. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  593. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  594. package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
  595. package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
  596. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  597. package/src/resources/extensions/mcp-client/index.ts +20 -0
  598. package/src/resources/extensions/voice/index.ts +11 -21
  599. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  600. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  601. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  602. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
  603. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  604. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  605. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  606. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  607. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
  608. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
@@ -62,7 +62,7 @@ function toScope(local: boolean): LifecycleHookScope {
62
62
  return local ? "project" : "user";
63
63
  }
64
64
 
65
- function readManifestRuntimeDeps(dir: string): string[] {
65
+ export function readManifestRuntimeDeps(dir: string): string[] {
66
66
  const manifestPath = join(dir, "extension-manifest.json");
67
67
  if (!existsSync(manifestPath)) return [];
68
68
  try {
@@ -73,7 +73,7 @@ function readManifestRuntimeDeps(dir: string): string[] {
73
73
  }
74
74
  }
75
75
 
76
- function collectRuntimeDependencies(installedPath: string, entryPaths: string[]): string[] {
76
+ export function collectRuntimeDependencies(installedPath: string, entryPaths: string[]): string[] {
77
77
  const deps = new Set<string>();
78
78
  const candidateDirs = new Set<string>([installedPath, ...entryPaths.map((entryPath) => dirname(entryPath))]);
79
79
  for (const dir of candidateDirs) {
@@ -84,7 +84,7 @@ function collectRuntimeDependencies(installedPath: string, entryPaths: string[])
84
84
  return Array.from(deps);
85
85
  }
86
86
 
87
- function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appName: string): void {
87
+ export function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appName: string): void {
88
88
  const missing: string[] = [];
89
89
  for (const dep of runtimeDeps) {
90
90
  const result = spawnSync(dep, ["--version"], { encoding: "utf-8", timeout: 5000 });
@@ -99,7 +99,7 @@ function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appNam
99
99
  );
100
100
  }
101
101
 
102
- function resolveLocalSourcePath(source: string, cwd: string): string | undefined {
102
+ export function resolveLocalSourcePath(source: string, cwd: string): string | undefined {
103
103
  const trimmed = source.trim();
104
104
  if (!trimmed) return undefined;
105
105
  if (trimmed.startsWith("npm:")) return undefined;
@@ -193,13 +193,19 @@ function getLegacyExportCandidates(phase: LifecycleHookPhase): string[] {
193
193
  return [phase];
194
194
  }
195
195
 
196
+ const _legacyModuleCache = new Map<string, Record<string, unknown>>();
197
+
196
198
  async function runLegacyExportHook(
197
199
  entryPath: string,
198
200
  phase: LifecycleHookPhase,
199
201
  context: LifecycleHookContext,
200
202
  ): Promise<LifecycleHookHandler | null> {
201
203
  try {
202
- const module = await importExtensionModule<Record<string, unknown>>(import.meta.url, pathToFileURL(entryPath).href);
204
+ let module = _legacyModuleCache.get(entryPath);
205
+ if (!module) {
206
+ module = await importExtensionModule<Record<string, unknown>>(import.meta.url, pathToFileURL(entryPath).href);
207
+ _legacyModuleCache.set(entryPath, module);
208
+ }
203
209
  for (const exportName of getLegacyExportCandidates(phase)) {
204
210
  const candidate = module[exportName];
205
211
  if (typeof candidate === "function") {
@@ -0,0 +1,45 @@
1
+ /**
2
+ * local-model-check.ts — Utility to detect if a model baseUrl is local.
3
+ *
4
+ * Leaf module with zero transitive dependencies on TypeScript parameter properties.
5
+ * Used by ModelRegistry and tests.
6
+ */
7
+
8
+ /**
9
+ * Check if a model's baseUrl points to a local endpoint.
10
+ * Returns true for localhost, 127.0.0.1, 0.0.0.0, ::1, or unix socket paths.
11
+ * Returns false if baseUrl is empty (cloud provider) or points to a remote host.
12
+ */
13
+ export function isLocalModel(model: { baseUrl: string }): boolean {
14
+ const url = model.baseUrl;
15
+ if (!url) return false;
16
+
17
+ // Unix socket paths
18
+ if (url.startsWith("unix://") || url.startsWith("unix:")) return true;
19
+
20
+ try {
21
+ const parsed = new URL(url);
22
+ const hostname = parsed.hostname;
23
+ if (
24
+ hostname === "localhost" ||
25
+ hostname === "127.0.0.1" ||
26
+ hostname === "0.0.0.0" ||
27
+ hostname === "::1" ||
28
+ hostname === "[::1]"
29
+ ) {
30
+ return true;
31
+ }
32
+ } catch {
33
+ // If URL parsing fails, check raw string for local patterns
34
+ if (
35
+ url.includes("localhost") ||
36
+ url.includes("127.0.0.1") ||
37
+ url.includes("0.0.0.0") ||
38
+ url.includes("[::1]")
39
+ ) {
40
+ return true;
41
+ }
42
+ }
43
+
44
+ return false;
45
+ }
@@ -1,6 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
- import type { Api, Model } from "@gsd/pi-ai";
3
+ import type { Api, Model, SimpleStreamOptions, Context, AssistantMessageEventStream } from "@gsd/pi-ai";
4
+ import { getApiProvider } from "@gsd/pi-ai";
4
5
  import type { AuthStorage } from "./auth-storage.js";
5
6
  import { ModelRegistry } from "./model-registry.js";
6
7
 
@@ -17,11 +18,11 @@ function createRegistry(hasAuthFn?: (provider: string) => boolean): ModelRegistr
17
18
  return new ModelRegistry(authStorage, undefined);
18
19
  }
19
20
 
20
- function createProviderModel(id: string): NonNullable<Parameters<ModelRegistry["registerProvider"]>[1]["models"]>[number] {
21
+ function createProviderModel(id: string, api?: string): NonNullable<Parameters<ModelRegistry["registerProvider"]>[1]["models"]>[number] {
21
22
  return {
22
23
  id,
23
24
  name: id,
24
- api: "openai-completions",
25
+ api: (api ?? "openai-completions") as Api,
25
26
  reasoning: false,
26
27
  input: ["text"],
27
28
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@@ -34,34 +35,89 @@ function findModel(registry: ModelRegistry, provider: string, id: string): Model
34
35
  return registry.getAvailable().find((m) => m.provider === provider && m.id === id);
35
36
  }
36
37
 
38
+ function makeModel(provider: string, id: string, api: string): Model<Api> {
39
+ return {
40
+ id,
41
+ name: id,
42
+ api: api as Api,
43
+ provider,
44
+ baseUrl: `${provider}:`,
45
+ reasoning: false,
46
+ input: ["text"],
47
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
48
+ contextWindow: 128000,
49
+ maxTokens: 16384,
50
+ };
51
+ }
52
+
53
+ function makeContext(): Context {
54
+ return {
55
+ systemPrompt: "test",
56
+ messages: [{ role: "user", content: "hello", timestamp: Date.now() }],
57
+ };
58
+ }
59
+
60
+ /** No-op streamSimple for tests that need one to pass validation but don't inspect it. */
61
+ const noopStreamSimple = (_model: Model<Api>, _context: Context, _options?: SimpleStreamOptions) => {
62
+ return {
63
+ [Symbol.asyncIterator]() { return { next: async () => ({ value: undefined, done: true as const }) }; },
64
+ result: () => Promise.resolve({ role: "assistant" as const, content: [], api: "test" as Api, provider: "test", model: "test", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, stopReason: "stop" as const, timestamp: Date.now() }),
65
+ push: () => {},
66
+ end: () => {},
67
+ } as unknown as AssistantMessageEventStream;
68
+ };
69
+
70
+ /** Create a spy streamSimple that captures the options it receives and returns a stub stream. */
71
+ function createStreamSpy(): {
72
+ streamSimple: (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
73
+ getCapturedOptions: () => SimpleStreamOptions | undefined;
74
+ } {
75
+ let capturedOptions: SimpleStreamOptions | undefined;
76
+ const streamSimple = (_model: Model<Api>, _context: Context, options?: SimpleStreamOptions) => {
77
+ capturedOptions = options;
78
+ // Return a minimal stub that satisfies AssistantMessageEventStream
79
+ return {
80
+ [Symbol.asyncIterator]() { return { next: async () => ({ value: undefined, done: true as const }) }; },
81
+ result: () => Promise.resolve({ role: "assistant" as const, content: [], api: "test" as Api, provider: "test", model: "test", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, stopReason: "stop" as const, timestamp: Date.now() }),
82
+ push: () => {},
83
+ end: () => {},
84
+ } as unknown as AssistantMessageEventStream;
85
+ };
86
+ return { streamSimple, getCapturedOptions: () => capturedOptions };
87
+ }
88
+
37
89
  // ─── Registration ─────────────────────────────────────────────────────────────
38
90
 
39
91
  describe("ModelRegistry authMode — registration", () => {
40
- it("registers externalCli provider without apiKey/oauth", () => {
92
+ it("registers externalCli provider with streamSimple and without apiKey/oauth", () => {
41
93
  const registry = createRegistry();
94
+ const spy = createStreamSpy();
42
95
  assert.doesNotThrow(() => {
43
96
  registry.registerProvider("cli-provider", {
44
97
  authMode: "externalCli",
45
98
  baseUrl: "https://cli.local",
46
99
  api: "openai-completions",
100
+ streamSimple: spy.streamSimple,
47
101
  models: [createProviderModel("cli-model")],
48
102
  });
49
103
  });
50
104
  });
51
105
 
52
- it("registers none provider without apiKey/oauth", () => {
106
+ it("registers none provider with streamSimple and without apiKey/oauth", () => {
53
107
  const registry = createRegistry();
108
+ const spy = createStreamSpy();
54
109
  assert.doesNotThrow(() => {
55
110
  registry.registerProvider("none-provider", {
56
111
  authMode: "none",
57
112
  baseUrl: "http://localhost:11434",
58
113
  api: "openai-completions",
114
+ streamSimple: spy.streamSimple,
59
115
  models: [createProviderModel("local-model")],
60
116
  });
61
117
  });
62
118
  });
63
119
 
64
- it("rejects apiKey provider without apiKey or oauth", () => {
120
+ it("rejects apiKey provider without apiKey or oauth — message mentions authMode", () => {
65
121
  const registry = createRegistry();
66
122
  assert.throws(() => {
67
123
  registry.registerProvider("apikey-provider", {
@@ -70,6 +126,10 @@ describe("ModelRegistry authMode — registration", () => {
70
126
  api: "openai-completions",
71
127
  models: [createProviderModel("model")],
72
128
  });
129
+ }, (err: Error) => {
130
+ assert.ok(err.message.includes("authMode"), "error message must mention authMode");
131
+ assert.ok(err.message.includes("externalCli"), "error message must suggest externalCli");
132
+ return true;
73
133
  });
74
134
  });
75
135
 
@@ -81,6 +141,79 @@ describe("ModelRegistry authMode — registration", () => {
81
141
  api: "openai-completions",
82
142
  models: [createProviderModel("model")],
83
143
  });
144
+ }, (err: Error) => {
145
+ assert.ok(err.message.includes("authMode"), "error message must mention authMode");
146
+ return true;
147
+ });
148
+ });
149
+
150
+ it("rejects externalCli provider without streamSimple", () => {
151
+ const registry = createRegistry();
152
+ assert.throws(() => {
153
+ registry.registerProvider("cli-no-stream", {
154
+ authMode: "externalCli",
155
+ baseUrl: "https://cli.local",
156
+ api: "openai-completions",
157
+ models: [createProviderModel("model")],
158
+ });
159
+ }, (err: Error) => {
160
+ assert.ok(err.message.includes("streamSimple"), "error message must mention streamSimple");
161
+ assert.ok(err.message.includes("externalCli"), "error message must mention authMode");
162
+ return true;
163
+ });
164
+ });
165
+
166
+ it("rejects none provider without streamSimple", () => {
167
+ const registry = createRegistry();
168
+ assert.throws(() => {
169
+ registry.registerProvider("none-no-stream", {
170
+ authMode: "none",
171
+ baseUrl: "http://localhost:11434",
172
+ api: "openai-completions",
173
+ models: [createProviderModel("model")],
174
+ });
175
+ }, (err: Error) => {
176
+ assert.ok(err.message.includes("streamSimple"), "error message must mention streamSimple");
177
+ assert.ok(err.message.includes("none"), "error message must mention authMode");
178
+ return true;
179
+ });
180
+ });
181
+
182
+ it("rejects externalCli provider that also sets apiKey", () => {
183
+ const registry = createRegistry();
184
+ const spy = createStreamSpy();
185
+ assert.throws(() => {
186
+ registry.registerProvider("cli-with-key", {
187
+ authMode: "externalCli",
188
+ baseUrl: "https://cli.local",
189
+ api: "openai-completions",
190
+ apiKey: "SHOULD_NOT_EXIST",
191
+ streamSimple: spy.streamSimple,
192
+ models: [createProviderModel("model")],
193
+ });
194
+ }, (err: Error) => {
195
+ assert.ok(err.message.includes("apiKey"), "error message must mention apiKey");
196
+ assert.ok(err.message.includes("externalCli"), "error message must mention authMode");
197
+ return true;
198
+ });
199
+ });
200
+
201
+ it("rejects none provider that also sets apiKey", () => {
202
+ const registry = createRegistry();
203
+ const spy = createStreamSpy();
204
+ assert.throws(() => {
205
+ registry.registerProvider("none-with-key", {
206
+ authMode: "none",
207
+ baseUrl: "http://localhost:11434",
208
+ api: "openai-completions",
209
+ apiKey: "SHOULD_NOT_EXIST",
210
+ streamSimple: spy.streamSimple,
211
+ models: [createProviderModel("model")],
212
+ });
213
+ }, (err: Error) => {
214
+ assert.ok(err.message.includes("apiKey"), "error message must mention apiKey");
215
+ assert.ok(err.message.includes("none"), "error message must mention authMode");
216
+ return true;
84
217
  });
85
218
  });
86
219
  });
@@ -99,6 +232,7 @@ describe("ModelRegistry authMode — getProviderAuthMode", () => {
99
232
  authMode: "externalCli",
100
233
  baseUrl: "https://cli.local",
101
234
  api: "openai-completions",
235
+ streamSimple: noopStreamSimple,
102
236
  models: [createProviderModel("m")],
103
237
  });
104
238
  assert.equal(registry.getProviderAuthMode("cli"), "externalCli");
@@ -110,6 +244,7 @@ describe("ModelRegistry authMode — getProviderAuthMode", () => {
110
244
  authMode: "none",
111
245
  baseUrl: "http://localhost:11434",
112
246
  api: "openai-completions",
247
+ streamSimple: noopStreamSimple,
113
248
  models: [createProviderModel("m")],
114
249
  });
115
250
  assert.equal(registry.getProviderAuthMode("local"), "none");
@@ -125,6 +260,7 @@ describe("ModelRegistry authMode — isProviderRequestReady", () => {
125
260
  authMode: "externalCli",
126
261
  baseUrl: "https://cli.local",
127
262
  api: "openai-completions",
263
+ streamSimple: noopStreamSimple,
128
264
  models: [createProviderModel("m")],
129
265
  });
130
266
  assert.equal(registry.isProviderRequestReady("cli"), true);
@@ -136,6 +272,7 @@ describe("ModelRegistry authMode — isProviderRequestReady", () => {
136
272
  authMode: "none",
137
273
  baseUrl: "http://localhost:11434",
138
274
  api: "openai-completions",
275
+ streamSimple: noopStreamSimple,
139
276
  models: [createProviderModel("m")],
140
277
  });
141
278
  assert.equal(registry.isProviderRequestReady("local"), true);
@@ -161,6 +298,7 @@ describe("ModelRegistry authMode — isReady callback", () => {
161
298
  authMode: "externalCli",
162
299
  baseUrl: "https://cli.local",
163
300
  api: "openai-completions",
301
+ streamSimple: noopStreamSimple,
164
302
  isReady: () => false,
165
303
  models: [createProviderModel("m")],
166
304
  });
@@ -185,6 +323,7 @@ describe("ModelRegistry authMode — isReady callback", () => {
185
323
  authMode: "externalCli",
186
324
  baseUrl: "https://cli.local",
187
325
  api: "openai-completions",
326
+ streamSimple: noopStreamSimple,
188
327
  isReady: () => true,
189
328
  models: [createProviderModel("m")],
190
329
  });
@@ -197,6 +336,7 @@ describe("ModelRegistry authMode — isReady callback", () => {
197
336
  authMode: "externalCli",
198
337
  baseUrl: "https://cli.local",
199
338
  api: "openai-completions",
339
+ streamSimple: noopStreamSimple,
200
340
  models: [createProviderModel("m")],
201
341
  });
202
342
  // externalCli without isReady → true (default)
@@ -213,6 +353,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
213
353
  authMode: "externalCli",
214
354
  baseUrl: "https://cli.local",
215
355
  api: "openai-completions",
356
+ streamSimple: noopStreamSimple,
216
357
  models: [createProviderModel("cli-model")],
217
358
  });
218
359
  assert.ok(findModel(registry, "cli", "cli-model"));
@@ -224,6 +365,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
224
365
  authMode: "none",
225
366
  baseUrl: "http://localhost:11434",
226
367
  api: "openai-completions",
368
+ streamSimple: noopStreamSimple,
227
369
  models: [createProviderModel("local-model")],
228
370
  });
229
371
  assert.ok(findModel(registry, "local", "local-model"));
@@ -235,6 +377,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
235
377
  authMode: "externalCli",
236
378
  baseUrl: "https://cli.local",
237
379
  api: "openai-completions",
380
+ streamSimple: noopStreamSimple,
238
381
  isReady: () => false,
239
382
  models: [createProviderModel("m")],
240
383
  });
@@ -243,10 +386,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
243
386
 
244
387
  it("excludes apiKey models without stored auth", () => {
245
388
  const registry = createRegistry(() => false);
246
- // Built-in providers have no registeredProviders entry, so authMode defaults to apiKey
247
- // getAvailable filters by isProviderRequestReady → hasAuth → false
248
389
  const available = registry.getAvailable();
249
- // No models should be available since hasAuth returns false for everything
250
390
  assert.equal(available.length, 0);
251
391
  });
252
392
  });
@@ -260,6 +400,7 @@ describe("ModelRegistry authMode — getApiKey", () => {
260
400
  authMode: "externalCli",
261
401
  baseUrl: "https://cli.local",
262
402
  api: "openai-completions",
403
+ streamSimple: noopStreamSimple,
263
404
  models: [createProviderModel("m")],
264
405
  });
265
406
  const model = registry.getAll().find((m) => m.provider === "cli")!;
@@ -272,6 +413,7 @@ describe("ModelRegistry authMode — getApiKey", () => {
272
413
  authMode: "none",
273
414
  baseUrl: "http://localhost:11434",
274
415
  api: "openai-completions",
416
+ streamSimple: noopStreamSimple,
275
417
  models: [createProviderModel("m")],
276
418
  });
277
419
  const model = registry.getAll().find((m) => m.provider === "local")!;
@@ -280,9 +422,153 @@ describe("ModelRegistry authMode — getApiKey", () => {
280
422
 
281
423
  it("delegates to authStorage for apiKey provider", async () => {
282
424
  const registry = createRegistry();
283
- // authStorage.getApiKey returns undefined (no key configured)
284
- // For apiKey providers this is an expected "no key" response, not early exit
285
425
  const key = await registry.getApiKeyForProvider("anthropic");
286
426
  assert.equal(key, undefined);
287
427
  });
288
428
  });
429
+
430
+ // ─── streamSimple apiKey stripping ────────────────────────────────────────────
431
+
432
+ describe("ModelRegistry authMode — streamSimple apiKey boundary", () => {
433
+ it("strips apiKey from options for externalCli provider", () => {
434
+ const registry = createRegistry();
435
+ const spy = createStreamSpy();
436
+ const apiType = `ext-cli-strip-${Date.now()}`;
437
+
438
+ registry.registerProvider("cli-strip", {
439
+ authMode: "externalCli",
440
+ baseUrl: "https://cli.local",
441
+ api: apiType as Api,
442
+ streamSimple: spy.streamSimple,
443
+ models: [createProviderModel("m", apiType)],
444
+ });
445
+
446
+ const provider = getApiProvider(apiType as Api);
447
+ assert.ok(provider, "provider must be registered in api registry");
448
+
449
+ provider.streamSimple(
450
+ makeModel("cli-strip", "m", apiType),
451
+ makeContext(),
452
+ { apiKey: "should-be-stripped", maxTokens: 1024 } as SimpleStreamOptions,
453
+ );
454
+
455
+ const captured = spy.getCapturedOptions();
456
+ assert.ok(captured, "streamSimple must have been called");
457
+ assert.equal("apiKey" in captured, false, "apiKey must not exist in options for externalCli provider");
458
+ assert.equal(captured.maxTokens, 1024, "other options must pass through");
459
+ });
460
+
461
+ it("strips apiKey from options for none provider", () => {
462
+ const registry = createRegistry();
463
+ const spy = createStreamSpy();
464
+ const apiType = `none-strip-${Date.now()}`;
465
+
466
+ registry.registerProvider("none-strip", {
467
+ authMode: "none",
468
+ baseUrl: "http://localhost:11434",
469
+ api: apiType as Api,
470
+ streamSimple: spy.streamSimple,
471
+ models: [createProviderModel("m", apiType)],
472
+ });
473
+
474
+ const provider = getApiProvider(apiType as Api);
475
+ assert.ok(provider, "provider must be registered in api registry");
476
+
477
+ provider.streamSimple(
478
+ makeModel("none-strip", "m", apiType),
479
+ makeContext(),
480
+ { apiKey: "should-be-stripped", maxTokens: 2048 } as SimpleStreamOptions,
481
+ );
482
+
483
+ const captured = spy.getCapturedOptions();
484
+ assert.ok(captured, "streamSimple must have been called");
485
+ assert.equal("apiKey" in captured, false, "apiKey must not exist in options for none provider");
486
+ assert.equal(captured.maxTokens, 2048, "other options must pass through");
487
+ });
488
+
489
+ it("preserves apiKey in options for apiKey provider", () => {
490
+ const registry = createRegistry();
491
+ const spy = createStreamSpy();
492
+ const apiType = `apikey-preserve-${Date.now()}`;
493
+
494
+ registry.registerProvider("apikey-preserve", {
495
+ apiKey: "MY_KEY",
496
+ baseUrl: "https://api.local",
497
+ api: apiType as Api,
498
+ streamSimple: spy.streamSimple,
499
+ models: [createProviderModel("m", apiType)],
500
+ });
501
+
502
+ const provider = getApiProvider(apiType as Api);
503
+ assert.ok(provider, "provider must be registered in api registry");
504
+
505
+ provider.streamSimple(
506
+ makeModel("apikey-preserve", "m", apiType),
507
+ makeContext(),
508
+ { apiKey: "sk-real-key", maxTokens: 4096 } as SimpleStreamOptions,
509
+ );
510
+
511
+ const captured = spy.getCapturedOptions();
512
+ assert.ok(captured, "streamSimple must have been called");
513
+ assert.equal(captured.apiKey, "sk-real-key", "apiKey must be preserved for apiKey provider");
514
+ assert.equal(captured.maxTokens, 4096, "other options must pass through");
515
+ });
516
+
517
+ it("handles undefined options for externalCli provider", () => {
518
+ const registry = createRegistry();
519
+ const spy = createStreamSpy();
520
+ const apiType = `ext-cli-undef-${Date.now()}`;
521
+
522
+ registry.registerProvider("cli-undef", {
523
+ authMode: "externalCli",
524
+ baseUrl: "https://cli.local",
525
+ api: apiType as Api,
526
+ streamSimple: spy.streamSimple,
527
+ models: [createProviderModel("m", apiType)],
528
+ });
529
+
530
+ const provider = getApiProvider(apiType as Api);
531
+ assert.ok(provider, "provider must be registered in api registry");
532
+
533
+ provider.streamSimple(
534
+ makeModel("cli-undef", "m", apiType),
535
+ makeContext(),
536
+ undefined,
537
+ );
538
+
539
+ const captured = spy.getCapturedOptions();
540
+ assert.ok(captured !== undefined, "streamSimple must have been called");
541
+ assert.equal("apiKey" in captured, false, "apiKey must not exist even when options is undefined");
542
+ });
543
+
544
+ it("strips apiKey but preserves signal and other fields for externalCli", () => {
545
+ const registry = createRegistry();
546
+ const spy = createStreamSpy();
547
+ const apiType = `ext-cli-fields-${Date.now()}`;
548
+ const abortController = new AbortController();
549
+
550
+ registry.registerProvider("cli-fields", {
551
+ authMode: "externalCli",
552
+ baseUrl: "https://cli.local",
553
+ api: apiType as Api,
554
+ streamSimple: spy.streamSimple,
555
+ models: [createProviderModel("m", apiType)],
556
+ });
557
+
558
+ const provider = getApiProvider(apiType as Api);
559
+ assert.ok(provider, "provider must be registered in api registry");
560
+
561
+ provider.streamSimple(
562
+ makeModel("cli-fields", "m", apiType),
563
+ makeContext(),
564
+ { apiKey: "strip-me", maxTokens: 8192, signal: abortController.signal, reasoning: "high" } as SimpleStreamOptions,
565
+ );
566
+
567
+ const captured = spy.getCapturedOptions();
568
+ assert.ok(captured, "streamSimple must have been called");
569
+ assert.equal("apiKey" in captured, false, "apiKey must be stripped");
570
+ assert.equal(captured.maxTokens, 8192, "maxTokens must pass through");
571
+ assert.equal(captured.signal, abortController.signal, "signal must pass through");
572
+ assert.equal((captured as Record<string, unknown>).reasoning, "high", "reasoning must pass through");
573
+ });
574
+ });
@@ -28,6 +28,7 @@ import { ModelDiscoveryCache } from "./discovery-cache.js";
28
28
  import type { DiscoveredModel, DiscoveryResult } from "./model-discovery.js";
29
29
  import { getDefaultTTL, getDiscoverableProviders, getDiscoveryAdapter } from "./model-discovery.js";
30
30
  import { clearConfigValueCache, resolveConfigValue, resolveHeaders } from "./resolve-config-value.js";
31
+ import { isLocalModel } from "./local-model-check.js";
31
32
 
32
33
  const Ajv = (AjvModule as any).default || AjvModule;
33
34
  const ajv = new Ajv();
@@ -557,7 +558,7 @@ export class ModelRegistry {
557
558
  async getApiKey(model: Model<Api>, sessionId?: string): Promise<string | undefined> {
558
559
  const authMode = this.getProviderAuthMode(model.provider);
559
560
  if (authMode === "externalCli" || authMode === "none") return undefined;
560
- return this.authStorage.getApiKey(model.provider, sessionId);
561
+ return this.authStorage.getApiKey(model.provider, sessionId, { baseUrl: model.baseUrl });
561
562
  }
562
563
 
563
564
  /**
@@ -622,7 +623,18 @@ export class ModelRegistry {
622
623
  if (!config.api) {
623
624
  throw new Error(`Provider ${providerName}: "api" is required when registering streamSimple.`);
624
625
  }
625
- const streamSimple = config.streamSimple;
626
+ const rawStreamSimple = config.streamSimple;
627
+ const authMode = config.authMode ?? "apiKey";
628
+
629
+ // Keyless providers never see apiKey in options — enforced at registration,
630
+ // not by convention. Prevents undefined from reaching any handler.
631
+ const streamSimple = (authMode === "externalCli" || authMode === "none")
632
+ ? ((model: Model<Api>, context: Context, options?: SimpleStreamOptions) => {
633
+ const { apiKey: _, ...opts } = options ?? {};
634
+ return rawStreamSimple(model, context, opts as SimpleStreamOptions);
635
+ })
636
+ : rawStreamSimple;
637
+
626
638
  registerApiProvider(
627
639
  {
628
640
  api: config.api,
@@ -648,7 +660,22 @@ export class ModelRegistry {
648
660
  }
649
661
  const authMode = config.authMode ?? (config.oauth ? "oauth" : config.apiKey ? "apiKey" : "apiKey");
650
662
  if (authMode === "apiKey" && !config.apiKey && !config.oauth) {
651
- throw new Error(`Provider ${providerName}: "apiKey" or "oauth" is required when defining models.`);
663
+ throw new Error(
664
+ `Provider ${providerName}: "apiKey" or "oauth" is required when authMode is "apiKey" (the default). ` +
665
+ `Set authMode to "externalCli" or "none" for keyless providers.`,
666
+ );
667
+ }
668
+ if ((authMode === "externalCli" || authMode === "none") && !config.streamSimple) {
669
+ throw new Error(
670
+ `Provider ${providerName}: "streamSimple" is required when authMode is "${authMode}". ` +
671
+ `Keyless providers must supply their own stream handler.`,
672
+ );
673
+ }
674
+ if ((authMode === "externalCli" || authMode === "none") && config.apiKey) {
675
+ throw new Error(
676
+ `Provider ${providerName}: "apiKey" cannot be set when authMode is "${authMode}". ` +
677
+ `Keyless providers should not provide API key credentials.`,
678
+ );
652
679
  }
653
680
 
654
681
  // Parse and add new models
@@ -807,6 +834,25 @@ export class ModelRegistry {
807
834
  }
808
835
  return converted;
809
836
  }
837
+
838
+ /**
839
+ * Check if a model's baseUrl points to a local endpoint.
840
+ * Delegates to standalone isLocalModel() function.
841
+ */
842
+ static isLocalModel(model: Model<Api>): boolean {
843
+ return isLocalModel(model);
844
+ }
845
+
846
+ /**
847
+ * Check if all models in the registry are local.
848
+ * Returns true only if every model passes isLocalModel().
849
+ * Returns false if there are no models.
850
+ */
851
+ isAllLocalChain(): boolean {
852
+ const models = this.getAll();
853
+ if (models.length === 0) return false;
854
+ return models.every((m) => isLocalModel(m));
855
+ }
810
856
  }
811
857
 
812
858
  /**
@@ -814,7 +860,8 @@ export class ModelRegistry {
814
860
  */
815
861
  export interface ProviderConfigInput {
816
862
  authMode?: ProviderAuthMode;
817
- /** Optional readiness check. Called by isProviderRequestReady() before default auth checks. */
863
+ /** Optional readiness check. Called by isProviderRequestReady() before default auth checks.
864
+ * Trusted at the same level as extension code — extensions already have arbitrary code execution. */
818
865
  isReady?: () => boolean;
819
866
  baseUrl?: string;
820
867
  apiKey?: string;