Merge pull request #1539 from code-yeongyu/feat/update-model-versions

chore: update model version references (gpt-5.2-codex → gpt-5.3-codex, claude-opus-4-5 → claude-opus-4-6)
This commit is contained in:
YeonGyu-Kim
2026-02-06 15:22:19 +09:00
committed by GitHub
39 changed files with 425 additions and 426 deletions

View File

@@ -204,14 +204,14 @@ oh-my-opencode/
| Agent | Model | Purpose |
|-------|-------|---------|
| Sisyphus | anthropic/claude-opus-4-5 | Primary orchestrator (fallback: kimi-k2.5 → glm-4.7 → gpt-5.2-codex → gemini-3-pro) |
| Hephaestus | openai/gpt-5.2-codex | Autonomous deep worker, "The Legitimate Craftsman" (requires gpt-5.2-codex, no fallback) |
| Sisyphus | anthropic/claude-opus-4-6 | Primary orchestrator (fallback: kimi-k2.5 → glm-4.7 → gpt-5.3-codex → gemini-3-pro) |
| Hephaestus | openai/gpt-5.3-codex | Autonomous deep worker, "The Legitimate Craftsman" (requires gpt-5.3-codex, no fallback) |
| Atlas | anthropic/claude-sonnet-4-5 | Master orchestrator (fallback: kimi-k2.5 → gpt-5.2) |
| oracle | openai/gpt-5.2 | Consultation, debugging |
| librarian | zai-coding-plan/glm-4.7 | Docs, GitHub search (fallback: glm-4.7-free) |
| explore | xai/grok-code-fast-1 | Fast codebase grep (fallback: claude-haiku-4-5 → gpt-5-mini → gpt-5-nano) |
| multimodal-looker | google/gemini-3-flash | PDF/image analysis |
| Prometheus | anthropic/claude-opus-4-5 | Strategic planning (fallback: kimi-k2.5 → gpt-5.2) |
| Prometheus | anthropic/claude-opus-4-6 | Strategic planning (fallback: kimi-k2.5 → gpt-5.2) |
## COMMANDS

View File

@@ -22,12 +22,12 @@ A Category is an agent configuration preset optimized for specific domains.
| Category | Default Model | Use Cases |
|----------|---------------|-----------|
| `visual-engineering` | `google/gemini-3-pro` | Frontend, UI/UX, design, styling, animation |
| `ultrabrain` | `openai/gpt-5.2-codex` (xhigh) | Deep logical reasoning, complex architecture decisions requiring extensive analysis |
| `deep` | `openai/gpt-5.2-codex` (medium) | Goal-oriented autonomous problem-solving. Thorough research before action. For hairy problems requiring deep understanding. |
| `ultrabrain` | `openai/gpt-5.3-codex` (xhigh) | Deep logical reasoning, complex architecture decisions requiring extensive analysis |
| `deep` | `openai/gpt-5.3-codex` (medium) | Goal-oriented autonomous problem-solving. Thorough research before action. For hairy problems requiring deep understanding. |
| `artistry` | `google/gemini-3-pro` (max) | Highly creative/artistic tasks, novel ideas |
| `quick` | `anthropic/claude-haiku-4-5` | Trivial tasks - single file changes, typo fixes, simple modifications |
| `unspecified-low` | `anthropic/claude-sonnet-4-5` | Tasks that don't fit other categories, low effort required |
| `unspecified-high` | `anthropic/claude-opus-4-5` (max) | Tasks that don't fit other categories, high effort required |
| `unspecified-high` | `anthropic/claude-opus-4-6` (max) | Tasks that don't fit other categories, high effort required |
| `writing` | `google/gemini-3-flash` | Documentation, prose, technical writing |
### Usage
@@ -159,7 +159,7 @@ You can fine-tune categories in `oh-my-opencode.json`.
| Field | Type | Description |
|-------|------|-------------|
| `description` | string | Human-readable description of the category's purpose. Shown in delegate_task prompt. |
| `model` | string | AI model ID to use (e.g., `anthropic/claude-opus-4-5`) |
| `model` | string | AI model ID to use (e.g., `anthropic/claude-opus-4-6`) |
| `variant` | string | Model variant (e.g., `max`, `xhigh`) |
| `temperature` | number | Creativity level (0.0 ~ 2.0). Lower is more deterministic. |
| `top_p` | number | Nucleus sampling parameter (0.0 ~ 1.0) |
@@ -191,7 +191,7 @@ You can fine-tune categories in `oh-my-opencode.json`.
// 3. Configure thinking model and restrict tools
"deep-reasoning": {
"model": "anthropic/claude-opus-4-5",
"model": "anthropic/claude-opus-4-6",
"thinking": {
"type": "enabled",
"budgetTokens": 32000

View File

@@ -693,7 +693,7 @@ Configure concurrency limits for background agent tasks. This controls how many
"google": 10
},
"modelConcurrency": {
"anthropic/claude-opus-4-5": 2,
"anthropic/claude-opus-4-6": 2,
"google/gemini-3-flash": 10
}
}
@@ -705,7 +705,7 @@ Configure concurrency limits for background agent tasks. This controls how many
| `defaultConcurrency` | - | Default maximum concurrent background tasks for all providers/models |
| `staleTimeoutMs` | `180000` | Stale timeout in milliseconds - interrupt tasks with no activity for this duration (minimum: 60000 = 1 minute) |
| `providerConcurrency` | - | Per-provider concurrency limits. Keys are provider names (e.g., `anthropic`, `openai`, `google`) |
| `modelConcurrency` | - | Per-model concurrency limits. Keys are full model names (e.g., `anthropic/claude-opus-4-5`). Overrides provider limits. |
| `modelConcurrency` | - | Per-model concurrency limits. Keys are full model names (e.g., `anthropic/claude-opus-4-6`). Overrides provider limits. |
**Priority Order**: `modelConcurrency` > `providerConcurrency` > `defaultConcurrency`
@@ -725,11 +725,11 @@ All 7 categories come with optimal model defaults, but **you must configure them
| Category | Built-in Default Model | Description |
| -------------------- | ---------------------------------- | -------------------------------------------------------------------- |
| `visual-engineering` | `google/gemini-3-pro-preview` | Frontend, UI/UX, design, styling, animation |
| `ultrabrain` | `openai/gpt-5.2-codex` (xhigh) | Deep logical reasoning, complex architecture decisions |
| `ultrabrain` | `openai/gpt-5.3-codex` (xhigh) | Deep logical reasoning, complex architecture decisions |
| `artistry` | `google/gemini-3-pro-preview` (max)| Highly creative/artistic tasks, novel ideas |
| `quick` | `anthropic/claude-haiku-4-5` | Trivial tasks - single file changes, typo fixes, simple modifications|
| `unspecified-low` | `anthropic/claude-sonnet-4-5` | Tasks that don't fit other categories, low effort required |
| `unspecified-high` | `anthropic/claude-opus-4-5` (max) | Tasks that don't fit other categories, high effort required |
| `unspecified-high` | `anthropic/claude-opus-4-6` (max) | Tasks that don't fit other categories, high effort required |
| `writing` | `google/gemini-3-flash-preview` | Documentation, prose, technical writing |
### ⚠️ Critical: Model Resolution Priority
@@ -768,7 +768,7 @@ All 7 categories come with optimal model defaults, but **you must configure them
"model": "google/gemini-3-pro-preview"
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh"
},
"artistry": {
@@ -782,7 +782,7 @@ All 7 categories come with optimal model defaults, but **you must configure them
"model": "anthropic/claude-sonnet-4-5"
},
"unspecified-high": {
"model": "anthropic/claude-opus-4-5",
"model": "anthropic/claude-opus-4-6",
"variant": "max"
},
"writing": {
@@ -870,9 +870,9 @@ At runtime, Oh My OpenCode uses a 3-step resolution process to determine which m
│ │ anthropic → github-copilot → opencode → antigravity │ │
│ │ │ │ │ │ │ │
│ │ ▼ ▼ ▼ ▼ │ │
│ │ Try: anthropic/claude-opus-4-5 │ │
│ │ Try: github-copilot/claude-opus-4-5 │ │
│ │ Try: opencode/claude-opus-4-5 │ │
│ │ Try: anthropic/claude-opus-4-6 │ │
│ │ Try: github-copilot/claude-opus-4-6 │ │
│ │ Try: opencode/claude-opus-4-6 │ │
│ │ ... │ │
│ │ │ │
│ │ Found in available models? → Return matched model │ │
@@ -894,13 +894,13 @@ Each agent has a defined provider priority chain. The system tries providers in
| Agent | Model (no prefix) | Provider Priority Chain |
|-------|-------------------|-------------------------|
| **Sisyphus** | `claude-opus-4-5` | anthropic → kimi-for-coding → zai-coding-plan → openai → google |
| **Sisyphus** | `claude-opus-4-6` | anthropic → kimi-for-coding → zai-coding-plan → openai → google |
| **oracle** | `gpt-5.2` | openai → google → anthropic |
| **librarian** | `glm-4.7` | zai-coding-plan → opencode → anthropic |
| **explore** | `claude-haiku-4-5` | anthropic → github-copilot → opencode |
| **multimodal-looker** | `gemini-3-flash` | google → openai → zai-coding-plan → kimi-for-coding → anthropic → opencode |
| **Prometheus (Planner)** | `claude-opus-4-5` | anthropic → kimi-for-coding → openai → google |
| **Metis (Plan Consultant)** | `claude-opus-4-5` | anthropic → kimi-for-coding → openai → google |
| **Prometheus (Planner)** | `claude-opus-4-6` | anthropic → kimi-for-coding → openai → google |
| **Metis (Plan Consultant)** | `claude-opus-4-6` | anthropic → kimi-for-coding → openai → google |
| **Momus (Plan Reviewer)** | `gpt-5.2` | openai → anthropic → google |
| **Atlas** | `claude-sonnet-4-5` | anthropic → kimi-for-coding → openai → google |
@@ -911,12 +911,12 @@ Categories follow the same resolution logic:
| Category | Model (no prefix) | Provider Priority Chain |
|----------|-------------------|-------------------------|
| **visual-engineering** | `gemini-3-pro` | google → anthropic → zai-coding-plan |
| **ultrabrain** | `gpt-5.2-codex` | openai → google → anthropic |
| **deep** | `gpt-5.2-codex` | openai → anthropic → google |
| **ultrabrain** | `gpt-5.3-codex` | openai → google → anthropic |
| **deep** | `gpt-5.3-codex` | openai → anthropic → google |
| **artistry** | `gemini-3-pro` | google → anthropic → openai |
| **quick** | `claude-haiku-4-5` | anthropic → google → opencode |
| **unspecified-low** | `claude-sonnet-4-5` | anthropic → openai → google |
| **unspecified-high** | `claude-opus-4-5` | anthropic → openai → google |
| **unspecified-high** | `claude-opus-4-6` | anthropic → openai → google |
| **writing** | `gemini-3-flash` | google → anthropic → zai-coding-plan → openai |
### Checking Your Configuration
@@ -949,7 +949,7 @@ Override any agent or category model in `oh-my-opencode.json`:
},
"categories": {
"visual-engineering": {
"model": "anthropic/claude-opus-4-5"
"model": "anthropic/claude-opus-4-6"
}
}
}

View File

@@ -10,8 +10,8 @@ Oh-My-OpenCode provides 11 specialized AI agents. Each has distinct expertise, o
| Agent | Model | Purpose |
|-------|-------|---------|
| **Sisyphus** | `anthropic/claude-opus-4-5` | **The default orchestrator.** Plans, delegates, and executes complex tasks using specialized subagents with aggressive parallel execution. Todo-driven workflow with extended thinking (32k budget). Fallback: kimi-k2.5 → glm-4.7 → gpt-5.2-codex → gemini-3-pro. |
| **Hephaestus** | `openai/gpt-5.2-codex` | **The Legitimate Craftsman.** Autonomous deep worker inspired by AmpCode's deep mode. Goal-oriented execution with thorough research before action. Explores codebase patterns, completes tasks end-to-end without premature stopping. Named after the Greek god of forge and craftsmanship. Requires gpt-5.2-codex (no fallback - only activates when this model is available). |
| **Sisyphus** | `anthropic/claude-opus-4-6` | **The default orchestrator.** Plans, delegates, and executes complex tasks using specialized subagents with aggressive parallel execution. Todo-driven workflow with extended thinking (32k budget). Fallback: kimi-k2.5 → glm-4.7 → gpt-5.3-codex → gemini-3-pro. |
| **Hephaestus** | `openai/gpt-5.3-codex` | **The Legitimate Craftsman.** Autonomous deep worker inspired by AmpCode's deep mode. Goal-oriented execution with thorough research before action. Explores codebase patterns, completes tasks end-to-end without premature stopping. Named after the Greek god of forge and craftsmanship. Requires gpt-5.3-codex (no fallback - only activates when this model is available). |
| **oracle** | `openai/gpt-5.2` | Architecture decisions, code review, debugging. Read-only consultation - stellar logical reasoning and deep analysis. Inspired by AmpCode. |
| **librarian** | `zai-coding-plan/glm-4.7` | Multi-repo analysis, documentation lookup, OSS implementation examples. Deep codebase understanding with evidence-based answers. Fallback: glm-4.7-free → claude-sonnet-4-5. |
| **explore** | `anthropic/claude-haiku-4-5` | Fast codebase exploration and contextual grep. Fallback: gpt-5-mini → gpt-5-nano. |
@@ -21,9 +21,9 @@ Oh-My-OpenCode provides 11 specialized AI agents. Each has distinct expertise, o
| Agent | Model | Purpose |
|-------|-------|---------|
| **Prometheus** | `anthropic/claude-opus-4-5` | Strategic planner with interview mode. Creates detailed work plans through iterative questioning. Fallback: kimi-k2.5 → gpt-5.2 → gemini-3-pro. |
| **Metis** | `anthropic/claude-opus-4-5` | Plan consultant - pre-planning analysis. Identifies hidden intentions, ambiguities, and AI failure points. Fallback: kimi-k2.5 → gpt-5.2 → gemini-3-pro. |
| **Momus** | `openai/gpt-5.2` | Plan reviewer - validates plans against clarity, verifiability, and completeness standards. Fallback: gpt-5.2 → claude-opus-4-5 → gemini-3-pro. |
| **Prometheus** | `anthropic/claude-opus-4-6` | Strategic planner with interview mode. Creates detailed work plans through iterative questioning. Fallback: kimi-k2.5 → gpt-5.2 → gemini-3-pro. |
| **Metis** | `anthropic/claude-opus-4-6` | Plan consultant - pre-planning analysis. Identifies hidden intentions, ambiguities, and AI failure points. Fallback: kimi-k2.5 → gpt-5.2 → gemini-3-pro. |
| **Momus** | `openai/gpt-5.2` | Plan reviewer - validates plans against clarity, verifiability, and completeness standards. Fallback: gpt-5.2 → claude-opus-4-6 → gemini-3-pro. |
### Invoking Agents

