gsd-pi 2.63.0 → 2.64.0-dev.9c14bd0

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 (448) hide show
  1. package/README.md +46 -134
  2. package/dist/cli.js +48 -6
  3. package/dist/headless-query.js +11 -1
  4. package/dist/help-text.js +4 -1
  5. package/dist/onboarding.js +15 -8
  6. package/dist/resource-loader.js +18 -3
  7. package/dist/resources/extensions/cmux/index.js +21 -12
  8. package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
  9. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +4 -0
  11. package/dist/resources/extensions/gsd/auto/phases.js +157 -22
  12. package/dist/resources/extensions/gsd/auto/session.js +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
  16. package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
  18. package/dist/resources/extensions/gsd/auto-start.js +10 -21
  19. package/dist/resources/extensions/gsd/auto-timers.js +2 -1
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
  21. package/dist/resources/extensions/gsd/auto-verification.js +138 -1
  22. package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
  23. package/dist/resources/extensions/gsd/auto.js +24 -2
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
  25. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
  26. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
  27. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
  28. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  29. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
  30. package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
  31. package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
  32. package/dist/resources/extensions/gsd/constants.js +42 -0
  33. package/dist/resources/extensions/gsd/db-writer.js +72 -4
  34. package/dist/resources/extensions/gsd/forensics.js +20 -4
  35. package/dist/resources/extensions/gsd/gsd-db.js +64 -17
  36. package/dist/resources/extensions/gsd/guided-flow.js +19 -0
  37. package/dist/resources/extensions/gsd/metrics.js +27 -1
  38. package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
  39. package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
  40. package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
  41. package/dist/resources/extensions/gsd/preferences-types.js +6 -0
  42. package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
  43. package/dist/resources/extensions/gsd/preferences.js +11 -2
  44. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  45. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  48. package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
  49. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  50. package/dist/resources/extensions/gsd/prompts/system.md +4 -7
  51. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  52. package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
  53. package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
  54. package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
  55. package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
  56. package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
  57. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
  58. package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
  59. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
  60. package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
  61. package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
  62. package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
  64. package/dist/resources/extensions/gsd/state.js +74 -14
  65. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  66. package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
  67. package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
  68. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
  69. package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
  70. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
  71. package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
  72. package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
  73. package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
  74. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
  75. package/dist/resources/extensions/mcp-client/auth.js +101 -0
  76. package/dist/resources/extensions/mcp-client/index.js +10 -1
  77. package/dist/resources/extensions/ollama/index.js +28 -22
  78. package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
  79. package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
  80. package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
  81. package/dist/resources/extensions/ollama/ollama-client.js +23 -32
  82. package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
  83. package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
  84. package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
  85. package/dist/update-cmd.js +4 -2
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  88. package/dist/web/standalone/.next/build-manifest.json +3 -3
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/required-server-files.json +4 -4
  91. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  92. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  94. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  102. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  116. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  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.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  137. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  177. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  183. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  189. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  197. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  203. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  205. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  207. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  209. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  210. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  212. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  213. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  217. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  218. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  221. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  223. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/index.html +1 -1
  225. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  226. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  227. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  228. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  229. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  230. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  231. package/dist/web/standalone/.next/server/app/page.js +2 -2
  232. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  233. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  234. package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
  235. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  236. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  237. package/dist/web/standalone/.next/server/middleware.js +2 -2
  238. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  239. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  240. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  241. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  242. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  243. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  244. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  245. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  248. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  249. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  250. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  251. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  252. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  253. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  254. package/dist/web/standalone/server.js +1 -1
  255. package/dist/welcome-screen.js +1 -1
  256. package/package.json +1 -1
  257. package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
  258. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  259. package/packages/pi-agent-core/dist/agent-loop.js +50 -0
  260. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  261. package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
  262. package/packages/pi-agent-core/src/agent-loop.ts +53 -0
  263. package/packages/pi-ai/dist/types.d.ts +16 -1
  264. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  265. package/packages/pi-ai/dist/types.js.map +1 -1
  266. package/packages/pi-ai/src/types.ts +18 -1
  267. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
  268. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  269. package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
  272. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
  274. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
  276. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
  278. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
  280. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
  281. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
  282. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
  283. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  284. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  286. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
  287. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  288. package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
  289. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
  291. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
  293. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  294. package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
  295. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  298. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  299. package/packages/pi-coding-agent/package.json +1 -1
  300. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
  301. package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
  302. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
  303. package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
  304. package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
  305. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  306. package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
  307. package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
  308. package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
  309. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  310. package/pkg/package.json +1 -1
  311. package/src/resources/extensions/cmux/index.ts +18 -12
  312. package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
  313. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
  314. package/src/resources/extensions/gsd/auto/loop.ts +5 -0
  315. package/src/resources/extensions/gsd/auto/phases.ts +194 -33
  316. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  317. package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
  318. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
  319. package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
  320. package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
  321. package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
  322. package/src/resources/extensions/gsd/auto-start.ts +11 -20
  323. package/src/resources/extensions/gsd/auto-timers.ts +2 -1
  324. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  325. package/src/resources/extensions/gsd/auto-verification.ts +190 -2
  326. package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
  327. package/src/resources/extensions/gsd/auto.ts +26 -1
  328. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
  329. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
  330. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
  331. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
  332. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
  333. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
  334. package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
  335. package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
  336. package/src/resources/extensions/gsd/constants.ts +44 -0
  337. package/src/resources/extensions/gsd/db-writer.ts +78 -4
  338. package/src/resources/extensions/gsd/forensics.ts +21 -5
  339. package/src/resources/extensions/gsd/gsd-db.ts +64 -17
  340. package/src/resources/extensions/gsd/guided-flow.ts +22 -0
  341. package/src/resources/extensions/gsd/metrics.ts +28 -1
  342. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
  343. package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
  344. package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
  345. package/src/resources/extensions/gsd/preferences-types.ts +44 -0
  346. package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
  347. package/src/resources/extensions/gsd/preferences.ts +13 -2
  348. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  349. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  350. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  351. package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  352. package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
  353. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  354. package/src/resources/extensions/gsd/prompts/system.md +4 -7
  355. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  356. package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
  357. package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
  358. package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
  359. package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
  360. package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
  361. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
  362. package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
  363. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
  364. package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
  365. package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
  366. package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
  367. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
  368. package/src/resources/extensions/gsd/state.ts +67 -12
  369. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  370. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
  371. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
  372. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
  373. package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
  374. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
  375. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  376. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
  377. package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
  378. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
  379. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
  380. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
  381. package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
  382. package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
  383. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
  384. package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
  385. package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
  386. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
  387. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
  388. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
  389. package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
  390. package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
  391. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
  392. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
  393. package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
  394. package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
  395. package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
  396. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
  397. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
  398. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
  399. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
  400. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
  401. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
  402. package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
  403. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
  404. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
  405. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
  406. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
  407. package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
  408. package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
  409. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
  410. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
  411. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
  412. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
  413. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
  414. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
  415. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
  416. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
  417. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
  418. package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
  419. package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
  420. package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
  421. package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
  422. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
  423. package/src/resources/extensions/gsd/types.ts +44 -22
  424. package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
  425. package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
  426. package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
  427. package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
  428. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
  429. package/src/resources/extensions/mcp-client/auth.ts +149 -0
  430. package/src/resources/extensions/mcp-client/index.ts +16 -1
  431. package/src/resources/extensions/ollama/index.ts +26 -25
  432. package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
  433. package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
  434. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
  435. package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
  436. package/src/resources/extensions/ollama/ollama-client.ts +30 -30
  437. package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
  438. package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
  439. package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
  440. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
  441. package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
  442. package/src/resources/extensions/ollama/types.ts +23 -0
  443. package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
  444. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  445. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  446. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  447. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
  448. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
