diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 363e9d506..0e740886c 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -12,11 +12,12 @@ ### Added -- Model roles: Configure different models for different purposes (default, small, etc.) via `/model` selector -- Model selector dual-key binding: Enter sets default model, S sets small model, Escape closes -- Model selector shows role markers: checkmark for default, lightning bolt for small -- `pi/` model aliases in Task tool agent definitions (e.g., `model: pi/small, haiku, flash, mini`) +- Model roles: Configure different models for different purposes (default, small, slow) via `/model` selector +- Model selector key bindings: Enter sets default, S sets small, L sets slow, Escape closes +- Model selector shows role markers: ✓ for default, ⚡ for small, 🧠 for slow +- `pi/` model aliases in Task tool agent definitions (e.g., `model: pi/smol, haiku, flash, mini`) - Small model auto-discovery using priority chain: haiku > flash > mini +- Slow model auto-discovery using priority chain: gpt-5.2-codex > codex > gpt > opus > pro - Title generation now uses configured small model from settings - LSP diagnostics on edit: Edit tool can now return LSP diagnostics after editing code files. Disabled by default to avoid noise during multi-edit sequences. Enable via `lsp.diagnosticsOnEdit` setting. diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index 2efcf7bbe..36f8cb27a 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -33,6 +33,9 @@ export interface ScopedModel { /** Priority chain for auto-discovering small/fast models */ export const SMALL_MODEL_PRIORITY = ["claude-haiku-4-5", "haiku", "flash", "mini"]; +/** Priority chain for auto-discovering slow/comprehensive models (reasoning, codex) */ +export const SLOW_MODEL_PRIORITY = ["gpt-5.2-codex", "gpt-5.2", "codex", "gpt", "opus", "pro"]; + /** * Parse a model string in "provider/modelId" format. * Returns undefined if the format is invalid. @@ -450,3 +453,42 @@ export async function findSmallModel( // 3. Fallback to first available (same as default) return availableModels[0]; } + +/** + * Find a slow/comprehensive model using the priority chain. + * Prioritizes reasoning and codex models for thorough analysis. + * + * @param modelRegistry The model registry to search + * @param savedModel Optional saved model string from settings (provider/modelId) + * @returns The best available slow model, or undefined if none found + */ +export async function findSlowModel( + modelRegistry: ModelRegistry, + savedModel?: string, +): Promise | undefined> { + const availableModels = await modelRegistry.getAvailable(); + if (availableModels.length === 0) return undefined; + + // 1. Try saved model from settings + if (savedModel) { + const parsed = parseModelString(savedModel); + if (parsed) { + const match = availableModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + if (match) return match; + } + } + + // 2. Try priority chain + for (const pattern of SLOW_MODEL_PRIORITY) { + // Try exact match first + const exactMatch = availableModels.find((m) => m.id.toLowerCase() === pattern.toLowerCase()); + if (exactMatch) return exactMatch; + + // Try fuzzy match (substring) + const fuzzyMatch = availableModels.find((m) => m.id.toLowerCase().includes(pattern.toLowerCase())); + if (fuzzyMatch) return fuzzyMatch; + } + + // 3. Fallback to first available (same as default) + return availableModels[0]; +} diff --git a/packages/coding-agent/src/core/tools/task/bundled-agents/explore.md b/packages/coding-agent/src/core/tools/task/bundled-agents/explore.md index bcded4859..bb4eca1fb 100644 --- a/packages/coding-agent/src/core/tools/task/bundled-agents/explore.md +++ b/packages/coding-agent/src/core/tools/task/bundled-agents/explore.md @@ -2,7 +2,7 @@ name: explore description: Fast read-only codebase scout that returns compressed context for handoff tools: read, grep, glob, ls, bash -model: pi/small, haiku, flash, mini +model: pi/smol, haiku, flash, mini --- You are a file search specialist and codebase scout. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything. diff --git a/packages/coding-agent/src/core/tools/task/bundled-agents/reviewer.md b/packages/coding-agent/src/core/tools/task/bundled-agents/reviewer.md index d392ee390..6c55386a2 100644 --- a/packages/coding-agent/src/core/tools/task/bundled-agents/reviewer.md +++ b/packages/coding-agent/src/core/tools/task/bundled-agents/reviewer.md @@ -2,7 +2,7 @@ name: reviewer description: Expert code reviewer for PRs and implementation changes tools: read, grep, glob, ls, bash -model: gpt-5.2-codex, gpt-5.2, codex, gpt +model: pi/slow, gpt-5.2-codex, gpt-5.2, codex, gpt --- You are an expert code reviewer. Analyze code changes and provide thorough reviews. diff --git a/packages/coding-agent/src/modes/interactive/components/model-selector.ts b/packages/coding-agent/src/modes/interactive/components/model-selector.ts index d6a2debb5..363637faa 100644 --- a/packages/coding-agent/src/modes/interactive/components/model-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/model-selector.ts @@ -33,6 +33,7 @@ export class ModelSelectorComponent extends Container { private currentModel?: Model; private defaultModel?: Model; private smallModel?: Model; + private slowModel?: Model; private settingsManager: SettingsManager; private modelRegistry: ModelRegistry; private onSelectCallback: (model: Model, role: string) => void; @@ -73,7 +74,7 @@ export class ModelSelectorComponent extends Container { ? "Showing models from --models scope" : "Only showing models with configured API keys (see README for details)"; this.addChild(new Text(theme.fg("warning", hintText), 0, 0)); - this.addChild(new Text(theme.fg("muted", "Enter: set default S: set small Esc: close"), 0, 0)); + this.addChild(new Text(theme.fg("muted", "Enter: default S: small L: slow Esc: close"), 0, 0)); this.addChild(new Spacer(1)); // Create search input @@ -126,6 +127,15 @@ export class ModelSelectorComponent extends Container { this.smallModel = allModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); } } + + // Load slow model + const slowStr = roles.slow; + if (slowStr) { + const parsed = parseModelString(slowStr); + if (parsed) { + this.slowModel = allModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + } + } } private async loadModels(): Promise { @@ -202,11 +212,13 @@ export class ModelSelectorComponent extends Container { const isSelected = i === this.selectedIndex; const isDefault = modelsAreEqual(this.defaultModel, item.model); const isSmall = modelsAreEqual(this.smallModel, item.model); + const isSlow = modelsAreEqual(this.slowModel, item.model); - // Build role markers: ✓ for default, ⚡ for small + // Build role markers: ✓ for default, ⚡ for small, 🧠 for slow let markers = ""; if (isDefault) markers += theme.fg("success", " ✓"); if (isSmall) markers += theme.fg("warning", " ⚡"); + if (isSlow) markers += theme.fg("accent", " 🧠"); let line = ""; if (isSelected) { @@ -268,6 +280,13 @@ export class ModelSelectorComponent extends Container { this.handleSelect(selectedModel.model, "small"); } } + // L key - set as slow model (don't close) + else if (keyData === "l" || keyData === "L") { + const selectedModel = this.filteredModels[this.selectedIndex]; + if (selectedModel) { + this.handleSelect(selectedModel.model, "slow"); + } + } // Escape - close else if (isEscape(keyData)) { this.onCancelCallback(); @@ -288,6 +307,8 @@ export class ModelSelectorComponent extends Container { this.defaultModel = model; } else if (role === "small") { this.smallModel = model; + } else if (role === "slow") { + this.slowModel = model; } // Notify caller (for updating agent state if needed)