View File

@@ -196,7 +196,7 @@ When GitHub Copilot is the best available provider, oh-my-opencode uses these mo
| Agent | Model |
| ------------- | -------------------------------- |
| **Sisyphus** | `github-copilot/claude-opus-4.5` |
| **Sisyphus** | `github-copilot/claude-opus-4.6` |
| **Oracle** | `github-copilot/gpt-5.2` |
| **Explore** | `opencode/gpt-5-nano` |
| **Librarian** | `zai-coding-plan/glm-4.7` (if Z.ai available) or fallback |
@@ -218,13 +218,13 @@ If Z.ai is the only provider available, all agents will use GLM models:
#### OpenCode Zen
OpenCode Zen provides access to `opencode/` prefixed models including `opencode/claude-opus-4-5`, `opencode/gpt-5.2`, `opencode/gpt-5-nano`, and `opencode/glm-4.7-free`.
OpenCode Zen provides access to `opencode/` prefixed models including `opencode/claude-opus-4-6`, `opencode/gpt-5.2`, `opencode/gpt-5-nano`, and `opencode/glm-4.7-free`.
When OpenCode Zen is the best available provider (no native or Copilot), these models are used:
| Agent | Model |
| ------------- | -------------------------------- |
| **Sisyphus** | `opencode/claude-opus-4-5` |
| **Sisyphus** | `opencode/claude-opus-4-6` |
| **Oracle** | `opencode/gpt-5.2` |
| **Explore** | `opencode/gpt-5-nano` |
| **Librarian** | `opencode/glm-4.7-free` |

View File

@@ -277,7 +277,7 @@ This "boulder pushing" mechanism is why the system is named after Sisyphus.
```typescript
// OLD: Model name creates distributional bias
delegate_task(agent="gpt-5.2", prompt="...") // Model knows its limitations
delegate_task(agent="claude-opus-4.5", prompt="...") // Different self-perception
delegate_task(agent="claude-opus-4.6", prompt="...") // Different self-perception
```
**The Solution: Semantic Categories:**

View File

@@ -275,7 +275,7 @@ flowchart TD
### 🔮 Prometheus (The Planner)
- **Model**: `anthropic/claude-opus-4-5`
- **Model**: `anthropic/claude-opus-4-6`
- **Role**: Strategic planning, requirements interviews, work plan creation
- **Constraint**: **READ-ONLY**. Can only create/modify markdown files within `.sisyphus/` directory.
- **Characteristic**: Never writes code directly, focuses solely on "how to do it".

View File

@@ -7,7 +7,7 @@
| Field | Value |
|-------|-------|
| Model | `anthropic/claude-opus-4-5` |
| Model | `anthropic/claude-opus-4-6` |
| Max Tokens | `64000` |
| Mode | `primary` |
| Thinking | Budget: 32000 |

View File

@@ -33,16 +33,16 @@ agents/
## AGENT MODELS
| Agent | Model | Temp | Purpose |
|-------|-------|------|---------|
| Sisyphus | anthropic/claude-opus-4-5 | 0.1 | Primary orchestrator (fallback: kimi-k2.5 → glm-4.7 → gpt-5.2-codex → gemini-3-pro) |
| Hephaestus | openai/gpt-5.2-codex | 0.1 | Autonomous deep worker, "The Legitimate Craftsman" (requires gpt-5.2-codex, no fallback) |
| Sisyphus | anthropic/claude-opus-4-6 | 0.1 | Primary orchestrator (fallback: kimi-k2.5 → glm-4.7 → gpt-5.3-codex → gemini-3-pro) |
| Hephaestus | openai/gpt-5.3-codex | 0.1 | Autonomous deep worker, "The Legitimate Craftsman" (requires gpt-5.3-codex, no fallback) |
| Atlas | anthropic/claude-sonnet-4-5 | 0.1 | Master orchestrator (fallback: kimi-k2.5 → gpt-5.2) |
| oracle | openai/gpt-5.2 | 0.1 | Consultation, debugging |
| librarian | zai-coding-plan/glm-4.7 | 0.1 | Docs, GitHub search (fallback: glm-4.7-free) |
| explore | xai/grok-code-fast-1 | 0.1 | Fast contextual grep (fallback: claude-haiku-4-5 → gpt-5-mini → gpt-5-nano) |
| multimodal-looker | google/gemini-3-flash | 0.1 | PDF/image analysis |
| Prometheus | anthropic/claude-opus-4-5 | 0.1 | Strategic planning (fallback: kimi-k2.5 → gpt-5.2) |
| Metis | anthropic/claude-opus-4-5 | 0.3 | Pre-planning analysis (fallback: kimi-k2.5 → gpt-5.2) |
| Momus | openai/gpt-5.2 | 0.1 | Plan validation (fallback: claude-opus-4-5) |
| Prometheus | anthropic/claude-opus-4-6 | 0.1 | Strategic planning (fallback: kimi-k2.5 → gpt-5.2) |
| Metis | anthropic/claude-opus-4-6 | 0.3 | Pre-planning analysis (fallback: kimi-k2.5 → gpt-5.2) |
| Momus | openai/gpt-5.2 | 0.1 | Plan validation (fallback: claude-opus-4-6) |
| Sisyphus-Junior | anthropic/claude-sonnet-4-5 | 0.1 | Category-spawned executor |
## HOW TO ADD

View File