@@ -19,6 +19,22 @@ import { logWarning } from "./workflow-logger.js";
19
19
  import { deriveState } from "./state.js";
20
20
  import type { GSDState } from "./types.js";
21
21
 
22
+ // ─── Helpers ─────────────────────────────────────────────────────────────
23
+
24
+ /**
25
+ * Strip a leading ID prefix (e.g. "M001: " or "S04: ") from a title
26
+ * to prevent double-prefixing when the renderer adds its own prefix.
27
+ * Handles repeated prefixes (e.g. "M001: M001: M001: Title" → "Title").
28
+ */
29
+ export function stripIdPrefix(title: string, id: string): string {
30
+ const prefix = `${id}: `;
31
+ let result = title;
32
+ while (result.startsWith(prefix)) {
33
+ result = result.slice(prefix.length);
34
+ }
35
+ return result.trim() || title;
36
+ }
37
+
22
38
  // ─── PLAN.md Projection ──────────────────────────────────────────────────
23
39
 
24
40
  /**
@@ -28,7 +44,8 @@ import type { GSDState } from "./types.js";
28
44
  export function renderPlanContent(sliceRow: SliceRow, taskRows: TaskRow[]): string {
29
45
  const lines: string[] = [];
30
46
 
31
- lines.push(`# ${sliceRow.id}: ${sliceRow.title}`);
47
+ const displayTitle = stripIdPrefix(sliceRow.title, sliceRow.id);
48
+ lines.push(`# ${sliceRow.id}: ${displayTitle}`);
32
49
  lines.push("");
33
50
  // #2945: never use full_summary_md/full_uat_md as display fallbacks —
34
51
  // they contain multi-line rendered markdown that corrupts single-line fields.
@@ -97,7 +114,8 @@ export function renderPlanProjection(basePath: string, milestoneId: string, slic
97
114
  export function renderRoadmapContent(milestoneRow: MilestoneRow, sliceRows: SliceRow[]): string {
98
115
  const lines: string[] = [];
99
116
 
100
- lines.push(`# ${milestoneRow.id}: ${milestoneRow.title}`);
117
+ const displayTitle = stripIdPrefix(milestoneRow.title, milestoneRow.id);
118
+ lines.push(`# ${milestoneRow.id}: ${displayTitle}`);
101
119
  lines.push("");
102
120
  lines.push("## Vision");
103
121
  lines.push(milestoneRow.vision || milestoneRow.title || "TBD");
@@ -265,14 +283,14 @@ export function renderStateContent(state: GSDState): string {
265
283
  lines.push("# GSD State", "");
266
284
 
267
285
  const activeSlice = state.activeSlice
268
- ? `${state.activeSlice.id}: ${state.activeSlice.title}`
286
+ ? `${state.activeSlice.id}: ${stripIdPrefix(state.activeSlice.title, state.activeSlice.id)}`
269
287
  : "None";
270
288
 
271
289
  if (state.phase === 'complete' && state.lastCompletedMilestone) {
272
290
  lines.push(`**Last Completed Milestone:** ${state.lastCompletedMilestone.id}: ${state.lastCompletedMilestone.title}`);
273
291
  } else {
274
292
  const activeMilestone = state.activeMilestone
275
- ? `${state.activeMilestone.id}: ${state.activeMilestone.title}`
293
+ ? `${state.activeMilestone.id}: ${stripIdPrefix(state.activeMilestone.title, state.activeMilestone.id)}`
276
294
  : "None";
277
295
  lines.push(`**Active Milestone:** ${activeMilestone}`);
278
296
  }
@@ -286,7 +304,7 @@ export function renderStateContent(state: GSDState): string {
286
304
 
287
305
  for (const entry of state.registry) {
288
306
  const glyph = entry.status === "complete" ? "\u2705" : entry.status === "active" ? "\uD83D\uDD04" : entry.status === "parked" ? "\u23F8\uFE0F" : "\u2B1C";
289
- lines.push(`- ${glyph} **${entry.id}:** ${entry.title}`);
307
+ lines.push(`- ${glyph} **${entry.id}:** ${stripIdPrefix(entry.title, entry.id)}`);
290
308
  }
291
309
 
292
310
  lines.push("");
@@ -113,6 +113,22 @@ export function worktreeBranchName(name: string): string {
113
113
  return `worktree/${name}`;
114
114
  }
115
115
 
116
+ /**
117
+ * Validate that a path is inside the .gsd/worktrees/ directory.
118
+ * Resolves symlinks and normalizes ".." traversals before comparison
119
+ * so that a symlink-resolved or crafted path cannot escape containment.
120
+ *
121
+ * Used as a safety gate before any destructive operation (rmSync,
122
+ * nativeWorktreeRemove --force) to prevent #2365-style data loss.
123
+ */
124
+ export function isInsideWorktreesDir(basePath: string, targetPath: string): boolean {
125
+ const wtDir = resolve(worktreesDir(basePath));
126
+ const resolved = resolve(targetPath);
127
+ // The resolved path must start with the worktrees dir followed by a separator,
128
+ // not merely be a prefix match (e.g. ".gsd/worktrees-extra" must not match).
129
+ return resolved === wtDir || resolved.startsWith(wtDir + sep);
130
+ }
131
+
116
132
  // ─── Core Operations ───────────────────────────────────────────────────────
