refactor(models): bump claude-opus-4-6 to claude-opus-4-7 across fallback chains, categories, and hooks

Updates the canonical Anthropic Opus model in every fallback chain
(sisyphus, oracle, prometheus, metis, momus, visual-engineering,
ultrabrain, deep, artistry, unspecified-high), the unspecified-high
category default, the think-mode HIGH_VARIANT_MAP, the Claude Code
alias map, the claude-thinking legacy alias, the context-limit GA
regex, and event.ts fallback strings.

Widens supportsCachedAnthropicLimit to accept both claude-*-4-6 and
claude-*-4-7 so the 1M context cache still applies across the bump.

Regenerates the bundled model-capabilities snapshot from models.dev
and the model-fallback snapshot to match the new source output.
This commit is contained in:
YeonGyu-Kim
2026-04-17 14:51:52 +09:00
parent b1764a880c
commit def44338ff
85 changed files with 39904 additions and 38116 deletions
@@ -47,7 +47,7 @@ export const ANTHROPIC_CATEGORIES: BuiltinCategoryDefinition[] = [
},
{
name: "unspecified-high",
config: { model: "anthropic/claude-opus-4-6", variant: "max" },
config: { model: "anthropic/claude-opus-4-7", variant: "max" },
description: "Tasks that don't fit other categories, high effort required",
promptAppend: UNSPECIFIED_HIGH_CATEGORY_PROMPT_APPEND,
},
@@ -99,7 +99,7 @@ describe("resolveCategoryExecution", () => {
const executorCtx = createMockExecutorContext()
executorCtx.userCategories = {
deep: {
model: "quotio/claude-opus-4-6",
model: "quotio/claude-opus-4-7",
fallback_models: ["quotio/kimi-k2.5", "openai/gpt-5.2(high)"],
},
}
+6 -6
View File
@@ -272,11 +272,11 @@ describe("executeSyncTask - cleanup on error paths", () => {
const initialModel = {
providerID: "anthropic",
modelID: "claude-opus-4-6",
modelID: "claude-opus-4-7",
variant: "max",
}
const fallbackChain = [
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic"], model: "claude-opus-4-7", variant: "max" },
{ providers: ["opencode-go"], model: "kimi-k2.5" },
]
@@ -289,7 +289,7 @@ describe("executeSyncTask - cleanup on error paths", () => {
expect(result).toContain("Task completed")
expect(result).toContain("Model: opencode-go/kimi-k2.5")
expect(attemptedModels).toEqual([
{ providerID: "anthropic", modelID: "claude-opus-4-6", variant: "max" },
{ providerID: "anthropic", modelID: "claude-opus-4-7", variant: "max" },
{ providerID: "opencode-go", modelID: "kimi-k2.5", variant: undefined },
])
})
@@ -339,11 +339,11 @@ describe("executeSyncTask - cleanup on error paths", () => {
const initialModel = {
providerID: "anthropic",
modelID: "claude-opus-4-6",
modelID: "claude-opus-4-7",
variant: "max",
}
const fallbackChain = [
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["anthropic"], model: "claude-opus-4-7", variant: "max" },
{ providers: ["opencode-go"], model: "kimi-k2.5" },
{ providers: ["openai"], model: "gpt-5.4", variant: "medium" },
]
@@ -356,7 +356,7 @@ describe("executeSyncTask - cleanup on error paths", () => {
//#then
expect(result).toBe("Final failure")
expect(attemptedModels).toEqual([
{ providerID: "anthropic", modelID: "claude-opus-4-6", variant: "max" },
{ providerID: "anthropic", modelID: "claude-opus-4-7", variant: "max" },
{ providerID: "opencode-go", modelID: "kimi-k2.5", variant: undefined },
{ providerID: "openai", modelID: "gpt-5.4", variant: "medium" },
])
+41 -41
View File
@@ -28,7 +28,7 @@ const SYSTEM_DEFAULT_MODEL = "anthropic/claude-sonnet-4-6"
const TEST_CONNECTED_PROVIDERS = ["anthropic", "google", "openai"]
const TEST_AVAILABLE_MODELS = new Set([
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
"anthropic/claude-sonnet-4-6",
"anthropic/claude-haiku-4-5",
"google/gemini-3.1-pro",
@@ -66,7 +66,7 @@ describe("sisyphus-task", () => {
cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["anthropic", "google", "openai"])
providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue({
models: {
anthropic: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"],
anthropic: ["claude-opus-4-7", "claude-sonnet-4-6", "claude-haiku-4-5"],
google: ["gemini-3.1-pro", "gemini-3-flash"],
openai: ["gpt-5.4", "gpt-5.3-codex"],
},
@@ -112,13 +112,13 @@ describe("sisyphus-task", () => {
expect(category.variant).toBe("medium")
})
test("unspecified-high category uses claude-opus-4-6 max as primary", () => {
test("unspecified-high category uses claude-opus-4-7 max as primary", () => {
// given
const category = DEFAULT_CATEGORIES["unspecified-high"]
// when / #then
expect(category).toBeDefined()
expect(category.model).toBe("anthropic/claude-opus-4-6")
expect(category.model).toBe("anthropic/claude-opus-4-7")
expect(category.variant).toBe("max")
})
})
@@ -757,7 +757,7 @@ describe("sisyphus-task", () => {
test("blocks requiresModel when availability is known and missing the required model", () => {
// given - artistry has requiresModel: gemini-3.1-pro
const categoryName = "artistry"
const availableModels = new Set<string>(["anthropic/claude-opus-4-6"])
const availableModels = new Set<string>(["anthropic/claude-opus-4-7"])
// when
const result = resolveCategoryConfig(categoryName, {
@@ -787,9 +787,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-6"])
const availableModels = new Set<string>(["anthropic/claude-opus-4-7"])
const userCategories = {
deep: { model: "anthropic/claude-opus-4-6" },
deep: { model: "anthropic/claude-opus-4-7" },
}
// #when
@@ -801,7 +801,7 @@ describe("sisyphus-task", () => {
// #then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("anthropic/claude-opus-4-6")
expect(result!.config.model).toBe("anthropic/claude-opus-4-7")
})
test("bypasses requiresModel when explicit user config provided even with empty availability", () => {
@@ -809,7 +809,7 @@ describe("sisyphus-task", () => {
const categoryName = "deep"
const availableModels = new Set<string>()
const userCategories = {
deep: { model: "anthropic/claude-opus-4-6" },
deep: { model: "anthropic/claude-opus-4-7" },
}
// #when
@@ -821,7 +821,7 @@ describe("sisyphus-task", () => {
// #then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("anthropic/claude-opus-4-6")
expect(result!.config.model).toBe("anthropic/claude-opus-4-7")
})
test("returns default model from DEFAULT_CATEGORIES for builtin category", () => {
@@ -841,7 +841,7 @@ describe("sisyphus-task", () => {
// given
const categoryName = "visual-engineering"
const userCategories = {
"visual-engineering": { model: "anthropic/claude-opus-4-6" },
"visual-engineering": { model: "anthropic/claude-opus-4-7" },
}
// when
@@ -849,7 +849,7 @@ describe("sisyphus-task", () => {
// then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("anthropic/claude-opus-4-6")
expect(result!.config.model).toBe("anthropic/claude-opus-4-7")
})
test("user prompt_append is appended to default", () => {
@@ -913,7 +913,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-6"
const inheritedModel = "cliproxy/claude-opus-4-7"
// when
const result = resolveCategoryConfig(categoryName, { inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -927,7 +927,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-6"
const inheritedModel = "cliproxy/claude-opus-4-7"
// when
const result = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -943,7 +943,7 @@ describe("sisyphus-task", () => {
const userCategories = {
"visual-engineering": { model: "my-provider/my-model" },
}
const inheritedModel = "cliproxy/claude-opus-4-6"
const inheritedModel = "cliproxy/claude-opus-4-7"
// when
const result = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -1054,7 +1054,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-6" }] },
model: { list: async () => [{ provider: "anthropic", id: "claude-opus-4-7" }] },
session: {
create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }),
@@ -1078,7 +1078,7 @@ describe("sisyphus-task", () => {
abort: new AbortController().signal,
}
// when - unspecified-high uses claude-opus-4-6 max in DEFAULT_CATEGORIES
// when - unspecified-high uses claude-opus-4-7 max in DEFAULT_CATEGORIES
await tool.execute(
{
description: "Test unspecified-high default variant",
@@ -1090,10 +1090,10 @@ describe("sisyphus-task", () => {
toolContext
)
// then - claude-opus-4-6 should be passed with max variant
// then - claude-opus-4-7 should be passed with max variant
expect(launchInput.model).toEqual({
providerID: "anthropic",
modelID: "claude-opus-4-6",
modelID: "claude-opus-4-7",
variant: "max",
})
}, { timeout: 20000 })
@@ -1113,7 +1113,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-6" }] },
model: { list: async () => [{ provider: "anthropic", id: "claude-opus-4-7" }] },
session: {
get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "ses_sync_default_variant" } }),
@@ -1139,7 +1139,7 @@ describe("sisyphus-task", () => {
abort: new AbortController().signal,
}
// when - unspecified-high uses claude-opus-4-6 max in DEFAULT_CATEGORIES
// when - unspecified-high uses claude-opus-4-7 max in DEFAULT_CATEGORIES
await tool.execute(
{
description: "Test unspecified-high sync variant",
@@ -1151,10 +1151,10 @@ describe("sisyphus-task", () => {
toolContext
)
// then - claude-opus-4-6 should be passed with max variant
// then - claude-opus-4-7 should be passed with max variant
expect(promptBody.model).toEqual({
providerID: "anthropic",
modelID: "claude-opus-4-6",
modelID: "claude-opus-4-7",
})
expect(promptBody.variant).toBe("max")
}, { timeout: 20000 })
@@ -1550,7 +1550,7 @@ describe("sisyphus-task", () => {
let promptCalled = false
const mockManager = { launch: async () => ({}) }
const mockClient = {
app: { agents: async () => ({ data: [{ name: "oracle", mode: "subagent", model: { providerID: "anthropic", modelID: "claude-opus-4-6" } }] }) },
app: { agents: async () => ({ data: [{ name: "oracle", mode: "subagent", model: { providerID: "anthropic", modelID: "claude-opus-4-7" } }] }) },
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
session: {
get: async () => ({ data: { directory: "/project" } }),
@@ -1835,7 +1835,7 @@ describe("sisyphus-task", () => {
id: "msg_001",
role: "user",
agent: "sisyphus-junior",
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
variant: "max",
time: { created: baseTime },
},
@@ -1917,7 +1917,7 @@ describe("sisyphus-task", () => {
const callArgs = promptMock.mock.calls[0][0]
expect(callArgs.body.variant).toBe("max")
expect(callArgs.body.agent).toBe("sisyphus-junior")
expect(callArgs.body.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" })
expect(callArgs.body.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-7" })
}, { timeout: 10000 })
test("task_id with background=true should return immediately without waiting", async () => {
@@ -2551,7 +2551,7 @@ describe("sisyphus-task", () => {
// Override provider cache to include kimi-for-coding provider
providerModelsSpy.mockReturnValue({
models: {
anthropic: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"],
anthropic: ["claude-opus-4-7", "claude-sonnet-4-6", "claude-haiku-4-5"],
google: ["gemini-3.1-pro", "gemini-3-flash"],
openai: ["gpt-5.4", "gpt-5.3-codex"],
"kimi-for-coding": ["k2p5"],
@@ -2798,7 +2798,7 @@ describe("sisyphus-task", () => {
manager: mockManager,
client: mockClient,
userCategories: {
"fallback-test": { model: "anthropic/claude-opus-4-6" },
"fallback-test": { model: "anthropic/claude-opus-4-7" },
},
connectedProvidersOverride: TEST_CONNECTED_PROVIDERS,
availableModelsOverride: createTestAvailableModels(),
@@ -3465,7 +3465,7 @@ 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-6"
const inheritedModel = "cliproxy/claude-opus-4-7"
// when
const resolved = resolveCategoryConfig(categoryName, { inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -3480,7 +3480,7 @@ describe("sisyphus-task", () => {
// given
const categoryName = "ultrabrain"
const userCategories = { "ultrabrain": { model: "my-provider/custom-model" } }
const inheritedModel = "cliproxy/claude-opus-4-6"
const inheritedModel = "cliproxy/claude-opus-4-7"
// when
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -3497,7 +3497,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-6"
const inheritedModel = "cliproxy/claude-opus-4-7"
const userCategories = { "ultrabrain": { model: "user/model" } }
// when - user model wins
@@ -3525,7 +3525,7 @@ 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-6"
const inheritedModel = "anthropic/claude-opus-4-7"
// when category has a built-in model (gpt-5.4 for ultrabrain)
const resolved = resolveCategoryConfig(categoryName, { inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -3556,7 +3556,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-6"
const inheritedModel = "anthropic/claude-opus-4-7"
const systemDefaultModel = "anthropic/claude-sonnet-4-6"
// when resolveCategoryConfig is called with all sources
@@ -3575,7 +3575,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-6"
const inheritedModel = "anthropic/claude-opus-4-7"
// when resolveCategoryConfig is called
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -3590,7 +3590,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-6"
const inheritedModel = "anthropic/claude-opus-4-7"
// when resolveCategoryConfig is called
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
@@ -3810,7 +3810,7 @@ describe("sisyphus-task", () => {
app: {
agents: async () => ({
data: [
{ name: "oracle", mode: "subagent", model: { providerID: "anthropic", modelID: "claude-opus-4-6" } },
{ name: "oracle", mode: "subagent", model: { providerID: "anthropic", modelID: "claude-opus-4-7" } },
],
}),
},
@@ -3854,7 +3854,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-6",
modelID: "claude-opus-4-7",
})
}, { timeout: 20000 })
@@ -3956,7 +3956,7 @@ describe("sisyphus-task", () => {
manager: mockManager,
client: mockClient,
agentOverrides: {
oracle: { model: "anthropic/claude-opus-4-6" },
oracle: { model: "anthropic/claude-opus-4-7" },
},
})
@@ -3982,7 +3982,7 @@ describe("sisyphus-task", () => {
// then - user-configured model should take priority over matchedAgent.model
expect(promptBody.model).toEqual({
providerID: "anthropic",
modelID: "claude-opus-4-6",
modelID: "claude-opus-4-7",
})
}, { timeout: 20000 })
@@ -4023,7 +4023,7 @@ describe("sisyphus-task", () => {
manager: mockManager,
client: mockClient,
agentOverrides: {
oracle: { model: "anthropic/claude-opus-4-6", variant: "max" },
oracle: { model: "anthropic/claude-opus-4-7", variant: "max" },
},
})
@@ -4111,7 +4111,7 @@ describe("sisyphus-task", () => {
)
// then - should resolve via AGENT_MODEL_REQUIREMENTS fallback chain for oracle
// oracle fallback chain: gpt-5.4 (openai) > gemini-3.1-pro (google) > claude-opus-4-6 (anthropic)
// oracle fallback chain: gpt-5.4 (openai) > gemini-3.1-pro (google) > claude-opus-4-7 (anthropic)
// Since openai is in connectedProviders, should resolve to openai/gpt-5.4
expect(promptBody.model).toBeDefined()
expect(promptBody.model.providerID).toBe("openai")