gsd-pi 2.63.0-dev.026d309 → 2.63.0-dev.786f0ff

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 (350) 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 +9 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +124 -10
  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-worktree.js +13 -7
  22. package/dist/resources/extensions/gsd/auto.js +19 -2
  23. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
  24. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
  25. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
  26. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
  27. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  28. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
  29. package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
  30. package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
  31. package/dist/resources/extensions/gsd/constants.js +42 -0
  32. package/dist/resources/extensions/gsd/db-writer.js +72 -4
  33. package/dist/resources/extensions/gsd/forensics.js +20 -4
  34. package/dist/resources/extensions/gsd/gsd-db.js +64 -17
  35. package/dist/resources/extensions/gsd/guided-flow.js +19 -0
  36. package/dist/resources/extensions/gsd/metrics.js +27 -1
  37. package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
  38. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  39. package/dist/resources/extensions/gsd/preferences.js +7 -2
  40. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  42. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  43. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  44. package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
  45. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/system.md +4 -7
  47. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  48. package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
  49. package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
  50. package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
  51. package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
  52. package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
  53. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
  54. package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
  55. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
  56. package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
  57. package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
  58. package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
  59. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
  60. package/dist/resources/extensions/gsd/state.js +74 -14
  61. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  62. package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
  63. package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
  64. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
  65. package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
  66. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
  67. package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
  68. package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
  69. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
  70. package/dist/resources/extensions/mcp-client/auth.js +101 -0
  71. package/dist/resources/extensions/mcp-client/index.js +10 -1
  72. package/dist/resources/extensions/ollama/index.js +28 -22
  73. package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
  74. package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
  75. package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
  76. package/dist/resources/extensions/ollama/ollama-client.js +23 -32
  77. package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
  78. package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
  79. package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
  80. package/dist/update-cmd.js +4 -2
  81. package/dist/web/standalone/.next/BUILD_ID +1 -1
  82. package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -20
  83. package/dist/web/standalone/.next/build-manifest.json +2 -2
  84. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  85. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  86. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  107. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  108. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  115. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  117. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  119. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  121. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  123. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  125. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  144. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  158. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/index.html +1 -1
  164. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  168. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  169. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  170. package/dist/web/standalone/.next/server/app-paths-manifest.json +20 -20
  171. package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
  172. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  173. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  174. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  175. package/package.json +1 -1
  176. package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
  177. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  178. package/packages/pi-agent-core/dist/agent-loop.js +50 -0
  179. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  180. package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
  181. package/packages/pi-agent-core/src/agent-loop.ts +53 -0
  182. package/packages/pi-ai/dist/types.d.ts +16 -1
  183. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  184. package/packages/pi-ai/dist/types.js.map +1 -1
  185. package/packages/pi-ai/src/types.ts +18 -1
  186. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
  187. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
  189. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
  191. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
  193. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
  195. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
  197. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
  199. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
  201. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
  202. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  203. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
  206. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
  208. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
  210. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
  212. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
  214. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  217. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  218. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
  219. package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
  220. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
  221. package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
  222. package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
  223. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  224. package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
  225. package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
  226. package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
  227. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  228. package/src/resources/extensions/cmux/index.ts +18 -12
  229. package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
  230. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
  231. package/src/resources/extensions/gsd/auto/loop.ts +5 -0
  232. package/src/resources/extensions/gsd/auto/phases.ts +194 -33
  233. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  234. package/src/resources/extensions/gsd/auto-dashboard.ts +11 -3
  235. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
  236. package/src/resources/extensions/gsd/auto-post-unit.ts +141 -12
  237. package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
  238. package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
  239. package/src/resources/extensions/gsd/auto-start.ts +11 -20
  240. package/src/resources/extensions/gsd/auto-timers.ts +2 -1
  241. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  242. package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
  243. package/src/resources/extensions/gsd/auto.ts +22 -1
  244. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
  245. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
  246. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
  247. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
  248. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
  249. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
  250. package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
  251. package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
  252. package/src/resources/extensions/gsd/constants.ts +44 -0
  253. package/src/resources/extensions/gsd/db-writer.ts +78 -4
  254. package/src/resources/extensions/gsd/forensics.ts +21 -5
  255. package/src/resources/extensions/gsd/gsd-db.ts +64 -17
  256. package/src/resources/extensions/gsd/guided-flow.ts +22 -0
  257. package/src/resources/extensions/gsd/metrics.ts +28 -1
  258. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
  259. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  260. package/src/resources/extensions/gsd/preferences.ts +9 -2
  261. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  262. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  263. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  264. package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  265. package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
  266. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  267. package/src/resources/extensions/gsd/prompts/system.md +4 -7
  268. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  269. package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
  270. package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
  271. package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
  272. package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
  273. package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
  274. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
  275. package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
  276. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
  277. package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
  278. package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
  279. package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
  280. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
  281. package/src/resources/extensions/gsd/state.ts +67 -12
  282. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  283. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
  284. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
  285. package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
  286. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
  287. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  288. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
  289. package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
  290. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
  291. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
  292. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
  293. package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
  294. package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
  295. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
  296. package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
  297. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
  298. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
  299. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
  300. package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
  301. package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
  302. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
  303. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
  304. package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
  305. package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
  306. package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
  307. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
  308. package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
  309. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
  310. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
  311. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
  312. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
  313. package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
  314. package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
  315. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
  316. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
  317. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
  318. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
  319. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
  320. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
  321. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
  322. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
  323. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
  324. package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
  325. package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
  326. package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
  327. package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
  328. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
  329. package/src/resources/extensions/gsd/types.ts +44 -22
  330. package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
  331. package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
  332. package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
  333. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
  334. package/src/resources/extensions/mcp-client/auth.ts +149 -0
  335. package/src/resources/extensions/mcp-client/index.ts +16 -1
  336. package/src/resources/extensions/ollama/index.ts +26 -25
  337. package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
  338. package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
  339. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
  340. package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
  341. package/src/resources/extensions/ollama/ollama-client.ts +30 -30
  342. package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
  343. package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
  344. package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
  345. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
  346. package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
  347. package/src/resources/extensions/ollama/types.ts +23 -0
  348. package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
  349. /package/dist/web/standalone/.next/static/{TTlAguZQ5vR9EOv6G8cel → SDB1T-4NqkMjYirjjqQhr}/_buildManifest.js +0 -0
  350. /package/dist/web/standalone/.next/static/{TTlAguZQ5vR9EOv6G8cel → SDB1T-4NqkMjYirjjqQhr}/_ssgManifest.js +0 -0