117
133
 
118
134
  /**
@@ -370,16 +386,37 @@ export function removeWorktree(
370
386
  // time, so its registered path points to the resolved external location.
371
387
  // If syncStateToProjectRoot later creates a real .gsd/ directory that
372
388
  // shadows the symlink, the computed path diverges from git's record.
389
+ let gitReportedPath: string | null = null;
373
390
  try {
374
391
  const entries = nativeWorktreeList(basePath);
375
392
  const entry = entries.find(e => e.branch === branch);
376
393
  if (entry?.path) {
377
- wtPath = entry.path;
394
+ gitReportedPath = entry.path;
378
395
  }
379
396
  } catch (e) { logWarning("worktree", `nativeWorktreeList parse failed: ${(e as Error).message}`); }
380
397
 
398
+ // Safety gate (#2365): only use the git-reported path if it is actually
399
+ // inside .gsd/worktrees/. When .gsd/ was a symlink, git may have resolved
400
+ // it to an external directory (e.g. a project data folder). Using that
401
+ // path for removal would destroy user data.
402
+ if (gitReportedPath && isInsideWorktreesDir(basePath, gitReportedPath)) {
403
+ wtPath = gitReportedPath;
404
+ } else if (gitReportedPath) {
405
+ console.error(
406
+ `[GSD] WARNING: git worktree list reported path outside .gsd/worktrees/: ${gitReportedPath}\n` +
407
+ ` Refusing to use it for removal — falling back to computed path: ${wtPath}`,
408
+ );
409
+ // Still tell git to unregister the worktree entry via its reported path,
410
+ // but do NOT use force and do NOT fall back to rmSync on this path.
411
+ try { nativeWorktreeRemove(basePath, gitReportedPath, false); } catch (e) { logWarning("worktree", `non-force worktree remove failed for ${gitReportedPath}: ${e instanceof Error ? e.message : String(e)}`); }
412
+ }
413
+
381
414
  const resolvedWtPath = existsSync(wtPath) ? realpathSync(wtPath) : wtPath;
382
415
 
416
+ // Double-check: the resolved path (after symlink resolution) must also be
417
+ // inside .gsd/worktrees/ — a symlink inside the directory could point out.
418
+ const resolvedPathSafe = isInsideWorktreesDir(basePath, resolvedWtPath);
419
+
383
420
  // If we're inside the worktree, move out first — git can't remove an in-use directory
384
421
  const cwd = process.cwd();
385
422
  const resolvedCwd = existsSync(cwd) ? realpathSync(cwd) : cwd;
@@ -453,37 +490,48 @@ export function removeWorktree(
453
490
  }
454
491
  }
455
492
 
456
- // Remove worktree: try non-force first when submodules have changes,
457
- // falling back to force only after submodule state has been preserved.
458
- const useForce = hasSubmoduleChanges ? false : force;
459
- try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch (e) { logWarning("worktree", `nativeWorktreeRemove failed: ${(e as Error).message}`); }
493
+ // Remove worktree only use force/rmSync when the path is safely contained
494
+ if (resolvedPathSafe) {
495
+ // Remove worktree: try non-force first when submodules have changes,
496
+ // falling back to force only after submodule state has been preserved.
497
+ const useForce = hasSubmoduleChanges ? false : force;
498
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch (e) { logWarning("worktree", `nativeWorktreeRemove failed: ${(e as Error).message}`); }
460
499
 
461
- // If the directory is still there (e.g. locked), try harder with force
462
- if (existsSync(resolvedWtPath)) {
463
- try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch (e) { logWarning("worktree", `nativeWorktreeRemove (force) failed: ${(e as Error).message}`); }
464
- }
500
+ // If the directory is still there (e.g. locked), try harder with force
501
+ if (existsSync(resolvedWtPath)) {
502
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch (e) { logWarning("worktree", `nativeWorktreeRemove (force) failed: ${(e as Error).message}`); }
503
+ }
465
504
 
466
- // (#2821) If the worktree directory STILL exists after both native removal
467
- // attempts (e.g. untracked files like ASSESSMENT/UAT-RESULT prevent git
468
- // worktree remove), force-remove the git internal worktree metadata first,
469
- // then remove the filesystem directory. Without this, the .git/worktrees/<name>
470
- // lock prevents rmSync from cleaning up, and the orphaned worktree directory
471
- // causes every subsequent `/gsd auto` to re-enter the stale worktree.
472
- if (existsSync(resolvedWtPath)) {
473
- try {
474
- const wtInternalDir = join(basePath, ".git", "worktrees", name);
475
- if (existsSync(wtInternalDir)) {
476
- rmSync(wtInternalDir, { recursive: true, force: true });
505
+ // (#2821) If the worktree directory STILL exists after both native removal
506
+ // attempts (e.g. untracked files like ASSESSMENT/UAT-RESULT prevent git
507
+ // worktree remove), force-remove the git internal worktree metadata first,
508
+ // then remove the filesystem directory. Without this, the .git/worktrees/<name>
509
+ // lock prevents rmSync from cleaning up, and the orphaned worktree directory
510
+ // causes every subsequent `/gsd auto` to re-enter the stale worktree.
511
+ if (existsSync(resolvedWtPath)) {
512
+ try {
513
+ const wtInternalDir = join(basePath, ".git", "worktrees", name);
514
+ if (existsSync(wtInternalDir)) {
515
+ rmSync(wtInternalDir, { recursive: true, force: true });
516
+ }
517
+ rmSync(resolvedWtPath, { recursive: true, force: true });
518
+ } catch {
519
+ logWarning(
520
+ "reconcile",
521
+ `Worktree directory could not be removed after git internal cleanup: ${resolvedWtPath}. ` +
522
+ `Manual cleanup: rm -rf "${resolvedWtPath.replaceAll("\\", "/")}"`,
523
+ { worktree: name },
524
+ );
477
525
  }
478
- rmSync(resolvedWtPath, { recursive: true, force: true });
479
- } catch {
480
- logWarning(
481
- "reconcile",
482
- `Worktree directory could not be removed after git internal cleanup: ${resolvedWtPath}. ` +
483
- `Manual cleanup: rm -rf "${resolvedWtPath.replaceAll("\\", "/")}"`,
484
- { worktree: name },
485
- );
486
526
  }
527
+ } else {
528
+ // Path is outside containment — only do a non-force git worktree remove
529
+ // (which refuses to delete dirty worktrees) and never fall back to rmSync.
530
+ console.error(
531
+ `[GSD] WARNING: Resolved worktree path is outside .gsd/worktrees/: ${resolvedWtPath}\n` +
532
+ ` Skipping forced removal to prevent data loss.`,
533
+ );
534
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, false); } catch (e) { logWarning("worktree", `non-force worktree remove failed for ${resolvedWtPath}: ${e instanceof Error ? e.message : String(e)}`); }
487
535
  }
488
536
 
489
537
  // Prune stale entries so git knows the worktree is gone
@@ -497,10 +497,11 @@ export class WorktreeResolver {
497
497
  });
498
498
  // Surface a clear, actionable error. The worktree and milestone branch are
499
499
  // intentionally preserved — nothing has been deleted. The user can retry
500
- // /gsd dispatch complete-milestone or merge manually once the underlying issue is fixed
501
- // (e.g. checkout to wrong branch, unresolved conflicts). (#1668)
500
+ // /gsd dispatch complete-milestone or merge manually once the underlying
501
+ // issue is fixed (e.g. checkout to wrong branch, unresolved conflicts).
502
+ // (#1668, #1891)
502
503
  ctx.notify(
503
- `Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /gsd dispatch complete-milestone or merge manually.`,
504
+ `Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry with \`/gsd dispatch complete-milestone\` or merge manually.`,
504
505
  "warning",
505
506
  );
506
507
 
@@ -0,0 +1,149 @@
1
+ /**
2
+ * MCP Client OAuth / Auth helpers
3
+ *
4
+ * Builds transport options (headers, OAuthClientProvider) from MCP server
5
+ * config entries so that HTTP transports can authenticate with remote
6
+ * servers (Sentry, Linear, etc.).
7
+ *
8
+ * Fixes #2160 — MCP HTTP transport lacked an OAuth auth provider.
9
+ */
10
+
11
+ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
12
+ import type { StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
13
+
14
+ // ─── Types ────────────────────────────────────────────────────────────────────
15
+
16
+ export interface McpHttpAuthHeaders {
17
+ /** Static headers to attach to every request, e.g. `{ Authorization: "Bearer ${TOKEN}" }`. */
18
+ headers?: Record<string, string>;
19
+ }
20
+
21
+ export interface McpHttpOAuthConfig {
22
+ /** OAuth configuration for servers that require the full OAuth flow. */
23
+ oauth?: {
24
+ clientId: string;
25
+ clientSecret?: string;
26
+ scopes?: string[];
27
+ redirectUrl?: string;
28
+ };
29
+ }
30
+
31
+ /** Union of all auth-related config fields for an HTTP MCP server. */
32
+ export type McpHttpAuthConfig = McpHttpAuthHeaders & McpHttpOAuthConfig;
33
+
34
+ // ─── Env resolution ───────────────────────────────────────────────────────────
35
+
36
+ /** Resolve `${VAR}` references in a string against `process.env`. */
37
+ function resolveEnvValue(value: string): string {
38
+ return value.replace(
39
+ /\$\{([^}]+)\}/g,
40
+ (_match, varName) => process.env[varName] ?? "",
41
+ );
42
+ }
43
+
44
+ function resolveHeaders(raw: Record<string, string>): Record<string, string> {
45
+ const resolved: Record<string, string> = {};
46
+ for (const [key, value] of Object.entries(raw)) {
47
+ resolved[key] = typeof value === "string" ? resolveEnvValue(value) : value;
48
+ }
49
+ return resolved;
50
+ }
51
+
52
+ // ─── OAuth provider (minimal CLI-friendly implementation) ─────────────────────
53
+
54
+ /**
55
+ * Creates a minimal `OAuthClientProvider` suitable for CLI / headless use.
56
+ *
57
+ * This provider supports:
58
+ * - Pre-configured client credentials (client_id, optional client_secret)
59
+ * - Token storage in memory (per-session)
60
+ * - Scopes
61
+ *
62
+ * For full interactive OAuth flows (browser redirect), a richer provider would
63
+ * be needed, but for server-to-server and pre-authed scenarios this is
64
+ * sufficient.
65
+ */
66
+ function createCliOAuthProvider(config: NonNullable<McpHttpOAuthConfig["oauth"]>): OAuthClientProvider {
67
+ let storedTokens: { access_token: string; token_type: string; refresh_token?: string } | undefined;
68
+ let storedCodeVerifier = "";
69
+
70
+ return {
71
+ get redirectUrl() {
72
+ return config.redirectUrl ?? "http://localhost:0/callback";
73
+ },
74
+
75
+ get clientMetadata() {
76
+ return {
77
+ redirect_uris: [config.redirectUrl ?? "http://localhost:0/callback"],
78
+ client_name: "gsd",
79
+ ...(config.scopes ? { scope: config.scopes.join(" ") } : {}),
80
+ };
81
+ },
82
+
83
+ clientInformation() {
84
+ return {
85
+ client_id: config.clientId,
86
+ ...(config.clientSecret ? { client_secret: config.clientSecret } : {}),
87
+ };
88
+ },
89
+
90
+ tokens() {
91
+ return storedTokens;
92
+ },
93
+
94
+ saveTokens(tokens) {
95
+ storedTokens = tokens as typeof storedTokens;
96
+ },
97
+
98
+ redirectToAuthorization(authorizationUrl: URL) {
99
+ // In a CLI context we can't open a browser automatically.
100
+ // Log the URL so the user can manually visit it.
101
+ // eslint-disable-next-line no-console
102
+ console.error(
103
+ `[MCP OAuth] Authorization required. Visit:\n ${authorizationUrl.toString()}`,
104
+ );
105
+ },
106
+
107
+ saveCodeVerifier(codeVerifier: string) {
108
+ storedCodeVerifier = codeVerifier;
109
+ },
110
+
111
+ codeVerifier() {
112
+ return storedCodeVerifier;
113
+ },
114
+ };
115
+ }
116
+
117
+ // ─── Public API ───────────────────────────────────────────────────────────────
118
+
119
+ /**
120
+ * Build `StreamableHTTPClientTransportOptions` from an MCP server config's
121
+ * auth-related fields.
122
+ *
123
+ * Supports two auth strategies:
124
+ * 1. **`headers`** — static Authorization (or other) headers, with `${VAR}` env resolution.
125
+ * 2. **`oauth`** — full OAuthClientProvider for servers that implement MCP OAuth.
126
+ *
127
+ * When both are provided, `oauth` takes precedence (the SDK's built-in OAuth
128
+ * flow handles token refresh automatically).
129
+ */
130
+ export function buildHttpTransportOpts(
131
+ authConfig: McpHttpAuthConfig,
132
+ ): StreamableHTTPClientTransportOptions {
133
+ const opts: StreamableHTTPClientTransportOptions = {};
134
+
135
+ // OAuth takes precedence
136
+ if (authConfig.oauth) {
137
+ opts.authProvider = createCliOAuthProvider(authConfig.oauth);
138
+ return opts;
139
+ }
140
+
141
+ // Static headers (with env var resolution)
142
+ if (authConfig.headers && Object.keys(authConfig.headers).length > 0) {
143
+ opts.requestInit = {
144
+ headers: resolveHeaders(authConfig.headers),
145
+ };
146
+ }
147
+
148
+ return opts;
149
+ }
@@ -25,6 +25,8 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
25
25
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
26
26
  import { readFileSync, existsSync } from "node:fs";
