gsd-pi 2.73.0 → 2.73.1-dev.6ddfa43

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 (224) hide show
  1. package/dist/cli.js +0 -47
  2. package/dist/help-text.js +1 -1
  3. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +9 -3
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +5 -3
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
  6. package/dist/resources/extensions/gsd/auto-prompts.js +9 -6
  7. package/dist/resources/extensions/gsd/auto-start.js +20 -6
  8. package/dist/resources/extensions/gsd/auto.js +5 -1
  9. package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
  10. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
  11. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -1
  12. package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
  13. package/dist/resources/extensions/gsd/gsd-db.js +36 -2
  14. package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
  15. package/dist/resources/extensions/gsd/preferences-models.js +43 -0
  16. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  17. package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
  18. package/dist/startup-model-validation.js +8 -5
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  21. package/dist/web/standalone/.next/build-manifest.json +3 -3
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/required-server-files.json +3 -3
  24. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  25. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  35. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  51. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  63. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  83. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  93. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  113. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  115. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  117. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  119. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/index.html +1 -1
  129. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  130. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  131. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  132. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  134. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/page.js +2 -2
  136. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  138. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  139. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  140. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  142. package/dist/web/standalone/.next/server/middleware.js +2 -2
  143. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  145. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  146. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  147. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  148. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  149. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  151. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  153. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  154. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  155. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  156. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  157. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  158. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  159. package/dist/web/standalone/server.js +1 -1
  160. package/package.json +1 -1
  161. package/packages/pi-ai/dist/index.d.ts +1 -0
  162. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  163. package/packages/pi-ai/dist/index.js +1 -0
  164. package/packages/pi-ai/dist/index.js.map +1 -1
  165. package/packages/pi-ai/src/index.ts +4 -0
  166. package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -1
  167. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
  169. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +175 -8
  171. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  173. package/packages/pi-coding-agent/dist/core/model-resolver.js +25 -68
  174. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +51 -26
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +95 -21
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
  185. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -0
  187. package/packages/pi-coding-agent/package.json +1 -1
  188. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +38 -0
  189. package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -1
  190. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +198 -8
  191. package/packages/pi-coding-agent/src/core/model-resolver.ts +26 -70
  192. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +62 -26
  193. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
  194. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +115 -26
  195. package/pkg/package.json +1 -1
  196. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -4
  197. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +23 -2
  198. package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
  199. package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
  200. package/src/resources/extensions/gsd/auto-prompts.ts +9 -3
  201. package/src/resources/extensions/gsd/auto-start.ts +27 -6
  202. package/src/resources/extensions/gsd/auto.ts +5 -0
  203. package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
  204. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
  205. package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -1
  206. package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
  207. package/src/resources/extensions/gsd/gsd-db.ts +52 -2
  208. package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
  209. package/src/resources/extensions/gsd/preferences-models.ts +41 -0
  210. package/src/resources/extensions/gsd/preferences-types.ts +12 -0
  211. package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
  212. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
  213. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
  214. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
  215. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
  216. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
  217. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
  218. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
  219. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +267 -0
  220. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  221. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  222. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  223. /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → r6AvNu-aMwn4nwqjHqAfw}/_buildManifest.js +0 -0
  224. /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → r6AvNu-aMwn4nwqjHqAfw}/_ssgManifest.js +0 -0
@@ -3,45 +3,13 @@
3
3
  */
4
4
 
5
5
  import type { ThinkingLevel } from "@gsd/pi-agent-core";
6
- import { type Api, type KnownProvider, type Model, modelsAreEqual } from "@gsd/pi-ai";
6
+ import { type Api, type Model, modelsAreEqual } from "@gsd/pi-ai";
7
7
  import chalk from "chalk";
8
8
  import { minimatch } from "minimatch";
9
9
  import { isValidThinkingLevel } from "../cli/args.js";
10
10
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
11
11
  import type { ModelRegistry } from "./model-registry.js";
12
12
 
