@corbat-tech/coco 2.29.0 → 2.31.0

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.
package/dist/cli/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { execFile, execFileSync, execSync, spawn, exec } from 'child_process';
2
3
  import { setGlobalDispatcher, EnvHttpProxyAgent } from 'undici';
3
4
  import * as fs5 from 'fs';
4
5
  import fs5__default, { accessSync, readFileSync, constants as constants$1 } from 'fs';
@@ -14,7 +15,6 @@ import JSON5 from 'json5';
14
15
  import * as crypto from 'crypto';
15
16
  import { randomUUID, randomBytes, createHash } from 'crypto';
16
17
  import * as http from 'http';
17
- import { execFile, execSync, spawn, execFileSync, exec } from 'child_process';
18
18
  import { promisify } from 'util';
19
19
  import * as p26 from '@clack/prompts';
20
20
  import chalk from 'chalk';
@@ -83,14 +83,111 @@ function maskProxyUrl(url) {
83
83
  return "[invalid proxy URL]";
84
84
  }
85
85
  }
86
- function installProxyDispatcher() {
87
- if (installed) return getProxyFromEnv() ? maskProxyUrl(getProxyFromEnv()) : null;
88
- const proxy = getProxyFromEnv();
89
- if (!proxy) return null;
86
+ function defaultRunner(cmd, args) {
87
+ try {
88
+ return execFileSync(cmd, args, {
89
+ encoding: "utf-8",
90
+ timeout: 2e3,
91
+ stdio: ["ignore", "pipe", "ignore"]
92
+ });
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+ function parseMacOsProxy(output) {
98
+ const getField = (name) => {
99
+ const re = new RegExp(`^\\s*${name}\\s*:\\s*(.+?)\\s*$`, "m");
100
+ return output.match(re)?.[1];
101
+ };
102
+ if (getField("ProxyAutoConfigEnable") === "1") {
103
+ return null;
104
+ }
105
+ const pick = (prefix) => {
106
+ if (getField(`${prefix}Enable`) !== "1") return null;
107
+ const host = getField(`${prefix}Proxy`);
108
+ const port = getField(`${prefix}Port`);
109
+ if (!host) return null;
110
+ return `http://${host}${port ? `:${port}` : ""}`;
111
+ };
112
+ const proxyUrl = pick("HTTPS") ?? pick("HTTP");
113
+ if (!proxyUrl) return null;
114
+ const exceptionsMatch = output.match(/ExceptionsList\s*:\s*<array>\s*\{([\s\S]*?)\}/);
115
+ const exceptions = [];
116
+ const exceptionsBody = exceptionsMatch?.[1];
117
+ if (exceptionsBody) {
118
+ for (const line of exceptionsBody.split("\n")) {
119
+ const entry = line.match(/^\s*\d+\s*:\s*(.+?)\s*$/)?.[1];
120
+ if (entry) exceptions.push(entry);
121
+ }
122
+ }
123
+ return {
124
+ proxyUrl,
125
+ noProxy: exceptions.length > 0 ? exceptions.join(",") : void 0
126
+ };
127
+ }
128
+ function parseWindowsProxy(output) {
129
+ if (/Direct access/i.test(output)) return null;
130
+ const raw = output.match(/Proxy\s+Server\(s\)\s*:\s*(\S.*?)\s*$/m)?.[1]?.trim();
131
+ if (!raw) return null;
132
+ let hostPort = raw;
133
+ if (raw.includes("=")) {
134
+ const parts = raw.split(";").map((p47) => p47.trim());
135
+ const httpsEntry = parts.find((p47) => p47.toLowerCase().startsWith("https="));
136
+ const httpEntry = parts.find((p47) => p47.toLowerCase().startsWith("http="));
137
+ const chosen = httpsEntry ?? httpEntry;
138
+ if (!chosen) return null;
139
+ hostPort = chosen.split("=", 2)[1]?.trim() ?? "";
140
+ if (!hostPort) return null;
141
+ }
142
+ const proxyUrl = /^https?:\/\//i.test(hostPort) ? hostPort : `http://${hostPort}`;
143
+ let noProxy;
144
+ const bypass = output.match(/Bypass\s+List\s*:\s*(\S.*?)\s*$/m)?.[1]?.trim();
145
+ if (bypass && !/\(none\)/i.test(bypass)) {
146
+ noProxy = bypass.replace(/;/g, ",");
147
+ }
148
+ return { proxyUrl, noProxy };
149
+ }
150
+ function getProxyFromSystem(platform = process.platform, run = defaultRunner) {
151
+ if (platform === "darwin") {
152
+ const out = run("scutil", ["--proxy"]);
153
+ return out ? parseMacOsProxy(out) : null;
154
+ }
155
+ if (platform === "win32") {
156
+ const out = run("netsh", ["winhttp", "show", "proxy"]);
157
+ return out ? parseWindowsProxy(out) : null;
158
+ }
159
+ return null;
160
+ }
161
+ function installProxyDispatcher(resolveSystem = () => getProxyFromSystem()) {
162
+ if (installed) {
163
+ const existing = getProxyFromEnv();
164
+ return existing ? maskProxyUrl(existing) : null;
165
+ }
166
+ const envProxy = getProxyFromEnv();
167
+ if (envProxy) {
168
+ return applyDispatcher(envProxy);
169
+ }
170
+ const sys = resolveSystem();
171
+ if (sys) {
172
+ seedEnv("HTTPS_PROXY", sys.proxyUrl);
173
+ seedEnv("HTTP_PROXY", sys.proxyUrl);
174
+ if (sys.noProxy && !process.env.NO_PROXY && !process.env.no_proxy) {
175
+ seedEnv("NO_PROXY", sys.noProxy);
176
+ }
177
+ return applyDispatcher(sys.proxyUrl);
178
+ }
179
+ return null;
180
+ }
181
+ function seedEnv(key, value) {
182
+ if (process.env[key] !== void 0) return;
183
+ process.env[key] = value;
184
+ seededEnvKeys.push(key);
185
+ }
186
+ function applyDispatcher(proxyUrl) {
90
187
  try {
91
188
  setGlobalDispatcher(new EnvHttpProxyAgent());
92
189
  installed = true;
93
- return maskProxyUrl(proxy);
190
+ return maskProxyUrl(proxyUrl);
94
191
  } catch {
95
192
  return null;
96
193
  }
@@ -119,11 +216,12 @@ function unwrapCause(error) {
119
216
  }
120
217
  return void 0;
121
218
  }
122
- var PROXY_ENV_VARS, installed;
219
+ var PROXY_ENV_VARS, installed, seededEnvKeys;
123
220
  var init_proxy = __esm({
124
221
  "src/utils/proxy.ts"() {
125
222
  PROXY_ENV_VARS = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
126
223
  installed = false;
224
+ seededEnvKeys = [];
127
225
  }
128
226
  });
129
227
  function findPackageJson() {
@@ -161,6 +259,7 @@ __export(schema_exports, {
161
259
  ShipConfigSchema: () => ShipConfigSchema,
162
260
  SkillsConfigSchema: () => SkillsConfigSchema,
163
261
  StackConfigSchema: () => StackConfigSchema,
262
+ ThinkingModeSchema: () => ThinkingModeSchema,
164
263
  ToolsConfigSchema: () => ToolsConfigSchema,
165
264
  createDefaultConfigObject: () => createDefaultConfigObject,
166
265
  validateConfig: () => validateConfig
@@ -205,9 +304,13 @@ function createDefaultConfigObject(projectName, language = "typescript") {
205
304
  }
206
305
  };
207
306
  }
208
- var ProviderConfigSchema, QualityConfigSchema, PersistenceConfigSchema, StackConfigSchema, ProjectConfigSchema, GitHubConfigSchema, IntegrationsConfigSchema, MCPServerConfigEntrySchema, MCPConfigSchema, ToolsConfigSchema, ShipConfigSchema, SkillsConfigSchema, CocoConfigSchema;
307
+ var ThinkingModeSchema, ProviderConfigSchema, QualityConfigSchema, PersistenceConfigSchema, StackConfigSchema, ProjectConfigSchema, GitHubConfigSchema, IntegrationsConfigSchema, MCPServerConfigEntrySchema, MCPConfigSchema, ToolsConfigSchema, ShipConfigSchema, SkillsConfigSchema, CocoConfigSchema;
209
308
  var init_schema = __esm({
210
309
  "src/config/schema.ts"() {
310
+ ThinkingModeSchema = z.union([
311
+ z.enum(["off", "auto", "low", "medium", "high"]),
312
+ z.object({ budget: z.number().int().min(0).max(2e5) })
313
+ ]);
211
314
  ProviderConfigSchema = z.object({
212
315
  type: z.enum([
213
316
  "anthropic",
@@ -369,6 +472,7 @@ var init_schema = __esm({
369
472
  timeout: 12e4
370
473
  }),
371
474
  providerModels: z.record(z.string(), z.string()).optional(),
475
+ providerThinking: z.record(z.string(), ThinkingModeSchema).optional(),
372
476
  quality: QualityConfigSchema.default({
373
477
  minScore: 85,
374
478
  minCoverage: 80,
@@ -2589,10 +2693,12 @@ __export(env_exports, {
2589
2693
  getInternalProviderId: () => getInternalProviderId,
2590
2694
  getLastUsedModel: () => getLastUsedModel,
2591
2695
  getLastUsedProvider: () => getLastUsedProvider,
2696
+ getLastUsedThinking: () => getLastUsedThinking,
2592
2697
  isOAuthProvider: () => isOAuthProvider,
2593
2698
  migrateOldPreferences: () => migrateOldPreferences,
2594
2699
  removeEnvProvider: () => removeEnvProvider,
2595
- saveProviderPreference: () => saveProviderPreference
2700
+ saveProviderPreference: () => saveProviderPreference,
2701
+ saveThinkingPreference: () => saveThinkingPreference
2596
2702
  });
2597
2703
  function loadGlobalCocoEnv() {
2598
2704
  try {
@@ -2812,6 +2918,51 @@ async function getLastUsedModel(provider) {
2812
2918
  }
2813
2919
  return void 0;
2814
2920
  }
2921
+ async function getLastUsedThinking(provider) {
2922
+ try {
2923
+ const config = await loadConfig(CONFIG_PATHS.config);
2924
+ const mode = config.providerThinking?.[provider];
2925
+ return mode;
2926
+ } catch {
2927
+ return void 0;
2928
+ }
2929
+ }
2930
+ async function saveThinkingPreference(provider, mode) {
2931
+ let config;
2932
+ try {
2933
+ config = await loadConfig(CONFIG_PATHS.config);
2934
+ } catch {
2935
+ config = {
2936
+ project: { name: "global", version: "0.1.0" },
2937
+ provider: {
2938
+ type: "anthropic",
2939
+ model: "claude-sonnet-4-6",
2940
+ maxTokens: 8192,
2941
+ temperature: 0,
2942
+ timeout: 12e4
2943
+ },
2944
+ quality: {
2945
+ minScore: 85,
2946
+ minCoverage: 80,
2947
+ maxIterations: 10,
2948
+ minIterations: 2,
2949
+ convergenceThreshold: 2,
2950
+ securityThreshold: 100
2951
+ },
2952
+ persistence: {
2953
+ checkpointInterval: 3e5,
2954
+ maxCheckpoints: 50,
2955
+ retentionDays: 7,
2956
+ compressOldCheckpoints: true
2957
+ }
2958
+ };
2959
+ }
2960
+ config.providerThinking = {
2961
+ ...config.providerThinking,
2962
+ [provider]: mode
2963
+ };
2964
+ await saveConfig(config, void 0, true);
2965
+ }
2815
2966
  async function saveProviderPreference(provider, model, options) {
2816
2967
  let config;
2817
2968
  try {
@@ -3182,6 +3333,160 @@ var init_logger = __esm({
3182
3333
  globalLogger = null;
3183
3334
  }
3184
3335
  });
3336
+
3337
+ // src/providers/thinking.ts
3338
+ function isAnthropicThinkingModel(model) {
3339
+ const m = model.toLowerCase();
3340
+ if (m === "kimi-for-coding") return false;
3341
+ return m.includes("claude-3-7") || m.includes("claude-opus-4") || m.includes("claude-sonnet-4") || m.includes("claude-haiku-4-5") || m.includes("claude-4");
3342
+ }
3343
+ function isOpenAIReasoningModel(model) {
3344
+ const m = model.toLowerCase();
3345
+ return m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.startsWith("gpt-5") || m.includes("codex");
3346
+ }
3347
+ function isGeminiThinkingModel(model) {
3348
+ const m = model.toLowerCase();
3349
+ return m.includes("gemini-2.5-pro") || m.includes("gemini-2.5-flash") || m.includes("gemini-3") && !m.includes("flash-lite") || m.includes("gemini-2.0-flash-thinking");
3350
+ }
3351
+ function isKimiThinkingModel(model) {
3352
+ const m = model.toLowerCase();
3353
+ return m.includes("kimi-k2") || m === "kimi-latest";
3354
+ }
3355
+ function getThinkingCapability(provider, model) {
3356
+ switch (provider) {
3357
+ case "anthropic":
3358
+ case "kimi-code":
3359
+ return isAnthropicThinkingModel(model) ? ANTHROPIC_CAPABILITY : UNSUPPORTED;
3360
+ case "openai":
3361
+ case "copilot":
3362
+ case "groq":
3363
+ case "openrouter":
3364
+ case "mistral":
3365
+ case "deepseek":
3366
+ case "together":
3367
+ case "huggingface":
3368
+ case "qwen":
3369
+ return isOpenAIReasoningModel(model) ? OPENAI_CAPABILITY : UNSUPPORTED;
3370
+ case "kimi":
3371
+ return isKimiThinkingModel(model) ? KIMI_CAPABILITY : UNSUPPORTED;
3372
+ case "gemini":
3373
+ case "vertex":
3374
+ return isGeminiThinkingModel(model) ? GEMINI_CAPABILITY : UNSUPPORTED;
3375
+ case "lmstudio":
3376
+ case "ollama":
3377
+ case "codex":
3378
+ return UNSUPPORTED;
3379
+ default:
3380
+ return UNSUPPORTED;
3381
+ }
3382
+ }
3383
+ function resolveDefaultThinking(provider, model) {
3384
+ return getThinkingCapability(provider, model).defaultMode;
3385
+ }
3386
+ function formatThinkingMode(mode) {
3387
+ if (typeof mode === "object") return `${mode.budget}t`;
3388
+ return mode;
3389
+ }
3390
+ function mapToAnthropic(mode, model) {
3391
+ if (!mode || mode === "off") return void 0;
3392
+ if (!isAnthropicThinkingModel(model)) return void 0;
3393
+ const cap = ANTHROPIC_CAPABILITY;
3394
+ const { min, max } = cap.budgetRange;
3395
+ if (typeof mode === "object") {
3396
+ return { type: "enabled", budget_tokens: Math.min(Math.max(mode.budget, min), max) };
3397
+ }
3398
+ const budgetMap = {
3399
+ auto: cap.budgetRange.default,
3400
+ low: ANTHROPIC_BUDGET.low,
3401
+ medium: ANTHROPIC_BUDGET.medium,
3402
+ high: ANTHROPIC_BUDGET.high
3403
+ };
3404
+ const budget = budgetMap[mode];
3405
+ if (budget === void 0) return void 0;
3406
+ return { type: "enabled", budget_tokens: budget };
3407
+ }
3408
+ function mapToOpenAIEffort(mode, model) {
3409
+ if (!mode || mode === "off") return void 0;
3410
+ if (!isOpenAIReasoningModel(model)) return void 0;
3411
+ if (typeof mode === "object") {
3412
+ const { budget } = mode;
3413
+ if (budget <= 2048) return "low";
3414
+ if (budget <= 8e3) return "medium";
3415
+ return "high";
3416
+ }
3417
+ if (mode === "auto") return "medium";
3418
+ if (mode === "low" || mode === "medium" || mode === "high") return mode;
3419
+ return void 0;
3420
+ }
3421
+ function mapToGeminiBudget(mode, model) {
3422
+ if (!isGeminiThinkingModel(model)) return void 0;
3423
+ if (!mode) return void 0;
3424
+ if (mode === "off") return 0;
3425
+ if (mode === "auto") return -1;
3426
+ const { min, max } = GEMINI_CAPABILITY.budgetRange;
3427
+ if (typeof mode === "object") {
3428
+ return Math.min(Math.max(mode.budget, min), max);
3429
+ }
3430
+ const budgetMap = {
3431
+ low: GEMINI_BUDGET.low,
3432
+ medium: GEMINI_BUDGET.medium,
3433
+ high: GEMINI_BUDGET.high
3434
+ };
3435
+ return budgetMap[mode];
3436
+ }
3437
+ function mapToKimiExtraBody(mode, model) {
3438
+ if (!isKimiThinkingModel(model)) return void 0;
3439
+ const effectiveMode = mode ?? "off";
3440
+ const enabled = effectiveMode !== "off";
3441
+ return { thinking: { type: enabled ? "enabled" : "disabled" } };
3442
+ }
3443
+ var ANTHROPIC_BUDGET, GEMINI_BUDGET, UNSUPPORTED, ANTHROPIC_CAPABILITY, OPENAI_CAPABILITY, GEMINI_CAPABILITY, KIMI_CAPABILITY;
3444
+ var init_thinking = __esm({
3445
+ "src/providers/thinking.ts"() {
3446
+ ANTHROPIC_BUDGET = {
3447
+ low: 2048,
3448
+ medium: 8e3,
3449
+ high: 16e3
3450
+ };
3451
+ GEMINI_BUDGET = {
3452
+ low: 2048,
3453
+ medium: 8e3,
3454
+ high: 16e3
3455
+ };
3456
+ UNSUPPORTED = {
3457
+ supported: false,
3458
+ kinds: [],
3459
+ levels: ["off"],
3460
+ defaultMode: "off"
3461
+ };
3462
+ ANTHROPIC_CAPABILITY = {
3463
+ supported: true,
3464
+ kinds: ["budget"],
3465
+ levels: ["off", "auto", "low", "medium", "high"],
3466
+ budgetRange: { min: 1024, max: 64e3, default: ANTHROPIC_BUDGET.medium },
3467
+ defaultMode: "off"
3468
+ };
3469
+ OPENAI_CAPABILITY = {
3470
+ supported: true,
3471
+ kinds: ["effort"],
3472
+ levels: ["off", "auto", "low", "medium", "high"],
3473
+ defaultMode: "medium"
3474
+ };
3475
+ GEMINI_CAPABILITY = {
3476
+ supported: true,
3477
+ kinds: ["budget"],
3478
+ levels: ["off", "auto", "low", "medium", "high"],
3479
+ budgetRange: { min: 0, max: 32e3, default: GEMINI_BUDGET.medium },
3480
+ defaultMode: "auto"
3481
+ };
3482
+ KIMI_CAPABILITY = {
3483
+ supported: true,
3484
+ kinds: ["effort"],
3485
+ levels: ["off", "auto"],
3486
+ defaultMode: "off"
3487
+ };
3488
+ }
3489
+ });
3185
3490
  function createAnthropicProvider(config) {
3186
3491
  const provider = new AnthropicProvider();
3187
3492
  if (config) {
@@ -3210,6 +3515,7 @@ var init_anthropic = __esm({
3210
3515
  init_errors();
3211
3516
  init_retry();
3212
3517
  init_logger();
3518
+ init_thinking();
3213
3519
  DEFAULT_MODEL = "claude-opus-4-6";
3214
3520
  CONTEXT_WINDOWS = {
3215
3521
  // Kimi Code model (Anthropic-compatible endpoint)
@@ -3270,13 +3576,19 @@ var init_anthropic = __esm({
3270
3576
  this.ensureInitialized();
3271
3577
  return withRetry(async () => {
3272
3578
  try {
3579
+ const model = options?.model ?? this.config.model ?? DEFAULT_MODEL;
3580
+ const thinkingParam = mapToAnthropic(options?.thinking, model);
3581
+ const baseMaxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
3273
3582
  const response = await this.client.messages.create({
3274
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
3275
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
3276
- temperature: options?.temperature ?? this.config.temperature ?? 0,
3583
+ model,
3584
+ // Anthropic requires max_tokens > budget_tokens
3585
+ max_tokens: thinkingParam ? Math.max(baseMaxTokens, thinkingParam.budget_tokens + 1024) : baseMaxTokens,
3586
+ // Anthropic requires temperature=1 when thinking is enabled
3587
+ temperature: thinkingParam ? 1 : options?.temperature ?? this.config.temperature ?? 0,
3277
3588
  system: this.extractSystem(messages, options?.system),
3278
3589
  messages: this.convertMessages(messages),
3279
- stop_sequences: options?.stopSequences
3590
+ stop_sequences: options?.stopSequences,
3591
+ ...thinkingParam && { thinking: thinkingParam }
3280
3592
  });
3281
3593
  return {
3282
3594
  id: response.id,
@@ -3300,14 +3612,18 @@ var init_anthropic = __esm({
3300
3612
  this.ensureInitialized();
3301
3613
  return withRetry(async () => {
3302
3614
  try {
3615
+ const model = options?.model ?? this.config.model ?? DEFAULT_MODEL;
3616
+ const thinkingParam = mapToAnthropic(options?.thinking, model);
3617
+ const baseMaxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
3303
3618
  const response = await this.client.messages.create({
3304
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
3305
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
3306
- temperature: options?.temperature ?? this.config.temperature ?? 0,
3619
+ model,
3620
+ max_tokens: thinkingParam ? Math.max(baseMaxTokens, thinkingParam.budget_tokens + 1024) : baseMaxTokens,
3621
+ temperature: thinkingParam ? 1 : options?.temperature ?? this.config.temperature ?? 0,
3307
3622
  system: this.extractSystem(messages, options?.system),
3308
3623
  messages: this.convertMessages(messages),
3309
3624
  tools: this.convertTools(options.tools),
3310
- tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
3625
+ tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0,
3626
+ ...thinkingParam && { thinking: thinkingParam }
3311
3627
  });
3312
3628
  const toolCalls = this.extractToolCalls(response.content);
3313
3629
  return {
@@ -3333,13 +3649,17 @@ var init_anthropic = __esm({
3333
3649
  this.ensureInitialized();
3334
3650
  let timeoutTriggered = false;
3335
3651
  try {
3652
+ const model = options?.model ?? this.config.model ?? DEFAULT_MODEL;
3653
+ const thinkingParam = mapToAnthropic(options?.thinking, model);
3654
+ const baseMaxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
3336
3655
  const stream = await this.client.messages.stream(
3337
3656
  {
3338
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
3339
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
3340
- temperature: options?.temperature ?? this.config.temperature ?? 0,
3657
+ model,
3658
+ max_tokens: thinkingParam ? Math.max(baseMaxTokens, thinkingParam.budget_tokens + 1024) : baseMaxTokens,
3659
+ temperature: thinkingParam ? 1 : options?.temperature ?? this.config.temperature ?? 0,
3341
3660
  system: this.extractSystem(messages, options?.system),
3342
- messages: this.convertMessages(messages)
3661
+ messages: this.convertMessages(messages),
3662
+ ...thinkingParam && { thinking: thinkingParam }
3343
3663
  },
3344
3664
  { signal: options?.signal }
3345
3665
  );
@@ -3395,15 +3715,19 @@ var init_anthropic = __esm({
3395
3715
  this.ensureInitialized();
3396
3716
  let timeoutTriggered = false;
3397
3717
  try {
3718
+ const model = options?.model ?? this.config.model ?? DEFAULT_MODEL;
3719
+ const thinkingParam = mapToAnthropic(options?.thinking, model);
3720
+ const baseMaxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
3398
3721
  const stream = await this.client.messages.stream(
3399
3722
  {
3400
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
3401
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
3402
- temperature: options?.temperature ?? this.config.temperature ?? 0,
3723
+ model,
3724
+ max_tokens: thinkingParam ? Math.max(baseMaxTokens, thinkingParam.budget_tokens + 1024) : baseMaxTokens,
3725
+ temperature: thinkingParam ? 1 : options?.temperature ?? this.config.temperature ?? 0,
3403
3726
  system: this.extractSystem(messages, options?.system),
3404
3727
  messages: this.convertMessages(messages),
3405
3728
  tools: this.convertTools(options.tools),
3406
- tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
3729
+ tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0,
3730
+ ...thinkingParam && { thinking: thinkingParam }
3407
3731
  },
3408
3732
  { signal: options?.signal }
3409
3733
  );
@@ -3924,6 +4248,7 @@ var init_openai = __esm({
3924
4248
  init_errors();
3925
4249
  init_retry();
3926
4250
  init_tool_call_normalizer();
4251
+ init_thinking();
3927
4252
  DEFAULT_MODEL2 = "gpt-5.3-codex";
3928
4253
  CONTEXT_WINDOWS2 = {
3929
4254
  // OpenAI models
@@ -4076,26 +4401,15 @@ var init_openai = __esm({
4076
4401
  return !MODELS_WITHOUT_TEMPERATURE.some((m) => model.toLowerCase().includes(m.toLowerCase()));
4077
4402
  }
4078
4403
  /**
4079
- * Check if a model needs thinking mode disabled for tool use
4080
- * Kimi models have thinking mode enabled by default which requires
4081
- * reasoning_content in multi-turn conversations with tools
4082
- */
4083
- needsThinkingDisabled(model) {
4084
- return MODELS_WITH_THINKING_MODE.some((m) => model.toLowerCase().includes(m.toLowerCase()));
4085
- }
4086
- /**
4087
- * Get extra body parameters for API calls
4088
- * Used to disable thinking mode for Kimi models
4089
- * See: https://huggingface.co/moonshotai/Kimi-K2.5
4090
- *
4091
- * For Official Moonshot API: {'thinking': {'type': 'disabled'}}
4092
- * For vLLM/SGLang: {'chat_template_kwargs': {"thinking": False}}
4404
+ * Get extra body parameters for API calls.
4405
+ * Honors the user's ThinkingMode for Kimi models; defaults to disabled
4406
+ * (preserving existing behavior) when no mode is specified.
4093
4407
  */
4094
- getExtraBody(model) {
4095
- if (this.needsThinkingDisabled(model)) {
4096
- return {
4097
- thinking: { type: "disabled" }
4098
- };
4408
+ getExtraBody(model, thinking) {
4409
+ const kimiBody = mapToKimiExtraBody(thinking, model);
4410
+ if (kimiBody) return kimiBody;
4411
+ if (MODELS_WITH_THINKING_MODE.some((m) => model.toLowerCase().includes(m.toLowerCase()))) {
4412
+ return { thinking: { type: "disabled" } };
4099
4413
  }
4100
4414
  return void 0;
4101
4415
  }
@@ -4112,6 +4426,7 @@ var init_openai = __esm({
4112
4426
  try {
4113
4427
  const supportsTemp = this.supportsTemperature(model);
4114
4428
  const maxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
4429
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4115
4430
  const response = await this.client.chat.completions.create({
4116
4431
  model,
4117
4432
  ...buildMaxTokensParam(model, maxTokens),
@@ -4119,7 +4434,8 @@ var init_openai = __esm({
4119
4434
  stop: options?.stopSequences,
4120
4435
  ...supportsTemp && {
4121
4436
  temperature: options?.temperature ?? this.config.temperature ?? 0
4122
- }
4437
+ },
4438
+ ...reasoningEffort && { reasoning_effort: reasoningEffort }
4123
4439
  });
4124
4440
  const choice = response.choices[0];
4125
4441
  return {
@@ -4149,7 +4465,8 @@ var init_openai = __esm({
4149
4465
  return withRetry(async () => {
4150
4466
  try {
4151
4467
  const supportsTemp = this.supportsTemperature(model);
4152
- const extraBody = this.getExtraBody(model);
4468
+ const extraBody = this.getExtraBody(model, options?.thinking);
4469
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4153
4470
  const maxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
4154
4471
  const requestParams = {
4155
4472
  model,
@@ -4161,6 +4478,9 @@ var init_openai = __esm({
4161
4478
  if (supportsTemp) {
4162
4479
  requestParams.temperature = options?.temperature ?? this.config.temperature ?? 0;
4163
4480
  }
4481
+ if (reasoningEffort) {
4482
+ requestParams.reasoning_effort = reasoningEffort;
4483
+ }
4164
4484
  if (extraBody) {
4165
4485
  Object.assign(requestParams, extraBody);
4166
4486
  }
@@ -4198,12 +4518,14 @@ var init_openai = __esm({
4198
4518
  try {
4199
4519
  const supportsTemp = this.supportsTemperature(model);
4200
4520
  const maxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
4521
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4201
4522
  const stream = await this.client.chat.completions.create({
4202
4523
  model,
4203
4524
  ...buildMaxTokensParam(model, maxTokens),
4204
4525
  messages: this.convertMessages(messages, options?.system),
4205
4526
  stream: true,
4206
- ...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 }
4527
+ ...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 },
4528
+ ...reasoningEffort && { reasoning_effort: reasoningEffort }
4207
4529
  });
4208
4530
  let streamStopReason;
4209
4531
  for await (const chunk of stream) {
@@ -4234,7 +4556,8 @@ var init_openai = __esm({
4234
4556
  let timeoutTriggered = false;
4235
4557
  try {
4236
4558
  const supportsTemp = this.supportsTemperature(model);
4237
- const extraBody = this.getExtraBody(model);
4559
+ const extraBody = this.getExtraBody(model, options?.thinking);
4560
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4238
4561
  const maxTokens = options?.maxTokens ?? this.config.maxTokens ?? 8192;
4239
4562
  const requestParams = {
4240
4563
  model,
@@ -4247,6 +4570,9 @@ var init_openai = __esm({
4247
4570
  if (supportsTemp) {
4248
4571
  requestParams.temperature = options?.temperature ?? this.config.temperature ?? 0;
4249
4572
  }
4573
+ if (reasoningEffort) {
4574
+ requestParams.reasoning_effort = reasoningEffort;
4575
+ }
4250
4576
  if (extraBody) {
4251
4577
  Object.assign(requestParams, extraBody);
4252
4578
  }
@@ -4714,6 +5040,7 @@ var init_openai = __esm({
4714
5040
  const model = options?.model ?? this.config.model ?? DEFAULT_MODEL2;
4715
5041
  const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
4716
5042
  const supportsTemp = this.supportsTemperature(model);
5043
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4717
5044
  const response = await this.client.responses.create({
4718
5045
  model,
4719
5046
  input,
@@ -4722,6 +5049,8 @@ var init_openai = __esm({
4722
5049
  ...supportsTemp && {
4723
5050
  temperature: options?.temperature ?? this.config.temperature ?? 0
4724
5051
  },
5052
+ // Responses API uses nested reasoning.effort (not top-level reasoning_effort)
5053
+ ...reasoningEffort && { reasoning: { effort: reasoningEffort } },
4725
5054
  store: false
4726
5055
  });
4727
5056
  return {
@@ -4750,6 +5079,7 @@ var init_openai = __esm({
4750
5079
  const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
4751
5080
  const tools = this.convertToolsForResponses(options.tools);
4752
5081
  const supportsTemp = this.supportsTemperature(model);
5082
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4753
5083
  const response = await this.client.responses.create({
4754
5084
  model,
4755
5085
  input,
@@ -4759,6 +5089,7 @@ var init_openai = __esm({
4759
5089
  ...supportsTemp && {
4760
5090
  temperature: options?.temperature ?? this.config.temperature ?? 0
4761
5091
  },
5092
+ ...reasoningEffort && { reasoning: { effort: reasoningEffort } },
4762
5093
  store: false
4763
5094
  });
4764
5095
  let content = "";
@@ -4804,12 +5135,14 @@ var init_openai = __esm({
4804
5135
  const model = options?.model ?? this.config.model ?? DEFAULT_MODEL2;
4805
5136
  const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
4806
5137
  const supportsTemp = this.supportsTemperature(model);
5138
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4807
5139
  const stream = await this.client.responses.create({
4808
5140
  model,
4809
5141
  input,
4810
5142
  instructions: instructions ?? void 0,
4811
5143
  max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
4812
5144
  ...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 },
5145
+ ...reasoningEffort && { reasoning: { effort: reasoningEffort } },
4813
5146
  store: false,
4814
5147
  stream: true
4815
5148
  });
@@ -4867,12 +5200,14 @@ var init_openai = __esm({
4867
5200
  const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
4868
5201
  const tools = options.tools.length > 0 ? this.convertToolsForResponses(options.tools) : void 0;
4869
5202
  const supportsTemp = this.supportsTemperature(model);
5203
+ const reasoningEffort = mapToOpenAIEffort(options?.thinking, model);
4870
5204
  const requestParams = {
4871
5205
  model,
4872
5206
  input,
4873
5207
  instructions: instructions ?? void 0,
4874
5208
  max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
4875
5209
  ...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 },
5210
+ ...reasoningEffort && { reasoning: { effort: reasoningEffort } },
4876
5211
  store: false,
4877
5212
  stream: true
4878
5213
  };
@@ -5912,6 +6247,7 @@ var DEFAULT_MODEL5, SKIP_THOUGHT_SIGNATURE_VALIDATOR, CONTEXT_WINDOWS5, GeminiPr
5912
6247
  var init_gemini = __esm({
5913
6248
  "src/providers/gemini.ts"() {
5914
6249
  init_errors();
6250
+ init_thinking();
5915
6251
  DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
5916
6252
  SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
5917
6253
  CONTEXT_WINDOWS5 = {
@@ -6070,12 +6406,17 @@ var init_gemini = __esm({
6070
6406
  return model ?? this.config.model ?? DEFAULT_MODEL5;
6071
6407
  }
6072
6408
  buildConfig(messages, options, tools, toolChoice) {
6409
+ const model = this.getModel(options?.model);
6410
+ const thinkingBudget = mapToGeminiBudget(options?.thinking, model);
6073
6411
  const config = {
6074
6412
  maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
6075
6413
  temperature: options?.temperature ?? this.config.temperature ?? 0,
6076
6414
  stopSequences: options?.stopSequences,
6077
6415
  systemInstruction: this.extractSystem(messages, options?.system)
6078
6416
  };
6417
+ if (thinkingBudget !== void 0) {
6418
+ config.thinkingConfig = { thinkingBudget };
6419
+ }
6079
6420
  if (tools && tools.length > 0) {
6080
6421
  config.tools = [{ functionDeclarations: this.convertTools(tools) }];
6081
6422
  config.toolConfig = {
@@ -10443,11 +10784,15 @@ function generateToolCatalog(registry) {
10443
10784
  async function createDefaultReplConfig() {
10444
10785
  const providerType = await getLastUsedProvider();
10445
10786
  const model = await getLastUsedModel(providerType) || getDefaultModel(providerType);
10787
+ const persistedThinking = await getLastUsedThinking(providerType);
10788
+ const thinking = persistedThinking ?? resolveDefaultThinking(providerType, model);
10789
+ const thinkingToStore = thinking === "off" ? void 0 : thinking;
10446
10790
  return {
10447
10791
  provider: {
10448
10792
  type: providerType,
10449
10793
  model,
10450
- maxTokens: 8192
10794
+ maxTokens: 8192,
10795
+ thinking: thinkingToStore
10451
10796
  },
10452
10797
  ui: {
10453
10798
  theme: "auto",
@@ -10882,6 +11227,7 @@ var MAX_SKILL_INSTRUCTIONS_CHARS, TRUST_SETTINGS_DIR, TRUST_SETTINGS_FILE, PROJE
10882
11227
  var init_session = __esm({
10883
11228
  "src/cli/repl/session.ts"() {
10884
11229
  init_env();
11230
+ init_thinking();
10885
11231
  init_manager();
10886
11232
  init_compactor();
10887
11233
  init_memory();
@@ -33585,6 +33931,7 @@ var helpCommand = {
33585
33931
  commands: [
33586
33932
  { cmd: "/model, /m", desc: "View or change the current model" },
33587
33933
  { cmd: "/provider", desc: "View or change the LLM provider" },
33934
+ { cmd: "/thinking, /think", desc: "View or change the reasoning/thinking mode" },
33588
33935
  { cmd: "/doctor, /dr", desc: "Run local diagnostics for config, auth, hooks, and tools" },
33589
33936
  { cmd: "/compact", desc: "Toggle compact mode (less verbose)" },
33590
33937
  { cmd: "/cost, /tokens", desc: "Show token usage and cost" },
@@ -34051,6 +34398,7 @@ function truncate2(str, maxLength, suffix = "...") {
34051
34398
 
34052
34399
  // src/cli/repl/commands/model.ts
34053
34400
  init_env();
34401
+ init_thinking();
34054
34402
  async function fetchLocalModels(providerType) {
34055
34403
  try {
34056
34404
  const baseUrl = getBaseUrl(providerType);
@@ -34232,6 +34580,32 @@ async function persistModelPreference(provider, model) {
34232
34580
  console.log(chalk.dim(" Model changed for this session only.\n"));
34233
34581
  }
34234
34582
  }
34583
+ function reconcileThinkingAfterModelChange(session, newModel) {
34584
+ const provider = session.config.provider.type;
34585
+ const cap = getThinkingCapability(provider, newModel);
34586
+ if (!cap.supported) {
34587
+ if (session.config.provider.thinking !== void 0) {
34588
+ session.config.provider.thinking = void 0;
34589
+ console.log(chalk.dim(" \u2139 Thinking not supported on this model \u2014 turned off."));
34590
+ }
34591
+ return;
34592
+ }
34593
+ const current = session.config.provider.thinking;
34594
+ if (current !== void 0 && typeof current === "object" && !cap.kinds.includes("budget")) {
34595
+ const newDefault = resolveDefaultThinking(provider, newModel);
34596
+ session.config.provider.thinking = newDefault === "off" ? void 0 : newDefault;
34597
+ console.log(
34598
+ chalk.dim(
34599
+ ` \u2139 Thinking reset to ${session.config.provider.thinking ?? "off"} (model uses effort levels).`
34600
+ )
34601
+ );
34602
+ return;
34603
+ }
34604
+ if (current === void 0) {
34605
+ const def = resolveDefaultThinking(provider, newModel);
34606
+ session.config.provider.thinking = def === "off" ? void 0 : def;
34607
+ }
34608
+ }
34235
34609
  var modelCommand = {
34236
34610
  name: "model",
34237
34611
  aliases: ["m"],
@@ -34280,6 +34654,7 @@ var modelCommand = {
34280
34654
  return false;
34281
34655
  }
34282
34656
  session.config.provider.model = selectedModel;
34657
+ reconcileThinkingAfterModelChange(session, selectedModel);
34283
34658
  await persistModelPreference(currentProvider, selectedModel);
34284
34659
  const modelInfo2 = providerDef.models.find((m) => m.id === selectedModel);
34285
34660
  console.log(chalk.green(`\u2713 Switched to ${modelInfo2?.name ?? selectedModel}
@@ -34302,6 +34677,7 @@ var modelCommand = {
34302
34677
  if (!foundInProvider) {
34303
34678
  console.log(chalk.yellow(`Model "${newModel}" not in known list, setting anyway...`));
34304
34679
  session.config.provider.model = newModel;
34680
+ reconcileThinkingAfterModelChange(session, newModel);
34305
34681
  await persistModelPreference(currentProvider, newModel);
34306
34682
  console.log(chalk.green(`\u2713 Model set to: ${newModel}
34307
34683
  `));
@@ -34317,6 +34693,7 @@ var modelCommand = {
34317
34693
  return false;
34318
34694
  }
34319
34695
  session.config.provider.model = newModel;
34696
+ reconcileThinkingAfterModelChange(session, newModel);
34320
34697
  await persistModelPreference(currentProvider, newModel);
34321
34698
  const modelInfo = providerDef.models.find((m) => m.id === newModel);
34322
34699
  console.log(chalk.green(`\u2713 Switched to ${modelInfo?.name ?? newModel}
@@ -36309,6 +36686,7 @@ async function promptVertexSettings2(defaults) {
36309
36686
  // src/cli/repl/commands/status.ts
36310
36687
  init_state();
36311
36688
  init_trust_store();
36689
+ init_thinking();
36312
36690
  function getGitStatus2(projectPath) {
36313
36691
  try {
36314
36692
  execSync("git rev-parse --git-dir", { cwd: projectPath, stdio: "pipe" });
@@ -36434,6 +36812,11 @@ var statusCommand = {
36434
36812
  p26.log.step("Session");
36435
36813
  p26.log.message(` \u{1F4C1} ${session.projectPath}`);
36436
36814
  p26.log.message(` \u{1F916} ${session.config.provider.type} / ${session.config.provider.model}`);
36815
+ const cap = getThinkingCapability(session.config.provider.type, session.config.provider.model);
36816
+ if (cap.supported) {
36817
+ const thinkingLabel = session.config.provider.thinking !== void 0 ? formatThinkingMode(session.config.provider.thinking) : "off";
36818
+ p26.log.message(` \u{1F9E0} thinking: ${thinkingLabel} ${chalk.dim("(/thinking to change)")}`);
36819
+ }
36437
36820
  p26.outro("Done");
36438
36821
  return false;
36439
36822
  }
@@ -50369,6 +50752,133 @@ var doctorCommand = {
50369
50752
  }
50370
50753
  };
50371
50754
 
50755
+ // src/cli/repl/commands/thinking.ts
50756
+ init_thinking();
50757
+ init_env();
50758
+ var EFFORT_LEVELS = ["off", "auto", "low", "medium", "high"];
50759
+ function isEffortLevel(s) {
50760
+ return EFFORT_LEVELS.includes(s);
50761
+ }
50762
+ function parseThinkingArg(arg) {
50763
+ if (isEffortLevel(arg)) return arg;
50764
+ const n = parseInt(arg, 10);
50765
+ if (!isNaN(n) && n >= 0) return { budget: n };
50766
+ return null;
50767
+ }
50768
+ var thinkingCommand = {
50769
+ name: "thinking",
50770
+ aliases: ["think", "reason"],
50771
+ description: "View or change the reasoning/thinking mode for the current model",
50772
+ usage: "/thinking [off|auto|low|medium|high|<budget-tokens>]",
50773
+ async execute(args, session) {
50774
+ const provider = session.config.provider.type;
50775
+ const model = session.config.provider.model;
50776
+ const capability = getThinkingCapability(provider, model);
50777
+ if (args.length === 0) {
50778
+ const current = session.config.provider.thinking;
50779
+ const display = current !== void 0 ? formatThinkingMode(current) : "off";
50780
+ if (!capability.supported) {
50781
+ console.log(
50782
+ chalk.yellow(`
50783
+ \u26A0 Thinking not supported for ${model} on ${provider}.
50784
+ `) + chalk.dim(
50785
+ " Compatible models: claude-3-7+, claude-4+, o3, o4-mini, gpt-5*, gemini-2.5+\n"
50786
+ )
50787
+ );
50788
+ return false;
50789
+ }
50790
+ console.log(chalk.cyan.bold("\n\u2550\u2550\u2550 Thinking Mode \u2550\u2550\u2550\n"));
50791
+ console.log(` Current: ${chalk.magenta(display)}`);
50792
+ console.log(` Provider: ${chalk.dim(provider)} / ${chalk.dim(model)}`);
50793
+ console.log(` Supports: ${capability.kinds.join(", ")}`);
50794
+ if (capability.budgetRange) {
50795
+ const { min, max, default: def } = capability.budgetRange;
50796
+ console.log(` Budget range: ${chalk.dim(`${min}\u2013${max} tokens (default ${def})`)}`);
50797
+ }
50798
+ console.log(`
50799
+ ${chalk.dim("Available modes:")}`);
50800
+ for (const level of capability.levels) {
50801
+ const label = formatThinkingMode(level);
50802
+ const isCurrent = label === display;
50803
+ console.log(
50804
+ ` ${isCurrent ? chalk.green("\u2192") : " "} ${isCurrent ? chalk.green(label) : chalk.dim(label)}`
50805
+ );
50806
+ }
50807
+ if (capability.kinds.includes("budget")) {
50808
+ console.log(chalk.dim(`
50809
+ You can also pass a token budget: /thinking 8000
50810
+ `));
50811
+ } else {
50812
+ console.log();
50813
+ }
50814
+ return false;
50815
+ }
50816
+ const rawArg = args[0].toLowerCase();
50817
+ const parsed = parseThinkingArg(rawArg);
50818
+ if (parsed === null) {
50819
+ console.log(chalk.red(`
50820
+ \u2717 Unknown thinking mode: "${args[0]}"`));
50821
+ console.log(
50822
+ chalk.dim(" Valid options: off, auto, low, medium, high, or a token budget number\n")
50823
+ );
50824
+ return false;
50825
+ }
50826
+ if (!capability.supported && parsed !== "off") {
50827
+ console.log(
50828
+ chalk.yellow(`
50829
+ \u26A0 Thinking not supported for ${model} on ${provider}.
50830
+ `) + chalk.dim(
50831
+ " Compatible models: claude-3-7+, claude-4+, o3, o4-mini, gpt-5*, gemini-2.5+\n"
50832
+ )
50833
+ );
50834
+ return false;
50835
+ }
50836
+ if (typeof parsed === "object" && !capability.kinds.includes("budget")) {
50837
+ console.log(
50838
+ chalk.red(`
50839
+ \u2717 ${provider}/${model} uses effort levels, not token budgets.`) + chalk.dim("\n Use: off, auto, low, medium, or high\n")
50840
+ );
50841
+ return false;
50842
+ }
50843
+ if (typeof parsed === "object" && capability.budgetRange) {
50844
+ const { min, max } = capability.budgetRange;
50845
+ if (parsed.budget < min || parsed.budget > max) {
50846
+ console.log(
50847
+ chalk.red(`
50848
+ \u2717 Budget ${parsed.budget} is out of range.`) + chalk.dim(`
50849
+ Valid range for ${model}: ${min}\u2013${max} tokens
50850
+ `)
50851
+ );
50852
+ return false;
50853
+ }
50854
+ }
50855
+ if ((provider === "kimi" || provider === "kimi-code") && parsed !== "off" && parsed !== "auto") {
50856
+ console.log(
50857
+ chalk.yellow(
50858
+ "\n\u26A0 Enabling thinking on Kimi may cause issues with tool calling.\n If you experience errors, run /thinking off to restore default behavior.\n"
50859
+ )
50860
+ );
50861
+ }
50862
+ const previousMode = session.config.provider.thinking;
50863
+ const newMode = parsed === "off" ? void 0 : parsed;
50864
+ session.config.provider.thinking = newMode;
50865
+ const modeToSave = newMode ?? resolveDefaultThinking(provider, model);
50866
+ await saveThinkingPreference(provider, modeToSave);
50867
+ const previousLabel = previousMode !== void 0 ? formatThinkingMode(previousMode) : "off";
50868
+ const newLabel = newMode !== void 0 ? formatThinkingMode(newMode) : "off";
50869
+ if (previousLabel === newLabel) {
50870
+ console.log(chalk.dim(`
50871
+ Already using thinking: ${newLabel}
50872
+ `));
50873
+ } else {
50874
+ console.log(chalk.green(`
50875
+ \u2713 Thinking: ${previousLabel} \u2192 ${newLabel}
50876
+ `));
50877
+ }
50878
+ return false;
50879
+ }
50880
+ };
50881
+
50372
50882
  // src/cli/repl/output/renderer.ts
50373
50883
  init_syntax();
50374
50884
  var lineBuffer = "";
@@ -51400,7 +51910,8 @@ var commands = [
51400
51910
  buildAppCommand,
51401
51911
  contextCommand,
51402
51912
  bestOfNCommand,
51403
- doctorCommand
51913
+ doctorCommand,
51914
+ thinkingCommand
51404
51915
  ];
51405
51916
  function isSlashCommand(input) {
51406
51917
  return input.startsWith("/");
@@ -53770,6 +54281,7 @@ ${tail}`;
53770
54281
  tools: [],
53771
54282
  maxTokens: session.config.provider.maxTokens,
53772
54283
  signal: options.signal
54284
+ // Omit thinking for the final explanation turn to avoid unnecessary cost
53773
54285
  })) {
53774
54286
  if (options.signal?.aborted) break;
53775
54287
  if (chunk.type === "text" && chunk.text) {
@@ -53824,7 +54336,8 @@ ${tail}`;
53824
54336
  for await (const chunk of provider.streamWithTools(messages, {
53825
54337
  tools,
53826
54338
  maxTokens: session.config.provider.maxTokens,
53827
- signal: options.signal
54339
+ signal: options.signal,
54340
+ thinking: session.config.provider.thinking
53828
54341
  })) {
53829
54342
  if (options.signal?.aborted) {
53830
54343
  break;
@@ -54936,6 +55449,7 @@ init_allowed_paths();
54936
55449
  // src/cli/repl/status-bar.ts
54937
55450
  init_env();
54938
55451
  init_full_access_mode();
55452
+ init_thinking();
54939
55453
  function formatContextUsage(percent) {
54940
55454
  const label = `ctx ${percent.toFixed(0)}%`;
54941
55455
  if (percent >= 90) return chalk.red(label);
@@ -54955,7 +55469,9 @@ function formatStatusBar(projectPath, config, gitCtx, contextUsagePercent) {
54955
55469
  parts.push(chalk.dim("\u{1F4C1} ") + chalk.magenta(projectName));
54956
55470
  const providerName = config.provider.type;
54957
55471
  const modelName = getDisplayModel(config);
54958
- parts.push(chalk.dim(`${providerName}/`) + chalk.cyan(modelName));
55472
+ const thinkingMode = config.provider.thinking;
55473
+ const thinkingSuffix = thinkingMode !== void 0 ? chalk.dim(" [") + chalk.magenta(formatThinkingMode(thinkingMode)) + chalk.dim("]") : "";
55474
+ parts.push(chalk.dim(`${providerName}/`) + chalk.cyan(modelName) + thinkingSuffix);
54959
55475
  if (isQualityLoop()) {
54960
55476
  parts.push(chalk.green("\u{1F504} quality loop"));
54961
55477
  }