27
27
  import { join } from "node:path";
28
+ import { buildHttpTransportOpts } from "./auth.js";
29
+ import type { McpHttpAuthConfig } from "./auth.js";
28
30
 
29
31
  // ─── Types ────────────────────────────────────────────────────────────────────
30
32
 
@@ -36,6 +38,10 @@ interface McpServerConfig {
36
38
  env?: Record<string, string>;
37
39
  url?: string;
38
40
  cwd?: string;
41
+ /** Static headers for HTTP transport (supports ${VAR} env resolution). */
42
+ headers?: Record<string, string>;
43
+ /** OAuth config for HTTP transport. */
44
+ oauth?: McpHttpAuthConfig["oauth"];
39
45
  }
40
46
 
41
47
  interface McpToolSchema {
@@ -87,6 +93,9 @@ function readConfigs(): McpServerConfig[] {
87
93
  ? "http"
88
94
  : "unknown";
89
95
 
96
+ const hasHeaders = hasUrl && config.headers && typeof config.headers === "object";
97
+ const hasOAuth = hasUrl && config.oauth && typeof config.oauth === "object";
98
+
90
99
  servers.push({
91
100
  name,
92
101
  transport,
@@ -99,6 +108,8 @@ function readConfigs(): McpServerConfig[] {
99
108
  cwd: typeof config.cwd === "string" ? config.cwd : undefined,
100
109
  }),
101
110
  ...(hasUrl && { url: config.url as string }),
111
+ headers: hasHeaders ? config.headers as Record<string, string> : undefined,
112
+ oauth: hasOAuth ? config.oauth as McpHttpAuthConfig["oauth"] : undefined,
102
113
  });
103
114
  }