13
- /** Default model IDs for each known provider */
14
- const defaultModelPerProvider: Record<KnownProvider, string> = {
15
- "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
16
- anthropic: "claude-opus-4-6",
17
- "anthropic-vertex": "claude-sonnet-4-6",
18
- openai: "gpt-5.4",
19
- "azure-openai-responses": "gpt-5.2",
20
- "openai-codex": "gpt-5.4",
21
- google: "gemini-2.5-pro",
22
- "google-gemini-cli": "gemini-2.5-pro",
23
- "google-antigravity": "gemini-3.1-pro-high",
24
- "google-vertex": "gemini-3-pro-preview",
25
- "github-copilot": "gpt-4o",
26
- openrouter: "openai/gpt-5.1-codex",
27
- "vercel-ai-gateway": "anthropic/claude-opus-4-6",
28
- xai: "grok-4-fast-non-reasoning",
29
- groq: "openai/gpt-oss-120b",
30
- cerebras: "zai-glm-4.6",
31
- zai: "glm-4.6",
32
- mistral: "devstral-medium-latest",
33
- minimax: "MiniMax-M2.1",
34
- "minimax-cn": "MiniMax-M2.1",
35
- huggingface: "moonshotai/Kimi-K2.5",
36
- opencode: "claude-opus-4-6",
37
- "opencode-go": "kimi-k2.5",
38
- "kimi-coding": "kimi-k2-thinking",
39
- "alibaba-coding-plan": "qwen3.5-plus",
40
- "alibaba-dashscope": "qwen3.5-plus",
41
- ollama: "llama3.1:8b",
42
- "ollama-cloud": "qwen3:32b",
43
- };
44
-
45
13
  export interface ScopedModel {
46
14
  model: Model<Api>;
47
15
  /** Thinking level if explicitly specified in pattern (e.g., "model:high"), undefined otherwise */
@@ -123,10 +91,11 @@ function buildFallbackModel(provider: string, modelId: string, availableModels:
123
91
  const providerModels = availableModels.filter((m) => m.provider === provider);
124
92
  if (providerModels.length === 0) return undefined;
125
93
 
126
- const defaultId = defaultModelPerProvider[provider as KnownProvider];
127
- const baseModel = defaultId
128
- ? (providerModels.find((m) => m.id === defaultId) ?? providerModels[0])
129
- : providerModels[0];
94
+ // Use the first available model from this provider as a template for
95
+ // capabilities (context window, reasoning support, etc.). The user is
96
+ // explicitly providing a custom model id, so we just need any shape of
97
+ // model from the same provider to inherit from.
98
+ const baseModel = providerModels[0];
130
99
 
131
100
  return {
132
101
  ...baseModel,
@@ -503,33 +472,19 @@ export async function findInitialModel(options: {
503
472
  };
504
473
  }
505
474
 
506
- // 3. Try saved default from settings
507
- if (defaultProvider && defaultModelId) {
508
- // Guard against stale settings defaults: only use the saved provider/model
509
- // if the provider is actually request-ready (auth/OAuth/CLI ready).
510
- if (modelRegistry.isProviderRequestReady(defaultProvider)) {
511
- const found = modelRegistry.find(defaultProvider, defaultModelId);
512
- if (found) {
513
- // Check if the provider's recommended default is a higher-capability variant
514
- // of the saved model (e.g. saved "claude-opus-4-6" vs recommended "claude-opus-4-6-extended").
515
- // If so, prefer the recommended variant to avoid using a smaller context window (#1125).
516
- const recommendedId = defaultModelPerProvider[defaultProvider as KnownProvider];
517
- if (recommendedId && recommendedId !== defaultModelId && recommendedId.startsWith(defaultModelId)) {
518
- const recommended = modelRegistry.find(defaultProvider, recommendedId);
519
- if (recommended) {
520
- model = recommended;
521
- if (defaultThinkingLevel) {
522
- thinkingLevel = defaultThinkingLevel;
523
- }
524
- return { model, thinkingLevel, fallbackMessage: undefined };
525
- }
526
- }
527
- model = found;
528
- if (defaultThinkingLevel) {
529
- thinkingLevel = defaultThinkingLevel;
530
- }
531
- return { model, thinkingLevel, fallbackMessage: undefined };
475
+ // 3. Try saved default from settings — use it exactly as configured.
476
+ // Whatever the user chose is what gets used; no silent substitution.
477
+ // Skip the saved default if its provider is not request-ready (no auth
478
+ // available) so we fall through to an actually-usable model instead of
479
+ // returning a stale selection every selector surface would display.
480
+ if (defaultProvider && defaultModelId && modelRegistry.isProviderRequestReady(defaultProvider)) {
481
+ const found = modelRegistry.find(defaultProvider, defaultModelId);
482
+ if (found) {
483
+ model = found;
484
+ if (defaultThinkingLevel) {
485
+ thinkingLevel = defaultThinkingLevel;
532
486
  }
487
+ return { model, thinkingLevel, fallbackMessage: undefined };
533
488
  }
534
489
  }
535
490
 
@@ -537,16 +492,17 @@ export async function findInitialModel(options: {
537
492
  const availableModels = await modelRegistry.getAvailable();
538
493
 
539
494
  if (availableModels.length > 0) {
540
- // Try to find a default model from known providers
541
- for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {
542
- const defaultId = defaultModelPerProvider[provider];
543
- const match = availableModels.find((m) => m.provider === provider && m.id === defaultId);
544
- if (match) {
545
- return { model: match, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };
495
+ // Prefer a model from the user's saved provider if any is still available —
496
+ // provider stickiness, not a hard-coded Anthropic/OpenAI preference.
497
+ if (defaultProvider) {
498
+ const sameProvider = availableModels.find((m) => m.provider === defaultProvider);
499
+ if (sameProvider) {
500
+ return { model: sameProvider, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };
546
501
  }
547
502
  }
548
503
 
549
- // If no default found, use first available
504
+ // Otherwise use the first available registry order reflects models.json
505
+ // order, which the user controls.
550
506
  return { model: availableModels[0], thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };
551
507
  }
552
508
 
@@ -3,8 +3,15 @@ import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-t
3
3
  import { getMarkdownTheme, theme } from "../theme/theme.js";
4
4
  import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
5
5
 
6
+ export interface ContentRange {
7
+ startIndex: number;
8
+ endIndex: number;
9
+ }
10
+
6
11
  /**
7
- * Component that renders a complete assistant message
12
+ * Component that renders a complete assistant message, or a sub-range of its content[].
13
+ * When `range` is provided, only content[startIndex..endIndex] (inclusive) is rendered.
14
+ * Non-text/thinking blocks within the range are silently skipped.
8
15
  */
9
16
  export class AssistantMessageComponent extends Container {
10
17
  private contentContainer: Container;
@@ -12,18 +19,26 @@ export class AssistantMessageComponent extends Container {
12
19
  private markdownTheme: MarkdownTheme;
13
20
  private lastMessage?: AssistantMessage;
14
21
  private timestampFormat: TimestampFormat;
22
+ private range?: ContentRange;
23
+ private showMetadata: boolean;
15
24
 
16
25
  constructor(
17
26
  message?: AssistantMessage,
18
27
  hideThinkingBlock = false,
19
28
  markdownTheme: MarkdownTheme = getMarkdownTheme(),
20
29
  timestampFormat: TimestampFormat = "date-time-iso",
30
+ range?: ContentRange,
21
31
  ) {
22
32
  super();
23
33
 
24
34
  this.hideThinkingBlock = hideThinkingBlock;
25
35
  this.markdownTheme = markdownTheme;
26
36
  this.timestampFormat = timestampFormat;
37
+ this.range = range;
38
+ // No range = legacy full-message rendering; show metadata by default.
39
+ // Ranged (interleaved) instances start with metadata hidden; chat-controller
40
+ // calls setShowMetadata(true) on the last segment at message_end.
41
+ this.showMetadata = !range;
27
42
 
28
43
  // Container for text/thinking content
29
44
  this.contentContainer = new Container();
@@ -34,6 +49,20 @@ export class AssistantMessageComponent extends Container {
34
49
  }
35
50
  }
36
51
 
52
+ setRange(range: ContentRange | undefined): void {
53
+ this.range = range;
54
+ if (this.lastMessage) {
55
+ this.updateContent(this.lastMessage);
56
+ }
57
+ }
58
+
59
+ setShowMetadata(show: boolean): void {
60
+ this.showMetadata = show;
61
+ if (this.lastMessage) {
62
+ this.updateContent(this.lastMessage);
63
+ }
64
+ }
65
+
37
66
  override invalidate(): void {
38
67
  super.invalidate();
39
68
  if (this.lastMessage) {
@@ -51,7 +80,11 @@ export class AssistantMessageComponent extends Container {
51
80
  // Clear content container
52
81
  this.contentContainer.clear();
53
82
 
54
- const hasVisibleContent = message.content.some(
83
+ const start = this.range?.startIndex ?? 0;
84
+ const end = this.range?.endIndex ?? message.content.length - 1;
85
+ const slice = message.content.slice(start, end + 1);
86
+
87
+ const hasVisibleContent = slice.some(
55
88
  (c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()),
56
89
  );
57
90
 
@@ -59,9 +92,9 @@ export class AssistantMessageComponent extends Container {
59
92
  this.contentContainer.addChild(new Spacer(1));
60
93
  }
61
94
 
62
- // Render content in order
63
- for (let i = 0; i < message.content.length; i++) {
64
- const content = message.content[i];
95
+ // Render content in order; non-text/thinking blocks are silently skipped
96
+ for (let i = 0; i < slice.length; i++) {
97
+ const content = slice[i];
65
98
  if (content.type === "text" && content.text.trim()) {
66
99
  // Assistant text messages with no background - trim the text
67
100
  // Set paddingY=0 to avoid extra spacing before tool executions
@@ -69,7 +102,7 @@ export class AssistantMessageComponent extends Container {
69
102
  } else if (content.type === "thinking" && content.thinking.trim()) {
70
103
  // Add spacing only when another visible assistant content block follows.
71
104
  // This avoids a superfluous blank line before separately-rendered tool execution blocks.
72
- const hasVisibleContentAfter = message.content
105
+ const hasVisibleContentAfter = slice
73
106
  .slice(i + 1)
74
107
  .some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
75
108
 
@@ -94,30 +127,33 @@ export class AssistantMessageComponent extends Container {
94
127
  }
95
128
  }
96
129
 
97
- // Check if aborted - show after partial content
98
- // But only if there are no tool calls (tool execution components will show the error)
99
- const hasToolCalls = message.content.some((c) => c.type === "toolCall");
100
- if (!hasToolCalls) {
101
- if (message.stopReason === "aborted") {
102
- const abortMessage =
103
- message.errorMessage && message.errorMessage !== "Request was aborted"
104
- ? message.errorMessage
105
- : "Operation aborted";
106
- if (hasVisibleContent) {
130
+ // Metadata (errors, timestamp): gated on showMetadata so ranged instances stay clean
131
+ // until chat-controller explicitly enables it on the last segment at message_end.
132
+ if (this.showMetadata) {
133
+ // Check if aborted - show after partial content
134
+ // But only if there are no tool calls (tool execution components will show the error)
135
+ const hasToolCalls = message.content.some((c) => c.type === "toolCall");
136
+ if (!hasToolCalls) {
137
+ if (message.stopReason === "aborted") {
138
+ const abortMessage =
139
+ message.errorMessage && message.errorMessage !== "Request was aborted"
140
+ ? message.errorMessage
141
+ : "Operation aborted";
142
+ if (hasVisibleContent) {
143
+ this.contentContainer.addChild(new Spacer(1));
144
+ }
145
+ this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
146
+ } else if (message.stopReason === "error") {
147
+ const errorMsg = message.errorMessage || "Unknown error";
107
148
  this.contentContainer.addChild(new Spacer(1));
149
+ this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
108
150
  }
109
- this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
110
- } else if (message.stopReason === "error") {
111
- const errorMsg = message.errorMessage || "Unknown error";
112
- this.contentContainer.addChild(new Spacer(1));
113
- this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
114
151
  }
115
- }
116
152
 
117
- // Show timestamp when the message is complete (has a stop reason)
118
- if (message.stopReason && message.timestamp) {
119
- const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
120
- this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
153
+ if (message.stopReason && message.timestamp) {
154
+ const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
155
+ this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
156
+ }
121
157
  }
122
158
  }
123
159
  }
@@ -0,0 +1,71 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { findLatestPinnableText } from "./chat-controller.js";
5
+
6
+ test("findLatestPinnableText: empty content returns empty string", () => {
7
+ assert.equal(findLatestPinnableText([]), "");
8
+ });
9
+
10
+ test("findLatestPinnableText: no tool calls returns empty string", () => {
11
+ const blocks = [
12
+ { type: "text", text: "hello" },
13
+ { type: "text", text: "world" },
14
+ ];
15
+ assert.equal(findLatestPinnableText(blocks), "");
16
+ });
17
+
18
+ test("findLatestPinnableText: returns text preceding a tool call", () => {
19
+ const blocks = [
20
+ { type: "text", text: "doing the thing" },
21
+ { type: "toolCall", id: "1", name: "Read" },
22
+ ];
23
+ assert.equal(findLatestPinnableText(blocks), "doing the thing");
24
+ });
25
+
26
+ test("findLatestPinnableText: ignores trailing streaming text after the last tool call (regression: pinned mirror duplicated chat-container tokens)", () => {
27
+ const blocks = [
28
+ { type: "text", text: "first prose" },
29
+ { type: "toolCall", id: "1", name: "Read" },
30
+ { type: "text", text: "second prose still streaming" },
31
+ ];
32
+ assert.equal(findLatestPinnableText(blocks), "first prose");
33
+ });
34
+
35
+ test("findLatestPinnableText: with multiple tools, picks text before the most recent tool call", () => {
36
+ const blocks = [
37
+ { type: "text", text: "first" },
38
+ { type: "toolCall", id: "1", name: "Read" },
39
+ { type: "text", text: "second" },
40
+ { type: "toolCall", id: "2", name: "Grep" },
41
+ { type: "text", text: "third streaming" },
42
+ ];
43
+ assert.equal(findLatestPinnableText(blocks), "second");
44
+ });
45
+
46
+ test("findLatestPinnableText: treats serverToolUse the same as toolCall", () => {
47
+ const blocks = [
48
+ { type: "text", text: "before web search" },
49
+ { type: "serverToolUse", id: "ws1", name: "web_search" },
50
+ { type: "text", text: "answer streaming" },
51
+ ];
52
+ assert.equal(findLatestPinnableText(blocks), "before web search");
53
+ });
54
+
55
+ test("findLatestPinnableText: skips empty/whitespace-only text blocks", () => {
56
+ const blocks = [
57
+ { type: "text", text: "real prose" },
58
+ { type: "text", text: " " },
59
+ { type: "text", text: "" },
60
+ { type: "toolCall", id: "1", name: "Read" },
61
+ ];
62
+ assert.equal(findLatestPinnableText(blocks), "real prose");
63
+ });
64
+
65
+ test("findLatestPinnableText: thinking blocks are not pinnable", () => {
66
+ const blocks = [
67
+ { type: "thinking", thinking: "internal" },
68
+ { type: "toolCall", id: "1", name: "Read" },
69
+ ];
70
+ assert.equal(findLatestPinnableText(blocks), "");
71
+ });
@@ -10,6 +10,13 @@ import { appKey } from "../components/keybinding-hints.js";
10
10
  // Tracks the last processed content index to avoid re-scanning all blocks on every message_update
11
11
  let lastProcessedContentIndex = 0;
12
12
 
13
+ // --- Segment walker state (per streaming assistant turn) ---
14
+ type RenderedSegment =
15
+ | { kind: "text-run"; startIndex: number; endIndex: number; component: AssistantMessageComponent }
16
+ | { kind: "tool"; contentIndex: number; component: ToolExecutionComponent };
17
+
18
+ let renderedSegments: RenderedSegment[] = [];
19
+
13
20
  function hasVisibleAssistantContent(message: { content: Array<any> }): boolean {
14
21
  return message.content.some(
15
22
  (c) =>
@@ -22,6 +29,28 @@ function hasAssistantToolBlocks(message: { content: Array<any> }): boolean {
22
29
  return message.content.some((c) => c.type === "toolCall" || c.type === "serverToolUse");
23
30
  }
24
31
 
32
+ // Pick the latest non-empty text block that appears strictly before the most
33
+ // recent tool call. Text blocks that come after the last tool call are still
34
+ // streaming live into the chat container, so mirroring them into the pinned
35
+ // "Latest Output" zone would render the same tokens twice.
36
+ export function findLatestPinnableText(contentBlocks: Array<any>): string {
37
+ let lastToolIdx = -1;
38
+ for (let i = contentBlocks.length - 1; i >= 0; i--) {
39
+ const c = contentBlocks[i];
40
+ if (c?.type === "toolCall" || c?.type === "serverToolUse") {
41
+ lastToolIdx = i;
42
+ break;
43
+ }
44
+ }
45
+ for (let i = lastToolIdx - 1; i >= 0; i--) {
46
+ const c = contentBlocks[i];
47
+ if (c?.type === "text" && typeof c.text === "string" && c.text.trim()) {
48
+ return c.text.trim();
49
+ }
50
+ }
51
+ return "";
52
+ }
53
+
25
54
  // Tracks the latest assistant text for the pinned message zone
26
55
  let lastPinnedText = "";
27
56
  // Whether any tool execution has been added in this assistant turn (triggers pinned display)
@@ -58,6 +87,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
58
87
  lastProcessedContentIndex = 0;
59
88
  lastPinnedText = "";
60
89
  hasToolsInTurn = false;
90
+ renderedSegments = [];
61
91
  if (pinnedBorder) pinnedBorder.stopSpinner();
62
92
  pinnedBorder = undefined;
63
93
  pinnedTextComponent = undefined;
@@ -77,6 +107,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
77
107
  host.pinnedMessageContainer.clear();
78
108
  lastPinnedText = "";
79
109
  hasToolsInTurn = false;
110
+ renderedSegments = [];
80
111
  if (pinnedBorder) pinnedBorder.stopSpinner();
81
112
  pinnedBorder = undefined;
82
113
  pinnedTextComponent = undefined;
@@ -251,24 +282,88 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
251
282
  }
252
283
  }
253
284
 
254
- // Render assistant text/thinking after tool components so mixed
255
- // streams keep chronological ordering in the chat container.
256
- const hasToolBlocks = hasAssistantToolBlocks(host.streamingMessage);
257
- if (!host.streamingComponent && hasVisibleAssistantContent(host.streamingMessage)) {
258
- host.streamingComponent = new AssistantMessageComponent(
259
- undefined,
260
- host.hideThinkingBlock,
261
- host.getMarkdownThemeWithSettings(),
262
- host.settingsManager.getTimestampFormat(),
263
- );
264
- host.chatContainer.addChild(host.streamingComponent);
265
- }
266
- if (host.streamingComponent) {
267
- if (hasToolBlocks) {
268
- host.chatContainer.removeChild(host.streamingComponent);
269
- host.chatContainer.addChild(host.streamingComponent);
285
+ // Segment walker: render content blocks in stream order, append-only.
286
+ // Build desired segment plan from content[].
287
+ {
288
+ const blocks = host.streamingMessage.content;
289
+ type DesiredSegment =
290
+ | { kind: "text-run"; startIndex: number; endIndex: number }
291
+ | { kind: "tool"; contentIndex: number; toolId: string };
292
+ const desired: DesiredSegment[] = [];
293
+ let runStart = -1;
294
+ for (let i = 0; i < blocks.length; i++) {
295
+ const b = blocks[i];
296
+ const isText = b.type === "text" || b.type === "thinking";
297
+ const isTool = b.type === "toolCall" || b.type === "serverToolUse";
298
+ if (isText) {
299
+ if (runStart === -1) runStart = i;
300
+ } else {
301
+ if (runStart !== -1) {
302
+ desired.push({ kind: "text-run", startIndex: runStart, endIndex: i - 1 });
303
+ runStart = -1;
304
+ }
305
+ if (isTool) {
306
+ desired.push({ kind: "tool", contentIndex: i, toolId: b.id });
307
+ }
308
+ }
309
+ }
310
+ if (runStart !== -1) {
311
+ desired.push({ kind: "text-run", startIndex: runStart, endIndex: blocks.length - 1 });
312
+ }
313
+
314
+ // Append any newly needed segments (never reorder existing ones).
315
+ for (const seg of desired) {
316
+ if (seg.kind === "tool") {
317
+ // Tool segments are already handled above via pendingTools; just
318
+ // register them in renderedSegments if not yet tracked.
319
+ const existing = renderedSegments.find(
320
+ (s) => s.kind === "tool" && s.contentIndex === seg.contentIndex,
321
+ );
322
+ if (!existing) {
323
+ const comp = host.pendingTools.get(seg.toolId);
324
+ if (comp) {
325
+ renderedSegments.push({ kind: "tool", contentIndex: seg.contentIndex, component: comp });
326
+ }
327
+ }
328
+ } else {
329
+ // text-run segment
330
+ const existing = renderedSegments.find(
331
+ (s) => s.kind === "text-run" && s.startIndex === seg.startIndex,
332
+ );
333
+ if (!existing) {
334
+ const comp = new AssistantMessageComponent(
335
+ undefined,
336
+ host.hideThinkingBlock,
337
+ host.getMarkdownThemeWithSettings(),
338
+ host.settingsManager.getTimestampFormat(),
339
+ { startIndex: seg.startIndex, endIndex: seg.endIndex },
340
+ );
341
+ host.chatContainer.addChild(comp);
342
+ renderedSegments.push({ kind: "text-run", startIndex: seg.startIndex, endIndex: seg.endIndex, component: comp });
343
+ host.streamingComponent = comp;
344
+ }
345
+ }
346
+ }
347
+
348
+ // Update all trailing text-run segments with the latest message so
349
+ // streaming text grows in place.
350
+ for (const seg of renderedSegments) {
351
+ if (seg.kind === "text-run") {
352
+ // Find corresponding desired segment to get current endIndex
353
+ const d = desired.find((ds) => ds.kind === "text-run" && ds.startIndex === seg.startIndex);
354
+ if (d && d.kind === "text-run" && d.endIndex !== seg.endIndex) {
355
+ seg.endIndex = d.endIndex;
356
+ seg.component.setRange({ startIndex: seg.startIndex, endIndex: seg.endIndex });
357
+ }
358
+ seg.component.updateContent(host.streamingMessage);
359
+ }
360
+ }
361
+
362
+ // Keep streamingComponent pointing at the last text-run for message_end compatibility.
363
+ const lastTextSeg = [...renderedSegments].reverse().find((s) => s.kind === "text-run");
364
+ if (lastTextSeg && lastTextSeg.kind === "text-run") {
365
+ host.streamingComponent = lastTextSeg.component;
270
366
  }
271
- host.streamingComponent.updateContent(host.streamingMessage);
272
367
  }
273
368
 
274
369
  // Update index: fully processed blocks won't need re-scanning.
@@ -286,15 +381,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
286
381
  if (hasTools) hasToolsInTurn = true;
287
382
 
288
383
  if (hasToolsInTurn) {
289
- // Collect the latest text block(s) from the assistant message
290
- let latestText = "";
291
- for (let i = contentBlocks.length - 1; i >= 0; i--) {
292
- const c = contentBlocks[i] as any;
293
- if (c.type === "text" && c.text?.trim()) {
294
- latestText = c.text.trim();
295
- break;
296
- }
297
- }
384
+ const latestText = findLatestPinnableText(contentBlocks);
298
385
 
299
386
  if (latestText && latestText !== lastPinnedText) {
300
387
  lastPinnedText = latestText;
@@ -362,6 +449,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
362
449
  host.chatContainer.addChild(host.streamingComponent);
363
450
  }
364
451
  if (host.streamingComponent) {
452
+ host.streamingComponent.setShowMetadata(true);
365
453
  host.streamingComponent.updateContent(host.streamingMessage);
366
454
  }
367
455
 
@@ -385,6 +473,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
385
473
  }
386
474
  host.streamingComponent = undefined;
387
475
  host.streamingMessage = undefined;
476
+ renderedSegments = [];
388
477
  // Clear pinned output once the message is finalized in the chat
389
478
  // container — prevents duplicate display when the agent continues
390
479
  // (e.g. form elicitation) after the assistant message ends.
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.73.0",
3
+ "version": "2.73.1",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -14,10 +14,11 @@ import type {
14
14
  Context,
15
15
  Model,
16
16
  SimpleStreamOptions,
17
+ ThinkingLevel,
17
18
  ToolCall,
18
19
  } from "@gsd/pi-ai";
19
20
  import type { ExtensionUIContext } from "@gsd/pi-coding-agent";
20
- import { EventStream } from "@gsd/pi-ai";
21
+ import { EventStream, mapThinkingLevelToEffort, supportsAdaptiveThinking } from "@gsd/pi-ai";
21
22
  import { execSync } from "node:child_process";
22
23
  import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
23
24
  import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
@@ -600,8 +601,9 @@ export function buildSdkOptions(
600
601
  modelId: string,
601
602
  prompt: string,
602
603
  overrides?: { permissionMode?: "bypassPermissions" | "acceptEdits" | "default" | "plan" },
603
- extraOptions: Record<string, unknown> = {},
604
+ extraOptions: Record<string, unknown> & { reasoning?: ThinkingLevel } = {},
604
605
  ): Record<string, unknown> {
606
+ const { reasoning, ...sdkExtraOptions } = extraOptions;
605
607
  const mcpServers = buildWorkflowMcpServers();
606
608
  const permissionMode = overrides?.permissionMode ?? "bypassPermissions";
607
609
  const disallowedTools = ["AskUserQuestion"];
@@ -620,6 +622,10 @@ export function buildSdkOptions(
620
622
  "Bash(pwd)",
621
623
  ...(mcpServers ? Object.keys(mcpServers).map((serverName) => `mcp__${serverName}__*`) : []),
622
624
  ];
625
+ const effort =
626
+ reasoning && supportsAdaptiveThinking(modelId)
627
+ ? mapThinkingLevelToEffort(reasoning, modelId)
628
+ : undefined;
623
629
  return {
624
630
  pathToClaudeCodeExecutable: getClaudePath(),
625
631
  model: modelId,
@@ -634,7 +640,8 @@ export function buildSdkOptions(
634
640
  ...(allowedTools.length > 0 ? { allowedTools } : {}),
635
641
  ...(mcpServers ? { mcpServers } : {}),
636
642
  betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
637
- ...extraOptions,
643
+ ...(effort ? { effort } : {}),
644
+ ...sdkExtraOptions,
638
645
  };
639
646
  }
640
647
 
@@ -828,11 +835,12 @@ async function pumpSdkMessages(
828
835
  { permissionMode },
829
836
  typeof (options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext === "object"
830
837
  ? {
838
+ reasoning: options?.reasoning,
831
839
  onElicitation: createClaudeCodeElicitationHandler(
832
840
  (options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext,
833
841
  ),
834
842
  }
835
- : {},
843
+ : { reasoning: options?.reasoning },
836
844
  );
837
845
 
838
846
  const queryResult = sdk.query({