@@ -0,0 +1,380 @@
1
+ // GSD2 — Ollama Extension: Native /api/chat stream provider
2
+ /**
3
+ * Implements the "ollama-chat" API provider, streaming responses directly
4
+ * from Ollama's native /api/chat endpoint instead of the OpenAI compatibility
5
+ * shim. This exposes Ollama-specific options (num_ctx, keep_alive, num_gpu,
6
+ * sampling parameters) and surfaces inference performance metrics.
7
+ */
8
+ import { EventStream, } from "@gsd/pi-ai";
9
+ import { chat } from "./ollama-client.js";
10
+ import { ThinkingTagParser } from "./thinking-parser.js";
11
+ /** Create an AssistantMessageEventStream using the base EventStream class. */
12
+ function createStream() {
13
+ return new EventStream((event) => event.type === "done" || event.type === "error", (event) => {
14
+ if (event.type === "done")
15
+ return event.message;
16
+ if (event.type === "error")
17
+ return event.error;
18
+ throw new Error("Unexpected event type for final result");
19
+ });
20
+ }
21
+ // ─── Stream handler ─────────────────────────────────────────────────────────
22
+ export function streamOllamaChat(model, context, options) {
23
+ const stream = createStream();
24
+ (async () => {
25
+ const output = buildInitialOutput(model);
26
+ try {
27
+ const request = buildRequest(model, context, options);
28
+ stream.push({ type: "start", partial: output });
29
+ const useThinkingParser = model.reasoning;
30
+ const thinkParser = useThinkingParser ? new ThinkingTagParser() : null;
31
+ let contentIndex = -1;
32
+ let currentBlockType = null;
33
+ function startBlock(type) {
34
+ contentIndex++;
35
+ currentBlockType = type;
36
+ if (type === "text") {
37
+ output.content.push({ type: "text", text: "" });
38
+ stream.push({ type: "text_start", contentIndex, partial: output });
39
+ }
40
+ else {
41
+ output.content.push({ type: "thinking", thinking: "" });
42
+ stream.push({ type: "thinking_start", contentIndex, partial: output });
43
+ }
44
+ }
45
+ function endBlock() {
46
+ if (currentBlockType === null)
47
+ return;
48
+ if (currentBlockType === "text") {
49
+ const block = output.content[contentIndex];
50
+ stream.push({ type: "text_end", contentIndex, content: block.text, partial: output });
51
+ }
52
+ else {
53
+ const block = output.content[contentIndex];
54
+ stream.push({ type: "thinking_end", contentIndex, content: block.thinking, partial: output });
55
+ }
56
+ currentBlockType = null;
57
+ }
58
+ function emitDelta(type, text) {
59
+ if (!text)
60
+ return;
61
+ if (currentBlockType !== type) {
62
+ endBlock();
63
+ startBlock(type);
64
+ }
65
+ if (type === "text") {
66
+ output.content[contentIndex].text += text;
67
+ stream.push({ type: "text_delta", contentIndex, delta: text, partial: output });
68
+ }
69
+ else {
70
+ output.content[contentIndex].thinking += text;
71
+ stream.push({ type: "thinking_delta", contentIndex, delta: text, partial: output });
72
+ }
73
+ }
74
+ function processChunks(chunks) {
75
+ for (const chunk of chunks) {
76
+ emitDelta(chunk.type, chunk.text);
77
+ }
78
+ }
79
+ function processToolCalls(toolCalls) {
80
+ endBlock();
81
+ for (const tc of toolCalls) {
82
+ contentIndex++;
83
+ const toolCall = {
84
+ type: "toolCall",
85
+ id: `ollama_tc_${contentIndex}`,
86
+ name: tc.function.name,
87
+ arguments: tc.function.arguments,
88
+ };
89
+ output.content.push(toolCall);
90
+ stream.push({ type: "toolcall_start", contentIndex, partial: output });
91
+ // Emit a delta with the serialized arguments (convention: start/delta/end)
92
+ stream.push({
93
+ type: "toolcall_delta",
94
+ contentIndex,
95
+ delta: JSON.stringify(tc.function.arguments),
96
+ partial: output,
97
+ });
98
+ stream.push({
99
+ type: "toolcall_end",
100
+ contentIndex,
101
+ toolCall,
102
+ partial: output,
103
+ });
104
+ }
105
+ output.stopReason = "toolUse";
106
+ }
107
+ for await (const chunk of chat(request, options?.signal)) {
108
+ // Handle text content — process independently of tool_calls
109
+ // (a chunk may contain both content and tool_calls)
110
+ const content = chunk.message?.content ?? "";
111
+ if (content) {
112
+ if (thinkParser) {
113
+ processChunks(thinkParser.push(content));
114
+ }
115
+ else {
116
+ emitDelta("text", content);
117
+ }
118
+ }
119
+ // Handle tool calls (Ollama sends them complete, may be on done:true chunk)
120
+ if (chunk.message?.tool_calls?.length) {
121
+ processToolCalls(chunk.message.tool_calls);
122
+ }
123
+ if (chunk.done) {
124
+ // Final chunk — extract metrics and usage
125
+ if (thinkParser)
126
+ processChunks(thinkParser.flush());
127
+ endBlock();
128
+ output.usage = buildUsage(chunk);
129
+ output.inferenceMetrics = extractMetrics(chunk);
130
+ // Preserve "toolUse" if tool calls were processed
131
+ if (output.stopReason !== "toolUse") {
132
+ output.stopReason = mapStopReason(chunk.done_reason);
133
+ }
134
+ break;
135
+ }
136
+ }
137
+ assertStreamSuccess(output, options?.signal);
138
+ finalizeStream(stream, output);
139
+ }
140
+ catch (error) {
141
+ handleStreamError(stream, output, error, options?.signal);
142
+ }
143
+ })();
144
+ return stream;
145
+ }
146
+ // ─── Request building ───────────────────────────────────────────────────────
147
+ function buildRequest(model, context, options) {
148
+ const ollamaOpts = (model.providerOptions ?? {});
149
+ const request = {
150
+ model: model.id,
151
+ messages: convertMessages(context),
152
+ stream: true,
153
+ };
154
+ // Build options block with all Ollama-specific parameters
155
+ const reqOptions = {};
156
+ // Context window — only sent when explicitly configured via providerOptions.
157
+ // Sending inferred/estimated values risks OOM on constrained hosts.
158
+ // Users can set num_ctx per-model in models.json ollamaOptions or the
159
+ // capability table can provide it for known model families.
160
+ if (ollamaOpts.num_ctx !== undefined && ollamaOpts.num_ctx > 0) {
161
+ reqOptions.num_ctx = ollamaOpts.num_ctx;
162
+ }
163
+ // Max output tokens
164
+ const maxTokens = options?.maxTokens ?? model.maxTokens;
165
+ if (maxTokens > 0) {
166
+ reqOptions.num_predict = maxTokens;
167
+ }
168
+ // Temperature
169
+ if (options?.temperature !== undefined) {
170
+ reqOptions.temperature = options.temperature;
171
+ }
172
+ // Per-model sampling options from providerOptions
173
+ if (ollamaOpts.top_p !== undefined)
174
+ reqOptions.top_p = ollamaOpts.top_p;
175
+ if (ollamaOpts.top_k !== undefined)
176
+ reqOptions.top_k = ollamaOpts.top_k;
177
+ if (ollamaOpts.repeat_penalty !== undefined)
178
+ reqOptions.repeat_penalty = ollamaOpts.repeat_penalty;
179
+ if (ollamaOpts.seed !== undefined)
180
+ reqOptions.seed = ollamaOpts.seed;
181
+ if (ollamaOpts.num_gpu !== undefined)
182
+ reqOptions.num_gpu = ollamaOpts.num_gpu;
183
+ if (Object.keys(reqOptions).length > 0) {
184
+ request.options = reqOptions;
185
+ }
186
+ // Keep alive
187
+ if (ollamaOpts.keep_alive !== undefined) {
188
+ request.keep_alive = ollamaOpts.keep_alive;
189
+ }
190
+ // Tools
191
+ if (context.tools?.length) {
192
+ request.tools = convertTools(context.tools);
193
+ }
194
+ return request;
195
+ }
196
+ // ─── Message conversion ─────────────────────────────────────────────────────
197
+ function convertMessages(context) {
198
+ const messages = [];
199
+ // System prompt
200
+ if (context.systemPrompt) {
201
+ messages.push({ role: "system", content: context.systemPrompt });
202
+ }
203
+ for (const msg of context.messages) {
204
+ switch (msg.role) {
205
+ case "user":
206
+ messages.push(convertUserMessage(msg));
207
+ break;
208
+ case "assistant":
209
+ messages.push(convertAssistantMessage(msg));
210
+ break;
211
+ case "toolResult":
212
+ messages.push({
213
+ role: "tool",
214
+ content: msg.content
215
+ .filter((c) => c.type === "text")
216
+ .map((c) => c.text)
217
+ .join("\n"),
218
+ name: msg.toolName,
219
+ });
220
+ break;
221
+ }
222
+ }
223
+ return messages;
224
+ }
225
+ function convertUserMessage(msg) {
226
+ if (typeof msg.content === "string") {
227
+ return { role: "user", content: msg.content };
228
+ }
229
+ const textParts = [];
230
+ const images = [];
231
+ for (const part of msg.content) {
232
+ if (part.type === "text") {
233
+ textParts.push(part.text);
234
+ }
235
+ else if (part.type === "image") {
236
+ // Strip data URI prefix if present
237
+ let data = part.data;
238
+ const commaIdx = data.indexOf(",");
239
+ if (commaIdx !== -1 && data.startsWith("data:")) {
240
+ data = data.slice(commaIdx + 1);
241
+ }
242
+ images.push(data);
243
+ }
244
+ }
245
+ const result = {
246
+ role: "user",
247
+ content: textParts.join("\n"),
248
+ };
249
+ if (images.length > 0) {
250
+ result.images = images;
251
+ }
252
+ return result;
253
+ }
254
+ function convertAssistantMessage(msg) {
255
+ let content = "";
256
+ const toolCalls = [];
257
+ for (const block of msg.content) {
258
+ if (block.type === "thinking") {
259
+ // Serialize thinking back inline for round-trip with Ollama
260
+ content += `<think>${block.thinking}</think>`;
261
+ }
262
+ else if (block.type === "text") {
263
+ content += block.text;
264
+ }
265
+ else if (block.type === "toolCall") {
266
+ const tc = block;
267
+ toolCalls.push({
268
+ function: {
269
+ name: tc.name,
270
+ arguments: tc.arguments,
271
+ },
272
+ });
273
+ }
274
+ }
275
+ const result = { role: "assistant", content };
276
+ if (toolCalls.length > 0) {
277
+ result.tool_calls = toolCalls;
278
+ }
279
+ return result;
280
+ }
281
+ // ─── Tool conversion ────────────────────────────────────────────────────────
282
+ function convertTools(tools) {
283
+ return tools.map((tool) => {
284
+ const params = tool.parameters;
285
+ return {
286
+ type: "function",
287
+ function: {
288
+ name: tool.name,
289
+ description: tool.description,
290
+ parameters: {
291
+ type: "object",
292
+ required: params.required,
293
+ properties: params.properties ?? {},
294
+ },
295
+ },
296
+ };
297
+ });
298
+ }
299
+ // ─── Response mapping ───────────────────────────────────────────────────────
300
+ function mapStopReason(doneReason) {
301
+ switch (doneReason) {
302
+ case "stop":
303
+ return "stop";
304
+ case "length":
305
+ return "length";
306
+ default:
307
+ return "stop";
308
+ }
309
+ }
310
+ function buildUsage(chunk) {
311
+ const input = chunk.prompt_eval_count ?? 0;
312
+ const outputTokens = chunk.eval_count ?? 0;
313
+ return {
314
+ input,
315
+ output: outputTokens,
316
+ cacheRead: 0,
317
+ cacheWrite: 0,
318
+ totalTokens: input + outputTokens,
319
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
320
+ };
321
+ }
322
+ function extractMetrics(chunk) {
323
+ if (!chunk.eval_duration && !chunk.total_duration)
324
+ return undefined;
325
+ const evalCount = chunk.eval_count ?? 0;
326
+ const evalDurationNs = chunk.eval_duration ?? 0;
327
+ const evalDurationMs = evalDurationNs / 1e6;
328
+ const tokensPerSecond = evalDurationNs > 0 ? evalCount / (evalDurationNs / 1e9) : 0;
329
+ return {
330
+ tokensPerSecond,
331
+ totalDurationMs: (chunk.total_duration ?? 0) / 1e6,
332
+ evalDurationMs,
333
+ promptEvalDurationMs: (chunk.prompt_eval_duration ?? 0) / 1e6,
334
+ };
335
+ }
336
+ // ─── Stream lifecycle helpers ───────────────────────────────────────────────
337
+ // Replicated from openai-shared.ts (not exported from @gsd/pi-ai)
338
+ function buildInitialOutput(model) {
339
+ return {
340
+ role: "assistant",
341
+ content: [],
342
+ api: model.api,
343
+ provider: model.provider,
344
+ model: model.id,
345
+ usage: {
346
+ input: 0,
347
+ output: 0,
348
+ cacheRead: 0,
349
+ cacheWrite: 0,
350
+ totalTokens: 0,
351
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
352
+ },
353
+ stopReason: "stop",
354
+ timestamp: Date.now(),
355
+ };
356
+ }
357
+ function assertStreamSuccess(output, signal) {
358
+ if (signal?.aborted) {
359
+ throw new Error("Request was aborted");
360
+ }
361
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
362
+ throw new Error("An unknown error occurred");
363
+ }
364
+ }
365
+ function finalizeStream(stream, output) {
366
+ stream.push({
367
+ type: "done",
368
+ reason: output.stopReason,
369
+ message: output,
370
+ });
371
+ stream.end();
372
+ }
373
+ function handleStreamError(stream, output, error, signal) {
374
+ for (const block of output.content)
375
+ delete block.index;
376
+ output.stopReason = signal?.aborted ? "aborted" : "error";
377
+ output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
378
+ stream.push({ type: "error", reason: output.stopReason, error: output });
379
+ stream.end();
380
+ }
@@ -1,4 +1,5 @@
1
1
  // GSD2 — HTTP client for Ollama REST API