104
115
  } catch {
@@ -159,7 +170,11 @@ async function getOrConnect(name: string, signal?: AbortSignal): Promise<Client>
159
170
  /\$\{([^}]+)\}/g,
160
171
  (_, varName) => process.env[varName] ?? "",
161
172
  );
162
- transport = new StreamableHTTPClientTransport(new URL(resolvedUrl));
173
+ const httpOpts = buildHttpTransportOpts({
174
+ headers: config.headers,
175
+ oauth: config.oauth,
176
+ });
177
+ transport = new StreamableHTTPClientTransport(new URL(resolvedUrl), httpOpts);
163
178
  } else {
164
179
  throw new Error(`Server "${config.name}" has unsupported transport: ${config.transport}`);
165
180
  }
@@ -17,19 +17,10 @@
17
17
  */
18
18
 
19
19
  import { importExtensionModule, type ExtensionAPI } from "@gsd/pi-coding-agent";
20
- import type { OpenAICompletionsCompat } from "@gsd/pi-ai";
21
20
  import * as client from "./ollama-client.js";
22
- import { discoverModels, getOllamaOpenAIBaseUrl } from "./ollama-discovery.js";
21
+ import { discoverModels } from "./ollama-discovery.js";
23
22
  import { registerOllamaCommands } from "./ollama-commands.js";
