diff --git a/.changeset/refresh-model-catalog-1080.md b/.changeset/refresh-model-catalog-1080.md new file mode 100644 index 000000000..8ed6623f6 --- /dev/null +++ b/.changeset/refresh-model-catalog-1080.md @@ -0,0 +1,13 @@ +--- +"@bradygaster/squad-sdk": minor +--- + +Refresh the model catalog to GitHub Copilot CLI-reachable IDs and prune dead fallback IDs. + +- MODEL_CATALOG now lists only currently-valid, CLI-reachable models (13 total) and drops removed IDs (gpt-4.1, gpt-5, gpt-5.1*, gpt-5.2*, gemini-3-pro-preview, claude-sonnet-4, claude-opus-4.5, claude-opus-4.6-fast). +- Fallback chains (runtime constants + SDK defaults), schema defaults, and the economy-mode map are updated to real IDs only, fixing routing that pointed at models no longer offered. +- Adds an optional `githubCategory` cost-ceiling field (lightweight/versatile/powerful) to `ModelInfo`, sourced from the models API `model_picker_category`. This is a separate cost axis from the existing quality `tier`; the two are intentionally not conflated. +- No hardcoded per-token USD pricing is added for new entries, and no `included`/zero-credit flag is introduced. +- `validateConfigDetailed` now emits a non-blocking `warnings` entry when `config.models.defaultModel` is a non-empty string that is not present in the current `MODEL_CATALOG`, helping users catch stale model IDs in config files without blocking config load. + +Refs #1080, #1183. diff --git a/.copilot/skills/model-selection/SKILL.md b/.copilot/skills/model-selection/SKILL.md index 611941bab..7643081c4 100644 --- a/.copilot/skills/model-selection/SKILL.md +++ b/.copilot/skills/model-selection/SKILL.md @@ -16,7 +16,7 @@ Before spawning an agent, the coordinator determines which model to use. This sk Check these layers in order — first match wins: -**Layer 1 — User Override:** Did the user specify a model? ("use opus", "save costs", "use gpt-5.2-codex for this"). If yes, use that model. Session-wide directives ("always use haiku") persist until contradicted. +**Layer 1 — User Override:** Did the user specify a model? ("use opus", "save costs", "use gpt-5.3-codex for this"). If yes, use that model. Session-wide directives ("always use haiku") persist until contradicted. **Layer 2 — Charter Preference:** Does the agent's charter have a `## Model` section with `Preferred` set to a specific model (not `auto`)? If yes, use that model. @@ -27,7 +27,7 @@ Check these layers in order — first match wins: | Writing code (implementation, refactoring, test code, bug fixes) | `claude-sonnet-4.6` | Standard | Quality and accuracy matter for code. Use standard tier. | | Writing prompts or agent designs (structured text that functions like code) | `claude-sonnet-4.6` | Standard | Prompts are executable — treat like code. | | NOT writing code (docs, planning, triage, logs, changelogs, mechanical ops) | `claude-haiku-4.5` | Fast | Cost first. Haiku handles non-code tasks. | -| Visual/design work requiring image analysis | `claude-opus-4.5` | Premium | Vision capability required. Overrides cost rule. | +| Visual/design work requiring image analysis | `claude-opus-4.6` | Premium | Vision capability required. Overrides cost rule. | **Role-to-model mapping** (applying cost-first principle): @@ -47,7 +47,7 @@ Check these layers in order — first match wins: - **Bump UP to premium:** architecture proposals, reviewer gates, security audits, multi-agent coordination (output feeds 3+ agents) - **Bump DOWN to fast/cheap:** typo fixes, renames, boilerplate, scaffolding, changelogs, version bumps - **Switch to code specialist (`gpt-5.3-codex`):** large multi-file refactors, complex implementation from spec, heavy code generation (500+ lines) -- **Switch to analytical diversity (`gemini-3-pro-preview`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection +- **Switch to analytical diversity (`gemini-2.5-pro`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection **Layer 4 — Default:** If nothing else matched, use `claude-haiku-4.5`. Cost wins when in doubt, unless code is being produced. @@ -56,9 +56,9 @@ Check these layers in order — first match wins: If a spawn fails because the selected model is unavailable (plan restriction, org policy, rate limit, deprecation, or any other reason), silently retry with the next model in the chain. Do NOT tell the user about fallback attempts. Maximum 3 retries before jumping to the nuclear fallback. ``` -Premium: claude-opus-4.6 → claude-opus-4.6-fast → claude-opus-4.5 → claude-sonnet-4.6 → (omit model param) -Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4 → (omit model param) -Fast: claude-haiku-4.5 → gpt-5.1-codex-mini → gpt-4.1 → gpt-5-mini → (omit model param) +Premium: claude-opus-4.6 → claude-sonnet-4.6 → (omit model param) +Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` `(omit model param)` = call the `task` tool WITHOUT the `model` parameter. The platform uses its built-in default. This is the nuclear fallback — it always works. @@ -101,9 +101,9 @@ Include tier annotation only when the model was bumped or a specialist was chose ### Valid Models -**Premium:** `claude-opus-4.6`, `claude-opus-4.6-fast`, `claude-opus-4.5` -**Standard:** `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-4`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.2`, `gpt-5.1-codex-max`, `gpt-5.1-codex`, `gpt-5.1`, `gpt-5`, `gemini-3-pro-preview` -**Fast/Cheap:** `claude-haiku-4.5`, `gpt-5.1-codex-mini`, `gpt-5-mini`, `gpt-4.1` +**Premium:** `claude-opus-4.6`, `claude-opus-4.7`, `claude-opus-4.8` +**Standard:** `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-5`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.5`, `gemini-2.5-pro` +**Fast/Cheap:** `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5-mini` ## Examples @@ -122,8 +122,8 @@ Include tier annotation only when the model was bumped or a specialist was chose - Role: Backend Dev - Task: "refactor 15 auth-related files to use new token system" - Layer 3 base: `claude-sonnet-4.5` -- Task complexity: heavy multi-file refactor → switch to `gpt-5.2-codex` -- Spawn: `🔧 Fenster (gpt-5.2-codex · code specialist) — refactoring auth to new token system` +- Task complexity: heavy multi-file refactor → switch to `gpt-5.3-codex` +- Spawn: `🔧 Fenster (gpt-5.3-codex · code specialist) — refactoring auth to new token system` **Example 4: Scribe logging** - Role: Scribe diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index e3e017f45..4fe2b89cb 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -1,6 +1,7 @@ --- name: Squad description: "Your AI team. Describe what you're building, get a team of specialists that live in your repo." +tools: ["*"] --- diff --git a/.squad-templates/model-selection-reference.md b/.squad-templates/model-selection-reference.md index 2421f537c..e19998b9a 100644 --- a/.squad-templates/model-selection-reference.md +++ b/.squad-templates/model-selection-reference.md @@ -21,7 +21,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Writing code (implementation, refactoring, test code, bug fixes) | `claude-sonnet-4.6` | Standard | Quality and accuracy matter for code. Use standard tier. | | Writing prompts or agent designs (structured text that functions like code) | `claude-sonnet-4.6` | Standard | Prompts are executable — treat like code. | | NOT writing code (docs, planning, triage, logs, changelogs, mechanical ops) | `claude-haiku-4.5` | Fast | Cost first. Haiku handles non-code tasks. | -| Visual/design work requiring image analysis | `claude-opus-4.5` | Premium | Vision capability required. Overrides cost rule. | +| Visual/design work requiring image analysis | `claude-opus-4.6` | Premium | Vision capability required. Overrides cost rule. | **Role-to-model mapping** (applying cost-first principle): @@ -32,7 +32,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Lead / Architect | auto (per-task) | Mixed: code review needs quality, planning needs cost | Architecture proposals → premium; triage/planning → haiku | | Prompt Engineer | auto (per-task) | Mixed: prompt design is like code, research is not | Prompt architecture → sonnet; research/analysis → haiku | | Copilot SDK Expert | `claude-sonnet-4.6` | Technical analysis that often touches code | Pure research → `claude-haiku-4.5` | -| Designer / Visual | `claude-opus-4.5` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | +| Designer / Visual | `claude-opus-4.6` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | | DevRel / Writer | `claude-haiku-4.5` | Docs and writing — not code | — | | Scribe / Logger | `claude-haiku-4.5` | Mechanical file ops — cheapest possible | — (never bump Scribe) | | Git / Release | `claude-haiku-4.5` | Mechanical ops — changelogs, tags, version bumps | — (never bump mechanical ops) | @@ -41,7 +41,7 @@ Before spawning an agent, determine which model to use. Check these layers in or - **Bump UP to premium:** architecture proposals, reviewer gates, security audits, multi-agent coordination (output feeds 3+ agents) - **Bump DOWN to fast/cheap:** typo fixes, renames, boilerplate, scaffolding, changelogs, version bumps - **Switch to code specialist (`gpt-5.3-codex`):** large multi-file refactors, complex implementation from spec, heavy code generation (500+ lines) -- **Switch to analytical diversity (`gemini-3-pro-preview`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection +- **Switch to analytical diversity (`gemini-2.5-pro`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection **Layer 4 — Default:** If nothing else matched, use `claude-haiku-4.5`. Cost wins when in doubt, unless code is being produced. @@ -50,9 +50,9 @@ Before spawning an agent, determine which model to use. Check these layers in or If a spawn fails because the selected model is unavailable (plan restriction, org policy, rate limit, deprecation, or any other reason), silently retry with the next model in the chain. Do NOT tell the user about fallback attempts. Maximum 3 retries before jumping to the nuclear fallback. ``` -Premium: claude-opus-4.6 → claude-opus-4.5 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) -Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → claude-sonnet-4 → (omit model param) -Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5.1-codex-mini → gpt-4.1 → (omit model param) +Premium: claude-opus-4.6 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) +Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` `(omit model param)` = call the `task` tool WITHOUT the `model` parameter. The platform uses its built-in default. This is the nuclear fallback — it always works. @@ -86,7 +86,7 @@ When spawning, include the model in your acknowledgment: ``` 🔧 Fenster (claude-sonnet-4.6) — refactoring auth module -🎨 Redfoot (claude-opus-4.5 · vision) — designing color system +🎨 Redfoot (claude-opus-4.6 · vision) — designing color system 📋 Scribe (claude-haiku-4.5 · fast) — logging session ⚡ Keaton (claude-opus-4.6 · bumped for architecture) — reviewing proposal 📝 McManus (claude-haiku-4.5 · fast) — updating docs @@ -96,6 +96,6 @@ Include tier annotation only when the model was bumped or a specialist was chose **Valid models (current platform catalog):** -Premium: `claude-opus-4.6`, `claude-opus-4.6-1m` (Internal only), `claude-opus-4.5` -Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-4`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.2`, `gpt-5.1-codex-max`, `gpt-5.1-codex`, `gpt-5.1`, `gemini-3-pro-preview` -Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5.1-codex-mini`, `gpt-5-mini`, `gpt-4.1` +Premium: `claude-opus-4.6`, `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-4.6-1m` (Internal only) +Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-5`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.5`, `gemini-2.5-pro` +Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5-mini` diff --git a/.squad-templates/ralph-circuit-breaker.md b/.squad-templates/ralph-circuit-breaker.md index 87be26015..9604733b2 100644 --- a/.squad-templates/ralph-circuit-breaker.md +++ b/.squad-templates/ralph-circuit-breaker.md @@ -16,7 +16,6 @@ Premium models burn quota fast: | `gpt-5.4` | 50x | Very high | | `gpt-5.4-mini` | **0x** | **Free — unlimited** | | `gpt-5-mini` | **0x** | **Free — unlimited** | -| `gpt-4.1` | **0x** | **Free — unlimited** | ## Circuit Breaker States @@ -43,7 +42,6 @@ Premium models burn quota fast: - Fall back through the free-tier model chain: 1. `gpt-5.4-mini` 2. `gpt-5-mini` - 3. `gpt-4.1` - Start cooldown timer (default: 10 minutes) - When cooldown expires → transition to HALF-OPEN @@ -58,7 +56,7 @@ Premium models burn quota fast: { "state": "closed", "preferredModel": "claude-sonnet-4.6", - "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"], + "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini"], "currentFallbackIndex": 0, "cooldownMinutes": 10, "openedAt": null, @@ -87,7 +85,7 @@ function Get-CircuitBreakerState { $default = @{ state = "closed" preferredModel = "claude-sonnet-4.6" - fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1") + fallbackChain = @("gpt-5.4-mini", "gpt-5-mini") currentFallbackIndex = 0 cooldownMinutes = 10 openedAt = $null @@ -294,7 +292,7 @@ Override defaults by editing `.squad/ralph-circuit-breaker.json`: | Field | Default | Description | |-------|---------|-------------| | `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed | -| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) | +| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini"]` | Ordered fallback models (all free-tier) | | `cooldownMinutes` | `10` | How long to wait before testing recovery | ## Metrics diff --git a/.squad/skills/economy-mode/SKILL.md b/.squad/skills/economy-mode/SKILL.md index 696e778c4..f8ed05a9b 100644 --- a/.squad/skills/economy-mode/SKILL.md +++ b/.squad/skills/economy-mode/SKILL.md @@ -40,13 +40,13 @@ When economy mode is **active**, Layer 3 auto-selection uses this table instead | Task Output | Normal Mode | Economy Mode | |-------------|-------------|--------------| -| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Architecture, code review, security audits | `claude-opus-4.5` | `claude-sonnet-4.5` | -| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-4.1` | +| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-5-mini` | +| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-5-mini` | +| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-5-mini` | +| Architecture, code review, security audits | `claude-opus-4.6` | `claude-sonnet-4.5` | +| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-5-mini` | -**Prefer `gpt-4.1` over `gpt-5-mini`** when the task involves structured output or agentic tool use. Prefer `gpt-5-mini` for pure text generation tasks where latency matters. +**Prefer `gpt-5-mini`** for all economy-mode tasks where cost is the priority. ## AGENT WORKFLOW diff --git a/.squad/skills/model-selection/SKILL.md b/.squad/skills/model-selection/SKILL.md index 9842616e5..4a9f0592a 100644 --- a/.squad/skills/model-selection/SKILL.md +++ b/.squad/skills/model-selection/SKILL.md @@ -117,9 +117,9 @@ After resolving the model and including it in the spawn template, this skill is If a model is unavailable (rate limit, plan restriction), retry within the same tier: ``` -Premium: claude-opus-4.6 → claude-opus-4.6-fast → claude-opus-4.5 → claude-sonnet-4.6 -Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4 -Fast: claude-haiku-4.5 → gpt-5.1-codex-mini → gpt-4.1 → gpt-5-mini +Premium: claude-opus-4.6 → claude-sonnet-4.6 → (omit model param) +Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` **Never fall UP in tier.** A fast task won't land on a premium model via fallback. diff --git a/packages/squad-cli/src/cli-entry.ts b/packages/squad-cli/src/cli-entry.ts index ea0263985..71ce72b22 100644 --- a/packages/squad-cli/src/cli-entry.ts +++ b/packages/squad-cli/src/cli-entry.ts @@ -220,7 +220,7 @@ async function main(): Promise { console.log(` Usage: start [--tunnel] [--port ] [--command ]`); console.log(` [copilot flags...]`); console.log(` Examples: start --tunnel --yolo`); - console.log(` start --tunnel --model claude-sonnet-4`); + console.log(` start --tunnel --model claude-sonnet-4.6`); console.log(` start --tunnel --command "gh copilot"`); console.log(` ${BOLD}nap${RESET} Context hygiene (compress, prune, archive .squad/ state)`); console.log(` Usage: nap [--deep] [--dry-run]`); diff --git a/packages/squad-cli/src/cli/commands/economy.ts b/packages/squad-cli/src/cli/commands/economy.ts index dc1189201..ab387c365 100644 --- a/packages/squad-cli/src/cli/commands/economy.ts +++ b/packages/squad-cli/src/cli/commands/economy.ts @@ -61,8 +61,8 @@ export async function runEconomy(cwd: string, subArgs: string[]): Promise if (enabled) { console.log(` When active, auto-selected models are downgraded:`); console.log(` ${DIM}claude-opus-4.6 → claude-sonnet-4.5 (architecture/review)${RESET}`); - console.log(` ${DIM}claude-sonnet-4.6 → gpt-4.1 (code writing)${RESET}`); - console.log(` ${DIM}claude-haiku-4.5 → gpt-4.1 (docs/mechanical)${RESET}`); + console.log(` ${DIM}claude-sonnet-4.6 → gpt-5-mini (code writing)${RESET}`); + console.log(` ${DIM}claude-haiku-4.5 → gpt-5-mini (docs/mechanical)${RESET}`); console.log(` Explicit overrides (config.json, charter) are never changed.\n`); } else { console.log(` Usage: squad economy on | off\n`); diff --git a/packages/squad-cli/src/cli/core/command-help.ts b/packages/squad-cli/src/cli/core/command-help.ts index 98cfaf95c..e3daccdd0 100644 --- a/packages/squad-cli/src/cli/core/command-help.ts +++ b/packages/squad-cli/src/cli/core/command-help.ts @@ -186,7 +186,7 @@ const COMMAND_HELP: Record = { console.log(` ${BOLD}--command ${RESET} Override the agent command (default: copilot)\n`); console.log(`Examples:`); console.log(` squad start --tunnel --yolo`); - console.log(` squad start --tunnel --model claude-sonnet-4`); + console.log(` squad start --tunnel --model claude-sonnet-4.6`); console.log(` squad start --tunnel --command "gh copilot"\n`); }, diff --git a/packages/squad-cli/src/cli/shell/error-messages.ts b/packages/squad-cli/src/cli/shell/error-messages.ts index bee06d31d..c7b9d6536 100644 --- a/packages/squad-cli/src/cli/shell/error-messages.ts +++ b/packages/squad-cli/src/cli/shell/error-messages.ts @@ -80,7 +80,7 @@ export function rateLimitGuidance(opts?: { retryAfter?: number; model?: string } recovery: [ retryStr, 'Enable economy mode to switch to cheaper models: `squad economy on`', - 'Or set a different model: add `"defaultModel": "gpt-4.1"` to .squad/config.json', + 'Or set a different model: add `"defaultModel": "gpt-5-mini"` to .squad/config.json', ], }; } diff --git a/packages/squad-cli/templates/model-selection-reference.md b/packages/squad-cli/templates/model-selection-reference.md index 2421f537c..e19998b9a 100644 --- a/packages/squad-cli/templates/model-selection-reference.md +++ b/packages/squad-cli/templates/model-selection-reference.md @@ -21,7 +21,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Writing code (implementation, refactoring, test code, bug fixes) | `claude-sonnet-4.6` | Standard | Quality and accuracy matter for code. Use standard tier. | | Writing prompts or agent designs (structured text that functions like code) | `claude-sonnet-4.6` | Standard | Prompts are executable — treat like code. | | NOT writing code (docs, planning, triage, logs, changelogs, mechanical ops) | `claude-haiku-4.5` | Fast | Cost first. Haiku handles non-code tasks. | -| Visual/design work requiring image analysis | `claude-opus-4.5` | Premium | Vision capability required. Overrides cost rule. | +| Visual/design work requiring image analysis | `claude-opus-4.6` | Premium | Vision capability required. Overrides cost rule. | **Role-to-model mapping** (applying cost-first principle): @@ -32,7 +32,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Lead / Architect | auto (per-task) | Mixed: code review needs quality, planning needs cost | Architecture proposals → premium; triage/planning → haiku | | Prompt Engineer | auto (per-task) | Mixed: prompt design is like code, research is not | Prompt architecture → sonnet; research/analysis → haiku | | Copilot SDK Expert | `claude-sonnet-4.6` | Technical analysis that often touches code | Pure research → `claude-haiku-4.5` | -| Designer / Visual | `claude-opus-4.5` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | +| Designer / Visual | `claude-opus-4.6` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | | DevRel / Writer | `claude-haiku-4.5` | Docs and writing — not code | — | | Scribe / Logger | `claude-haiku-4.5` | Mechanical file ops — cheapest possible | — (never bump Scribe) | | Git / Release | `claude-haiku-4.5` | Mechanical ops — changelogs, tags, version bumps | — (never bump mechanical ops) | @@ -41,7 +41,7 @@ Before spawning an agent, determine which model to use. Check these layers in or - **Bump UP to premium:** architecture proposals, reviewer gates, security audits, multi-agent coordination (output feeds 3+ agents) - **Bump DOWN to fast/cheap:** typo fixes, renames, boilerplate, scaffolding, changelogs, version bumps - **Switch to code specialist (`gpt-5.3-codex`):** large multi-file refactors, complex implementation from spec, heavy code generation (500+ lines) -- **Switch to analytical diversity (`gemini-3-pro-preview`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection +- **Switch to analytical diversity (`gemini-2.5-pro`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection **Layer 4 — Default:** If nothing else matched, use `claude-haiku-4.5`. Cost wins when in doubt, unless code is being produced. @@ -50,9 +50,9 @@ Before spawning an agent, determine which model to use. Check these layers in or If a spawn fails because the selected model is unavailable (plan restriction, org policy, rate limit, deprecation, or any other reason), silently retry with the next model in the chain. Do NOT tell the user about fallback attempts. Maximum 3 retries before jumping to the nuclear fallback. ``` -Premium: claude-opus-4.6 → claude-opus-4.5 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) -Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → claude-sonnet-4 → (omit model param) -Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5.1-codex-mini → gpt-4.1 → (omit model param) +Premium: claude-opus-4.6 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) +Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` `(omit model param)` = call the `task` tool WITHOUT the `model` parameter. The platform uses its built-in default. This is the nuclear fallback — it always works. @@ -86,7 +86,7 @@ When spawning, include the model in your acknowledgment: ``` 🔧 Fenster (claude-sonnet-4.6) — refactoring auth module -🎨 Redfoot (claude-opus-4.5 · vision) — designing color system +🎨 Redfoot (claude-opus-4.6 · vision) — designing color system 📋 Scribe (claude-haiku-4.5 · fast) — logging session ⚡ Keaton (claude-opus-4.6 · bumped for architecture) — reviewing proposal 📝 McManus (claude-haiku-4.5 · fast) — updating docs @@ -96,6 +96,6 @@ Include tier annotation only when the model was bumped or a specialist was chose **Valid models (current platform catalog):** -Premium: `claude-opus-4.6`, `claude-opus-4.6-1m` (Internal only), `claude-opus-4.5` -Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-4`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.2`, `gpt-5.1-codex-max`, `gpt-5.1-codex`, `gpt-5.1`, `gemini-3-pro-preview` -Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5.1-codex-mini`, `gpt-5-mini`, `gpt-4.1` +Premium: `claude-opus-4.6`, `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-4.6-1m` (Internal only) +Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-5`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.5`, `gemini-2.5-pro` +Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5-mini` diff --git a/packages/squad-cli/templates/ralph-circuit-breaker.md b/packages/squad-cli/templates/ralph-circuit-breaker.md index 87be26015..9604733b2 100644 --- a/packages/squad-cli/templates/ralph-circuit-breaker.md +++ b/packages/squad-cli/templates/ralph-circuit-breaker.md @@ -16,7 +16,6 @@ Premium models burn quota fast: | `gpt-5.4` | 50x | Very high | | `gpt-5.4-mini` | **0x** | **Free — unlimited** | | `gpt-5-mini` | **0x** | **Free — unlimited** | -| `gpt-4.1` | **0x** | **Free — unlimited** | ## Circuit Breaker States @@ -43,7 +42,6 @@ Premium models burn quota fast: - Fall back through the free-tier model chain: 1. `gpt-5.4-mini` 2. `gpt-5-mini` - 3. `gpt-4.1` - Start cooldown timer (default: 10 minutes) - When cooldown expires → transition to HALF-OPEN @@ -58,7 +56,7 @@ Premium models burn quota fast: { "state": "closed", "preferredModel": "claude-sonnet-4.6", - "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"], + "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini"], "currentFallbackIndex": 0, "cooldownMinutes": 10, "openedAt": null, @@ -87,7 +85,7 @@ function Get-CircuitBreakerState { $default = @{ state = "closed" preferredModel = "claude-sonnet-4.6" - fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1") + fallbackChain = @("gpt-5.4-mini", "gpt-5-mini") currentFallbackIndex = 0 cooldownMinutes = 10 openedAt = $null @@ -294,7 +292,7 @@ Override defaults by editing `.squad/ralph-circuit-breaker.json`: | Field | Default | Description | |-------|---------|-------------| | `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed | -| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) | +| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini"]` | Ordered fallback models (all free-tier) | | `cooldownMinutes` | `10` | How long to wait before testing recovery | ## Metrics diff --git a/packages/squad-cli/templates/skills/economy-mode/SKILL.md b/packages/squad-cli/templates/skills/economy-mode/SKILL.md index 696e778c4..f8ed05a9b 100644 --- a/packages/squad-cli/templates/skills/economy-mode/SKILL.md +++ b/packages/squad-cli/templates/skills/economy-mode/SKILL.md @@ -40,13 +40,13 @@ When economy mode is **active**, Layer 3 auto-selection uses this table instead | Task Output | Normal Mode | Economy Mode | |-------------|-------------|--------------| -| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Architecture, code review, security audits | `claude-opus-4.5` | `claude-sonnet-4.5` | -| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-4.1` | +| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-5-mini` | +| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-5-mini` | +| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-5-mini` | +| Architecture, code review, security audits | `claude-opus-4.6` | `claude-sonnet-4.5` | +| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-5-mini` | -**Prefer `gpt-4.1` over `gpt-5-mini`** when the task involves structured output or agentic tool use. Prefer `gpt-5-mini` for pure text generation tasks where latency matters. +**Prefer `gpt-5-mini`** for all economy-mode tasks where cost is the priority. ## AGENT WORKFLOW diff --git a/packages/squad-cli/templates/skills/model-selection/SKILL.md b/packages/squad-cli/templates/skills/model-selection/SKILL.md index 9842616e5..4a9f0592a 100644 --- a/packages/squad-cli/templates/skills/model-selection/SKILL.md +++ b/packages/squad-cli/templates/skills/model-selection/SKILL.md @@ -117,9 +117,9 @@ After resolving the model and including it in the spawn template, this skill is If a model is unavailable (rate limit, plan restriction), retry within the same tier: ``` -Premium: claude-opus-4.6 → claude-opus-4.6-fast → claude-opus-4.5 → claude-sonnet-4.6 -Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4 -Fast: claude-haiku-4.5 → gpt-5.1-codex-mini → gpt-4.1 → gpt-5-mini +Premium: claude-opus-4.6 → claude-sonnet-4.6 → (omit model param) +Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` **Never fall UP in tier.** A fast task won't land on a premium model via fallback. diff --git a/packages/squad-cli/templates/skills/squad-commands/SKILL.md b/packages/squad-cli/templates/skills/squad-commands/SKILL.md new file mode 100644 index 000000000..046dde513 --- /dev/null +++ b/packages/squad-cli/templates/skills/squad-commands/SKILL.md @@ -0,0 +1,303 @@ +--- +name: squad-commands +description: > + Categorized catalog of common Squad operations. Coordinator reads this + file and presents it as an interactive menu when the user asks for + available commands or help. +domain: squad-operations +confidence: high +source: first-party +triggers: ["squad commands", "what can squad do", "show me squad options", "slash commands"] +--- + +## Menu Presentation Rules + +When the user triggers this skill ("squad commands", "help", "what can squad do", etc.): + +1. **Category-level menu first.** Present category names as an `ask_user` choice list: + ``` + 📋 Squad Commands — pick a category: + 1. Install & Upgrade + 2. Team Management + 3. Issues & PRs + 4. Plugins & Skills + 5. Model & Cost + 6. Sessions & State + ``` +2. **Drill-down.** After selection, show operation titles in that category as a second `ask_user` list. +3. **Direct match skips the menu.** If the user says "how do I upgrade with state backend," match to the specific entry and go straight to argument collection. +4. **Compact fallback.** If `ask_user` is unavailable, render as a markdown table instead. +5. **Back / Cancel.** Include "← Back to categories" in sub-menus. Include "Cancel" in confirmation prompts. Respect "never mind" / "cancel" at any point. + +**Argument collection:** For entries with `args`, iterate the list sequentially. Use `ask_user` with choices when `choices` is provided; free-text prompt otherwise. If the user says "just do it" or "defaults are fine," skip remaining args and use their defaults. + +**Confirmation template:** +``` +⚠️ This will {action-description}. +{what will change} +Proceed? (yes / no) +``` + +--- + +## Install & Upgrade + +### Upgrade Squad CLI + +- **intent:** upgrade squad, update squad, install latest version, get new version +- **summary:** Upgrade Squad CLI to the latest version for your channel +- **action:** shell +- **command:** squad upgrade +- **args:** + - `state-backend`: Which state backend? | choices: {worktree, git-notes, orphan, two-layer} | default: (keep current) +- **confirm:** false +- **platform_caveats:** Requires terminal. In VS Code, open the integrated terminal and run the command directly. + +### Initialize Squad + +- **intent:** set up squad, initialize squad, create team, start squad in this project +- **summary:** Scaffold Squad in the current directory (idempotent) +- **action:** shell +- **command:** squad init +- **args:** (none) +- **confirm:** false +- **platform_caveats:** Requires terminal. Recommend a standalone terminal for best results. + +### Switch State Backend + +- **intent:** switch state backend, change state storage, use git-notes, use orphan branch +- **summary:** Change where Squad stores mutable state (config.json) +- **action:** file-edit +- **command:** .squad/config.json → stateBackend +- **args:** + - `stateBackend`: Which state backend? | choices: {worktree, git-notes, orphan, two-layer} | default: (keep current) +- **confirm:** true +- **platform_caveats:** May require migration if switching away from worktree. Show current value and new value before confirming. + +--- + +## Team Management + +### Add Team Member + +- **intent:** add team member, hire agent, add agent, add developer, recruit +- **summary:** Add a new agent to the team roster +- **action:** coordinator +- **command:** Add Team Member flow (Init Mode / Team Mode) +- **args:** + - `role`: What role should this agent fill? (e.g., Frontend Dev, Backend Dev, QA Engineer) + - `name`: Preferred name or casting universe? | default: (auto-cast from active universe) +- **confirm:** false + +### Remove Team Member + +- **intent:** remove team member, fire agent, delete agent, remove developer +- **summary:** Remove an agent and delete their charter and history files +- **action:** coordinator +- **command:** Remove Team Member flow +- **args:** + - `member`: Which team member to remove? (name or role) +- **confirm:** true + +### Reassign Roles + +- **intent:** reassign role, change role, swap roles, update team member role +- **summary:** Update a team member's role in team.md and their charter +- **action:** coordinator +- **command:** Update team.md roster + charter.md +- **args:** + - `member`: Which team member? + - `newRole`: New role? +- **confirm:** false + +### Show Roster + +- **intent:** show roster, who is on the team, list team members, show team, capability profile +- **summary:** Display the current team roster and capability profile +- **action:** coordinator +- **command:** Direct Mode — read team.md, answer +- **args:** (none) +- **confirm:** false + +--- + +## Issues & PRs + +### Connect GitHub Repo + +- **intent:** connect github, enable issues, set up issues, link repository, github issues mode +- **summary:** Connect this project to GitHub Issues via gh auth +- **action:** coordinator +- **command:** GitHub Issues Mode (connection flow) +- **args:** (none) +- **confirm:** false +- **platform_caveats:** Requires `gh auth login` to have been run in the terminal. + +### Triage Issues + +- **intent:** triage issues, review issues, assign issues, label issues +- **summary:** Run the Lead triage flow on open GitHub issues +- **action:** coordinator +- **command:** GitHub Issues Mode → Lead triage +- **args:** (none) +- **confirm:** false + +### Activate Ralph + +- **intent:** activate ralph, start ralph, ralph go, start work monitor, start auto-work +- **summary:** Activate Ralph — Work Monitor — to pick up and run queued issues +- **action:** coordinator +- **command:** Ralph — Work Monitor triggers +- **args:** (none) +- **confirm:** false + +### Set Ralph Polling Interval + +- **intent:** set ralph interval, change ralph timing, how often does ralph check, ralph every N minutes +- **summary:** Tell Ralph how frequently to poll for new work +- **action:** coordinator +- **command:** Ralph trigger: "Ralph, check every N minutes" +- **args:** + - `interval`: How often should Ralph poll? (in minutes) | default: 10 +- **confirm:** false + +### Start Squad Watch + +- **intent:** start watch, squad watch, monitor issues, watch for issues, auto-triage +- **summary:** Start squad watch to continuously poll and triage issues +- **action:** shell +- **command:** squad watch +- **args:** + - `interval`: Poll interval in minutes | default: 10 +- **confirm:** false +- **platform_caveats:** CLI-only — long-running foreground process. Not viable in VS Code without an integrated terminal. Run: `squad watch --interval {n}` in your terminal. + +--- + +## Plugins & Skills + +### Browse Plugin Marketplace + +- **intent:** browse plugins, explore plugins, what plugins are available, plugin marketplace +- **summary:** Browse available plugins in the Squad marketplace +- **action:** shell +- **command:** squad plugin marketplace browse +- **args:** + - `name`: Plugin name to search for | default: (browse all) +- **confirm:** false + +### Add Marketplace Plugin + +- **intent:** add plugin, install plugin, get plugin from marketplace +- **summary:** Add a plugin from the marketplace to this Squad +- **action:** shell +- **command:** squad plugin marketplace add +- **args:** + - `plugin`: Plugin owner/repo (e.g., owner/plugin-name) +- **confirm:** false + +### Remove Marketplace Plugin + +- **intent:** remove plugin, uninstall plugin, delete plugin +- **summary:** Remove an installed marketplace plugin +- **action:** shell +- **command:** squad plugin marketplace remove +- **args:** + - `name`: Plugin name to remove +- **confirm:** true + +### List Marketplace Plugins + +- **intent:** list plugins, show installed plugins, what plugins do I have +- **summary:** List all plugins registered in this Squad +- **action:** shell +- **command:** squad plugin marketplace list +- **args:** (none) +- **confirm:** false + +### List Installed Skills + +- **intent:** list skills, show skills, what skills are installed, skill catalog +- **summary:** List all skills installed in .squad/skills/ and .copilot/skills/ +- **action:** coordinator +- **command:** Direct Mode — list .squad/skills/ and .copilot/skills/ directories +- **args:** (none) +- **confirm:** false + +--- + +## Model & Cost + +### Set Default Model + +- **intent:** set default model, change model, use gpt-4, use claude, switch model +- **summary:** Set the default model for all agents in config.json +- **action:** file-edit +- **command:** .squad/config.json → defaultModel +- **args:** + - `model`: Model name (e.g., gpt-4o, claude-sonnet-4.5, o3) +- **confirm:** false + +### Override Per-Agent Model + +- **intent:** set model for agent, agent model override, use different model for one agent +- **summary:** Set a model override for a specific agent in config.json +- **action:** file-edit +- **command:** .squad/config.json → agentModelOverrides.{agentName} +- **args:** + - `agent`: Agent name (must match name in team.md) + - `model`: Model name (e.g., gpt-4o, claude-sonnet-4.5) +- **confirm:** false + +### Clear Model Preference + +- **intent:** clear model, reset model, remove model preference, use default model +- **summary:** Remove a model override from config.json (reverts to system default) +- **action:** file-edit +- **command:** .squad/config.json → remove defaultModel or agentModelOverrides.{agentName} +- **args:** + - `scope`: Clear default or a specific agent? | choices: {default model, specific agent} | default: default model + - `agent`: Agent name (only if scope = specific agent) +- **confirm:** false + +--- + +## Sessions & State + +### Catch-Up Summary + +- **intent:** catch me up, what happened, status, what did the team do, session summary +- **summary:** Summarize recent agent activity and key decisions +- **action:** coordinator +- **command:** Session catch-up flow (lazy scan) +- **args:** (none) +- **confirm:** false + +### Show Recent Decisions + +- **intent:** show decisions, recent decisions, what decisions were made, decision log +- **summary:** Display recent entries from .squad/decisions.md +- **action:** coordinator +- **command:** Direct Mode — read decisions.md, answer +- **args:** (none) +- **confirm:** false + +### Archive Old Decisions + +- **intent:** archive decisions, clean up decisions, move old decisions, compact decisions +- **summary:** Move old decisions from decisions.md to decisions-archive.md +- **action:** coordinator +- **command:** Move entries older than threshold from .squad/decisions.md → .squad/decisions-archive.md +- **args:** + - `olderThan`: Archive decisions older than how many days? | default: 30 +- **confirm:** true + +### Summarize Agent History + +- **intent:** summarize history, what did agent do, agent history, compress history +- **summary:** Spawn an agent to summarize and compress a team member's history file +- **action:** coordinator +- **command:** Spawn agent with history.md summarization task +- **args:** + - `member`: Which team member's history to summarize? +- **confirm:** false diff --git a/packages/squad-sdk/src/config/models.ts b/packages/squad-sdk/src/config/models.ts index 7ac6f1ab4..a96003db1 100644 --- a/packages/squad-sdk/src/config/models.ts +++ b/packages/squad-sdk/src/config/models.ts @@ -23,6 +23,17 @@ export interface ModelPricing { outputPerToken: number; } +/** + * GitHub Copilot billing cost-ceiling category (`model_picker_category`). + * + * This is a COST axis and is intentionally SEPARATE from {@link ModelTier} + * (the quality axis). A model may be standard-tier (quality) yet + * powerful-category (cost) — e.g. `gpt-5.4`. Do not conflate the two. + * Source: GitHub Copilot models API `model_picker_category` (canonical), + * with the public `github/docs` models-and-pricing.yml as fallback. + */ +export type GitHubModelCategory = 'lightweight' | 'versatile' | 'powerful'; + /** * Model capability information. */ @@ -30,7 +41,7 @@ export interface ModelInfo { /** Model identifier */ id: ModelId; - /** Model tier */ + /** Model tier (quality axis) */ tier: ModelTier; /** Provider (anthropic, openai, google) */ @@ -38,6 +49,12 @@ export interface ModelInfo { /** Model family */ family: 'claude' | 'gpt' | 'gemini'; + + /** + * GitHub Copilot billing cost-ceiling category (cost axis). + * Separate from {@link tier}; optional so out-of-catalog IDs still pass through. + */ + githubCategory?: GitHubModelCategory; /** Supports vision/multimodal input */ vision?: boolean; @@ -56,218 +73,184 @@ export interface ModelInfo { } /** - * Full model catalog from squad.agent.md. + * Full model catalog. + * + * Restricted to model IDs verified reachable from the GitHub Copilot CLI + * surface (the `copilot-cli` integration subset, 13 enabled models, verified + * 2026-07-04). Each entry carries an optional {@link ModelInfo.githubCategory} + * (cost axis) sourced from the models API `model_picker_category`, kept + * separate from {@link ModelInfo.tier} (quality axis). + * + * Notes: + * - No hardcoded per-token pricing is added for models whose pricing is not + * already known; pricing is sourced out-of-band and is intentionally absent + * on newer entries rather than guessed. + * - Out-of-catalog IDs still pass through the selector (0-cost estimate + + * default chain); this catalog drives routing quality/cost, not correctness. + * + * Refs: #1080, #1183. */ export const MODEL_CATALOG: ModelInfo[] = [ - // Premium tier - highest quality, slowest, most expensive + // Premium tier (quality) — powerful category (cost) { - id: 'claude-opus-4.6', + id: 'claude-opus-4.8', tier: 'premium', provider: 'anthropic', family: 'claude', + githubCategory: 'powerful', vision: true, useCases: ['architecture proposals', 'security audits', 'complex design'], cost: 10, speed: 3, - pricing: { inputPerToken: 0.000015, outputPerToken: 0.000075 }, }, { - id: 'claude-opus-4.6-fast', + id: 'claude-opus-4.7', tier: 'premium', provider: 'anthropic', family: 'claude', + githubCategory: 'powerful', vision: true, - useCases: ['architecture proposals', 'urgent reviews'], - cost: 9, - speed: 6, - pricing: { inputPerToken: 0.000015, outputPerToken: 0.000075 }, + useCases: ['architecture proposals', 'security audits', 'complex design'], + cost: 10, + speed: 3, }, { - id: 'claude-opus-4.5', + id: 'claude-opus-4.6', tier: 'premium', provider: 'anthropic', family: 'claude', + githubCategory: 'powerful', vision: true, - useCases: ['architecture proposals', 'reviewer gates'], - cost: 9, + useCases: ['architecture proposals', 'security audits', 'complex design'], + cost: 10, speed: 3, pricing: { inputPerToken: 0.000015, outputPerToken: 0.000075 }, }, - - // Standard tier - balanced quality, speed, cost + + // Standard tier (quality) — versatile/powerful category (cost) { - id: 'claude-sonnet-4.6', + id: 'claude-sonnet-5', tier: 'standard', provider: 'anthropic', family: 'claude', + githubCategory: 'versatile', vision: true, - useCases: ['code generation', 'test writing', 'refactoring', 'prompt engineering'], + useCases: ['code generation', 'test writing', 'refactoring'], cost: 5, speed: 7, - pricing: { inputPerToken: 0.000003, outputPerToken: 0.000015 }, }, { - id: 'claude-sonnet-4.5', + id: 'claude-sonnet-4.6', tier: 'standard', provider: 'anthropic', family: 'claude', + githubCategory: 'versatile', vision: true, - useCases: ['code generation', 'test writing', 'refactoring'], + useCases: ['code generation', 'test writing', 'refactoring', 'prompt engineering'], cost: 5, speed: 7, pricing: { inputPerToken: 0.000003, outputPerToken: 0.000015 }, }, { - id: 'claude-sonnet-4', + id: 'claude-sonnet-4.5', tier: 'standard', provider: 'anthropic', family: 'claude', - useCases: ['code generation', 'documentation'], - cost: 4, + githubCategory: 'versatile', + vision: true, + useCases: ['code generation', 'test writing', 'refactoring'], + cost: 5, speed: 7, pricing: { inputPerToken: 0.000003, outputPerToken: 0.000015 }, }, { - id: 'gpt-5.4', + id: 'gpt-5.5', tier: 'standard', provider: 'openai', family: 'gpt', + githubCategory: 'powerful', useCases: ['general purpose', 'code generation', 'analysis'], cost: 6, speed: 7, - pricing: { inputPerToken: 0.000005, outputPerToken: 0.000015 }, - }, - { - id: 'gpt-5.3-codex', - tier: 'standard', - provider: 'openai', - family: 'gpt', - useCases: ['heavy code generation', 'multi-file refactors'], - cost: 5, - speed: 6, - pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, - }, - { - id: 'gpt-5.2-codex', - tier: 'standard', - provider: 'openai', - family: 'gpt', - useCases: ['heavy code generation', 'multi-file refactors'], - cost: 5, - speed: 6, - pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, - }, - { - id: 'gpt-5.2', - tier: 'standard', - provider: 'openai', - family: 'gpt', - useCases: ['general coding', 'analysis'], - cost: 5, - speed: 6, - pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, }, { - id: 'gpt-5.1-codex-max', + id: 'gpt-5.4', tier: 'standard', provider: 'openai', family: 'gpt', - useCases: ['complex implementation', 'large codebases'], + githubCategory: 'powerful', + useCases: ['general purpose', 'code generation', 'analysis'], cost: 6, - speed: 5, - pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, - }, - { - id: 'gpt-5.1-codex', - tier: 'standard', - provider: 'openai', - family: 'gpt', - useCases: ['code generation', 'implementation'], - cost: 5, - speed: 6, - pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, - }, - { - id: 'gpt-5.1', - tier: 'standard', - provider: 'openai', - family: 'gpt', - useCases: ['general purpose', 'analysis'], - cost: 5, - speed: 6, - pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, + speed: 7, + pricing: { inputPerToken: 0.000005, outputPerToken: 0.000015 }, }, { - id: 'gpt-5', + id: 'gpt-5.3-codex', tier: 'standard', provider: 'openai', family: 'gpt', - useCases: ['general purpose'], + githubCategory: 'powerful', + useCases: ['heavy code generation', 'multi-file refactors'], cost: 5, speed: 6, pricing: { inputPerToken: 0.0000025, outputPerToken: 0.00001 }, }, { - id: 'gemini-3-pro-preview', + id: 'gemini-2.5-pro', tier: 'standard', provider: 'google', family: 'gemini', + githubCategory: 'powerful', + vision: true, useCases: ['code reviews', 'second opinion', 'diversity'], cost: 5, speed: 7, - pricing: { inputPerToken: 0.00000125, outputPerToken: 0.00001 }, }, - - // Fast tier - lowest cost, fastest, good enough quality + + // Fast tier (quality) — lightweight category (cost) { id: 'claude-haiku-4.5', tier: 'fast', provider: 'anthropic', family: 'claude', + githubCategory: 'lightweight', useCases: ['boilerplate', 'changelogs', 'simple fixes'], cost: 2, speed: 9, pricing: { inputPerToken: 0.0000008, outputPerToken: 0.000004 }, }, { - id: 'gpt-5.1-codex-mini', + id: 'gpt-5.4-mini', tier: 'fast', provider: 'openai', family: 'gpt', - useCases: ['scaffolding', 'test boilerplate'], - cost: 2, - speed: 9, - pricing: { inputPerToken: 0.0000003, outputPerToken: 0.0000012 }, + githubCategory: 'lightweight', + useCases: ['scaffolding', 'test boilerplate', 'simple tasks'], + cost: 1, + speed: 10, }, { id: 'gpt-5-mini', tier: 'fast', provider: 'openai', family: 'gpt', + githubCategory: 'lightweight', useCases: ['typo fixes', 'renames', 'simple tasks'], cost: 1, speed: 10, pricing: { inputPerToken: 0.00000015, outputPerToken: 0.0000006 }, }, - { - id: 'gpt-4.1', - tier: 'fast', - provider: 'openai', - family: 'gpt', - useCases: ['lightweight tasks', 'triage'], - cost: 2, - speed: 9, - pricing: { inputPerToken: 0.0000002, outputPerToken: 0.0000008 }, - } ]; /** - * Default fallback chains per tier from squad.agent.md. + * Default fallback chains per tier — real, CLI-reachable IDs ordered by preference. */ export const DEFAULT_FALLBACK_CHAINS: Record = { - premium: ['claude-opus-4.6', 'claude-opus-4.6-fast', 'claude-opus-4.5', 'claude-sonnet-4.6'], - standard: ['claude-sonnet-4.6', 'gpt-5.4', 'claude-sonnet-4.5', 'gpt-5.3-codex', 'claude-sonnet-4', 'gpt-5.2'], - fast: ['claude-haiku-4.5', 'gpt-5.1-codex-mini', 'gpt-4.1', 'gpt-5-mini'] + premium: ['claude-opus-4.8', 'claude-opus-4.7', 'claude-opus-4.6', 'claude-sonnet-4.6'], + // claude-sonnet-4.6 leads as the established default (MODELS.DEFAULT); claude-sonnet-5 was + // newly added in this catalog refresh and is promoted to first fallback. + standard: ['claude-sonnet-4.6', 'claude-sonnet-5', 'gpt-5.4', 'gpt-5.3-codex', 'claude-sonnet-4.5', 'gemini-2.5-pro'], + fast: ['claude-haiku-4.5', 'gpt-5.4-mini', 'gpt-5-mini'], }; /** @@ -524,14 +507,15 @@ export function estimateCost(model: string, inputTokens: number, outputTokens: n */ export const ECONOMY_MODEL_MAP: Record = { // Premium → standard downgrade (architecture/review tasks) + 'claude-opus-4.8': 'claude-sonnet-4.5', + 'claude-opus-4.7': 'claude-sonnet-4.5', 'claude-opus-4.6': 'claude-sonnet-4.5', - 'claude-opus-4.6-fast': 'claude-sonnet-4.5', - 'claude-opus-4.5': 'claude-sonnet-4.5', // Standard → fast downgrade (code writing, docs, planning, triage) - 'claude-sonnet-4.6': 'gpt-4.1', - 'claude-sonnet-4.5': 'gpt-4.1', + 'claude-sonnet-5': 'gpt-5-mini', + 'claude-sonnet-4.6': 'gpt-5-mini', + 'claude-sonnet-4.5': 'gpt-5-mini', // Fast → cheapest fast (scribe/mechanical, docs) - 'claude-haiku-4.5': 'gpt-4.1', + 'claude-haiku-4.5': 'gpt-5-mini', }; /** diff --git a/packages/squad-sdk/src/config/schema.ts b/packages/squad-sdk/src/config/schema.ts index 24c47dab6..e18a7cbab 100644 --- a/packages/squad-sdk/src/config/schema.ts +++ b/packages/squad-sdk/src/config/schema.ts @@ -90,12 +90,12 @@ export const DEFAULT_CONFIG: SquadConfig = { fallbackBehavior: 'coordinator', }, models: { - default: 'claude-sonnet-4', + default: 'claude-sonnet-4.6', defaultTier: 'standard', tiers: { - premium: ['claude-opus-4', 'claude-opus-4.5'], - standard: ['claude-sonnet-4', 'claude-sonnet-4.5', 'gpt-5.1-codex'], - fast: ['claude-haiku-4.5', 'gpt-5.1-codex-mini'], + premium: ['claude-opus-4.8', 'claude-opus-4.7', 'claude-opus-4.6'], + standard: ['claude-sonnet-4.6', 'claude-sonnet-5', 'gpt-5.4', 'gpt-5.3-codex'], + fast: ['claude-haiku-4.5', 'gpt-5.4-mini', 'gpt-5-mini'], }, }, agents: [], diff --git a/packages/squad-sdk/src/runtime/config.ts b/packages/squad-sdk/src/runtime/config.ts index 768c193c0..4e486bf75 100644 --- a/packages/squad-sdk/src/runtime/config.ts +++ b/packages/squad-sdk/src/runtime/config.ts @@ -12,6 +12,7 @@ import { pathToFileURL } from 'url'; import { FSStorageProvider } from '../storage/fs-storage-provider.js'; import { MODELS } from './constants.js'; import type { AgentRole } from './constants.js'; +import { MODEL_CATALOG } from '../config/models.js'; const storage = new FSStorageProvider(); @@ -600,6 +601,8 @@ export function validateConfigDetailed(config: unknown): ValidationResult { if (!models.defaultModel || typeof models.defaultModel !== 'string') { errors.push('config.models.defaultModel is required and must be a string'); + } else if (!MODEL_CATALOG.some(m => m.id === models.defaultModel)) { + warnings.push(`config.models.defaultModel "${models.defaultModel}" is not in the current model catalog; it will fall back to a default at runtime.`); } if (!models.defaultTier || !['premium', 'standard', 'fast'].includes(models.defaultTier)) { diff --git a/packages/squad-sdk/src/runtime/constants.ts b/packages/squad-sdk/src/runtime/constants.ts index 03e5eac01..45b68660b 100644 --- a/packages/squad-sdk/src/runtime/constants.ts +++ b/packages/squad-sdk/src/runtime/constants.ts @@ -22,23 +22,24 @@ export const MODELS = { /** Fallback chains by tier — ordered by preference */ FALLBACK_CHAINS: { premium: [ + 'claude-opus-4.8', + 'claude-opus-4.7', 'claude-opus-4.6', - 'claude-opus-4.6-fast', - 'claude-opus-4.5', 'claude-sonnet-4.6', ], - standard: [ + // claude-sonnet-4.6 leads as the established default (MODELS.DEFAULT); claude-sonnet-5 was + // newly added in this catalog refresh and is promoted to first fallback. + standard: [ 'claude-sonnet-4.6', + 'claude-sonnet-5', 'gpt-5.4', - 'claude-sonnet-4.5', 'gpt-5.3-codex', - 'claude-sonnet-4', - 'gpt-5.2', + 'claude-sonnet-4.5', + 'gemini-2.5-pro', ], fast: [ 'claude-haiku-4.5', - 'gpt-5.1-codex-mini', - 'gpt-4.1', + 'gpt-5.4-mini', 'gpt-5-mini', ], }, diff --git a/packages/squad-sdk/templates/model-selection-reference.md b/packages/squad-sdk/templates/model-selection-reference.md index 2421f537c..e19998b9a 100644 --- a/packages/squad-sdk/templates/model-selection-reference.md +++ b/packages/squad-sdk/templates/model-selection-reference.md @@ -21,7 +21,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Writing code (implementation, refactoring, test code, bug fixes) | `claude-sonnet-4.6` | Standard | Quality and accuracy matter for code. Use standard tier. | | Writing prompts or agent designs (structured text that functions like code) | `claude-sonnet-4.6` | Standard | Prompts are executable — treat like code. | | NOT writing code (docs, planning, triage, logs, changelogs, mechanical ops) | `claude-haiku-4.5` | Fast | Cost first. Haiku handles non-code tasks. | -| Visual/design work requiring image analysis | `claude-opus-4.5` | Premium | Vision capability required. Overrides cost rule. | +| Visual/design work requiring image analysis | `claude-opus-4.6` | Premium | Vision capability required. Overrides cost rule. | **Role-to-model mapping** (applying cost-first principle): @@ -32,7 +32,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Lead / Architect | auto (per-task) | Mixed: code review needs quality, planning needs cost | Architecture proposals → premium; triage/planning → haiku | | Prompt Engineer | auto (per-task) | Mixed: prompt design is like code, research is not | Prompt architecture → sonnet; research/analysis → haiku | | Copilot SDK Expert | `claude-sonnet-4.6` | Technical analysis that often touches code | Pure research → `claude-haiku-4.5` | -| Designer / Visual | `claude-opus-4.5` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | +| Designer / Visual | `claude-opus-4.6` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | | DevRel / Writer | `claude-haiku-4.5` | Docs and writing — not code | — | | Scribe / Logger | `claude-haiku-4.5` | Mechanical file ops — cheapest possible | — (never bump Scribe) | | Git / Release | `claude-haiku-4.5` | Mechanical ops — changelogs, tags, version bumps | — (never bump mechanical ops) | @@ -41,7 +41,7 @@ Before spawning an agent, determine which model to use. Check these layers in or - **Bump UP to premium:** architecture proposals, reviewer gates, security audits, multi-agent coordination (output feeds 3+ agents) - **Bump DOWN to fast/cheap:** typo fixes, renames, boilerplate, scaffolding, changelogs, version bumps - **Switch to code specialist (`gpt-5.3-codex`):** large multi-file refactors, complex implementation from spec, heavy code generation (500+ lines) -- **Switch to analytical diversity (`gemini-3-pro-preview`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection +- **Switch to analytical diversity (`gemini-2.5-pro`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection **Layer 4 — Default:** If nothing else matched, use `claude-haiku-4.5`. Cost wins when in doubt, unless code is being produced. @@ -50,9 +50,9 @@ Before spawning an agent, determine which model to use. Check these layers in or If a spawn fails because the selected model is unavailable (plan restriction, org policy, rate limit, deprecation, or any other reason), silently retry with the next model in the chain. Do NOT tell the user about fallback attempts. Maximum 3 retries before jumping to the nuclear fallback. ``` -Premium: claude-opus-4.6 → claude-opus-4.5 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) -Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → claude-sonnet-4 → (omit model param) -Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5.1-codex-mini → gpt-4.1 → (omit model param) +Premium: claude-opus-4.6 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) +Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` `(omit model param)` = call the `task` tool WITHOUT the `model` parameter. The platform uses its built-in default. This is the nuclear fallback — it always works. @@ -86,7 +86,7 @@ When spawning, include the model in your acknowledgment: ``` 🔧 Fenster (claude-sonnet-4.6) — refactoring auth module -🎨 Redfoot (claude-opus-4.5 · vision) — designing color system +🎨 Redfoot (claude-opus-4.6 · vision) — designing color system 📋 Scribe (claude-haiku-4.5 · fast) — logging session ⚡ Keaton (claude-opus-4.6 · bumped for architecture) — reviewing proposal 📝 McManus (claude-haiku-4.5 · fast) — updating docs @@ -96,6 +96,6 @@ Include tier annotation only when the model was bumped or a specialist was chose **Valid models (current platform catalog):** -Premium: `claude-opus-4.6`, `claude-opus-4.6-1m` (Internal only), `claude-opus-4.5` -Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-4`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.2`, `gpt-5.1-codex-max`, `gpt-5.1-codex`, `gpt-5.1`, `gemini-3-pro-preview` -Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5.1-codex-mini`, `gpt-5-mini`, `gpt-4.1` +Premium: `claude-opus-4.6`, `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-4.6-1m` (Internal only) +Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-5`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.5`, `gemini-2.5-pro` +Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5-mini` diff --git a/packages/squad-sdk/templates/ralph-circuit-breaker.md b/packages/squad-sdk/templates/ralph-circuit-breaker.md index 87be26015..9604733b2 100644 --- a/packages/squad-sdk/templates/ralph-circuit-breaker.md +++ b/packages/squad-sdk/templates/ralph-circuit-breaker.md @@ -16,7 +16,6 @@ Premium models burn quota fast: | `gpt-5.4` | 50x | Very high | | `gpt-5.4-mini` | **0x** | **Free — unlimited** | | `gpt-5-mini` | **0x** | **Free — unlimited** | -| `gpt-4.1` | **0x** | **Free — unlimited** | ## Circuit Breaker States @@ -43,7 +42,6 @@ Premium models burn quota fast: - Fall back through the free-tier model chain: 1. `gpt-5.4-mini` 2. `gpt-5-mini` - 3. `gpt-4.1` - Start cooldown timer (default: 10 minutes) - When cooldown expires → transition to HALF-OPEN @@ -58,7 +56,7 @@ Premium models burn quota fast: { "state": "closed", "preferredModel": "claude-sonnet-4.6", - "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"], + "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini"], "currentFallbackIndex": 0, "cooldownMinutes": 10, "openedAt": null, @@ -87,7 +85,7 @@ function Get-CircuitBreakerState { $default = @{ state = "closed" preferredModel = "claude-sonnet-4.6" - fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1") + fallbackChain = @("gpt-5.4-mini", "gpt-5-mini") currentFallbackIndex = 0 cooldownMinutes = 10 openedAt = $null @@ -294,7 +292,7 @@ Override defaults by editing `.squad/ralph-circuit-breaker.json`: | Field | Default | Description | |-------|---------|-------------| | `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed | -| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) | +| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini"]` | Ordered fallback models (all free-tier) | | `cooldownMinutes` | `10` | How long to wait before testing recovery | ## Metrics diff --git a/packages/squad-sdk/templates/skills/economy-mode/SKILL.md b/packages/squad-sdk/templates/skills/economy-mode/SKILL.md index 696e778c4..f8ed05a9b 100644 --- a/packages/squad-sdk/templates/skills/economy-mode/SKILL.md +++ b/packages/squad-sdk/templates/skills/economy-mode/SKILL.md @@ -40,13 +40,13 @@ When economy mode is **active**, Layer 3 auto-selection uses this table instead | Task Output | Normal Mode | Economy Mode | |-------------|-------------|--------------| -| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-4.1` or `gpt-5-mini` | -| Architecture, code review, security audits | `claude-opus-4.5` | `claude-sonnet-4.5` | -| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-4.1` | +| Writing code (implementation, refactoring, bug fixes) | `claude-sonnet-4.5` | `gpt-5-mini` | +| Writing prompts or agent designs | `claude-sonnet-4.5` | `gpt-5-mini` | +| Docs, planning, triage, changelogs, mechanical ops | `claude-haiku-4.5` | `gpt-5-mini` | +| Architecture, code review, security audits | `claude-opus-4.6` | `claude-sonnet-4.5` | +| Scribe / logger / mechanical file ops | `claude-haiku-4.5` | `gpt-5-mini` | -**Prefer `gpt-4.1` over `gpt-5-mini`** when the task involves structured output or agentic tool use. Prefer `gpt-5-mini` for pure text generation tasks where latency matters. +**Prefer `gpt-5-mini`** for all economy-mode tasks where cost is the priority. ## AGENT WORKFLOW diff --git a/packages/squad-sdk/templates/skills/model-selection/SKILL.md b/packages/squad-sdk/templates/skills/model-selection/SKILL.md index 9842616e5..4a9f0592a 100644 --- a/packages/squad-sdk/templates/skills/model-selection/SKILL.md +++ b/packages/squad-sdk/templates/skills/model-selection/SKILL.md @@ -117,9 +117,9 @@ After resolving the model and including it in the spawn template, this skill is If a model is unavailable (rate limit, plan restriction), retry within the same tier: ``` -Premium: claude-opus-4.6 → claude-opus-4.6-fast → claude-opus-4.5 → claude-sonnet-4.6 -Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4 -Fast: claude-haiku-4.5 → gpt-5.1-codex-mini → gpt-4.1 → gpt-5-mini +Premium: claude-opus-4.6 → claude-sonnet-4.6 → (omit model param) +Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` **Never fall UP in tier.** A fast task won't land on a premium model via fallback. diff --git a/templates/model-selection-reference.md b/templates/model-selection-reference.md index 2421f537c..e19998b9a 100644 --- a/templates/model-selection-reference.md +++ b/templates/model-selection-reference.md @@ -21,7 +21,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Writing code (implementation, refactoring, test code, bug fixes) | `claude-sonnet-4.6` | Standard | Quality and accuracy matter for code. Use standard tier. | | Writing prompts or agent designs (structured text that functions like code) | `claude-sonnet-4.6` | Standard | Prompts are executable — treat like code. | | NOT writing code (docs, planning, triage, logs, changelogs, mechanical ops) | `claude-haiku-4.5` | Fast | Cost first. Haiku handles non-code tasks. | -| Visual/design work requiring image analysis | `claude-opus-4.5` | Premium | Vision capability required. Overrides cost rule. | +| Visual/design work requiring image analysis | `claude-opus-4.6` | Premium | Vision capability required. Overrides cost rule. | **Role-to-model mapping** (applying cost-first principle): @@ -32,7 +32,7 @@ Before spawning an agent, determine which model to use. Check these layers in or | Lead / Architect | auto (per-task) | Mixed: code review needs quality, planning needs cost | Architecture proposals → premium; triage/planning → haiku | | Prompt Engineer | auto (per-task) | Mixed: prompt design is like code, research is not | Prompt architecture → sonnet; research/analysis → haiku | | Copilot SDK Expert | `claude-sonnet-4.6` | Technical analysis that often touches code | Pure research → `claude-haiku-4.5` | -| Designer / Visual | `claude-opus-4.5` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | +| Designer / Visual | `claude-opus-4.6` | Vision-capable model required | — (never downgrade — vision is non-negotiable) | | DevRel / Writer | `claude-haiku-4.5` | Docs and writing — not code | — | | Scribe / Logger | `claude-haiku-4.5` | Mechanical file ops — cheapest possible | — (never bump Scribe) | | Git / Release | `claude-haiku-4.5` | Mechanical ops — changelogs, tags, version bumps | — (never bump mechanical ops) | @@ -41,7 +41,7 @@ Before spawning an agent, determine which model to use. Check these layers in or - **Bump UP to premium:** architecture proposals, reviewer gates, security audits, multi-agent coordination (output feeds 3+ agents) - **Bump DOWN to fast/cheap:** typo fixes, renames, boilerplate, scaffolding, changelogs, version bumps - **Switch to code specialist (`gpt-5.3-codex`):** large multi-file refactors, complex implementation from spec, heavy code generation (500+ lines) -- **Switch to analytical diversity (`gemini-3-pro-preview`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection +- **Switch to analytical diversity (`gemini-2.5-pro`):** code reviews where a second perspective helps, security reviews, architecture reviews after a rejection **Layer 4 — Default:** If nothing else matched, use `claude-haiku-4.5`. Cost wins when in doubt, unless code is being produced. @@ -50,9 +50,9 @@ Before spawning an agent, determine which model to use. Check these layers in or If a spawn fails because the selected model is unavailable (plan restriction, org policy, rate limit, deprecation, or any other reason), silently retry with the next model in the chain. Do NOT tell the user about fallback attempts. Maximum 3 retries before jumping to the nuclear fallback. ``` -Premium: claude-opus-4.6 → claude-opus-4.5 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) -Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → claude-sonnet-4 → (omit model param) -Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5.1-codex-mini → gpt-4.1 → (omit model param) +Premium: claude-opus-4.6 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) +Standard: claude-sonnet-4.6 → claude-sonnet-4.5 → gpt-5.4 → gpt-5.3-codex → (omit model param) +Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-5-mini → (omit model param) ``` `(omit model param)` = call the `task` tool WITHOUT the `model` parameter. The platform uses its built-in default. This is the nuclear fallback — it always works. @@ -86,7 +86,7 @@ When spawning, include the model in your acknowledgment: ``` 🔧 Fenster (claude-sonnet-4.6) — refactoring auth module -🎨 Redfoot (claude-opus-4.5 · vision) — designing color system +🎨 Redfoot (claude-opus-4.6 · vision) — designing color system 📋 Scribe (claude-haiku-4.5 · fast) — logging session ⚡ Keaton (claude-opus-4.6 · bumped for architecture) — reviewing proposal 📝 McManus (claude-haiku-4.5 · fast) — updating docs @@ -96,6 +96,6 @@ Include tier annotation only when the model was bumped or a specialist was chose **Valid models (current platform catalog):** -Premium: `claude-opus-4.6`, `claude-opus-4.6-1m` (Internal only), `claude-opus-4.5` -Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-4`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.2`, `gpt-5.1-codex-max`, `gpt-5.1-codex`, `gpt-5.1`, `gemini-3-pro-preview` -Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5.1-codex-mini`, `gpt-5-mini`, `gpt-4.1` +Premium: `claude-opus-4.6`, `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-4.6-1m` (Internal only) +Standard: `claude-sonnet-4.6`, `claude-sonnet-4.5`, `claude-sonnet-5`, `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.5`, `gemini-2.5-pro` +Fast/Cheap: `claude-haiku-4.5`, `gpt-5.4-mini`, `gpt-5-mini` diff --git a/templates/ralph-circuit-breaker.md b/templates/ralph-circuit-breaker.md index 87be26015..9604733b2 100644 --- a/templates/ralph-circuit-breaker.md +++ b/templates/ralph-circuit-breaker.md @@ -16,7 +16,6 @@ Premium models burn quota fast: | `gpt-5.4` | 50x | Very high | | `gpt-5.4-mini` | **0x** | **Free — unlimited** | | `gpt-5-mini` | **0x** | **Free — unlimited** | -| `gpt-4.1` | **0x** | **Free — unlimited** | ## Circuit Breaker States @@ -43,7 +42,6 @@ Premium models burn quota fast: - Fall back through the free-tier model chain: 1. `gpt-5.4-mini` 2. `gpt-5-mini` - 3. `gpt-4.1` - Start cooldown timer (default: 10 minutes) - When cooldown expires → transition to HALF-OPEN @@ -58,7 +56,7 @@ Premium models burn quota fast: { "state": "closed", "preferredModel": "claude-sonnet-4.6", - "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"], + "fallbackChain": ["gpt-5.4-mini", "gpt-5-mini"], "currentFallbackIndex": 0, "cooldownMinutes": 10, "openedAt": null, @@ -87,7 +85,7 @@ function Get-CircuitBreakerState { $default = @{ state = "closed" preferredModel = "claude-sonnet-4.6" - fallbackChain = @("gpt-5.4-mini", "gpt-5-mini", "gpt-4.1") + fallbackChain = @("gpt-5.4-mini", "gpt-5-mini") currentFallbackIndex = 0 cooldownMinutes = 10 openedAt = $null @@ -294,7 +292,7 @@ Override defaults by editing `.squad/ralph-circuit-breaker.json`: | Field | Default | Description | |-------|---------|-------------| | `preferredModel` | `claude-sonnet-4.6` | Model to use when circuit is closed | -| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini", "gpt-4.1"]` | Ordered fallback models (all free-tier) | +| `fallbackChain` | `["gpt-5.4-mini", "gpt-5-mini"]` | Ordered fallback models (all free-tier) | | `cooldownMinutes` | `10` | How long to wait before testing recovery | ## Metrics diff --git a/test/agents.test.ts b/test/agents.test.ts index a3f8d1a54..f25bbf69d 100644 --- a/test/agents.test.ts +++ b/test/agents.test.ts @@ -167,7 +167,7 @@ describe('Per-Agent Model Selection (M1-9)', () => { expect(result.model).toBe('claude-opus-4.6'); expect(result.tier).toBe('premium'); - expect(result.fallbackChain).toContain('claude-opus-4.6-fast'); + expect(result.fallbackChain).toContain('claude-opus-4.7'); }); it('should infer fast tier for haiku models', () => { @@ -180,7 +180,7 @@ describe('Per-Agent Model Selection (M1-9)', () => { expect(result.model).toBe('claude-haiku-4.5'); expect(result.tier).toBe('fast'); - expect(result.fallbackChain).toContain('gpt-5.1-codex-mini'); + expect(result.fallbackChain).toContain('gpt-5.4-mini'); }); }); @@ -325,9 +325,9 @@ describe('Per-Agent Model Selection (M1-9)', () => { const result = resolveModel(options); expect(result.fallbackChain).toEqual([ + 'claude-opus-4.8', + 'claude-opus-4.7', 'claude-opus-4.6', - 'claude-opus-4.6-fast', - 'claude-opus-4.5', 'claude-sonnet-4.6', ]); }); @@ -341,11 +341,11 @@ describe('Per-Agent Model Selection (M1-9)', () => { expect(result.fallbackChain).toEqual([ 'claude-sonnet-4.6', + 'claude-sonnet-5', 'gpt-5.4', - 'claude-sonnet-4.5', 'gpt-5.3-codex', - 'claude-sonnet-4', - 'gpt-5.2', + 'claude-sonnet-4.5', + 'gemini-2.5-pro', ]); }); @@ -358,8 +358,7 @@ describe('Per-Agent Model Selection (M1-9)', () => { expect(result.fallbackChain).toEqual([ 'claude-haiku-4.5', - 'gpt-5.1-codex-mini', - 'gpt-4.1', + 'gpt-5.4-mini', 'gpt-5-mini', ]); }); diff --git a/test/catalog-refresh.test.ts b/test/catalog-refresh.test.ts new file mode 100644 index 000000000..f8e45081d --- /dev/null +++ b/test/catalog-refresh.test.ts @@ -0,0 +1,158 @@ +/** + * Catalog refresh invariants (issue #1080 / #1183). + * + * Guards against stale/dead model IDs leaking into the fallback chains and + * ensures every ID referenced by a fallback chain actually exists in the + * MODEL_CATALOG. Covers BOTH chain sources: the SDK config chains + * (DEFAULT_FALLBACK_CHAINS in config/models.ts) and the runtime chains + * (MODELS.FALLBACK_CHAINS in runtime/constants.ts). + * + * @module test/catalog-refresh + */ + +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { globSync } from 'node:fs'; +import { MODEL_CATALOG, DEFAULT_FALLBACK_CHAINS } from '@bradygaster/squad-sdk/config'; +import { MODELS } from '@bradygaster/squad-sdk/runtime/constants'; + +/** + * Model IDs verified as NOT picker-reachable via the copilot-cli models API + * (verified 2026-07-04). Guards against reintroduction into fallback chains + * AND shipped prompt/template assets. + * + * NOTE: `claude-opus-4.5` and `claude-opus-4.6-fast` are classified as + * "live-but-dropped-from-seed" for runtime chain purposes (they are still GA + * in GitHub's public catalog), so the runtime chain invariants below do NOT + * require them to be absent from chains. However, templates steering agent + * spawns toward them will behave unpredictably in practice, so they ARE + * included here for the template-asset scan that follows. + */ +const DEAD_MODEL_IDS = [ + 'gpt-4.1', + 'gpt-5', + 'gemini-3-pro-preview', + 'claude-sonnet-4', + 'gpt-5.1', + 'gpt-5.1-codex', + 'gpt-5.1-codex-max', + 'gpt-5.1-codex-mini', + 'gpt-5.2', + 'gpt-5.2-codex', + 'claude-opus-4.6-fast', + 'claude-opus-4.5', +]; + +/** + * The exact set of CLI-reachable seed model IDs (verified 2026-07-04). A + * positive invariant: any unexpected reintroduction — including live-but-dropped + * IDs like `claude-opus-4.5` — fails immediately, which a blocklist alone cannot + * guarantee. + */ +const EXPECTED_CATALOG_IDS = [ + 'claude-haiku-4.5', + 'claude-opus-4.6', + 'claude-opus-4.7', + 'claude-opus-4.8', + 'claude-sonnet-4.5', + 'claude-sonnet-4.6', + 'claude-sonnet-5', + 'gemini-2.5-pro', + 'gpt-5-mini', + 'gpt-5.3-codex', + 'gpt-5.4', + 'gpt-5.4-mini', + 'gpt-5.5', +]; + +const CONFIG_CHAINS = DEFAULT_FALLBACK_CHAINS as Record; +const RUNTIME_CHAINS = MODELS.FALLBACK_CHAINS as Record; + +describe('catalog refresh invariants (#1080/#1183)', () => { + const catalogIds = new Set(MODEL_CATALOG.map(m => m.id)); + + it('config fallback chains contain no dead/stale model IDs', () => { + for (const [tier, chain] of Object.entries(CONFIG_CHAINS)) { + for (const id of chain) { + expect(DEAD_MODEL_IDS, `config chain "${tier}" must not contain dead ID "${id}"`).not.toContain(id); + } + } + }); + + it('runtime fallback chains contain no dead/stale model IDs', () => { + for (const [tier, chain] of Object.entries(RUNTIME_CHAINS)) { + for (const id of chain) { + expect(DEAD_MODEL_IDS, `runtime chain "${tier}" must not contain dead ID "${id}"`).not.toContain(id); + } + } + }); + + it('every ID in every config fallback chain exists in MODEL_CATALOG', () => { + for (const [tier, chain] of Object.entries(CONFIG_CHAINS)) { + for (const id of chain) { + expect(catalogIds.has(id), `config chain "${tier}" references unknown model "${id}"`).toBe(true); + } + } + }); + + it('every ID in every runtime fallback chain exists in MODEL_CATALOG', () => { + for (const [tier, chain] of Object.entries(RUNTIME_CHAINS)) { + for (const id of chain) { + expect(catalogIds.has(id), `runtime chain "${tier}" references unknown model "${id}"`).toBe(true); + } + } + }); + + it('no MODEL_CATALOG entry is a known dead ID', () => { + for (const model of MODEL_CATALOG) { + expect(DEAD_MODEL_IDS, `catalog must not contain dead ID "${model.id}"`).not.toContain(model.id); + } + }); + + it('MODEL_CATALOG ID set exactly equals the expected curated seed set', () => { + const actual = [...catalogIds].sort(); + const expected = [...EXPECTED_CATALOG_IDS].sort(); + expect(actual).toEqual(expected); + }); +}); + +// --------------------------------------------------------------------------- +// Resolve repo root relative to this test file so globs work regardless of +// where vitest is invoked from. +// --------------------------------------------------------------------------- +const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); + +describe('template asset catalog invariants (#1080/#1183)', () => { + // Scan canonical sources only — not sync targets — to avoid triple-counting + // the same bug across squad-cli/templates, squad-sdk/templates, .squad-templates. + const TEMPLATE_GLOBS = [ + '.squad-templates/**/*.md', + '.squad/skills/**/*.md', + '.copilot/skills/**/*.md', + ]; + + const templateFiles = TEMPLATE_GLOBS.flatMap(g => + globSync(g, { cwd: REPO_ROOT }) + ); + + it('no shipped template asset references a dead model ID', () => { + const violations: string[] = []; + for (const relPath of templateFiles) { + const content = readFileSync(join(REPO_ROOT, relPath), 'utf-8'); + for (const deadId of DEAD_MODEL_IDS) { + // Escape regex metacharacters in the ID (e.g. the dots in "gpt-4.1"), + // then use a negative lookahead (?![-.\d]) so that "gpt-5" does not + // match inside "gpt-5-mini" or "gpt-5.4", and "claude-sonnet-4" does + // not match inside "claude-sonnet-4.5". + const escaped = deadId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const pattern = new RegExp(escaped + '(?![-\\.\\d])', 'g'); + if (pattern.test(content)) { + violations.push(`${relPath}: contains dead ID "${deadId}"`); + } + } + } + expect(violations, `Dead model IDs found in shipped template assets`).toEqual([]); + }); +}); diff --git a/test/compat-v041.test.ts b/test/compat-v041.test.ts index ddeaeddbf..935a2d436 100644 --- a/test/compat-v041.test.ts +++ b/test/compat-v041.test.ts @@ -467,23 +467,23 @@ describe('Compat v0.4.1: Event Bus Shape', () => { describe('Compat v0.4.1: Model Catalog', () => { it('catalog contains expected premium models', () => { expect(isModelAvailable('claude-opus-4.6')).toBe(true); - expect(isModelAvailable('claude-opus-4.5')).toBe(true); + expect(isModelAvailable('claude-opus-4.8')).toBe(true); }); it('catalog contains expected standard models', () => { expect(isModelAvailable('claude-sonnet-4.5')).toBe(true); - expect(isModelAvailable('claude-sonnet-4')).toBe(true); - expect(isModelAvailable('gpt-5.2-codex')).toBe(true); + expect(isModelAvailable('claude-sonnet-4.6')).toBe(true); + expect(isModelAvailable('gpt-5.4')).toBe(true); }); it('catalog contains expected fast models', () => { expect(isModelAvailable('claude-haiku-4.5')).toBe(true); - expect(isModelAvailable('gpt-5.1-codex-mini')).toBe(true); - expect(isModelAvailable('gpt-4.1')).toBe(true); + expect(isModelAvailable('gpt-5.4-mini')).toBe(true); + expect(isModelAvailable('gpt-5-mini')).toBe(true); }); it('premium fallback chain starts with opus', () => { - expect(DEFAULT_FALLBACK_CHAINS.premium[0]).toBe('claude-opus-4.6'); + expect(DEFAULT_FALLBACK_CHAINS.premium[0]).toBe('claude-opus-4.8'); }); it('standard fallback chain starts with sonnet', () => { @@ -632,3 +632,25 @@ describe('Compat v0.4.1: Version Comparison', () => { expect(() => parseSemVer('not-a-version')).toThrow(); }); }); + +// ============================================================================ +// 12. Removed model IDs degrade safely through fallback chains +// ============================================================================ + +describe('Compat v0.4.1: Removed Model Fallback Degradation', () => { + it('getNextFallback for removed "gpt-4.1" resolves to a live catalog model without throwing', () => { + const registry = new ModelRegistry(); + let result: string | null; + expect(() => { result = registry.getNextFallback('gpt-4.1', 'standard'); }).not.toThrow(); + expect(result!).not.toBeNull(); + expect(isModelAvailable(result!)).toBe(true); + }); + + it('getNextFallback for removed "claude-sonnet-4" resolves to a live catalog model without throwing', () => { + const registry = new ModelRegistry(); + let result: string | null; + expect(() => { result = registry.getNextFallback('claude-sonnet-4', 'standard'); }).not.toThrow(); + expect(result!).not.toBeNull(); + expect(isModelAvailable(result!)).toBe(true); + }); +}); diff --git a/test/config-integration.test.ts b/test/config-integration.test.ts index 8d1496330..9ddf7c035 100644 --- a/test/config-integration.test.ts +++ b/test/config-integration.test.ts @@ -695,6 +695,19 @@ describe('Integration: Config validation error paths', () => { expect(result.warnings?.some(w => w.includes('Duplicate'))).toBe(true); }); + it('validateConfigDetailed warns when defaultModel is not in MODEL_CATALOG', () => { + const result = validateConfigDetailed({ + ...RUNTIME_DEFAULT, + models: { + ...RUNTIME_DEFAULT.models, + defaultModel: 'gpt-4.1', + }, + }); + expect(result.valid).toBe(true); + expect(result.errors.length).toBe(0); + expect(result.warnings?.some(w => w.includes('gpt-4.1') && w.includes('not in the current model catalog'))).toBe(true); + }); + it('schema validateConfig rejects non-object', () => { expect(validateSchemaConfig(null)).toBe(false); expect(validateSchemaConfig(42)).toBe(false); @@ -906,7 +919,7 @@ describe('Integration: Full pipeline — discover → compile → resolve → fi expect(config.team.name).toBe('Integration Squad'); expect(config.agents.length).toBe(1); - expect(config.models.default).toBe('claude-sonnet-4'); + expect(config.models.default).toBe('claude-sonnet-4.6'); }); }); diff --git a/test/config-schema.test.ts b/test/config-schema.test.ts index 8d27101b7..61eb5b885 100644 --- a/test/config-schema.test.ts +++ b/test/config-schema.test.ts @@ -20,13 +20,13 @@ describe('Configuration Schema', () => { expect(DEFAULT_CONFIG.version).toBe('0.6.0'); expect(DEFAULT_CONFIG.team.name).toBe('Default Squad'); expect(DEFAULT_CONFIG.routing.fallbackBehavior).toBe('coordinator'); - expect(DEFAULT_CONFIG.models.default).toBe('claude-sonnet-4'); + expect(DEFAULT_CONFIG.models.default).toBe('claude-sonnet-4.6'); expect(DEFAULT_CONFIG.agents).toEqual([]); }); it('should have tier definitions', () => { - expect(DEFAULT_CONFIG.models.tiers.premium).toContain('claude-opus-4'); - expect(DEFAULT_CONFIG.models.tiers.standard).toContain('claude-sonnet-4'); + expect(DEFAULT_CONFIG.models.tiers.premium).toContain('claude-opus-4.8'); + expect(DEFAULT_CONFIG.models.tiers.standard).toContain('claude-sonnet-4.6'); expect(DEFAULT_CONFIG.models.tiers.fast).toContain('claude-haiku-4.5'); }); }); diff --git a/test/economy-mode.test.ts b/test/economy-mode.test.ts index 9835b4816..0e724cac1 100644 --- a/test/economy-mode.test.ts +++ b/test/economy-mode.test.ts @@ -39,25 +39,25 @@ afterEach(() => { describe('ECONOMY_MODEL_MAP', () => { it('maps premium models to standard', () => { expect(ECONOMY_MODEL_MAP['claude-opus-4.6']).toBe('claude-sonnet-4.5'); - expect(ECONOMY_MODEL_MAP['claude-opus-4.6-fast']).toBe('claude-sonnet-4.5'); - expect(ECONOMY_MODEL_MAP['claude-opus-4.5']).toBe('claude-sonnet-4.5'); + expect(ECONOMY_MODEL_MAP['claude-opus-4.7']).toBe('claude-sonnet-4.5'); + expect(ECONOMY_MODEL_MAP['claude-opus-4.8']).toBe('claude-sonnet-4.5'); }); it('maps standard sonnet models to fast', () => { - expect(ECONOMY_MODEL_MAP['claude-sonnet-4.6']).toBe('gpt-4.1'); - expect(ECONOMY_MODEL_MAP['claude-sonnet-4.5']).toBe('gpt-4.1'); + expect(ECONOMY_MODEL_MAP['claude-sonnet-4.6']).toBe('gpt-5-mini'); + expect(ECONOMY_MODEL_MAP['claude-sonnet-4.5']).toBe('gpt-5-mini'); }); it('maps haiku to cheapest fast', () => { - expect(ECONOMY_MODEL_MAP['claude-haiku-4.5']).toBe('gpt-4.1'); + expect(ECONOMY_MODEL_MAP['claude-haiku-4.5']).toBe('gpt-5-mini'); }); }); describe('applyEconomyMode', () => { it('returns economy model for known models', () => { expect(applyEconomyMode('claude-opus-4.6')).toBe('claude-sonnet-4.5'); - expect(applyEconomyMode('claude-sonnet-4.6')).toBe('gpt-4.1'); - expect(applyEconomyMode('claude-haiku-4.5')).toBe('gpt-4.1'); + expect(applyEconomyMode('claude-sonnet-4.6')).toBe('gpt-5-mini'); + expect(applyEconomyMode('claude-haiku-4.5')).toBe('gpt-5-mini'); }); it('returns original model when no economy mapping exists', () => { @@ -149,24 +149,24 @@ describe('writeEconomyMode', () => { // ============================================================================ describe('resolveModel economy mode (option)', () => { - it('Layer 4 default: uses gpt-4.1 instead of haiku when economyMode: true', () => { - expect(resolveModel({ economyMode: true })).toBe('gpt-4.1'); + it('Layer 4 default: uses gpt-5-mini instead of haiku when economyMode: true', () => { + expect(resolveModel({ economyMode: true })).toBe('gpt-5-mini'); }); it('Layer 4 default: uses haiku when economyMode: false', () => { expect(resolveModel({ economyMode: false })).toBe('claude-haiku-4.5'); }); - it('Layer 3 code task: uses gpt-4.1 instead of sonnet when economyMode: true', () => { - expect(resolveModel({ taskModel: 'claude-sonnet-4.6', economyMode: true })).toBe('gpt-4.1'); + it('Layer 3 code task: uses gpt-5-mini instead of sonnet when economyMode: true', () => { + expect(resolveModel({ taskModel: 'claude-sonnet-4.6', economyMode: true })).toBe('gpt-5-mini'); }); it('Layer 3 architecture task: uses sonnet instead of opus when economyMode: true', () => { expect(resolveModel({ taskModel: 'claude-opus-4.6', economyMode: true })).toBe('claude-sonnet-4.5'); }); - it('Layer 3 docs task: uses gpt-4.1 instead of haiku when economyMode: true', () => { - expect(resolveModel({ taskModel: 'claude-haiku-4.5', economyMode: true })).toBe('gpt-4.1'); + it('Layer 3 docs task: uses gpt-5-mini instead of haiku when economyMode: true', () => { + expect(resolveModel({ taskModel: 'claude-haiku-4.5', economyMode: true })).toBe('gpt-5-mini'); }); it('Layer 2 charter preference: NOT overridden by economy mode', () => { @@ -215,7 +215,7 @@ describe('resolveModel economy mode (from config)', () => { join(squadDir, 'config.json'), JSON.stringify({ version: 1, economyMode: true }) ); - expect(resolveModel({ squadDir, taskModel: 'claude-sonnet-4.6' })).toBe('gpt-4.1'); + expect(resolveModel({ squadDir, taskModel: 'claude-sonnet-4.6' })).toBe('gpt-5-mini'); }); it('uses normal model when economyMode absent from config', () => { @@ -234,7 +234,7 @@ describe('resolveModel economy mode (from config)', () => { // Option says true, config says false → option wins expect( resolveModel({ squadDir, taskModel: 'claude-sonnet-4.6', economyMode: true }) - ).toBe('gpt-4.1'); + ).toBe('gpt-5-mini'); }); }); @@ -243,20 +243,20 @@ describe('resolveModel economy mode (from config)', () => { // ============================================================================ describe('SDK resolveModel (agents) economy mode', () => { - it('code task → gpt-4.1 when economyMode: true', () => { + it('code task → gpt-5-mini when economyMode: true', () => { const result = sdkResolveModel({ taskType: 'code', economyMode: true }); - expect(result.model).toBe('gpt-4.1'); + expect(result.model).toBe('gpt-5-mini'); expect(result.source).toBe('task-auto'); }); - it('docs task → gpt-4.1 when economyMode: true', () => { + it('docs task → gpt-5-mini when economyMode: true', () => { const result = sdkResolveModel({ taskType: 'docs', economyMode: true }); - expect(result.model).toBe('gpt-4.1'); + expect(result.model).toBe('gpt-5-mini'); }); - it('mechanical task → gpt-4.1 when economyMode: true', () => { + it('mechanical task → gpt-5-mini when economyMode: true', () => { const result = sdkResolveModel({ taskType: 'mechanical', economyMode: true }); - expect(result.model).toBe('gpt-4.1'); + expect(result.model).toBe('gpt-5-mini'); }); it('visual task → claude-sonnet-4.5 when economyMode: true', () => { diff --git a/test/integration.test.ts b/test/integration.test.ts index 4e7e99939..578f10242 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -446,7 +446,7 @@ describe('Integration: Charter → Model → Session Pipeline', () => { expect(premiumResult.tier).toBe('premium'); expect(premiumResult.fallbackChain.length).toBeGreaterThan(1); - expect(premiumResult.fallbackChain[0]).toBe('claude-opus-4.6'); + expect(premiumResult.fallbackChain[0]).toBe('claude-opus-4.8'); }); }); }); diff --git a/test/model-fallback.test.ts b/test/model-fallback.test.ts index c09b5b32d..64d959ef2 100644 --- a/test/model-fallback.test.ts +++ b/test/model-fallback.test.ts @@ -47,7 +47,7 @@ describe('Cross-tier fallback — standard chain exhaustion', () => { it('premium chain starts with opus and walks through all premium options', () => { const chain = DEFAULT_FALLBACK_CHAINS.premium; - expect(chain[0]).toBe('claude-opus-4.6'); + expect(chain[0]).toBe('claude-opus-4.8'); const attempted = new Set(); let count = 0; @@ -176,7 +176,7 @@ describe('Provider preference — Claude family preference', () => { }); it('prefer GPT: getFallbackChain reorders for OpenAI models', () => { - const chain = registry.getFallbackChain('standard', true, 'gpt-5.2-codex'); + const chain = registry.getFallbackChain('standard', true, 'gpt-5.4'); // First models should be OpenAI const firstInfo = registry.getModelInfo(chain[0]!); @@ -259,9 +259,9 @@ describe('Model fallback — edge cases', () => { const registry = new ModelRegistry(); it('getNextFallback with empty attempted set returns second in chain', () => { - const next = registry.getNextFallback('claude-opus-4.6', 'premium'); + const next = registry.getNextFallback('claude-opus-4.8', 'premium'); // With no attempted set, it should return the next in chain after current - expect(next).toBe('claude-opus-4.6-fast'); + expect(next).toBe('claude-opus-4.7'); }); it('getNextFallback for unknown model returns null', () => { diff --git a/test/models.test.ts b/test/models.test.ts index 51daee01e..fb17913a0 100644 --- a/test/models.test.ts +++ b/test/models.test.ts @@ -27,30 +27,30 @@ describe('MODEL_CATALOG', () => { const modelIds = MODEL_CATALOG.map(m => m.id); expect(modelIds).toContain('claude-opus-4.6'); - expect(modelIds).toContain('claude-opus-4.6-fast'); - expect(modelIds).toContain('claude-opus-4.5'); + expect(modelIds).toContain('claude-opus-4.8'); + expect(modelIds).toContain('claude-opus-4.7'); }); it('includes expected standard models', () => { const modelIds = MODEL_CATALOG.map(m => m.id); expect(modelIds).toContain('claude-sonnet-4.5'); - expect(modelIds).toContain('gpt-5.2-codex'); - expect(modelIds).toContain('gemini-3-pro-preview'); + expect(modelIds).toContain('gpt-5.4'); + expect(modelIds).toContain('gemini-2.5-pro'); }); it('includes expected fast models', () => { const modelIds = MODEL_CATALOG.map(m => m.id); expect(modelIds).toContain('claude-haiku-4.5'); - expect(modelIds).toContain('gpt-5.1-codex-mini'); + expect(modelIds).toContain('gpt-5.4-mini'); expect(modelIds).toContain('gpt-5-mini'); }); it('assigns correct providers', () => { const claude = MODEL_CATALOG.find(m => m.id === 'claude-sonnet-4.5'); - const gpt = MODEL_CATALOG.find(m => m.id === 'gpt-5.2-codex'); - const gemini = MODEL_CATALOG.find(m => m.id === 'gemini-3-pro-preview'); + const gpt = MODEL_CATALOG.find(m => m.id === 'gpt-5.4'); + const gemini = MODEL_CATALOG.find(m => m.id === 'gemini-2.5-pro'); expect(claude?.provider).toBe('anthropic'); expect(gpt?.provider).toBe('openai'); @@ -72,7 +72,7 @@ describe('DEFAULT_FALLBACK_CHAINS', () => { }); it('starts premium chain with opus models', () => { - expect(DEFAULT_FALLBACK_CHAINS.premium[0]).toBe('claude-opus-4.6'); + expect(DEFAULT_FALLBACK_CHAINS.premium[0]).toBe('claude-opus-4.8'); }); it('starts standard chain with sonnet', () => { @@ -107,7 +107,7 @@ describe('ModelRegistry', () => { describe('isModelAvailable', () => { it('returns true for catalog models', () => { expect(registry.isModelAvailable('claude-opus-4.6')).toBe(true); - expect(registry.isModelAvailable('gpt-5.2-codex')).toBe(true); + expect(registry.isModelAvailable('gpt-5.4')).toBe(true); expect(registry.isModelAvailable('claude-haiku-4.5')).toBe(true); }); @@ -190,14 +190,14 @@ describe('ModelRegistry', () => { describe('getNextFallback', () => { it('returns next model in chain', () => { - const next = registry.getNextFallback('claude-opus-4.6', 'premium'); + const next = registry.getNextFallback('claude-opus-4.8', 'premium'); - expect(next).toBe('claude-opus-4.6-fast'); + expect(next).toBe('claude-opus-4.7'); }); it('skips already attempted models', () => { - const attempted = new Set(['claude-opus-4.6-fast', 'claude-opus-4.5']); - const next = registry.getNextFallback('claude-opus-4.6', 'premium', attempted); + const attempted = new Set(['claude-opus-4.7', 'claude-opus-4.6']); + const next = registry.getNextFallback('claude-opus-4.8', 'premium', attempted); expect(next).not.toBeNull(); expect(attempted.has(next!)).toBe(false); @@ -205,7 +205,7 @@ describe('ModelRegistry', () => { it('returns null when chain exhausted', () => { const allModels = new Set(DEFAULT_FALLBACK_CHAINS.premium); - const next = registry.getNextFallback('claude-opus-4.6', 'premium', allModels); + const next = registry.getNextFallback('claude-opus-4.8', 'premium', allModels); expect(next).toBeNull(); }); @@ -241,7 +241,7 @@ describe('ModelRegistry', () => { expect(ids.length).toBe(MODEL_CATALOG.length); expect(ids).toContain('claude-opus-4.6'); - expect(ids).toContain('gpt-5.2-codex'); + expect(ids).toContain('gpt-5.4'); expect(ids).toContain('claude-haiku-4.5'); }); }); diff --git a/test/telemetry-auto-wiring.test.ts b/test/telemetry-auto-wiring.test.ts index f18a61b16..5e1b61819 100644 --- a/test/telemetry-auto-wiring.test.ts +++ b/test/telemetry-auto-wiring.test.ts @@ -189,12 +189,15 @@ describe('estimateCost()', () => { // ============================================================================ describe('MODEL_CATALOG pricing', () => { - it('all models have pricing data', () => { + it('models expose valid pricing when present', () => { + // Pricing is optional: newer catalog entries intentionally omit per-token + // USD pricing rather than hardcoding guessed values (issue #1080). When a + // model does carry pricing, it must be well-formed. for (const model of MODEL_CATALOG) { - expect(model.pricing, `Model ${model.id} missing pricing`).toBeDefined(); + if (!model.pricing) continue; const pricing = model.pricing as ModelPricing; - expect(pricing.inputPerToken).toBeGreaterThan(0); - expect(pricing.outputPerToken).toBeGreaterThan(0); + expect(pricing.inputPerToken, `Model ${model.id} inputPerToken`).toBeGreaterThan(0); + expect(pricing.outputPerToken, `Model ${model.id} outputPerToken`).toBeGreaterThan(0); } });