2
+ import { parseNDJsonStream } from "./ndjson-stream.js";
2
3
  const DEFAULT_HOST = "http://localhost:11434";
3
4
  const PROBE_TIMEOUT_MS = 1500;
4
5
  const REQUEST_TIMEOUT_MS = 10000;
@@ -104,40 +105,30 @@ export async function pullModel(name, onProgress, signal) {
104
105
  if (!response.body) {
105
106
  throw new Error("Ollama /api/pull returned no body");
106
107
  }
107
- const reader = response.body.getReader();
108
- const decoder = new TextDecoder();
109
- let buffer = "";
110
- while (true) {
111
- const { done, value } = await reader.read();
112
- if (done)
113
- break;
114
- buffer += decoder.decode(value, { stream: true });
115
- const lines = buffer.split("\n");
116
- buffer = lines.pop() ?? "";
117
- for (const line of lines) {
118
- const trimmed = line.trim();
119
- if (!trimmed)
120
- continue;
121
- try {
122
- const progress = JSON.parse(trimmed);
123
- onProgress?.(progress);
124
- }
125
- catch {
126
- // Skip malformed lines
127
- }
128
- }
129
- }
130
- // Process remaining buffer
131
- if (buffer.trim()) {
132
- try {
133
- const progress = JSON.parse(buffer.trim());
134
- onProgress?.(progress);
135
- }
136
- catch {
137
- // Ignore
138
- }
108
+ for await (const progress of parseNDJsonStream(response.body, signal)) {
109
+ onProgress?.(progress);
139
110
  }
140
111
  }
112
+ /**
113
+ * Stream a chat completion via /api/chat.
114
+ * Returns an async generator yielding each NDJSON response chunk.
115
+ */
116
+ export async function* chat(request, signal) {
117
+ const response = await fetch(`${getOllamaHost()}/api/chat`, {
118
+ method: "POST",
119
+ headers: { "Content-Type": "application/json" },
120
+ body: JSON.stringify(request),
121
+ signal,
122
+ });
123
+ if (!response.ok) {
124
+ const text = await response.text();
125
+ throw new Error(`Ollama /api/chat returned ${response.status}: ${text}`);
126
+ }
127
+ if (!response.body) {
128
+ throw new Error("Ollama /api/chat returned no body");
129
+ }
130
+ yield* parseNDJsonStream(response.body, signal, true);
131
+ }
141
132
  /**
142
133
  * Delete a local model.
143
134
  */
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Returns models in the format expected by pi.registerProvider().
8
8
  */
9
- import { listModels, getOllamaHost } from "./ollama-client.js";
9
+ import { listModels } from "./ollama-client.js";
10
10
  import { estimateContextFromParams, formatModelSize, getModelCapabilities, humanizeModelName, } from "./model-capabilities.js";
11
11
  const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
12
12
  function enrichModel(info) {
@@ -32,6 +32,7 @@ function enrichModel(info) {
32
32
  maxTokens,
33
33
  sizeBytes: info.size,
34
34
  parameterSize,
35
+ ollamaOptions: caps.ollamaOptions,
35
36
  };
36
37
  }
37
38
  /**
@@ -61,9 +62,3 @@ export function formatModelForDisplay(model) {
61
62
  }
62
63
  return parts.join(" ");
63
64
  }
64
- /**
65
- * Build the OpenAI-compat base URL for Ollama.
66
- */
67
- export function getOllamaOpenAIBaseUrl() {
68
- return `${getOllamaHost()}/v1`;
69
- }
@@ -19,6 +19,8 @@ export function registerOllamaTool(pi) {
19
19
  promptGuidelines: [
20
20
  "Use 'list' to see what models are available locally before trying to use one.",
21
21
  "Use 'pull' to download a model that isn't available yet.",
22
+ "Use 'remove' to delete a local model that is no longer needed.",
23
+ "Use 'show' to get detailed info about a model (parameters, quantization, families).",
22
24
  "Use 'status' to check if Ollama is running.",
23
25
  "Use 'ps' to see which models are loaded in memory and VRAM usage.",
24
26
  "Common models: llama3.1:8b, qwen2.5-coder:7b, deepseek-r1:8b, codestral:22b",
@@ -27,6 +29,8 @@ export function registerOllamaTool(pi) {
27
29
  action: Type.Union([
28
30
  Type.Literal("list"),
29
31
  Type.Literal("pull"),
32
+ Type.Literal("remove"),
33
+ Type.Literal("show"),
30
34
  Type.Literal("status"),
31
35
  Type.Literal("ps"),
32
36
  ], { description: "Action to perform" }),
@@ -134,6 +138,64 @@ export function registerOllamaTool(pi) {
134
138
  details: { action, modelCount: ps.models.length, durationMs: Date.now() - startTime },
135
139
  };
136
140
  }
141
+ case "remove": {
142
+ if (!model) {
143
+ return {
144
+ content: [{ type: "text", text: "Error: 'model' parameter is required for remove action." }],
145
+ isError: true,
146
+ details: { action, durationMs: Date.now() - startTime, error: "missing_model" },
147
+ };
148
+ }
149
+ const running = await client.isRunning();
150
+ if (!running) {
151
+ return {
152
+ content: [{ type: "text", text: "Ollama is not running." }],
153
+ isError: true,
154
+ details: { action, model, durationMs: Date.now() - startTime, error: "not_running" },
155
+ };
156
+ }
157
+ await client.deleteModel(model);
158
+ return {
159
+ content: [{ type: "text", text: `Successfully removed ${model}` }],
160
+ details: { action, model, durationMs: Date.now() - startTime },
161
+ };
162
+ }
163
+ case "show": {
164
+ if (!model) {
165
+ return {
166
+ content: [{ type: "text", text: "Error: 'model' parameter is required for show action." }],
167
+ isError: true,
168
+ details: { action, durationMs: Date.now() - startTime, error: "missing_model" },
169
+ };
170
+ }
171
+ const running = await client.isRunning();
172
+ if (!running) {
173
+ return {
174
+ content: [{ type: "text", text: "Ollama is not running." }],
175
+ isError: true,
176
+ details: { action, model, durationMs: Date.now() - startTime, error: "not_running" },
177
+ };
178
+ }
179
+ const info = await client.showModel(model);
180
+ const details = info.details;
181
+ const infoLines = [
182
+ `Model: ${model}`,
183
+ `Family: ${details.family}`,
184
+ `Parameters: ${details.parameter_size}`,
185
+ `Quantization: ${details.quantization_level}`,
186
+ `Format: ${details.format}`,
187
+ ];
188
+ if (details.families?.length) {
189
+ infoLines.push(`Families: ${details.families.join(", ")}`);
190
+ }
191
+ if (info.parameters) {
192
+ infoLines.push(`\nModelfile parameters:\n${info.parameters}`);
193
+ }
194
+ return {
195
+ content: [{ type: "text", text: infoLines.join("\n") }],
196
+ details: { action, model, durationMs: Date.now() - startTime },
197
+ };
198
+ }
137
199
  default:
138
200
  return {
139
201
  content: [{ type: "text", text: `Unknown action: ${action}` }],
@@ -0,0 +1,104 @@
1
+ // GSD2 — Ollama Extension: Stateful <think> tag stream parser
2
+ const OPEN_TAG = "<think>";
3
+ const CLOSE_TAG = "</think>";
4
+ const MAX_TAG_LEN = Math.max(OPEN_TAG.length, CLOSE_TAG.length);
5
+ export class ThinkingTagParser {
6
+ buffer = "";
7
+ inThinking = false;
8
+ /**
9
+ * Feed a chunk of text and get back parsed segments.
10
+ * May return zero or more segments depending on tag boundaries.
11
+ */
12
+ push(chunk) {
13
+ const results = [];
14
+ let input = this.buffer + chunk;
15
+ this.buffer = "";
16
+ while (input.length > 0) {
17
+ if (this.inThinking) {
18
+ const closeIdx = input.indexOf(CLOSE_TAG);
19
+ if (closeIdx !== -1) {
20
+ // Found close tag — emit thinking content before it
21
+ const thinking = input.slice(0, closeIdx);
22
+ if (thinking)
23
+ results.push({ type: "thinking", text: thinking });
24
+ this.inThinking = false;
25
+ input = input.slice(closeIdx + CLOSE_TAG.length);
26
+ }
27
+ else if (this.couldBePartialTag(input, CLOSE_TAG)) {
28
+ // Possible partial close tag at end — buffer only the matching tail
29
+ const tailLen = this.getPartialTagTailLength(input, CLOSE_TAG);
30
+ const safe = input.slice(0, input.length - tailLen);
31
+ if (safe)
32
+ results.push({ type: "thinking", text: safe });
33
+ this.buffer = input.slice(-tailLen);
34
+ break;
35
+ }
36
+ else {
37
+ // No close tag — emit all as thinking
38
+ results.push({ type: "thinking", text: input });
39
+ break;
40
+ }
41
+ }
42
+ else {
43
+ const openIdx = input.indexOf(OPEN_TAG);
44
+ if (openIdx !== -1) {
45
+ // Found open tag — emit text before it
46
+ const text = input.slice(0, openIdx);
47
+ if (text)
48
+ results.push({ type: "text", text });
49
+ this.inThinking = true;
50
+ input = input.slice(openIdx + OPEN_TAG.length);
51
+ }
52
+ else if (this.couldBePartialTag(input, OPEN_TAG)) {
53
+ // Possible partial open tag at end — buffer only the matching tail
54
+ const tailLen = this.getPartialTagTailLength(input, OPEN_TAG);
55
+ const safe = input.slice(0, input.length - tailLen);
56
+ if (safe)
57
+ results.push({ type: "text", text: safe });
58
+ this.buffer = input.slice(-tailLen);
59
+ break;
60
+ }
61
+ else {
62
+ // No open tag — emit all as text
63
+ results.push({ type: "text", text: input });
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ return results;
69
+ }
70
+ /**
71
+ * Flush any remaining buffered content. Call at end of stream.
72
+ */
73
+ flush() {
74
+ if (!this.buffer)
75
+ return [];
76
+ const result = {
77
+ type: this.inThinking ? "thinking" : "text",
78
+ text: this.buffer,
79
+ };
80
+ this.buffer = "";
81
+ return [result];
82
+ }
83
+ /**
84
+ * Check if the end of input could be the start of a partial tag.
85
+ * Only buffers when the tail of input matches a prefix of the tag.
86
+ */
87
+ couldBePartialTag(input, tag) {
88
+ return this.getPartialTagTailLength(input, tag) > 0;
89
+ }
90
+ /**
91
+ * Get the length of the tail of input that matches a prefix of the tag.
92
+ * Returns 0 if no partial match.
93
+ */
94
+ getPartialTagTailLength(input, tag) {
95
+ const maxCheck = Math.min(input.length, tag.length - 1);
96
+ for (let len = maxCheck; len >= 1; len--) {
97
+ const tail = input.slice(-len);
98
+ if (tag.startsWith(tail)) {
99
+ return len;
100
+ }
101
+ }
102
+ return 0;
103
+ }
104
+ }
@@ -10,18 +10,20 @@ export async function runUpdate() {
10
10
  const reset = '\x1b[0m';
11
11
  process.stdout.write(`${dim}Current version:${reset} v${current}\n`);
12
12
  process.stdout.write(`${dim}Checking npm registry...${reset}\n`);
13
- // Fetch latest version
13
+ // Fetch latest version — bypass npm client cache to avoid stale results (#3445)
14
14
  let latest;
15
15
  try {
16
- latest = execSync(`npm view ${NPM_PACKAGE} version`, {
16
+ latest = execSync(`npm view ${NPM_PACKAGE} version --fetch-retry-mintimeout=3000`, {
17
17
  encoding: 'utf-8',
18
18
  stdio: ['ignore', 'pipe', 'ignore'],
19
+ env: { ...process.env, npm_config_cache: '' },
19
20
  }).trim();
20
21
  }
21
22
  catch {
22
23
  process.stderr.write(`${yellow}Failed to reach npm registry.${reset}\n`);
23
24
  process.exit(1);
24
25
  }
26
+ process.stdout.write(`${dim}Latest version:${reset} v${latest}\n`);
25
27
  if (compareSemver(latest, current) <= 0) {
26
28
  process.stdout.write(`${green}Already up to date.${reset}\n`);
27
29
  return;
@@ -1 +1 @@
1
- TTlAguZQ5vR9EOv6G8cel
1
+ SDB1T-4NqkMjYirjjqQhr