24
-
25
- /** Default compat settings for Ollama models via OpenAI-compat endpoint */
26
- const OLLAMA_COMPAT: OpenAICompletionsCompat = {
27
- supportsDeveloperRole: false,
28
- supportsReasoningEffort: false,
29
- supportsUsageInStreaming: false,
30
- maxTokensField: "max_tokens",
31
- supportsStore: false,
32
- };
23
+ import { streamOllamaChat } from "./ollama-chat-provider.js";
33
24
 
34
25
  let toolsPromise: Promise<void> | null = null;
35
26
 
@@ -68,12 +59,18 @@ async function probeAndRegister(pi: ExtensionAPI): Promise<boolean> {
68
59
  const models = await discoverModels();
69
60
  if (models.length === 0) return true; // Running but no models pulled
70
61
 
71
- const baseUrl = getOllamaOpenAIBaseUrl();
62
+ const baseUrl = client.getOllamaHost();
72
63
 
64
+ // Use authMode "apiKey" with a dummy key (#3440).
65
+ // authMode "none" requires a custom streamSimple handler, but Ollama uses
66
+ // the standard OpenAI-compatible streaming endpoint. Ollama ignores the
67
+ // Authorization header so the dummy key is harmless.
73
68
  pi.registerProvider("ollama", {
74
- authMode: "none",
69
+ authMode: "apiKey",
70
+ apiKey: "ollama",
75
71
  baseUrl,
76
- api: "openai-completions",
72
+ api: "ollama-chat",
73
+ streamSimple: streamOllamaChat,
77
74
  isReady: () => true,
78
75
  models: models.map((m) => ({
79
76
  id: m.id,
@@ -83,7 +80,7 @@ async function probeAndRegister(pi: ExtensionAPI): Promise<boolean> {
83
80
  cost: m.cost,
84
81
  contextWindow: m.contextWindow,
85
82
  maxTokens: m.maxTokens,
86
- compat: OLLAMA_COMPAT,
83
+ providerOptions: (m.ollamaOptions ?? {}) as Record<string, unknown>,
87
84
  })),
88
85
  });
89
86
 
@@ -108,16 +105,20 @@ export default function ollama(pi: ExtensionAPI) {
108
105
  await registerOllamaTools(pi);
109
106
  }
110
107
 
111
- // Async probe don't block startup
112
- probeAndRegister(pi)
113
- .then((found) => {
114
- if (found && ctx.hasUI) {
115
- ctx.ui.setStatus("ollama", "Ollama");
116
- }
117
- })
118
- .catch(() => {
119
- // Silently ignore probe failures
120
- });
108
+ // In headless/auto mode, await the probe so the fallback resolver can
109
+ // see Ollama before the first LLM call (#3531 race condition).
110
+ // In interactive mode, keep it async for fast startup.
111
+ if (!ctx.hasUI) {
112
+ try {
113
+ await probeAndRegister(pi);
114
+ } catch { /* non-fatal */ }
115
+ } else {
116
+ probeAndRegister(pi)
117
+ .then((found) => {
118
+ if (found) ctx.ui.setStatus("ollama", "Ollama");
119
+ })
120
+ .catch(() => {});
121
+ }
121
122
  });