@@ -6,14 +6,14 @@ import * as connectedProvidersCache from "../shared/connected-providers-cache"
import * as modelAvailability from "../shared/model-availability"
import * as shared from "../shared"
const TEST_DEFAULT_MODEL = "anthropic/claude-opus-4-5"
const TEST_DEFAULT_MODEL = "anthropic/claude-opus-4-6"
describe("createBuiltinAgents with model overrides", () => {
test("Sisyphus with default model has thinking config when all models available", async () => {
// #given
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set([
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
"kimi-for-coding/k2p5",
"opencode/kimi-k2.5-free",
"zai-coding-plan/glm-4.7",
@@ -26,7 +26,7 @@ describe("createBuiltinAgents with model overrides", () => {
const agents = await createBuiltinAgents([], {}, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], {})
// #then
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-5")
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-6")
expect(agents.sisyphus.thinking).toEqual({ type: "enabled", budgetTokens: 32000 })
expect(agents.sisyphus.reasoningEffort).toBeUndefined()
} finally {
@@ -81,7 +81,7 @@ describe("createBuiltinAgents with model overrides", () => {
test("Sisyphus is created on first run when no availableModels or cache exist", async () => {
// #given
const systemDefaultModel = "anthropic/claude-opus-4-5"
const systemDefaultModel = "anthropic/claude-opus-4-6"
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(null)
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set())
@@ -218,7 +218,7 @@ describe("createBuiltinAgents without systemDefaultModel", () => {
])
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set([
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
"kimi-for-coding/k2p5",
"opencode/kimi-k2.5-free",
"zai-coding-plan/glm-4.7",
@@ -232,7 +232,7 @@ describe("createBuiltinAgents without systemDefaultModel", () => {
// #then
expect(agents.sisyphus).toBeDefined()
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-5")
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-6")
} finally {
cacheSpy.mockRestore()
fetchSpy.mockRestore()
@@ -244,7 +244,7 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () =>
test("hephaestus is not created when no required provider is connected", async () => {
// #given - only anthropic models available, not in hephaestus requiresProvider
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["anthropic/claude-opus-4-5"])
new Set(["anthropic/claude-opus-4-6"])
)
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["anthropic"])
@@ -263,7 +263,7 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () =>
test("hephaestus is created when openai provider is connected", async () => {
// #given - openai provider has models available
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["openai/gpt-5.2-codex"])
new Set(["openai/gpt-5.3-codex"])
)
try {
@@ -280,7 +280,7 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () =>
test("hephaestus is created when github-copilot provider is connected", async () => {
// #given - github-copilot provider has models available
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["github-copilot/gpt-5.2-codex"])
new Set(["github-copilot/gpt-5.3-codex"])
)
try {
@@ -297,7 +297,7 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () =>
test("hephaestus is created when opencode provider is connected", async () => {
// #given - opencode provider has models available
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["opencode/gpt-5.2-codex"])
new Set(["opencode/gpt-5.3-codex"])
)
try {
@@ -322,7 +322,7 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () =>
// #then
expect(agents.hephaestus).toBeDefined()
expect(agents.hephaestus.model).toBe("openai/gpt-5.2-codex")
expect(agents.hephaestus.model).toBe("openai/gpt-5.3-codex")
} finally {
cacheSpy.mockRestore()
fetchSpy.mockRestore()
@@ -332,10 +332,10 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () =>
test("hephaestus is created when explicit config provided even if provider unavailable", async () => {
// #given
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["anthropic/claude-opus-4-5"])
new Set(["anthropic/claude-opus-4-6"])
)
const overrides = {
hephaestus: { model: "anthropic/claude-opus-4-5" },
hephaestus: { model: "anthropic/claude-opus-4-6" },
}
try {
@@ -354,7 +354,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
test("sisyphus is created when at least one fallback model is available", async () => {
// #given
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["anthropic/claude-opus-4-5"])
new Set(["anthropic/claude-opus-4-6"])
)
try {
@@ -390,7 +390,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
// #given
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set())
const overrides = {
sisyphus: { model: "anthropic/claude-opus-4-5" },
sisyphus: { model: "anthropic/claude-opus-4-6" },
}
try {
@@ -426,7 +426,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
describe("buildAgent with category and skills", () => {
const { buildAgent } = require("./utils")
const TEST_MODEL = "anthropic/claude-opus-4-5"
const TEST_MODEL = "anthropic/claude-opus-4-6"
beforeEach(() => {
clearSkillCache()
@@ -572,7 +572,7 @@ describe("buildAgent with category and skills", () => {
const agent = buildAgent(source["test-agent"], TEST_MODEL)
// #then - category's built-in model and skills are applied
expect(agent.model).toBe("openai/gpt-5.2-codex")
expect(agent.model).toBe("openai/gpt-5.3-codex")
expect(agent.variant).toBe("xhigh")
expect(agent.prompt).toContain("Role: Designer-Turned-Developer")
expect(agent.prompt).toContain("Task description")
@@ -685,9 +685,9 @@ describe("override.category expansion in createBuiltinAgents", () => {
// #when
const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL)
// #then - ultrabrain category: model=openai/gpt-5.2-codex, variant=xhigh
// #then - ultrabrain category: model=openai/gpt-5.3-codex, variant=xhigh
expect(agents.oracle).toBeDefined()
expect(agents.oracle.model).toBe("openai/gpt-5.2-codex")
expect(agents.oracle.model).toBe("openai/gpt-5.3-codex")
expect(agents.oracle.variant).toBe("xhigh")
})
@@ -754,9 +754,9 @@ describe("override.category expansion in createBuiltinAgents", () => {
// #when
const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL)
// #then - ultrabrain category: model=openai/gpt-5.2-codex, variant=xhigh
// #then - ultrabrain category: model=openai/gpt-5.3-codex, variant=xhigh
expect(agents.sisyphus).toBeDefined()
expect(agents.sisyphus.model).toBe("openai/gpt-5.2-codex")
expect(agents.sisyphus.model).toBe("openai/gpt-5.3-codex")
expect(agents.sisyphus.variant).toBe("xhigh")
})
@@ -769,9 +769,9 @@ describe("override.category expansion in createBuiltinAgents", () => {
// #when
const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL)
// #then - ultrabrain category: model=openai/gpt-5.2-codex, variant=xhigh
// #then - ultrabrain category: model=openai/gpt-5.3-codex, variant=xhigh
expect(agents.atlas).toBeDefined()
expect(agents.atlas.model).toBe("openai/gpt-5.2-codex")
expect(agents.atlas.model).toBe("openai/gpt-5.3-codex")
expect(agents.atlas.variant).toBe("xhigh")
})

View File

@@ -197,7 +197,7 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"model": "opencode/gpt-5-nano",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -225,22 +225,22 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
},
"categories": {
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "opencode/glm-4.7-free",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"unspecified-low": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"visual-engineering": {
@@ -264,7 +264,7 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"model": "opencode/gpt-5-nano",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -292,14 +292,14 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
},
"categories": {
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "opencode/glm-4.7-free",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -307,7 +307,7 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"variant": "high",
},
"unspecified-low": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"visual-engineering": {
@@ -451,7 +451,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal
"model": "anthropic/claude-haiku-4-5",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -487,14 +487,14 @@ exports[`generateModelConfig all native providers uses preferred models from fal
"variant": "high",
},
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -524,7 +524,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM
"model": "anthropic/claude-haiku-4-5",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -560,14 +560,14 @@ exports[`generateModelConfig all native providers uses preferred models with isM
"variant": "high",
},
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -598,14 +598,14 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"model": "opencode/claude-haiku-4-5",
},
"hephaestus": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
"model": "opencode/glm-4.7-free",
},
"metis": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
"momus": {
@@ -620,11 +620,11 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"variant": "high",
},
"prometheus": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
"sisyphus": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
},
@@ -634,14 +634,14 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"variant": "high",
},
"deep": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "opencode/claude-haiku-4-5",
},
"ultrabrain": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -671,14 +671,14 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"model": "opencode/claude-haiku-4-5",
},
"hephaestus": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
"model": "opencode/glm-4.7-free",
},
"metis": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
"momus": {
@@ -693,11 +693,11 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"variant": "high",
},
"prometheus": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
"sisyphus": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
},
@@ -707,18 +707,18 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"variant": "high",
},
"deep": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "opencode/claude-haiku-4-5",
},
"ultrabrain": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
"model": "opencode/claude-opus-4-5",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
"unspecified-low": {
@@ -745,14 +745,14 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"model": "github-copilot/gpt-5-mini",
},
"hephaestus": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
"model": "github-copilot/claude-sonnet-4.5",
},
"metis": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"momus": {
@@ -767,11 +767,11 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"variant": "high",
},
"prometheus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"sisyphus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
},
@@ -781,14 +781,14 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"variant": "high",
},
"deep": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -818,14 +818,14 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"model": "github-copilot/gpt-5-mini",
},
"hephaestus": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
"model": "github-copilot/claude-sonnet-4.5",
},
"metis": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"momus": {
@@ -840,11 +840,11 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"variant": "high",
},
"prometheus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"sisyphus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
},
@@ -854,18 +854,18 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"variant": "high",
},
"deep": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"unspecified-low": {
@@ -1002,7 +1002,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"model": "anthropic/claude-haiku-4-5",
},
"hephaestus": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -1038,14 +1038,14 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"variant": "high",
},
"deep": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "opencode/gpt-5.2-codex",
"model": "opencode/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -1075,14 +1075,14 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"model": "github-copilot/gpt-5-mini",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
"model": "github-copilot/claude-sonnet-4.5",
},
"metis": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"momus": {
@@ -1097,11 +1097,11 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"variant": "high",
},
"prometheus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"sisyphus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
},
@@ -1111,14 +1111,14 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"variant": "high",
},
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -1275,14 +1275,14 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"model": "opencode/claude-haiku-4-5",
},
"hephaestus": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
"model": "zai-coding-plan/glm-4.7",
},
"metis": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"momus": {
@@ -1297,11 +1297,11 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"variant": "high",
},
"prometheus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"sisyphus": {
"model": "github-copilot/claude-opus-4.5",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
},
@@ -1311,14 +1311,14 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"variant": "high",
},
"deep": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "github-copilot/gpt-5.2-codex",
"model": "github-copilot/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -1348,7 +1348,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"model": "anthropic/claude-haiku-4-5",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -1384,14 +1384,14 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"variant": "high",
},
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {
@@ -1421,7 +1421,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"model": "anthropic/claude-haiku-4-5",
},
"hephaestus": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"librarian": {
@@ -1457,14 +1457,14 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"variant": "high",
},
"deep": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"quick": {
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.2-codex",
"model": "openai/gpt-5.3-codex",
"variant": "xhigh",
},
"unspecified-high": {

View File

@@ -298,8 +298,8 @@ describe("generateOmoConfig - model fallback system", () => {
// #when generating config
const result = generateOmoConfig(config)
// #then Sisyphus uses Copilot (OR logic - copilot is in claude-opus-4-5 providers)
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("github-copilot/claude-opus-4.5")
// #then Sisyphus uses Copilot (OR logic - copilot is in claude-opus-4-6 providers)
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("github-copilot/claude-opus-4.6")
})
test("uses ultimate fallback when no providers configured", () => {

View File

@@ -42,7 +42,7 @@ describe("model-resolution check", () => {
// given: User has override for oracle agent
const mockConfig = {
agents: {
oracle: { model: "anthropic/claude-opus-4-5" },
oracle: { model: "anthropic/claude-opus-4-6" },
},
}
@@ -51,8 +51,8 @@ describe("model-resolution check", () => {
// then: Oracle should show the override
const oracle = info.agents.find((a) => a.name === "oracle")
expect(oracle).toBeDefined()
expect(oracle!.userOverride).toBe("anthropic/claude-opus-4-5")
expect(oracle!.effectiveResolution).toBe("User override: anthropic/claude-opus-4-5")
expect(oracle!.userOverride).toBe("anthropic/claude-opus-4-6")
expect(oracle!.effectiveResolution).toBe("User override: anthropic/claude-opus-4-6")
})
it("shows user override for category when configured", async () => {

View File

@@ -43,7 +43,7 @@ Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi):
OpenAI Native openai/ models (GPT-5.2 for Oracle)
Gemini Native google/ models (Gemini 3 Pro, Flash)
Copilot github-copilot/ models (fallback)
OpenCode Zen opencode/ models (opencode/claude-opus-4-5, etc.)
OpenCode Zen opencode/ models (opencode/claude-opus-4-6, etc.)
Z.ai zai-coding-plan/glm-4.7 (Librarian priority)
Kimi kimi-for-coding/k2p5 (Sisyphus/Prometheus fallback)
`)

View File

@@ -243,7 +243,7 @@ async function runTuiMode(detected: DetectedConfig): Promise<InstallConfig | nul
message: "Do you have access to OpenCode Zen (opencode/ models)?",
options: [
{ value: "no" as const, label: "No", hint: "Will use other configured providers" },
{ value: "yes" as const, label: "Yes", hint: "opencode/claude-opus-4-5, opencode/gpt-5.2, etc." },
{ value: "yes" as const, label: "Yes", hint: "opencode/claude-opus-4-6, opencode/gpt-5.2, etc." },
],
initialValue: initial.opencodeZen,
})

View File

@@ -417,7 +417,7 @@ describe("generateModelConfig", () => {
const result = generateModelConfig(config)
// #then
expect(result.agents?.hephaestus?.model).toBe("openai/gpt-5.2-codex")
expect(result.agents?.hephaestus?.model).toBe("openai/gpt-5.3-codex")
expect(result.agents?.hephaestus?.variant).toBe("medium")
})
@@ -429,7 +429,7 @@ describe("generateModelConfig", () => {
const result = generateModelConfig(config)
// #then
expect(result.agents?.hephaestus?.model).toBe("github-copilot/gpt-5.2-codex")
expect(result.agents?.hephaestus?.model).toBe("github-copilot/gpt-5.3-codex")
expect(result.agents?.hephaestus?.variant).toBe("medium")
})
@@ -441,7 +441,7 @@ describe("generateModelConfig", () => {
const result = generateModelConfig(config)
// #then
expect(result.agents?.hephaestus?.model).toBe("opencode/gpt-5.2-codex")
expect(result.agents?.hephaestus?.model).toBe("opencode/gpt-5.3-codex")
expect(result.agents?.hephaestus?.variant).toBe("medium")
})

View File

@@ -71,7 +71,7 @@ function isProviderAvailable(provider: string, avail: ProviderAvailability): boo
function transformModelForProvider(provider: string, model: string): string {
if (provider === "github-copilot") {
return model
.replace("claude-opus-4-5", "claude-opus-4.5")
.replace("claude-opus-4-6", "claude-opus-4.6")
.replace("claude-sonnet-4-5", "claude-sonnet-4.5")
.replace("claude-haiku-4-5", "claude-haiku-4.5")
.replace("claude-sonnet-4", "claude-sonnet-4")

View File

@@ -94,7 +94,7 @@ describe("ConcurrencyManager.getConcurrencyLimit", () => {
// when
const modelLimit = manager.getConcurrencyLimit("anthropic/claude-sonnet-4-5")
const providerLimit = manager.getConcurrencyLimit("anthropic/claude-opus-4-5")
const providerLimit = manager.getConcurrencyLimit("anthropic/claude-opus-4-6")
const defaultLimit = manager.getConcurrencyLimit("google/gemini-3-pro")
// then

View File

@@ -783,7 +783,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () =>
}
const currentMessage: CurrentMessage = {
agent: "sisyphus",
model: { providerID: "anthropic", modelID: "claude-opus-4-5" },
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
}
// when
@@ -791,7 +791,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () =>
// then - uses currentMessage values, not task.parentModel/parentAgent
expect(promptBody.agent).toBe("sisyphus")
expect(promptBody.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-5" })
expect(promptBody.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" })
})
test("should fallback to parentAgent when currentMessage.agent is undefined", async () => {
@@ -997,7 +997,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
test("should release concurrency and clear key on completion", async () => {
// given
const concurrencyKey = "anthropic/claude-opus-4-5"
const concurrencyKey = "anthropic/claude-opus-4-6"
const concurrencyManager = getConcurrencyManager(manager)
await concurrencyManager.acquire(concurrencyKey)
@@ -1026,7 +1026,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
test("should prevent double completion and double release", async () => {
// given
const concurrencyKey = "anthropic/claude-opus-4-5"
const concurrencyKey = "anthropic/claude-opus-4-6"
const concurrencyManager = getConcurrencyManager(manager)
await concurrencyManager.acquire(concurrencyKey)
@@ -1657,7 +1657,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
description: "Task 1",
prompt: "Do something",
agent: "test-agent",
model: { providerID: "anthropic", modelID: "claude-opus-4-5" },
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
parentSessionID: "parent-session",
parentMessageID: "parent-message",
}

View File

@@ -192,7 +192,7 @@ describe("TaskToastManager", () => {
description: "Task with inherited model",
agent: "sisyphus-junior",
isBackground: false,
modelInfo: { model: "cliproxy/claude-opus-4-5", type: "inherited" as const },
modelInfo: { model: "cliproxy/claude-opus-4-6", type: "inherited" as const },
}
// when - addTask is called
@@ -202,7 +202,7 @@ describe("TaskToastManager", () => {
expect(mockClient.tui.showToast).toHaveBeenCalled()
const call = mockClient.tui.showToast.mock.calls[0][0]
expect(call.body.message).toContain("[FALLBACK]")
expect(call.body.message).toContain("cliproxy/claude-opus-4-5")
expect(call.body.message).toContain("cliproxy/claude-opus-4-6")
expect(call.body.message).toContain("(inherited from parent)")
})

View File

@@ -80,7 +80,7 @@ describe("executeCompact lock management", () => {
let fakeTimeouts: FakeTimeouts
const sessionID = "test-session-123"
const directory = "/test/dir"
const msg = { providerID: "anthropic", modelID: "claude-opus-4-5" }
const msg = { providerID: "anthropic", modelID: "claude-opus-4-6" }
beforeEach(() => {
// given: Fresh state for each test
@@ -332,7 +332,7 @@ describe("executeCompact lock management", () => {
expect(mockClient.session.summarize).toHaveBeenCalledWith(
expect.objectContaining({
path: { id: sessionID },
body: { providerID: "anthropic", modelID: "claude-opus-4-5", auto: true },
body: { providerID: "anthropic", modelID: "claude-opus-4-6", auto: true },
}),
)

View File

@@ -47,7 +47,7 @@ describe("atlas hook", () => {
}
const messageData = {
agent,
model: { providerID: "anthropic", modelID: "claude-opus-4-5" },
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
}
writeFileSync(join(messageDir, "msg_test001.json"), JSON.stringify(messageData))
}

View File

@@ -34,7 +34,7 @@ describe("preemptive-compaction", () => {
info: {
role: "assistant",
providerID: "anthropic",
modelID: "claude-opus-4-5",
modelID: "claude-opus-4-6",
tokens: {
input: 180000,
output: 0,
@@ -69,7 +69,7 @@ describe("preemptive-compaction", () => {
info: {
role: "assistant",
providerID: "anthropic",
modelID: "claude-opus-4-5",
modelID: "claude-opus-4-6",
tokens: {
input: 100000,
output: 0,

View File

@@ -41,7 +41,7 @@ describe("createThinkModeHook integration", () => {
const hook = createThinkModeHook()
const input = createMockInput(
"github-copilot",
"claude-opus-4-5",
"claude-opus-4-6",
"Please think deeply about this problem"
)
@@ -50,7 +50,7 @@ describe("createThinkModeHook integration", () => {
// then should upgrade to high variant and inject thinking config
const message = input.message as MessageWithInjectedProps
expect(input.message.model?.modelID).toBe("claude-opus-4-5-high")
expect(input.message.model?.modelID).toBe("claude-opus-4-6-high")
expect(message.thinking).toBeDefined()
expect((message.thinking as Record<string, unknown>)?.type).toBe(
"enabled"
@@ -61,11 +61,11 @@ describe("createThinkModeHook integration", () => {
})
it("should handle github-copilot Claude with dots in version", async () => {
// given a github-copilot Claude model with dot format (claude-opus-4.5)
// given a github-copilot Claude model with dot format (claude-opus-4.6)
const hook = createThinkModeHook()
const input = createMockInput(
"github-copilot",
"claude-opus-4.5",
"claude-opus-4.6",
"ultrathink mode"
)
@@ -74,7 +74,7 @@ describe("createThinkModeHook integration", () => {
// then should upgrade to high variant (hyphen format)
const message = input.message as MessageWithInjectedProps
expect(input.message.model?.modelID).toBe("claude-opus-4-5-high")
expect(input.message.model?.modelID).toBe("claude-opus-4-6-high")
expect(message.thinking).toBeDefined()
})
@@ -179,7 +179,7 @@ describe("createThinkModeHook integration", () => {
const hook = createThinkModeHook()
const input = createMockInput(
"github-copilot",
"claude-opus-4-5",
"claude-opus-4-6",
"Just do this task"
)
const originalModelID = input.message.model?.modelID
@@ -271,7 +271,7 @@ describe("createThinkModeHook integration", () => {
const hook = createThinkModeHook()
const input = createMockInput(
"github-copilot",
"claude-opus-4-5-high",
"claude-opus-4-6-high",
"think deeply"
)
@@ -280,7 +280,7 @@ describe("createThinkModeHook integration", () => {
// then should NOT modify the model (already high)
const message = input.message as MessageWithInjectedProps
expect(input.message.model?.modelID).toBe("claude-opus-4-5-high")
expect(input.message.model?.modelID).toBe("claude-opus-4-6-high")
// No additional thinking config should be injected
expect(message.thinking).toBeUndefined()
})
@@ -341,13 +341,13 @@ describe("createThinkModeHook integration", () => {
it("should handle empty prompt gracefully", async () => {
// given empty prompt
const hook = createThinkModeHook()
const input = createMockInput("github-copilot", "claude-opus-4-5", "")
const input = createMockInput("github-copilot", "claude-opus-4-6", "")
// when the chat.params hook is called
await hook["chat.params"](input, sessionID)
// then should not upgrade (no think keyword)
expect(input.message.model?.modelID).toBe("claude-opus-4-5")
expect(input.message.model?.modelID).toBe("claude-opus-4-6")
})
})

View File

@@ -12,7 +12,7 @@ describe("think-mode switcher", () => {
it("should resolve github-copilot Claude Opus to anthropic config", () => {
// given a github-copilot provider with Claude Opus model
const providerID = "github-copilot"
const modelID = "claude-opus-4-5"
const modelID = "claude-opus-4-6"
// when getting thinking config
const config = getThinkingConfig(providerID, modelID)
@@ -38,8 +38,8 @@ describe("think-mode switcher", () => {
})
it("should handle Claude with dots in version number", () => {
// given a model ID with dots (claude-opus-4.5)
const config = getThinkingConfig("github-copilot", "claude-opus-4.5")
// given a model ID with dots (claude-opus-4.6)
const config = getThinkingConfig("github-copilot", "claude-opus-4.6")
// then should still return anthropic thinking config
expect(config).not.toBeNull()
@@ -127,18 +127,18 @@ describe("think-mode switcher", () => {
describe("getHighVariant with dots vs hyphens", () => {
it("should handle dots in Claude version numbers", () => {
// given a Claude model ID with dot format
const variant = getHighVariant("claude-opus-4.5")
const variant = getHighVariant("claude-opus-4.6")
// then should return high variant with hyphen format
expect(variant).toBe("claude-opus-4-5-high")
expect(variant).toBe("claude-opus-4-6-high")
})
it("should handle hyphens in Claude version numbers", () => {
// given a Claude model ID with hyphen format
const variant = getHighVariant("claude-opus-4-5")
const variant = getHighVariant("claude-opus-4-6")
// then should return high variant
expect(variant).toBe("claude-opus-4-5-high")
expect(variant).toBe("claude-opus-4-6-high")
})
it("should handle claude-opus-4-6 high variant", () => {
@@ -177,7 +177,7 @@ describe("think-mode switcher", () => {
it("should return null for already-high variants", () => {
// given model IDs that are already high variants
expect(getHighVariant("claude-opus-4-5-high")).toBeNull()
expect(getHighVariant("claude-opus-4-6-high")).toBeNull()
expect(getHighVariant("gpt-5-2-high")).toBeNull()
expect(getHighVariant("gemini-3-pro-high")).toBeNull()
})
@@ -193,7 +193,7 @@ describe("think-mode switcher", () => {
describe("isAlreadyHighVariant", () => {
it("should detect -high suffix", () => {
// given model IDs with -high suffix
expect(isAlreadyHighVariant("claude-opus-4-5-high")).toBe(true)
expect(isAlreadyHighVariant("claude-opus-4-6-high")).toBe(true)
expect(isAlreadyHighVariant("gpt-5-2-high")).toBe(true)
expect(isAlreadyHighVariant("gemini-3-pro-high")).toBe(true)
})
@@ -205,8 +205,8 @@ describe("think-mode switcher", () => {
it("should return false for base models", () => {
// given base model IDs without -high suffix
expect(isAlreadyHighVariant("claude-opus-4-5")).toBe(false)
expect(isAlreadyHighVariant("claude-opus-4.5")).toBe(false)
expect(isAlreadyHighVariant("claude-opus-4-6")).toBe(false)
expect(isAlreadyHighVariant("claude-opus-4.6")).toBe(false)
expect(isAlreadyHighVariant("gpt-5.2")).toBe(false)
expect(isAlreadyHighVariant("gemini-3-pro")).toBe(false)
})
@@ -222,7 +222,7 @@ describe("think-mode switcher", () => {
it("should return null for already-high variants", () => {
// given already-high model variants
expect(
getThinkingConfig("anthropic", "claude-opus-4-5-high")
getThinkingConfig("anthropic", "claude-opus-4-6-high")
).toBeNull()
expect(getThinkingConfig("openai", "gpt-5-2-high")).toBeNull()
expect(getThinkingConfig("google", "gemini-3-pro-high")).toBeNull()
@@ -231,7 +231,7 @@ describe("think-mode switcher", () => {
it("should return null for already-high variants via github-copilot", () => {
// given already-high model variants via github-copilot
expect(
getThinkingConfig("github-copilot", "claude-opus-4-5-high")
getThinkingConfig("github-copilot", "claude-opus-4-6-high")
).toBeNull()
expect(getThinkingConfig("github-copilot", "gpt-5.2-high")).toBeNull()
})
@@ -258,7 +258,7 @@ describe("think-mode switcher", () => {
describe("Direct provider configs (backwards compatibility)", () => {
it("should still work for direct anthropic provider", () => {
// given direct anthropic provider
const config = getThinkingConfig("anthropic", "claude-opus-4-5")
const config = getThinkingConfig("anthropic", "claude-opus-4-6")
// then should return anthropic thinking config
expect(config).not.toBeNull()
@@ -351,10 +351,10 @@ describe("think-mode switcher", () => {
it("should handle prefixes with dots in version numbers", () => {
// given a model ID with prefix and dots
const variant = getHighVariant("vertex_ai/claude-opus-4.5")
const variant = getHighVariant("vertex_ai/claude-opus-4.6")
// then should normalize dots and preserve prefix
expect(variant).toBe("vertex_ai/claude-opus-4-5-high")
expect(variant).toBe("vertex_ai/claude-opus-4-6-high")
})
it("should handle multiple different prefixes", () => {
@@ -372,7 +372,7 @@ describe("think-mode switcher", () => {
it("should return null for already-high prefixed models", () => {
// given prefixed model IDs that are already high
expect(getHighVariant("vertex_ai/claude-opus-4-5-high")).toBeNull()
expect(getHighVariant("vertex_ai/claude-opus-4-6-high")).toBeNull()
expect(getHighVariant("openai/gpt-5-2-high")).toBeNull()
})
})
@@ -380,14 +380,14 @@ describe("think-mode switcher", () => {
describe("isAlreadyHighVariant with prefixes", () => {
it("should detect -high suffix in prefixed models", () => {
// given prefixed model IDs with -high suffix
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-5-high")).toBe(true)
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-6-high")).toBe(true)
expect(isAlreadyHighVariant("openai/gpt-5-2-high")).toBe(true)
expect(isAlreadyHighVariant("custom/gemini-3-pro-high")).toBe(true)
})
it("should return false for prefixed base models", () => {
// given prefixed base model IDs without -high suffix
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-5")).toBe(false)
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-6")).toBe(false)
expect(isAlreadyHighVariant("openai/gpt-5-2")).toBe(false)
})
@@ -410,7 +410,7 @@ describe("think-mode switcher", () => {
it("should work with prefixed models on known providers", () => {
// given known provider (anthropic) with prefixed model
// This tests that the base model name is correctly extracted for capability check
const config = getThinkingConfig("anthropic", "custom-prefix/claude-opus-4-5")
const config = getThinkingConfig("anthropic", "custom-prefix/claude-opus-4-6")
// then should return thinking config (base model is capable)
expect(config).not.toBeNull()
@@ -419,7 +419,7 @@ describe("think-mode switcher", () => {
it("should return null for prefixed models that are already high", () => {
// given prefixed already-high model
const config = getThinkingConfig("anthropic", "vertex_ai/claude-opus-4-5-high")
const config = getThinkingConfig("anthropic", "vertex_ai/claude-opus-4-6-high")
// then should return null
expect(config).toBeNull()
@@ -452,11 +452,11 @@ describe("think-mode switcher", () => {
it("should not break when switching to high variant in think mode", () => {
// given think mode switching vertex_ai/claude model to high variant
const original = "vertex_ai/claude-opus-4-5"
const original = "vertex_ai/claude-opus-4-6"
const high = getHighVariant(original)
// then the high variant should be valid
expect(high).toBe("vertex_ai/claude-opus-4-5-high")
expect(high).toBe("vertex_ai/claude-opus-4-6-high")
// #and should be recognized as already high
expect(isAlreadyHighVariant(high!)).toBe(true)

View File

@@ -38,14 +38,14 @@ function extractModelPrefix(modelID: string): { prefix: string; base: string } {
/**
* Normalizes model IDs to use consistent hyphen formatting.
* GitHub Copilot may use dots (claude-opus-4.5) but our maps use hyphens (claude-opus-4-5).
* GitHub Copilot may use dots (claude-opus-4.6) but our maps use hyphens (claude-opus-4-6).
* This ensures lookups work regardless of format.
*
* @example
* normalizeModelID("claude-opus-4.5") // "claude-opus-4-5"
* normalizeModelID("claude-opus-4.6") // "claude-opus-4-6"
* normalizeModelID("gemini-3.5-pro") // "gemini-3-5-pro"
* normalizeModelID("gpt-5.2") // "gpt-5-2"
* normalizeModelID("vertex_ai/claude-opus-4.5") // "vertex_ai/claude-opus-4-5"
* normalizeModelID("vertex_ai/claude-opus-4.6") // "vertex_ai/claude-opus-4-6"
*/
function normalizeModelID(modelID: string): string {
// Replace dots with hyphens when followed by a digit
@@ -59,10 +59,10 @@ function normalizeModelID(modelID: string): string {
* model provider (Anthropic, Google, OpenAI).
*
* @example
* resolveProvider("github-copilot", "claude-opus-4-5") // "anthropic"
* resolveProvider("github-copilot", "claude-opus-4-6") // "anthropic"
* resolveProvider("github-copilot", "gemini-3-pro") // "google"
* resolveProvider("github-copilot", "gpt-5.2") // "openai"
* resolveProvider("anthropic", "claude-opus-4-5") // "anthropic" (unchanged)
* resolveProvider("anthropic", "claude-opus-4-6") // "anthropic" (unchanged)
*/
function resolveProvider(providerID: string, modelID: string): string {
// GitHub Copilot is a proxy - infer actual provider from model name
@@ -89,7 +89,6 @@ const HIGH_VARIANT_MAP: Record<string, string> = {
// Claude
"claude-sonnet-4-5": "claude-sonnet-4-5-high",
"claude-opus-4-6": "claude-opus-4-6-high",
"claude-opus-4-5": "claude-opus-4-5-high",
// Gemini
"gemini-3-pro": "gemini-3-pro-high",
"gemini-3-pro-low": "gemini-3-pro-high",

View File

@@ -849,7 +849,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
await compactionContextInjector({
sessionID: input.sessionID,
providerID: "anthropic",
modelID: "claude-opus-4-5",
modelID: "claude-opus-4-6",
usageRatio: 0.8,
directory: ctx.directory,
});

View File

@@ -63,7 +63,7 @@ beforeEach(() => {
spyOn(mcpModule, "createBuiltinMcps" as any).mockReturnValue({})
spyOn(shared, "log" as any).mockImplementation(() => {})
spyOn(shared, "fetchAvailableModels" as any).mockResolvedValue(new Set(["anthropic/claude-opus-4-5"]))
spyOn(shared, "fetchAvailableModels" as any).mockResolvedValue(new Set(["anthropic/claude-opus-4-6"]))
spyOn(shared, "readConnectedProvidersCache" as any).mockReturnValue(null)
spyOn(configDir, "getOpenCodeConfigPaths" as any).mockReturnValue({
@@ -73,7 +73,7 @@ beforeEach(() => {
spyOn(permissionCompat, "migrateAgentConfig" as any).mockImplementation((config: Record<string, unknown>) => config)
spyOn(modelResolver, "resolveModelWithFallback" as any).mockReturnValue({ model: "anthropic/claude-opus-4-5" })
spyOn(modelResolver, "resolveModelWithFallback" as any).mockReturnValue({ model: "anthropic/claude-opus-4-6" })
})
afterEach(() => {
@@ -123,7 +123,7 @@ describe("Plan agent demote behavior", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -154,7 +154,7 @@ describe("Plan agent demote behavior", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {
plan: {
name: "plan",
@@ -191,7 +191,7 @@ describe("Plan agent demote behavior", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {
plan: {
name: "plan",
@@ -228,7 +228,7 @@ describe("Plan agent demote behavior", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -263,7 +263,7 @@ describe("Agent permission defaults", () => {
})
const pluginConfig: OhMyOpenCodeConfig = {}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -295,7 +295,7 @@ describe("Prometheus category config resolution", () => {
// then
expect(config).toBeDefined()
expect(config?.model).toBe("openai/gpt-5.2-codex")
expect(config?.model).toBe("openai/gpt-5.3-codex")
expect(config?.variant).toBe("xhigh")
})
@@ -355,7 +355,7 @@ describe("Prometheus category config resolution", () => {
// then - falls back to DEFAULT_CATEGORIES
expect(config).toBeDefined()
expect(config?.model).toBe("openai/gpt-5.2-codex")
expect(config?.model).toBe("openai/gpt-5.3-codex")
expect(config?.variant).toBe("xhigh")
})
@@ -406,7 +406,7 @@ describe("Prometheus direct override priority over category", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -446,7 +446,7 @@ describe("Prometheus direct override priority over category", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -487,7 +487,7 @@ describe("Prometheus direct override priority over category", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -522,7 +522,7 @@ describe("Prometheus direct override priority over category", () => {
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const handler = createConfigHandler({
@@ -560,7 +560,7 @@ describe("Deadlock prevention - fetchAvailableModels must not receive client", (
},
}
const config: Record<string, unknown> = {
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
agent: {},
}
const mockClient = {

View File

@@ -8,9 +8,9 @@ describe("Agent Config Integration", () => {
test("migrates old format agent keys to lowercase", () => {
// given - config with old format keys
const oldConfig = {
Sisyphus: { model: "anthropic/claude-opus-4-5" },
Atlas: { model: "anthropic/claude-opus-4-5" },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
Sisyphus: { model: "anthropic/claude-opus-4-6" },
Atlas: { model: "anthropic/claude-opus-4-6" },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-6" },
"Metis (Plan Consultant)": { model: "anthropic/claude-sonnet-4-5" },
"Momus (Plan Reviewer)": { model: "anthropic/claude-sonnet-4-5" },
}
@@ -33,9 +33,9 @@ describe("Agent Config Integration", () => {
expect(result.migrated).not.toHaveProperty("Momus (Plan Reviewer)")
// then - values are preserved
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-5" })
expect(result.migrated.atlas).toEqual({ model: "anthropic/claude-opus-4-5" })
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-5" })
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-6" })
expect(result.migrated.atlas).toEqual({ model: "anthropic/claude-opus-4-6" })
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-6" })
// then - changed flag is true
expect(result.changed).toBe(true)
@@ -44,7 +44,7 @@ describe("Agent Config Integration", () => {
test("preserves already lowercase keys", () => {
// given - config with lowercase keys
const config = {
sisyphus: { model: "anthropic/claude-opus-4-5" },
sisyphus: { model: "anthropic/claude-opus-4-6" },
oracle: { model: "openai/gpt-5.2" },
librarian: { model: "opencode/glm-4.7-free" },
}
@@ -62,9 +62,9 @@ describe("Agent Config Integration", () => {
test("handles mixed case config", () => {
// given - config with mixed old and new format
const mixedConfig = {
Sisyphus: { model: "anthropic/claude-opus-4-5" },
Sisyphus: { model: "anthropic/claude-opus-4-6" },
oracle: { model: "openai/gpt-5.2" },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-6" },
librarian: { model: "opencode/glm-4.7-free" },
}
@@ -172,8 +172,8 @@ describe("Agent Config Integration", () => {
test("old config migrates and displays correctly", () => {
// given - old format config
const oldConfig = {
Sisyphus: { model: "anthropic/claude-opus-4-5", temperature: 0.1 },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
Sisyphus: { model: "anthropic/claude-opus-4-6", temperature: 0.1 },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-6" },
}
// when - config is migrated
@@ -192,15 +192,15 @@ describe("Agent Config Integration", () => {
expect(prometheusDisplay).toBe("Prometheus (Plan Builder)")
// then - config values are preserved
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-5", temperature: 0.1 })
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-5" })
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-6", temperature: 0.1 })
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-6" })
})
test("new config works without migration", () => {
// given - new format config (already lowercase)
const newConfig = {
sisyphus: { model: "anthropic/claude-opus-4-5" },
atlas: { model: "anthropic/claude-opus-4-5" },
sisyphus: { model: "anthropic/claude-opus-4-6" },
atlas: { model: "anthropic/claude-opus-4-6" },
}
// when - migration is applied (should be no-op)

View File

@@ -84,14 +84,14 @@ describe("applyAgentVariant", () => {
describe("resolveVariantForModel", () => {
test("returns agent override variant when configured", () => {
// given - use a model in sisyphus chain (claude-opus-4-5 has default variant "max")
// given - use a model in sisyphus chain (claude-opus-4-6 has default variant "max")
// to verify override takes precedence over fallback chain
const config = {
agents: {
sisyphus: { variant: "high" },
},
} as OhMyOpenCodeConfig
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
// when
const variant = resolveVariantForModel(config, "sisyphus", model)
@@ -103,7 +103,7 @@ describe("resolveVariantForModel", () => {
test("returns correct variant for anthropic provider", () => {
// given
const config = {} as OhMyOpenCodeConfig
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
// when
const variant = resolveVariantForModel(config, "sisyphus", model)
@@ -113,9 +113,9 @@ describe("resolveVariantForModel", () => {
})
test("returns correct variant for openai provider (hephaestus agent)", () => {
// #given hephaestus has openai/gpt-5.2-codex with variant "medium" in its chain
// #given hephaestus has openai/gpt-5.3-codex with variant "medium" in its chain
const config = {} as OhMyOpenCodeConfig
const model = { providerID: "openai", modelID: "gpt-5.2-codex" }
const model = { providerID: "openai", modelID: "gpt-5.3-codex" }
// #when
const variant = resolveVariantForModel(config, "hephaestus", model)
@@ -151,7 +151,7 @@ describe("resolveVariantForModel", () => {
test("returns undefined for unknown agent", () => {
// given
const config = {} as OhMyOpenCodeConfig
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
// when
const variant = resolveVariantForModel(config, "nonexistent-agent", model)
@@ -179,7 +179,7 @@ describe("resolveVariantForModel", () => {
"custom-agent": { category: "ultrabrain" },
},
} as OhMyOpenCodeConfig
const model = { providerID: "openai", modelID: "gpt-5.2-codex" }
const model = { providerID: "openai", modelID: "gpt-5.3-codex" }
// when
const variant = resolveVariantForModel(config, "custom-agent", model)
@@ -203,7 +203,7 @@ describe("resolveVariantForModel", () => {
test("returns correct variant for oracle agent with anthropic", () => {
// given
const config = {} as OhMyOpenCodeConfig
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
// when
const variant = resolveVariantForModel(config, "oracle", model)

View File

@@ -15,7 +15,7 @@ describe("migrateAgentNames", () => {
test("migrates legacy OmO names to lowercase", () => {
// given: Config with legacy OmO agent names
const agents = {
omo: { model: "anthropic/claude-opus-4-5" },
omo: { model: "anthropic/claude-opus-4-6" },
OmO: { temperature: 0.5 },
"OmO-Plan": { prompt: "custom prompt" },
}
@@ -84,7 +84,7 @@ describe("migrateAgentNames", () => {
test("migrates orchestrator-sisyphus to atlas", () => {
// given: Config with legacy orchestrator-sisyphus agent name
const agents = {
"orchestrator-sisyphus": { model: "anthropic/claude-opus-4-5" },
"orchestrator-sisyphus": { model: "anthropic/claude-opus-4-6" },
}
// when: Migrate agent names
@@ -92,14 +92,14 @@ describe("migrateAgentNames", () => {
// then: orchestrator-sisyphus should be migrated to atlas
expect(changed).toBe(true)
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-5" })
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-6" })
expect(migrated["orchestrator-sisyphus"]).toBeUndefined()
})
test("migrates lowercase atlas to atlas", () => {
// given: Config with lowercase atlas agent name
const agents = {
atlas: { model: "anthropic/claude-opus-4-5" },
atlas: { model: "anthropic/claude-opus-4-6" },
}
// when: Migrate agent names
@@ -107,7 +107,7 @@ describe("migrateAgentNames", () => {
// then: lowercase atlas should remain atlas (no change needed)
expect(changed).toBe(false)
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-5" })
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-6" })
})
test("migrates Sisyphus variants to lowercase", () => {
@@ -470,7 +470,7 @@ describe("migrateAgentConfigToCategory", () => {
{ model: "google/gemini-3-flash" },
{ model: "openai/gpt-5.2" },
{ model: "anthropic/claude-haiku-4-5" },
{ model: "anthropic/claude-opus-4-5" },
{ model: "anthropic/claude-opus-4-6" },
{ model: "anthropic/claude-sonnet-4-5" },
]
@@ -550,7 +550,7 @@ describe("shouldDeleteAgentConfig", () => {
// given: Config with custom model override
const config = {
category: "visual-engineering",
model: "anthropic/claude-opus-4-5",
model: "anthropic/claude-opus-4-6",
}
// when: Check if config should be deleted

View File

@@ -85,7 +85,7 @@ export const MODEL_TO_CATEGORY_MAP: Record<string, string> = {
"google/gemini-3-flash": "writing",
"openai/gpt-5.2": "ultrabrain",
"anthropic/claude-haiku-4-5": "quick",
"anthropic/claude-opus-4-5": "unspecified-high",
"anthropic/claude-opus-4-6": "unspecified-high",
"anthropic/claude-sonnet-4-5": "unspecified-low",
}

View File

@@ -33,7 +33,7 @@ describe("fetchAvailableModels", () => {
it("#given cache file with models #when fetchAvailableModels called with connectedProviders #then returns Set of model IDs", async () => {
writeModelsCache({
openai: { id: "openai", models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
google: { id: "google", models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
})
@@ -44,7 +44,7 @@ describe("fetchAvailableModels", () => {
expect(result).toBeInstanceOf(Set)
expect(result.size).toBe(3)
expect(result.has("openai/gpt-5.2")).toBe(true)
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(true)
})
@@ -67,7 +67,7 @@ describe("fetchAvailableModels", () => {
model: {
list: async () => ({
data: [
{ id: "gpt-5.2-codex", provider: "openai" },
{ id: "gpt-5.3-codex", provider: "openai" },
{ id: "gemini-3-pro", provider: "google" },
],
}),
@@ -77,7 +77,7 @@ describe("fetchAvailableModels", () => {
const result = await fetchAvailableModels(client)
expect(result).toBeInstanceOf(Set)
expect(result.has("openai/gpt-5.2-codex")).toBe(true)
expect(result.has("openai/gpt-5.3-codex")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(false)
})
@@ -96,7 +96,7 @@ describe("fetchAvailableModels", () => {
model: {
list: async () => ({
data: [
{ id: "gpt-5.2-codex", provider: "openai" },
{ id: "gpt-5.3-codex", provider: "openai" },
{ id: "gemini-3-pro", provider: "google" },
],
}),
@@ -106,14 +106,14 @@ describe("fetchAvailableModels", () => {
const result = await fetchAvailableModels(client, { connectedProviders: ["openai", "google"] })
expect(result).toBeInstanceOf(Set)
expect(result.has("openai/gpt-5.2-codex")).toBe(true)
expect(result.has("openai/gpt-5.3-codex")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(true)
})
it("#given cache read twice #when second call made with same providers #then reads fresh each time", async () => {
writeModelsCache({
openai: { id: "openai", models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
})
const result1 = await fetchAvailableModels(undefined, { connectedProviders: ["openai"] })
@@ -134,7 +134,7 @@ describe("fetchAvailableModels", () => {
it("#given cache file with various providers #when fetchAvailableModels called with all providers #then extracts all IDs correctly", async () => {
writeModelsCache({
openai: { id: "openai", models: { "gpt-5.2-codex": { id: "gpt-5.2-codex" } } },
openai: { id: "openai", models: { "gpt-5.3-codex": { id: "gpt-5.3-codex" } } },
anthropic: { id: "anthropic", models: { "claude-sonnet-4-5": { id: "claude-sonnet-4-5" } } },
google: { id: "google", models: { "gemini-3-flash": { id: "gemini-3-flash" } } },
opencode: { id: "opencode", models: { "gpt-5-nano": { id: "gpt-5-nano" } } },
@@ -145,7 +145,7 @@ describe("fetchAvailableModels", () => {
})
expect(result.size).toBe(4)
expect(result.has("openai/gpt-5.2-codex")).toBe(true)
expect(result.has("openai/gpt-5.3-codex")).toBe(true)
expect(result.has("anthropic/claude-sonnet-4-5")).toBe(true)
expect(result.has("google/gemini-3-flash")).toBe(true)
expect(result.has("opencode/gpt-5-nano")).toBe(true)
@@ -159,8 +159,8 @@ describe("fuzzyMatchModel", () => {
it("should match substring in model name", () => {
const available = new Set([
"openai/gpt-5.2",
"openai/gpt-5.2-codex",
"anthropic/claude-opus-4-5",
"openai/gpt-5.3-codex",
"anthropic/claude-opus-4-6",
])
const result = fuzzyMatchModel("gpt-5.2", available)
expect(result).toBe("openai/gpt-5.2")
@@ -185,7 +185,7 @@ describe("fuzzyMatchModel", () => {
it("should prefer exact match over substring match", () => {
const available = new Set([
"openai/gpt-5.2",
"openai/gpt-5.2-codex",
"openai/gpt-5.3-codex",
"openai/gpt-5.2-ultra",
])
const result = fuzzyMatchModel("gpt-5.2", available)
@@ -207,13 +207,13 @@ describe("fuzzyMatchModel", () => {
// given available models with claude variants
// when searching for claude-opus
// then return matching claude-opus model
it("should match claude-opus to claude-opus-4-5", () => {
it("should match claude-opus to claude-opus-4-6", () => {
const available = new Set([
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
"anthropic/claude-sonnet-4-5",
])
const result = fuzzyMatchModel("claude-opus", available)
expect(result).toBe("anthropic/claude-opus-4-5")
expect(result).toBe("anthropic/claude-opus-4-6")
})
// given available models from multiple providers
@@ -222,7 +222,7 @@ describe("fuzzyMatchModel", () => {
it("should filter by provider when providers array is given", () => {
const available = new Set([
"openai/gpt-5.2",
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
"google/gemini-3",
])
const result = fuzzyMatchModel("gpt", available, ["openai"])
@@ -235,7 +235,7 @@ describe("fuzzyMatchModel", () => {
it("should return null when provider filter excludes all matches", () => {
const available = new Set([
"openai/gpt-5.2",
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
])
const result = fuzzyMatchModel("claude", available, ["openai"])
expect(result).toBeNull()
@@ -247,7 +247,7 @@ describe("fuzzyMatchModel", () => {
it("should return null when no match found", () => {
const available = new Set([
"openai/gpt-5.2",
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
])
const result = fuzzyMatchModel("gemini", available)
expect(result).toBeNull()
@@ -259,7 +259,7 @@ describe("fuzzyMatchModel", () => {
it("should match case-insensitively", () => {
const available = new Set([
"openai/gpt-5.2",
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
])
const result = fuzzyMatchModel("GPT-5.2", available)
expect(result).toBe("openai/gpt-5.2")
@@ -270,11 +270,11 @@ describe("fuzzyMatchModel", () => {
// then return exact match first
it("should prioritize exact match over longer variants", () => {
const available = new Set([
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-5-extended",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-6-extended",
])
const result = fuzzyMatchModel("claude-opus-4-5", available)
expect(result).toBe("anthropic/claude-opus-4-5")
const result = fuzzyMatchModel("claude-opus-4-6", available)
expect(result).toBe("anthropic/claude-opus-4-6")
})
// given available models with similar model IDs (e.g., glm-4.7 and glm-4.7-free)
@@ -319,7 +319,7 @@ describe("fuzzyMatchModel", () => {
it("should search all specified providers", () => {
const available = new Set([
"openai/gpt-5.2",
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
"google/gemini-3",
])
const result = fuzzyMatchModel("gpt", available, ["openai", "google"])
@@ -464,7 +464,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should filter models by connected providers", async () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
google: { models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
})
@@ -473,7 +473,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
})
expect(result.size).toBe(1)
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("openai/gpt-5.2")).toBe(false)
expect(result.has("google/gemini-3-pro")).toBe(false)
})
@@ -484,7 +484,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should filter models by multiple connected providers", async () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
google: { models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
})
@@ -493,7 +493,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
})
expect(result.size).toBe(2)
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(true)
expect(result.has("openai/gpt-5.2")).toBe(false)
})
@@ -504,7 +504,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should return empty set when connectedProviders is empty", async () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -520,7 +520,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should return empty set when connectedProviders not specified", async () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
})
const result = await fetchAvailableModels()
@@ -549,7 +549,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should return models from providers that exist in both cache and connected list", async () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -557,7 +557,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
})
expect(result.size).toBe(1)
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
})
// given filtered fetch
@@ -566,7 +566,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should not cache filtered results", async () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
})
// First call with anthropic
@@ -641,13 +641,13 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
writeProviderModelsCache({
models: {
opencode: ["glm-4.7-free", "gpt-5-nano"],
anthropic: ["claude-opus-4-5"]
anthropic: ["claude-opus-4-6"]
},
connected: ["opencode", "anthropic"]
})
writeModelsCache({
opencode: { models: { "glm-4.7-free": {}, "gpt-5-nano": {}, "gpt-5.2": {} } },
anthropic: { models: { "claude-opus-4-5": {}, "claude-sonnet-4-5": {} } }
anthropic: { models: { "claude-opus-4-6": {}, "claude-sonnet-4-5": {} } }
})
const result = await fetchAvailableModels(undefined, {
@@ -657,7 +657,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
expect(result.size).toBe(3)
expect(result.has("opencode/glm-4.7-free")).toBe(true)
expect(result.has("opencode/gpt-5-nano")).toBe(true)
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("opencode/gpt-5.2")).toBe(false)
expect(result.has("anthropic/claude-sonnet-4-5")).toBe(false)
})
@@ -708,7 +708,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
writeProviderModelsCache({
models: {
opencode: ["glm-4.7-free"],
anthropic: ["claude-opus-4-5"],
anthropic: ["claude-opus-4-6"],
google: ["gemini-3-pro"]
},
connected: ["opencode", "anthropic", "google"]
@@ -720,7 +720,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
expect(result.size).toBe(1)
expect(result.has("opencode/glm-4.7-free")).toBe(true)
expect(result.has("anthropic/claude-opus-4-5")).toBe(false)
expect(result.has("anthropic/claude-opus-4-6")).toBe(false)
expect(result.has("google/gemini-3-pro")).toBe(false)
})
@@ -747,7 +747,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
it("should handle mixed string[] and object[] formats across providers", async () => {
writeProviderModelsCache({
models: {
anthropic: ["claude-opus-4-5", "claude-sonnet-4-5"],
anthropic: ["claude-opus-4-6", "claude-sonnet-4-5"],
ollama: [
{ id: "ministral-3:14b-32k-agent", provider: "ollama" },
{ id: "qwen3-coder:32k-agent", provider: "ollama" }
@@ -761,7 +761,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
})
expect(result.size).toBe(4)
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-sonnet-4-5")).toBe(true)
expect(result.has("ollama/ministral-3:14b-32k-agent")).toBe(true)
expect(result.has("ollama/qwen3-coder:32k-agent")).toBe(true)
@@ -794,10 +794,10 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
describe("isModelAvailable", () => {
it("returns true when model exists via fuzzy match", () => {
// given
const available = new Set(["openai/gpt-5.2-codex", "anthropic/claude-opus-4-5"])
const available = new Set(["openai/gpt-5.3-codex", "anthropic/claude-opus-4-6"])
// when
const result = isModelAvailable("gpt-5.2-codex", available)
const result = isModelAvailable("gpt-5.3-codex", available)
// then
expect(result).toBe(true)
@@ -805,10 +805,10 @@ describe("isModelAvailable", () => {
it("returns false when model not found", () => {
// given
const available = new Set(["anthropic/claude-opus-4-5"])
const available = new Set(["anthropic/claude-opus-4-6"])
// when
const result = isModelAvailable("gpt-5.2-codex", available)
const result = isModelAvailable("gpt-5.3-codex", available)
// then
expect(result).toBe(false)
@@ -819,7 +819,7 @@ describe("isModelAvailable", () => {
const available = new Set<string>()
// when
const result = isModelAvailable("gpt-5.2-codex", available)
const result = isModelAvailable("gpt-5.3-codex", available)
// then
expect(result).toBe(false)

View File

@@ -20,7 +20,7 @@ import { readProviderModelsCache, hasProviderModelsCache, readConnectedProviders
* If providers array is given, only models starting with "provider/" are considered.
*
* @example
* const available = new Set(["openai/gpt-5.2", "openai/gpt-5.2-codex", "anthropic/claude-opus-4-5"])
* const available = new Set(["openai/gpt-5.2", "openai/gpt-5.3-codex", "anthropic/claude-opus-4-6"])
* fuzzyMatchModel("gpt-5.2", available) // → "openai/gpt-5.2"
* fuzzyMatchModel("claude", available, ["openai"]) // → null (provider filter excludes anthropic)
*/
@@ -105,7 +105,7 @@ export function fuzzyMatchModel(
/**
* Check if a target model is available (fuzzy match by model name, no provider filtering)
*
* @param targetModel - Model name to check (e.g., "gpt-5.2-codex")
* @param targetModel - Model name to check (e.g., "gpt-5.3-codex")
* @param availableModels - Set of available models in "provider/model" format
* @returns true if model is available, false otherwise
*/

View File

@@ -23,12 +23,12 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.variant).toBe("high")
})
test("sisyphus has claude-opus-4-6 as primary before claude-opus-4-5 and requiresAnyModel", () => {
test("sisyphus has claude-opus-4-6 as primary before claude-opus-4-6 and requiresAnyModel", () => {
// #given - sisyphus agent requirement
const sisyphus = AGENT_MODEL_REQUIREMENTS["sisyphus"]
// #when - accessing Sisyphus requirement
// #then - fallbackChain has claude-opus-4-6 first, claude-opus-4-5 second, glm-4.7-free last
// #then - fallbackChain has claude-opus-4-6 first, claude-opus-4-6 second, glm-4.7-free last
expect(sisyphus).toBeDefined()
expect(sisyphus.fallbackChain).toBeArray()
expect(sisyphus.fallbackChain).toHaveLength(6)
@@ -41,7 +41,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
const secondary = sisyphus.fallbackChain[1]
expect(secondary.providers[0]).toBe("anthropic")
expect(secondary.model).toBe("claude-opus-4-5")
expect(secondary.model).toBe("claude-opus-4-6")
expect(secondary.variant).toBe("max")
const last = sisyphus.fallbackChain[5]
@@ -103,12 +103,12 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.model).toBe("gemini-3-flash")
})
test("prometheus has claude-opus-4-6 as primary before claude-opus-4-5", () => {
test("prometheus has claude-opus-4-6 as primary before claude-opus-4-6", () => {
// #given - prometheus agent requirement
const prometheus = AGENT_MODEL_REQUIREMENTS["prometheus"]
// #when - accessing Prometheus requirement
// #then - claude-opus-4-6 is first, claude-opus-4-5 is second
// #then - claude-opus-4-6 is first, claude-opus-4-6 is second
expect(prometheus).toBeDefined()
expect(prometheus.fallbackChain).toBeArray()
expect(prometheus.fallbackChain.length).toBeGreaterThan(1)
@@ -119,17 +119,17 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.variant).toBe("max")
const secondary = prometheus.fallbackChain[1]
expect(secondary.model).toBe("claude-opus-4-5")
expect(secondary.model).toBe("claude-opus-4-6")
expect(secondary.providers[0]).toBe("anthropic")
expect(secondary.variant).toBe("max")
})
test("metis has claude-opus-4-6 as primary before claude-opus-4-5", () => {
test("metis has claude-opus-4-6 as primary before claude-opus-4-6", () => {
// #given - metis agent requirement
const metis = AGENT_MODEL_REQUIREMENTS["metis"]
// #when - accessing Metis requirement
// #then - claude-opus-4-6 is first, claude-opus-4-5 is second
// #then - claude-opus-4-6 is first, claude-opus-4-6 is second
expect(metis).toBeDefined()
expect(metis.fallbackChain).toBeArray()
expect(metis.fallbackChain.length).toBeGreaterThan(1)
@@ -140,7 +140,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.variant).toBe("max")
const secondary = metis.fallbackChain[1]
expect(secondary.model).toBe("claude-opus-4-5")
expect(secondary.model).toBe("claude-opus-4-6")
expect(secondary.providers[0]).toBe("anthropic")
expect(secondary.variant).toBe("max")
})
@@ -224,35 +224,35 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
})
describe("CATEGORY_MODEL_REQUIREMENTS", () => {
test("ultrabrain has valid fallbackChain with gpt-5.2-codex as primary", () => {
test("ultrabrain has valid fallbackChain with gpt-5.3-codex as primary", () => {
// given - ultrabrain category requirement
const ultrabrain = CATEGORY_MODEL_REQUIREMENTS["ultrabrain"]
// when - accessing ultrabrain requirement
// then - fallbackChain exists with gpt-5.2-codex as first entry
// then - fallbackChain exists with gpt-5.3-codex as first entry
expect(ultrabrain).toBeDefined()
expect(ultrabrain.fallbackChain).toBeArray()
expect(ultrabrain.fallbackChain.length).toBeGreaterThan(0)
const primary = ultrabrain.fallbackChain[0]
expect(primary.variant).toBe("xhigh")
expect(primary.model).toBe("gpt-5.2-codex")
expect(primary.model).toBe("gpt-5.3-codex")
expect(primary.providers[0]).toBe("openai")
})
test("deep has valid fallbackChain with gpt-5.2-codex as primary", () => {
test("deep has valid fallbackChain with gpt-5.3-codex as primary", () => {
// given - deep category requirement
const deep = CATEGORY_MODEL_REQUIREMENTS["deep"]
// when - accessing deep requirement
// then - fallbackChain exists with gpt-5.2-codex as first entry, medium variant
// then - fallbackChain exists with gpt-5.3-codex as first entry, medium variant
expect(deep).toBeDefined()
expect(deep.fallbackChain).toBeArray()
expect(deep.fallbackChain.length).toBeGreaterThan(0)
const primary = deep.fallbackChain[0]
expect(primary.variant).toBe("medium")
expect(primary.model).toBe("gpt-5.2-codex")
expect(primary.model).toBe("gpt-5.3-codex")
expect(primary.providers[0]).toBe("openai")
})
@@ -301,12 +301,12 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
expect(primary.providers[0]).toBe("anthropic")
})
test("unspecified-high has claude-opus-4-6 as primary before claude-opus-4-5", () => {
test("unspecified-high has claude-opus-4-6 as primary before claude-opus-4-6", () => {
// #given - unspecified-high category requirement
const unspecifiedHigh = CATEGORY_MODEL_REQUIREMENTS["unspecified-high"]
// #when - accessing unspecified-high requirement
// #then - claude-opus-4-6 is first, claude-opus-4-5 is second
// #then - claude-opus-4-6 is first, claude-opus-4-6 is second
expect(unspecifiedHigh).toBeDefined()
expect(unspecifiedHigh.fallbackChain).toBeArray()
expect(unspecifiedHigh.fallbackChain.length).toBeGreaterThan(1)
@@ -317,7 +317,7 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
expect(primary.providers).toEqual(["anthropic"])
const secondary = unspecifiedHigh.fallbackChain[1]
expect(secondary.model).toBe("claude-opus-4-5")
expect(secondary.model).toBe("claude-opus-4-6")
expect(secondary.variant).toBe("max")
expect(secondary.providers[0]).toBe("anthropic")
})
@@ -392,14 +392,14 @@ describe("FallbackEntry type", () => {
// given - a valid FallbackEntry object
const entry: FallbackEntry = {
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-opus-4-5",
model: "claude-opus-4-6",
variant: "high",
}
// when - accessing properties
// then - all properties are accessible
expect(entry.providers).toEqual(["anthropic", "github-copilot", "opencode"])
expect(entry.model).toBe("claude-opus-4-5")
expect(entry.model).toBe("claude-opus-4-6")
expect(entry.variant).toBe("high")
})
@@ -421,7 +421,7 @@ describe("ModelRequirement type", () => {
// given - a valid ModelRequirement object
const requirement: ModelRequirement = {
fallbackChain: [
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["openai", "github-copilot"], model: "gpt-5.2", variant: "high" },
],
}
@@ -430,7 +430,7 @@ describe("ModelRequirement type", () => {
// then - fallbackChain is accessible with correct structure
expect(requirement.fallbackChain).toBeArray()
expect(requirement.fallbackChain).toHaveLength(2)
expect(requirement.fallbackChain[0].model).toBe("claude-opus-4-5")
expect(requirement.fallbackChain[0].model).toBe("claude-opus-4-6")
expect(requirement.fallbackChain[1].model).toBe("gpt-5.2")
})
@@ -480,12 +480,12 @@ describe("ModelRequirement type", () => {
})
describe("requiresModel field in categories", () => {
test("deep category has requiresModel set to gpt-5.2-codex", () => {
test("deep category has requiresModel set to gpt-5.3-codex", () => {
// given
const deep = CATEGORY_MODEL_REQUIREMENTS["deep"]
// when / #then
expect(deep.requiresModel).toBe("gpt-5.2-codex")
expect(deep.requiresModel).toBe("gpt-5.3-codex")
})
test("artistry category has requiresModel set to gemini-3-pro", () => {

View File

@@ -16,7 +16,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
sisyphus: {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
@@ -26,7 +26,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
},
hephaestus: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
],
requiresProvider: ["openai", "github-copilot", "opencode"],
},
@@ -35,7 +35,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
},
librarian: {
@@ -66,7 +66,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
prometheus: {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
@@ -76,7 +76,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
metis: {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
@@ -87,7 +87,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
],
},
@@ -107,32 +107,32 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
],
},
ultrabrain: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "xhigh" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "xhigh" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
},
deep: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
],
requiresModel: "gpt-5.2-codex",
requiresModel: "gpt-5.3-codex",
},
artistry: {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
],
requiresModel: "gemini-3-pro",
@@ -147,14 +147,14 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
"unspecified-low": {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
],
},
"unspecified-high": {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],

View File

@@ -8,7 +8,7 @@ describe("resolveModel", () => {
test("returns userModel when all three are set", () => {
// given
const input: ModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
inheritedModel: "openai/gpt-5.2",
systemDefault: "google/gemini-3-pro",
}
@@ -17,7 +17,7 @@ describe("resolveModel", () => {
const result = resolveModel(input)
// then
expect(result).toBe("anthropic/claude-opus-4-5")
expect(result).toBe("anthropic/claude-opus-4-6")
})
test("returns inheritedModel when userModel is undefined", () => {
@@ -87,7 +87,7 @@ describe("resolveModel", () => {
test("same input returns same output (referential transparency)", () => {
// given
const input: ModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
inheritedModel: "openai/gpt-5.2",
systemDefault: "google/gemini-3-pro",
}
@@ -118,11 +118,11 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
uiSelectedModel: "opencode/glm-4.7-free",
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
fallbackChain: [
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-5" },
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5", "github-copilot/claude-opus-4-5-preview"]),
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -139,8 +139,8 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
uiSelectedModel: "opencode/glm-4.7-free",
userModel: "anthropic/claude-opus-4-5",
availableModels: new Set(["anthropic/claude-opus-4-5"]),
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -156,8 +156,8 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
uiSelectedModel: " ",
userModel: "anthropic/claude-opus-4-5",
availableModels: new Set(["anthropic/claude-opus-4-5"]),
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -165,16 +165,16 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-5" })
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-6" })
})
test("empty string uiSelectedModel falls through to config override", () => {
// given
const input: ExtendedModelResolutionInput = {
uiSelectedModel: "",
userModel: "anthropic/claude-opus-4-5",
availableModels: new Set(["anthropic/claude-opus-4-5"]),
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -182,7 +182,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
})
})
@@ -190,11 +190,11 @@ describe("resolveModelWithFallback", () => {
test("returns userModel with override source when userModel is provided", () => {
// given
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
fallbackChain: [
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-5" },
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5", "github-copilot/claude-opus-4-5-preview"]),
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -202,9 +202,9 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("override")
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-5" })
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-6" })
})
test("override takes priority even if model not in availableModels", () => {
@@ -212,9 +212,9 @@ describe("resolveModelWithFallback", () => {
const input: ExtendedModelResolutionInput = {
userModel: "custom/my-model",
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -231,9 +231,9 @@ describe("resolveModelWithFallback", () => {
const input: ExtendedModelResolutionInput = {
userModel: " ",
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -249,9 +249,9 @@ describe("resolveModelWithFallback", () => {
const input: ExtendedModelResolutionInput = {
userModel: "",
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -268,9 +268,9 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6" },
],
availableModels: new Set(["github-copilot/claude-opus-4-5-preview", "opencode/claude-opus-4-7"]),
availableModels: new Set(["github-copilot/claude-opus-4-6-preview", "opencode/claude-opus-4-7"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -278,12 +278,12 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("github-copilot/claude-opus-4-5-preview")
expect(result!.model).toBe("github-copilot/claude-opus-4-6-preview")
expect(result!.source).toBe("provider-fallback")
expect(logSpy).toHaveBeenCalledWith("Model resolved via fallback chain (availability confirmed)", {
provider: "github-copilot",
model: "claude-opus-4-5",
match: "github-copilot/claude-opus-4-5-preview",
model: "claude-opus-4-6",
match: "github-copilot/claude-opus-4-6-preview",
variant: undefined,
})
})
@@ -294,7 +294,7 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["openai", "anthropic", "google"], model: "gpt-5.2" },
],
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-5", "google/gemini-3-pro"]),
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6", "google/gemini-3-pro"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -330,7 +330,7 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["anthropic", "github-copilot"], model: "claude-opus" },
],
availableModels: new Set(["anthropic/claude-opus-4-5", "github-copilot/claude-opus-4-5-preview"]),
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -338,14 +338,14 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("provider-fallback")
})
test("skips fallback chain when not provided", () => {
// given
const input: ExtendedModelResolutionInput = {
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -360,7 +360,7 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
fallbackChain: [],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -377,7 +377,7 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["anthropic"], model: "CLAUDE-OPUS" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -385,7 +385,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("provider-fallback")
})
@@ -476,7 +476,7 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["anthropic"], model: "nonexistent-model" },
],
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-5"]),
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -494,7 +494,7 @@ describe("resolveModelWithFallback", () => {
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(null)
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(),
systemDefaultModel: undefined, // no system default configured
@@ -513,7 +513,7 @@ describe("resolveModelWithFallback", () => {
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai", "google"])
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["anthropic", "openai"], model: "claude-opus-4-5" },
{ providers: ["anthropic", "openai"], model: "claude-opus-4-6" },
],
availableModels: new Set(),
systemDefaultModel: "google/gemini-3-pro",
@@ -523,7 +523,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then - should use connected provider (openai) from fallback chain
expect(result!.model).toBe("openai/claude-opus-4-5")
expect(result!.model).toBe("openai/claude-opus-4-6")
expect(result!.source).toBe("provider-fallback")
cacheSpy.mockRestore()
})
@@ -556,14 +556,14 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
],
availableModels: new Set(),
systemDefaultModel: "quotio/claude-opus-4-5-20251101",
systemDefaultModel: "quotio/claude-opus-4-6-20251101",
}
// when
const result = resolveModelWithFallback(input)
// then - no provider in fallback is connected, fall through to system default
expect(result!.model).toBe("quotio/claude-opus-4-5-20251101")
expect(result!.model).toBe("quotio/claude-opus-4-6-20251101")
expect(result!.source).toBe("system-default")
cacheSpy.mockRestore()
})
@@ -573,7 +573,7 @@ describe("resolveModelWithFallback", () => {
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(null)
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(),
systemDefaultModel: "google/gemini-3-pro",
@@ -607,20 +607,20 @@ describe("resolveModelWithFallback", () => {
describe("Multi-entry fallbackChain", () => {
test("resolves to claude-opus when OpenAI unavailable but Anthropic available (oracle scenario)", () => {
// given
const availableModels = new Set(["anthropic/claude-opus-4-5"])
const availableModels = new Set(["anthropic/claude-opus-4-6"])
// when
const result = resolveModelWithFallback({
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
availableModels,
systemDefaultModel: "system/default",
})
// then
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("provider-fallback")
})
@@ -647,14 +647,14 @@ describe("resolveModelWithFallback", () => {
// given
const availableModels = new Set([
"openai/gpt-5.2",
"anthropic/claude-opus-4-5",
"anthropic/claude-opus-4-6",
])
// when
const result = resolveModelWithFallback({
fallbackChain: [
{ providers: ["openai"], model: "gpt-5.2" },
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels,
systemDefaultModel: "system/default",
@@ -673,7 +673,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback({
fallbackChain: [
{ providers: ["openai"], model: "gpt-5.2" },
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
{ providers: ["google"], model: "gemini-3-pro" },
],
availableModels,
@@ -690,7 +690,7 @@ describe("resolveModelWithFallback", () => {
test("result has correct ModelResolutionResult shape", () => {
// given
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -713,7 +713,7 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-5"]),
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-6"]),
systemDefaultModel: "anthropic/claude-sonnet-4-5",
}
@@ -749,9 +749,9 @@ describe("resolveModelWithFallback", () => {
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "system/default",
}
@@ -759,19 +759,19 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then - should fall through to fallbackChain
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("provider-fallback")
})
test("userModel takes priority over categoryDefaultModel", () => {
// given - both userModel and categoryDefaultModel provided
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
categoryDefaultModel: "google/gemini-3-pro",
fallbackChain: [
{ providers: ["google"], model: "gemini-3-pro" },
],
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-5"]),
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-6"]),
systemDefaultModel: "system/default",
}
@@ -779,7 +779,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then - userModel wins
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("override")
})
@@ -837,7 +837,7 @@ describe("resolveModelWithFallback", () => {
test("still returns override when userModel provided even if systemDefaultModel undefined", () => {
// given
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-5",
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(),
systemDefaultModel: undefined,
}
@@ -847,7 +847,7 @@ describe("resolveModelWithFallback", () => {
// then
expect(result).toBeDefined()
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("override")
})
@@ -855,9 +855,9 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-5" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-5"]),
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: undefined,
}
@@ -866,7 +866,7 @@ describe("resolveModelWithFallback", () => {
// then
expect(result).toBeDefined()
expect(result!.model).toBe("anthropic/claude-opus-4-5")
expect(result!.model).toBe("anthropic/claude-opus-4-6")
expect(result!.source).toBe("provider-fallback")
})
})

View File

@@ -194,12 +194,12 @@ You are NOT an interactive assistant. You are an autonomous problem-solver.
export const DEFAULT_CATEGORIES: Record<string, CategoryConfig> = {
"visual-engineering": { model: "google/gemini-3-pro" },
ultrabrain: { model: "openai/gpt-5.2-codex", variant: "xhigh" },
deep: { model: "openai/gpt-5.2-codex", variant: "medium" },
ultrabrain: { model: "openai/gpt-5.3-codex", variant: "xhigh" },
deep: { model: "openai/gpt-5.3-codex", variant: "medium" },
artistry: { model: "google/gemini-3-pro", variant: "high" },
quick: { model: "anthropic/claude-haiku-4-5" },
"unspecified-low": { model: "anthropic/claude-sonnet-4-5" },
"unspecified-high": { model: "anthropic/claude-opus-4-5", variant: "max" },
"unspecified-high": { model: "anthropic/claude-opus-4-6", variant: "max" },
writing: { model: "google/gemini-3-flash" },
}
@@ -343,11 +343,11 @@ FOR EVERY TASK, YOU MUST RECOMMEND:
| Category | Best For | Model |
|----------|----------|-------|
| \`visual-engineering\` | Frontend, UI/UX, design, styling, animation | google/gemini-3-pro |
| \`ultrabrain\` | Complex architecture, deep logical reasoning | openai/gpt-5.2-codex |
| \`ultrabrain\` | Complex architecture, deep logical reasoning | openai/gpt-5.3-codex |
| \`artistry\` | Highly creative/artistic tasks, novel ideas | google/gemini-3-pro |
| \`quick\` | Trivial tasks - single file, typo fixes | anthropic/claude-haiku-4-5 |
| \`unspecified-low\` | Moderate effort, doesn't fit other categories | anthropic/claude-sonnet-4-5 |
| \`unspecified-high\` | High effort, doesn't fit other categories | anthropic/claude-opus-4-5 |
| \`unspecified-high\` | High effort, doesn't fit other categories | anthropic/claude-opus-4-6 |
| \`writing\` | Documentation, prose, technical writing | google/gemini-3-flash |
### AVAILABLE SKILLS (ALWAYS EVALUATE ALL)

View File

@@ -28,9 +28,9 @@ describe("sisyphus-task", () => {
cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["anthropic", "google", "openai"])
providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue({
models: {
anthropic: ["claude-opus-4-5", "claude-sonnet-4-5", "claude-haiku-4-5"],
anthropic: ["claude-opus-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
google: ["gemini-3-pro", "gemini-3-flash"],
openai: ["gpt-5.2", "gpt-5.2-codex"],
openai: ["gpt-5.2", "gpt-5.3-codex"],
},
connected: ["anthropic", "google", "openai"],
updatedAt: "2026-01-01T00:00:00.000Z",
@@ -59,7 +59,7 @@ describe("sisyphus-task", () => {
// when / #then
expect(category).toBeDefined()
expect(category.model).toBe("openai/gpt-5.2-codex")
expect(category.model).toBe("openai/gpt-5.3-codex")
expect(category.variant).toBe("xhigh")
})
@@ -69,7 +69,7 @@ describe("sisyphus-task", () => {
// when / #then
expect(category).toBeDefined()
expect(category.model).toBe("openai/gpt-5.2-codex")
expect(category.model).toBe("openai/gpt-5.3-codex")
expect(category.variant).toBe("medium")
})
})
@@ -216,7 +216,7 @@ describe("sisyphus-task", () => {
app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({}) }, // No model configured
provider: { list: async () => ({ data: { connected: ["openai"] } }) },
model: { list: async () => ({ data: [{ provider: "openai", id: "gpt-5.2-codex" }] }) },
model: { list: async () => ({ data: [{ provider: "openai", id: "gpt-5.3-codex" }] }) },
session: {
create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }),
@@ -319,7 +319,7 @@ describe("sisyphus-task", () => {
test("blocks requiresModel when availability is known and missing the required model", () => {
// given
const categoryName = "deep"
const availableModels = new Set<string>(["anthropic/claude-opus-4-5"])
const availableModels = new Set<string>(["anthropic/claude-opus-4-6"])
// when
const result = resolveCategoryConfig(categoryName, {
@@ -349,9 +349,9 @@ describe("sisyphus-task", () => {
test("bypasses requiresModel when explicit user config provided", () => {
// #given
const categoryName = "deep"
const availableModels = new Set<string>(["anthropic/claude-opus-4-5"])
const availableModels = new Set<string>(["anthropic/claude-opus-4-6"])
const userCategories = {
deep: { model: "anthropic/claude-opus-4-5" },
deep: { model: "anthropic/claude-opus-4-6" },
}
// #when
@@ -363,7 +363,7 @@ describe("sisyphus-task", () => {
// #then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("anthropic/claude-opus-4-5")
expect(result!.config.model).toBe("anthropic/claude-opus-4-6")
})
test("bypasses requiresModel when explicit user config provided even with empty availability", () => {
@@ -371,7 +371,7 @@ describe("sisyphus-task", () => {
const categoryName = "deep"
const availableModels = new Set<string>()
const userCategories = {
deep: { model: "anthropic/claude-opus-4-5" },
deep: { model: "anthropic/claude-opus-4-6" },
}
// #when
@@ -383,7 +383,7 @@ describe("sisyphus-task", () => {
// #then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("anthropic/claude-opus-4-5")
expect(result!.config.model).toBe("anthropic/claude-opus-4-6")
})
test("returns default model from DEFAULT_CATEGORIES for builtin category", () => {
@@ -403,7 +403,7 @@ describe("sisyphus-task", () => {
// given
const categoryName = "visual-engineering"
const userCategories = {
"visual-engineering": { model: "anthropic/claude-opus-4-5" },
"visual-engineering": { model: "anthropic/claude-opus-4-6" },
}
// when
@@ -411,7 +411,7 @@ describe("sisyphus-task", () => {
// then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("anthropic/claude-opus-4-5")
expect(result!.config.model).toBe("anthropic/claude-opus-4-6")
})
test("user prompt_append is appended to default", () => {
@@ -475,7 +475,7 @@ describe("sisyphus-task", () => {
test("category built-in model takes precedence over inheritedModel", () => {
// given - builtin category with its own model, parent model also provided
const categoryName = "visual-engineering"
const inheritedModel = "cliproxy/claude-opus-4-5"
const inheritedModel = "cliproxy/claude-opus-4-6"
// when
const result = resolveCategoryConfig(categoryName, { inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -489,7 +489,7 @@ describe("sisyphus-task", () => {
// given - custom category with no model defined
const categoryName = "my-custom-no-model"
const userCategories = { "my-custom-no-model": { temperature: 0.5 } } as unknown as Record<string, CategoryConfig>
const inheritedModel = "cliproxy/claude-opus-4-5"
const inheritedModel = "cliproxy/claude-opus-4-6"
// when
const result = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -505,7 +505,7 @@ describe("sisyphus-task", () => {
const userCategories = {
"visual-engineering": { model: "my-provider/my-model" },
}
const inheritedModel = "cliproxy/claude-opus-4-5"
const inheritedModel = "cliproxy/claude-opus-4-6"
// when
const result = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -613,7 +613,7 @@ describe("sisyphus-task", () => {
const mockClient = {
app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
model: { list: async () => [{ provider: "anthropic", id: "claude-opus-4-5" }] },
model: { list: async () => [{ provider: "anthropic", id: "claude-opus-4-6" }] },
session: {
create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }),
@@ -649,7 +649,7 @@ describe("sisyphus-task", () => {
// then - variant MUST be "max" from DEFAULT_CATEGORIES
expect(launchInput.model).toEqual({
providerID: "anthropic",
modelID: "claude-opus-4-5",
modelID: "claude-opus-4-6",
variant: "max",
})
})
@@ -664,7 +664,7 @@ describe("sisyphus-task", () => {
const mockClient = {
app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
model: { list: async () => [{ provider: "anthropic", id: "claude-opus-4-5" }] },
model: { list: async () => [{ provider: "anthropic", id: "claude-opus-4-6" }] },
session: {
get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "ses_sync_default_variant" } }),
@@ -707,7 +707,7 @@ describe("sisyphus-task", () => {
// then - variant MUST be "max" from DEFAULT_CATEGORIES (passed as separate field)
expect(promptBody.model).toEqual({
providerID: "anthropic",
modelID: "claude-opus-4-5",
modelID: "claude-opus-4-6",
})
expect(promptBody.variant).toBe("max")
}, { timeout: 20000 })
@@ -1754,7 +1754,7 @@ describe("sisyphus-task", () => {
abort: new AbortController().signal,
}
// when - using ultrabrain category (default model is openai/gpt-5.2-codex)
// when - using ultrabrain category (default model is openai/gpt-5.3-codex)
await tool.execute(
{
description: "Override precedence test",
@@ -1805,7 +1805,7 @@ describe("sisyphus-task", () => {
client: mockClient,
sisyphusJuniorModel: "anthropic/claude-sonnet-4-5",
userCategories: {
ultrabrain: { model: "openai/gpt-5.2-codex" },
ultrabrain: { model: "openai/gpt-5.3-codex" },
},
})
@@ -1830,7 +1830,7 @@ describe("sisyphus-task", () => {
// then - explicit category model should win
expect(launchInput.model.providerID).toBe("openai")
expect(launchInput.model.modelID).toBe("gpt-5.2-codex")
expect(launchInput.model.modelID).toBe("gpt-5.3-codex")
})
})
@@ -2083,7 +2083,7 @@ describe("sisyphus-task", () => {
// then - catalog model is used
expect(resolved).not.toBeNull()
expect(resolved!.config.model).toBe("openai/gpt-5.2-codex")
expect(resolved!.config.model).toBe("openai/gpt-5.3-codex")
expect(resolved!.config.variant).toBe("xhigh")
})
@@ -2102,22 +2102,22 @@ describe("sisyphus-task", () => {
test("category built-in model takes precedence over inheritedModel for builtin category", () => {
// given - builtin ultrabrain category with its own model, inherited model also provided
const categoryName = "ultrabrain"
const inheritedModel = "cliproxy/claude-opus-4-5"
const inheritedModel = "cliproxy/claude-opus-4-6"
// when
const resolved = resolveCategoryConfig(categoryName, { inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
// then - category's built-in model wins (ultrabrain uses gpt-5.2-codex)
// then - category's built-in model wins (ultrabrain uses gpt-5.3-codex)
expect(resolved).not.toBeNull()
const actualModel = resolved!.config.model
expect(actualModel).toBe("openai/gpt-5.2-codex")
expect(actualModel).toBe("openai/gpt-5.3-codex")
})
test("when user defines model - modelInfo should report user-defined regardless of inheritedModel", () => {
// given
const categoryName = "ultrabrain"
const userCategories = { "ultrabrain": { model: "my-provider/custom-model" } }
const inheritedModel = "cliproxy/claude-opus-4-5"
const inheritedModel = "cliproxy/claude-opus-4-6"
// when
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -2134,7 +2134,7 @@ describe("sisyphus-task", () => {
// given - This test verifies the fix for PR #770 bug
// The bug was: checking `if (inheritedModel)` instead of `if (actualModel === inheritedModel)`
const categoryName = "ultrabrain"
const inheritedModel = "cliproxy/claude-opus-4-5"
const inheritedModel = "cliproxy/claude-opus-4-6"
const userCategories = { "ultrabrain": { model: "user/model" } }
// when - user model wins
@@ -2162,14 +2162,14 @@ describe("sisyphus-task", () => {
// given a builtin category with its own model, and an inherited model from parent
// The CORRECT chain: userConfig?.model ?? categoryBuiltIn ?? systemDefaultModel
const categoryName = "ultrabrain"
const inheritedModel = "anthropic/claude-opus-4-5"
const inheritedModel = "anthropic/claude-opus-4-6"
// when category has a built-in model (gpt-5.2-codex for ultrabrain)
// when category has a built-in model (gpt-5.3-codex for ultrabrain)
const resolved = resolveCategoryConfig(categoryName, { inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
// then category's built-in model should be used, NOT inheritedModel
expect(resolved).not.toBeNull()
expect(resolved!.model).toBe("openai/gpt-5.2-codex")
expect(resolved!.model).toBe("openai/gpt-5.3-codex")
})
test("FIXED: systemDefaultModel is used when no userConfig.model and no inheritedModel", () => {
@@ -2193,7 +2193,7 @@ describe("sisyphus-task", () => {
// given userConfig.model is explicitly set
const categoryName = "ultrabrain"
const userCategories = { "ultrabrain": { model: "custom/user-model" } }
const inheritedModel = "anthropic/claude-opus-4-5"
const inheritedModel = "anthropic/claude-opus-4-6"
const systemDefaultModel = "anthropic/claude-sonnet-4-5"
// when resolveCategoryConfig is called with all sources
@@ -2212,7 +2212,7 @@ describe("sisyphus-task", () => {
// given userConfig.model is empty string "" for a custom category (no built-in model)
const categoryName = "custom-empty-model"
const userCategories = { "custom-empty-model": { model: "", temperature: 0.3 } }
const inheritedModel = "anthropic/claude-opus-4-5"
const inheritedModel = "anthropic/claude-opus-4-6"
// when resolveCategoryConfig is called
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -2227,7 +2227,7 @@ describe("sisyphus-task", () => {
const categoryName = "visual-engineering"
// Using type assertion since we're testing fallback behavior for categories without model
const userCategories = { "visual-engineering": { temperature: 0.2 } } as unknown as Record<string, CategoryConfig>
const inheritedModel = "anthropic/claude-opus-4-5"
const inheritedModel = "anthropic/claude-opus-4-6"
// when resolveCategoryConfig is called
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -2472,7 +2472,7 @@ describe("sisyphus-task", () => {
app: {
agents: async () => ({
data: [
{ name: "oracle", mode: "subagent", model: { providerID: "anthropic", modelID: "claude-opus-4-5" } },
{ name: "oracle", mode: "subagent", model: { providerID: "anthropic", modelID: "claude-opus-4-6" } },
],
}),
},
@@ -2518,7 +2518,7 @@ describe("sisyphus-task", () => {
// then - matched agent's model should be passed to session.prompt
expect(promptBody.model).toEqual({
providerID: "anthropic",
modelID: "claude-opus-4-5",
modelID: "claude-opus-4-6",
})
}, { timeout: 20000 })