diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index e08a7a5f..e8e9a0be 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -543,6 +543,9 @@ export class AgentSession { this.agent.setModel(model); this.sessionManager.saveModelChange(model.provider, model.id); this.settingsManager.setDefaultModelAndProvider(model.provider, model.id); + + // Re-clamp thinking level for new model's capabilities + this.setThinkingLevel(this.thinkingLevel); } /** @@ -580,13 +583,10 @@ export class AgentSession { this.sessionManager.saveModelChange(next.model.provider, next.model.id); this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id); - // Apply thinking level (silently use "off" if not supported) - const effectiveThinking = next.model.reasoning ? next.thinkingLevel : "off"; - this.agent.setThinkingLevel(effectiveThinking); - this.sessionManager.saveThinkingLevelChange(effectiveThinking); - this.settingsManager.setDefaultThinkingLevel(effectiveThinking); + // Apply thinking level (setThinkingLevel clamps to model capabilities) + this.setThinkingLevel(next.thinkingLevel); - return { model: next.model, thinkingLevel: effectiveThinking, isScoped: true }; + return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true }; } private async _cycleAvailableModel(): Promise { @@ -612,6 +612,9 @@ export class AgentSession { this.sessionManager.saveModelChange(nextModel.provider, nextModel.id); this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id); + // Re-clamp thinking level for new model's capabilities + this.setThinkingLevel(this.thinkingLevel); + return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false }; } @@ -630,11 +633,16 @@ export class AgentSession { /** * Set thinking level. - * Silently uses "off" if model doesn't support thinking. + * Clamps to model capabilities: "off" if no reasoning, "high" if xhigh unsupported. * Saves to session and settings. */ setThinkingLevel(level: ThinkingLevel): void { - const effectiveLevel = this.supportsThinking() ? level : "off"; + let effectiveLevel = level; + if (!this.supportsThinking()) { + effectiveLevel = "off"; + } else if (level === "xhigh" && !this.supportsXhighThinking()) { + effectiveLevel = "high"; + } this.agent.setThinkingLevel(effectiveLevel); this.sessionManager.saveThinkingLevelChange(effectiveLevel); this.settingsManager.setDefaultThinkingLevel(effectiveLevel); @@ -1183,10 +1191,10 @@ export class AgentSession { } } - // Restore thinking level if saved + // Restore thinking level if saved (setThinkingLevel clamps to model capabilities) const savedThinking = this.sessionManager.loadThinkingLevel(); if (savedThinking) { - this.agent.setThinkingLevel(savedThinking as ThinkingLevel); + this.setThinkingLevel(savedThinking as ThinkingLevel); } this._reconnectToAgent(); diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index 2deb5d5d..c94d0e8b 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -3,6 +3,7 @@ */ import { Agent, type Attachment, ProviderTransport, type ThinkingLevel } from "@mariozechner/pi-agent-core"; +import { supportsXhigh } from "@mariozechner/pi-ai"; import chalk from "chalk"; import { type Args, parseArgs, printHelp } from "./cli/args.js"; import { processFileArguments } from "./cli/file-processor.js"; @@ -294,6 +295,15 @@ export async function main(args: string[]) { initialThinking = parsed.thinking; } + // Clamp thinking level to model capabilities + if (initialModel) { + if (!initialModel.reasoning) { + initialThinking = "off"; + } else if (initialThinking === "xhigh" && !supportsXhigh(initialModel)) { + initialThinking = "high"; + } + } + // Determine which tools to use let selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools; @@ -378,11 +388,6 @@ export async function main(args: string[]) { }), }); - // If initial thinking was requested but model doesn't support it, reset to off - if (initialThinking !== "off" && initialModel && !initialModel.reasoning) { - agent.setThinkingLevel("off"); - } - // Load previous messages if continuing, resuming, or using --session if (parsed.continue || parsed.resume || parsed.session) { const messages = sessionManager.loadMessages(); diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 48f29c0a..648ab7bf 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -1172,6 +1172,7 @@ export class InteractiveMode { if (newLevel === null) { this.showStatus("Current model does not support thinking"); } else { + this.footer.updateState(this.session.state); this.updateEditorBorderColor(); this.showStatus(`Thinking level: ${newLevel}`); } @@ -1184,6 +1185,7 @@ export class InteractiveMode { const msg = this.session.scopedModels.length > 0 ? "Only one model in scope" : "Only one model available"; this.showStatus(msg); } else { + this.footer.updateState(this.session.state); this.updateEditorBorderColor(); const thinkingStr = result.model.reasoning && result.thinkingLevel !== "off" ? ` (thinking: ${result.thinkingLevel})` : ""; @@ -1310,6 +1312,7 @@ export class InteractiveMode { this.session.getAvailableThinkingLevels(), (level) => { this.session.setThinkingLevel(level); + this.footer.updateState(this.session.state); this.updateEditorBorderColor(); done(); this.showStatus(`Thinking level: ${level}`); @@ -1379,11 +1382,17 @@ export class InteractiveMode { this.ui, this.session.model, this.settingsManager, - (model) => { - this.agent.setModel(model); - this.sessionManager.saveModelChange(model.provider, model.id); - done(); - this.showStatus(`Model: ${model.id}`); + async (model) => { + try { + await this.session.setModel(model); + this.footer.updateState(this.session.state); + this.updateEditorBorderColor(); + done(); + this.showStatus(`Model: ${model.id}`); + } catch (error) { + done(); + this.showError(error instanceof Error ? error.message : String(error)); + } }, () => { done();