122
123
 
123
124
  pi.on("session_shutdown", async () => {
@@ -8,11 +8,15 @@
8
8
  * Fallback: estimate from parameter count if model isn't in the table.
9
9
  */
10
10
 
11
+ import type { OllamaChatOptions } from "./types.js";
12
+
11
13
  export interface ModelCapability {
12
14
  contextWindow?: number;
13
15
  maxTokens?: number;
14
16
  input?: ("text" | "image")[];
15
17
  reasoning?: boolean;
18
+ /** Ollama-specific default inference options for this model family. */
19
+ ollamaOptions?: OllamaChatOptions;
16
20
  }
17
21
 
18
22
  /**
@@ -20,58 +24,61 @@ export interface ModelCapability {
20
24
  * Keys are matched as prefixes against the model name (before the colon/tag).
21
25
  * More specific entries should appear first.
22
26
  */
27
+ // Note: ollamaOptions.num_ctx is set for known model families where the context
28
+ // window is authoritative. For unknown/estimated models, num_ctx is NOT sent
29
+ // to avoid OOM risk — Ollama uses its own safe default instead.
23
30
  const KNOWN_MODELS: Array<[pattern: string, caps: ModelCapability]> = [
24
31
  // ─── Reasoning models ───────────────────────────────────────────────
25
- ["deepseek-r1", { contextWindow: 131072, reasoning: true }],
26
- ["qwq", { contextWindow: 131072, reasoning: true }],
32
+ ["deepseek-r1", { contextWindow: 131072, reasoning: true, ollamaOptions: { num_ctx: 131072 } }],
33
+ ["qwq", { contextWindow: 131072, reasoning: true, ollamaOptions: { num_ctx: 131072 } }],
27
34
 
28
35
  // ─── Vision models ──────────────────────────────────────────────────
29
- ["llava", { contextWindow: 4096, input: ["text", "image"] }],
30
- ["bakllava", { contextWindow: 4096, input: ["text", "image"] }],
31
- ["moondream", { contextWindow: 8192, input: ["text", "image"] }],
32
- ["llama3.2-vision", { contextWindow: 131072, input: ["text", "image"] }],
33
- ["minicpm-v", { contextWindow: 4096, input: ["text", "image"] }],
36
+ ["llava", { contextWindow: 4096, input: ["text", "image"], ollamaOptions: { num_ctx: 4096 } }],
37
+ ["bakllava", { contextWindow: 4096, input: ["text", "image"], ollamaOptions: { num_ctx: 4096 } }],
38
+ ["moondream", { contextWindow: 8192, input: ["text", "image"], ollamaOptions: { num_ctx: 8192 } }],
39
+ ["llama3.2-vision", { contextWindow: 131072, input: ["text", "image"], ollamaOptions: { num_ctx: 131072 } }],
40
+ ["minicpm-v", { contextWindow: 4096, input: ["text", "image"], ollamaOptions: { num_ctx: 4096 } }],
34
41
 
35
42
  // ─── Code models ────────────────────────────────────────────────────
36
- ["codestral", { contextWindow: 262144, maxTokens: 32768 }],
37
- ["qwen2.5-coder", { contextWindow: 131072, maxTokens: 32768 }],
38
- ["deepseek-coder-v2", { contextWindow: 131072, maxTokens: 16384 }],
39
- ["starcoder2", { contextWindow: 16384, maxTokens: 8192 }],
40
- ["codegemma", { contextWindow: 8192, maxTokens: 8192 }],
41
- ["codellama", { contextWindow: 16384, maxTokens: 8192 }],
42
- ["devstral", { contextWindow: 131072, maxTokens: 32768 }],
43
+ ["codestral", { contextWindow: 262144, maxTokens: 32768, ollamaOptions: { num_ctx: 262144 } }],
44
+ ["qwen2.5-coder", { contextWindow: 131072, maxTokens: 32768, ollamaOptions: { num_ctx: 131072 } }],
45
+ ["deepseek-coder-v2", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
46
+ ["starcoder2", { contextWindow: 16384, maxTokens: 8192, ollamaOptions: { num_ctx: 16384 } }],
47
+ ["codegemma", { contextWindow: 8192, maxTokens: 8192, ollamaOptions: { num_ctx: 8192 } }],
48
+ ["codellama", { contextWindow: 16384, maxTokens: 8192, ollamaOptions: { num_ctx: 16384 } }],
49
+ ["devstral", { contextWindow: 131072, maxTokens: 32768, ollamaOptions: { num_ctx: 131072 } }],
43
50
 
44
51
  // ─── Llama family ───────────────────────────────────────────────────
45
- ["llama3.3", { contextWindow: 131072, maxTokens: 16384 }],
46
- ["llama3.2", { contextWindow: 131072, maxTokens: 16384 }],
47
- ["llama3.1", { contextWindow: 131072, maxTokens: 16384 }],
48
- ["llama3", { contextWindow: 8192, maxTokens: 8192 }],
49
- ["llama2", { contextWindow: 4096, maxTokens: 4096 }],
52
+ ["llama3.3", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
53
+ ["llama3.2", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
54
+ ["llama3.1", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
55
+ ["llama3", { contextWindow: 8192, maxTokens: 8192, ollamaOptions: { num_ctx: 8192 } }],
56
+ ["llama2", { contextWindow: 4096, maxTokens: 4096, ollamaOptions: { num_ctx: 4096 } }],
50
57
 
51
58
  // ─── Qwen family ────────────────────────────────────────────────────
52
- ["qwen3", { contextWindow: 131072, maxTokens: 32768 }],
53
- ["qwen2.5", { contextWindow: 131072, maxTokens: 32768 }],
54
- ["qwen2", { contextWindow: 131072, maxTokens: 32768 }],
59
+ ["qwen3", { contextWindow: 131072, maxTokens: 32768, ollamaOptions: { num_ctx: 131072 } }],
60
+ ["qwen2.5", { contextWindow: 131072, maxTokens: 32768, ollamaOptions: { num_ctx: 131072 } }],
61
+ ["qwen2", { contextWindow: 131072, maxTokens: 32768, ollamaOptions: { num_ctx: 131072 } }],
55
62
 
56
63
  // ─── Gemma family ───────────────────────────────────────────────────
57
- ["gemma3", { contextWindow: 131072, maxTokens: 16384 }],
58
- ["gemma2", { contextWindow: 8192, maxTokens: 8192 }],
64
+ ["gemma3", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
65
+ ["gemma2", { contextWindow: 8192, maxTokens: 8192, ollamaOptions: { num_ctx: 8192 } }],
59
66
 
60
67
  // ─── Mistral family ─────────────────────────────────────────────────
61
- ["mistral-large", { contextWindow: 131072, maxTokens: 16384 }],
62
- ["mistral-small", { contextWindow: 131072, maxTokens: 16384 }],
63
- ["mistral-nemo", { contextWindow: 131072, maxTokens: 16384 }],
64
- ["mistral", { contextWindow: 32768, maxTokens: 8192 }],
65
- ["mixtral", { contextWindow: 32768, maxTokens: 8192 }],
68
+ ["mistral-large", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
69
+ ["mistral-small", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
70
+ ["mistral-nemo", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
71
+ ["mistral", { contextWindow: 32768, maxTokens: 8192, ollamaOptions: { num_ctx: 32768 } }],
72
+ ["mixtral", { contextWindow: 32768, maxTokens: 8192, ollamaOptions: { num_ctx: 32768 } }],
66
73
 
67
74
  // ─── Phi family ─────────────────────────────────────────────────────
68
- ["phi4", { contextWindow: 16384, maxTokens: 16384 }],
69
- ["phi3.5", { contextWindow: 131072, maxTokens: 16384 }],
70
- ["phi3", { contextWindow: 131072, maxTokens: 4096 }],
75
+ ["phi4", { contextWindow: 16384, maxTokens: 16384, ollamaOptions: { num_ctx: 16384 } }],
76
+ ["phi3.5", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
77
+ ["phi3", { contextWindow: 131072, maxTokens: 4096, ollamaOptions: { num_ctx: 131072 } }],
71
78
 
72
79
  // ─── Command R ──────────────────────────────────────────────────────
73
- ["command-r-plus", { contextWindow: 131072, maxTokens: 16384 }],
74
- ["command-r", { contextWindow: 131072, maxTokens: 16384 }],
80
+ ["command-r-plus", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
81
+ ["command-r", { contextWindow: 131072, maxTokens: 16384, ollamaOptions: { num_ctx: 131072 } }],
75
82
  ];
76
83
 
77
84
  /**