fix(model-routing): support Claude Sonnet 5 defaults (#3370)

* fix(model-routing): support Claude Sonnet 5 defaults

* chore(model-routing): drop generated build artifacts

---------

Co-authored-by: Codex Review <codex-review@example.com>
This commit is contained in:
BellmanandCodex Review authored and GitHub committed 2026-07-01 06:47:26 +09:00
1 parent da2ee4f327
commit 33c4ed2eaf
21 files changed
+53 -53

No files matched your search

+1 -1
View File
@@ -63,7 +63,7 @@ Run vanilla Claude Code benchmark:
**Options:**
- `--limit N` - Limit to N instances (default: all)
- `--skip N` - Skip first N instances (default: 0)
- `--model MODEL` - Claude model to use (default: claude-sonnet-4-6-20260217)
- `--model MODEL` - Claude model to use (default: claude-sonnet-5)
- `--timeout SECS` - Timeout per instance (default: 300)
**Examples:**
+2 -2
View File
@@ -36,7 +36,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Quick test configuration
TEST_LIMIT=5
MODEL="claude-sonnet-4-6-20260217"
MODEL="claude-sonnet-5"
TIMEOUT="180" # 3 minutes per instance for quick test
# Parse arguments
@@ -61,7 +61,7 @@ while [[ $# -gt 0 ]]; do
echo ""
echo "Options:"
echo " --limit N Number of instances to test (default: 5)"
echo " --model MODEL Claude model to use (default: claude-sonnet-4-6-20260217)"
echo " --model MODEL Claude model to use (default: claude-sonnet-5)"
echo " --timeout SECS Timeout per instance (default: 180)"
echo " -h, --help Show this help message"
exit 0
+1 -1
View File
@@ -19,7 +19,7 @@
### Evaluation Setup
- **Model:** Claude Sonnet 4.6 (claude-sonnet-4-6-20260217)
- **Model:** Claude Sonnet 5 (claude-sonnet-5)
- **Max Tokens:** 16,384 output tokens per instance
- **Timeout:** 30 minutes per instance
- **Workers:** 4 parallel evaluations
+3 -3
View File
@@ -58,7 +58,7 @@ class BenchmarkConfig:
limit: Optional[int] = None
retries: int = 3
retry_delay: int = 30
model: str = "claude-sonnet-4-6-20260217"
model: str = "claude-sonnet-5"
skip: int = 0
@@ -581,8 +581,8 @@ Examples:
)
parser.add_argument(
"--model",
default="claude-sonnet-4-6-20260217",
help="Claude model to use (default: claude-sonnet-4-6-20260217)",
default="claude-sonnet-5",
help="Claude model to use (default: claude-sonnet-5)",
)
parser.add_argument(
"--skip",
+2 -2
View File
@@ -37,7 +37,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Parse arguments
LIMIT=""
SKIP=""
MODEL="claude-sonnet-4-6-20260217"
MODEL="claude-sonnet-5"
TIMEOUT="300"
SKIP_VANILLA=false
SKIP_OMC=false
@@ -81,7 +81,7 @@ while [[ $# -gt 0 ]]; do
echo "Options:"
echo " --limit N Limit to N instances (default: all)"
echo " --skip N Skip first N instances (default: 0)"
echo " --model MODEL Claude model to use (default: claude-sonnet-4-6-20260217)"
echo " --model MODEL Claude model to use (default: claude-sonnet-5)"
echo " --timeout SECS Timeout per instance (default: 300)"
echo " --skip-vanilla Skip vanilla benchmark run"
echo " --skip-omc Skip OMC benchmark run"
+2 -2
View File
@@ -38,7 +38,7 @@ LOG_FILE="$LOGS_DIR/${RUN_MODE}_${TIMESTAMP}.log"
# Parse arguments
LIMIT=""
SKIP=""
MODEL="claude-sonnet-4-6-20260217"
MODEL="claude-sonnet-5"
TIMEOUT="300"
while [[ $# -gt 0 ]]; do
@@ -65,7 +65,7 @@ while [[ $# -gt 0 ]]; do
echo "Options:"
echo " --limit N Limit to N instances (default: all)"
echo " --skip N Skip first N instances (default: 0)"
echo " --model MODEL Claude model to use (default: claude-sonnet-4-6-20260217)"
echo " --model MODEL Claude model to use (default: claude-sonnet-5)"
echo " --timeout SECS Timeout per instance (default: 300)"
echo " -h, --help Show this help message"
exit 0
+2 -2
View File
@@ -38,7 +38,7 @@ LOG_FILE="$LOGS_DIR/${RUN_MODE}_${TIMESTAMP}.log"
# Parse arguments
LIMIT=""
SKIP=""
MODEL="claude-sonnet-4-6-20260217"
MODEL="claude-sonnet-5"
TIMEOUT="300"
while [[ $# -gt 0 ]]; do
@@ -65,7 +65,7 @@ while [[ $# -gt 0 ]]; do
echo "Options:"
echo " --limit N Limit to N instances (default: all)"
echo " --skip N Skip first N instances (default: 0)"
echo " --model MODEL Claude model to use (default: claude-sonnet-4-6-20260217)"
echo " --model MODEL Claude model to use (default: claude-sonnet-5)"
echo " --timeout SECS Timeout per instance (default: 300)"
echo " -h, --help Show this help message"
exit 0
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: build-fixer
description: Build and compilation error resolution specialist (minimal diffs, no architecture changes)
model: claude-sonnet-4-6
model: claude-sonnet-5
---
<Agent_Prompt>
+1 -1
View File
@@ -25,7 +25,7 @@ async function main() {
config: {
agents: {
// Use a faster model for the orchestrator in dev
omc: { model: 'claude-sonnet-4-6-20260217' },
omc: { model: 'claude-sonnet-5' },
// Override model for specific agents
designer: { model: 'claude-haiku-4-5-20251001' },
writer: { model: 'claude-haiku-4-5-20251001' }
+1 -1
View File
@@ -1328,7 +1328,7 @@ async function main() {
} else if (sessionHasLmSuffix) {
// No model param, but the session model has a [1m] context-window suffix.
// Sub-agents would inherit it and fail — the runtime strips [1m] to a bare
// Anthropic model ID (e.g. claude-sonnet-4-6) which is invalid on Bedrock.
// Anthropic model ID (e.g. claude-sonnet-5) which is invalid on Bedrock.
// Fix: pass a tier alias (sonnet/haiku/opus). The Agent tool schema only accepts
// tier aliases for the model param — full Bedrock IDs are rejected by the schema.
const tierAlias = normalizeToCcAlias(sessionModel) || 'sonnet';
+2 -2
View File
@@ -67,8 +67,8 @@ npm run dev
# 불필요한 로그 숨기기
export OMC_QUIET=true
# 빠른 모델 (Sonnet 4.6)
export OMC_MODEL=anthropic/claude-sonnet-4-6
# 빠른 모델 (Sonnet 5)
export OMC_MODEL=anthropic/claude-sonnet-5
```
### 타이밍
+7 -7
View File
@@ -1,16 +1,16 @@
/**
* Repro test for Bedrock model routing bug
*
* Bug: On Bedrock, workers get model ID "claude-sonnet-4-6" (bare builtin default)
* Bug: On Bedrock, workers get model ID "claude-sonnet-5" (bare builtin default)
* instead of inheriting the parent model. On Bedrock, this bare ID is invalid
* — Bedrock requires full IDs like "us.anthropic.claude-sonnet-4-6-v1:0".
*
* Root cause chain:
* 1. buildDefaultConfig() → config.agents.executor.model = 'claude-sonnet-4-6'
* 1. buildDefaultConfig() → config.agents.executor.model = 'claude-sonnet-5'
* (from CLAUDE_FAMILY_DEFAULTS.SONNET, because no Bedrock env vars found)
* 2. getAgentDefinitions() resolves executor.model = 'claude-sonnet-4-6'
* 2. getAgentDefinitions() resolves executor.model = 'claude-sonnet-5'
* (configuredModel from config takes precedence over agent's defaultModel)
* 3. enforceModel() injects 'claude-sonnet-4-6' into Task calls
* 3. enforceModel() injects 'claude-sonnet-5' into Task calls
* 4. Claude Code passes it to Bedrock API → 400 invalid model
*
* The defense (forceInherit) works IF CLAUDE_CODE_USE_BEDROCK=1 is in the env.
@@ -103,10 +103,10 @@ describe('Bedrock model routing repro', () => {
expect(getDefaultModelMedium()).toBe('global.anthropic.claude-sonnet-4-6-v1:0');
});
it('falls back to bare "claude-sonnet-4-6" without env vars', async () => {
it('falls back to bare "claude-sonnet-5" without env vars', async () => {
const { getDefaultModelMedium } = await import('../config/models.js');
// getDefaultModelMedium returns the raw config value (not normalized)
expect(getDefaultModelMedium()).toBe('claude-sonnet-4-6');
expect(getDefaultModelMedium()).toBe('claude-sonnet-5');
});
});
@@ -132,7 +132,7 @@ describe('Bedrock model routing repro', () => {
// 3. Agent definitions use full builtin model IDs from config
const { getAgentDefinitions } = await import('../agents/definitions.js');
const defs = getAgentDefinitions({ config });
expect(defs['executor'].model).toBe('claude-sonnet-4-6');
expect(defs['executor'].model).toBe('claude-sonnet-5');
expect(defs['explore'].model).toBe('claude-haiku-4-5');
expect(defs['architect'].model).toBe('claude-opus-4-8');
+4 -4
View File
@@ -62,7 +62,7 @@ describe('delegation-enforcer', () => {
description: 'Test task',
prompt: 'Do something',
subagent_type: 'oh-my-claudecode:executor',
model: 'claude-sonnet-4-6'
model: 'claude-sonnet-5'
};
const result = enforceModel(input);
@@ -109,7 +109,7 @@ describe('delegation-enforcer', () => {
const result = enforceModel(input);
expect(result.injected).toBe(true);
expect(result.modifiedInput.model).toBe('sonnet'); // executor defaults to claude-sonnet-4-6
expect(result.modifiedInput.model).toBe('sonnet'); // executor defaults to claude-sonnet-5
expect(result.originalInput.model).toBeUndefined();
});
@@ -123,7 +123,7 @@ describe('delegation-enforcer', () => {
const result = enforceModel(input);
expect(result.injected).toBe(true);
expect(result.modifiedInput.model).toBe('sonnet'); // debugger defaults to claude-sonnet-4-6
expect(result.modifiedInput.model).toBe('sonnet'); // debugger defaults to claude-sonnet-5
});
it('rewrites deprecated aliases to canonical agent names before injecting model', () => {
@@ -167,7 +167,7 @@ describe('delegation-enforcer', () => {
const resultWithDebug = enforceModel(input);
expect(resultWithDebug.warning).toBeDefined();
expect(resultWithDebug.warning).toContain('Auto-injecting model');
expect(resultWithDebug.warning).toContain('claude-sonnet-4-6');
expect(resultWithDebug.warning).toContain('claude-sonnet-5');
expect(resultWithDebug.warning).toContain('executor');
});
+4 -4
View File
@@ -123,7 +123,7 @@ describe('Tier-0 contract docs consistency', () => {
const omc = readProjectFile('benchmark', 'run_omc.sh');
const fullComparison = readProjectFile('benchmark', 'run_full_comparison.sh');
const resultsReadme = readProjectFile('benchmark', 'results', 'README.md');
const expectedModel = 'claude-sonnet-4-6-20260217';
const expectedModel = 'claude-sonnet-5';
for (const content of [benchmarkReadme, benchmarkRunner, quickTest, vanilla, omc, fullComparison, resultsReadme]) {
expect(content).toContain(expectedModel);
@@ -131,7 +131,7 @@ describe('Tier-0 contract docs consistency', () => {
expect(benchmarkReadme).not.toContain('claude-sonnet-4.5-20250929');
expect(benchmarkRunner).not.toContain('claude-sonnet-4-20250514');
expect(resultsReadme).toContain('Claude Sonnet 4.6');
expect(resultsReadme).toContain('Claude Sonnet 5');
});
it('removes dead package build aliases and keeps seminar demo model guidance current', () => {
@@ -140,8 +140,8 @@ describe('Tier-0 contract docs consistency', () => {
expect(packageJson.scripts).not.toHaveProperty('build:codex');
expect(packageJson.scripts).not.toHaveProperty('build:gemini');
expect(seminarDemo).toContain('# 빠른 모델 (Sonnet 4.6)');
expect(seminarDemo).toContain('export OMC_MODEL=anthropic/claude-sonnet-4-6');
expect(seminarDemo).toContain('# 빠른 모델 (Sonnet 5)');
expect(seminarDemo).toContain('export OMC_MODEL=anthropic/claude-sonnet-5');
expect(seminarDemo).not.toContain('anthropic/claude-sonnet-4-5');
});
});
+3 -3
View File
@@ -55,14 +55,14 @@ describe('Type Tests', () => {
it('should support agent configuration', () => {
const config: PluginConfig = {
agents: {
omc: { model: 'claude-sonnet-4-6' },
omc: { model: 'claude-sonnet-5' },
architect: { model: 'claude-opus-4-8' },
explore: { model: 'claude-haiku-4-5' },
documentSpecialist: { model: 'claude-haiku-4-5' },
},
};
expect(config.agents?.omc?.model).toBe('claude-sonnet-4-6');
expect(config.agents?.omc?.model).toBe('claude-sonnet-5');
expect(config.agents?.architect?.model).toBe('claude-opus-4-8');
});
@@ -75,7 +75,7 @@ describe('Type Tests', () => {
maxEscalations: 2,
tierModels: {
LOW: 'claude-haiku-4',
MEDIUM: 'claude-sonnet-4-6',
MEDIUM: 'claude-sonnet-5',
HIGH: 'claude-opus-4-8',
},
},
+4 -4
View File
@@ -89,28 +89,28 @@ describe("loadConfig() — auto-forceInherit for non-standard providers", () =>
});
it("does NOT auto-enable forceInherit when direct Claude CLAUDE_MODEL beats stale ANTHROPIC_MODEL", () => {
process.env.CLAUDE_MODEL = "claude-sonnet-4-6";
process.env.CLAUDE_MODEL = "claude-sonnet-5";
process.env.ANTHROPIC_MODEL = "kimi-k2.6:cloud";
const config = loadConfig();
expect(config.routing?.forceInherit).toBe(false);
});
it("does NOT auto-enable forceInherit when direct Claude CLAUDE_MODEL beats stale OMC tier env vars", () => {
process.env.CLAUDE_MODEL = "claude-sonnet-4-6";
process.env.CLAUDE_MODEL = "claude-sonnet-5";
process.env.OMC_MODEL_MEDIUM = "glm-5.1:cloud";
const config = loadConfig();
expect(config.routing?.forceInherit).toBe(false);
});
it("does NOT auto-enable forceInherit when direct Claude ANTHROPIC_MODEL beats stale OMC tier env vars", () => {
process.env.ANTHROPIC_MODEL = "claude-sonnet-4-6";
process.env.ANTHROPIC_MODEL = "claude-sonnet-5";
process.env.OMC_MODEL_MEDIUM = "glm-5.1:cloud";
const config = loadConfig();
expect(config.routing?.forceInherit).toBe(false);
});
it("does NOT auto-enable forceInherit for standard Anthropic API usage", () => {
process.env.ANTHROPIC_MODEL = "claude-sonnet-4-6";
process.env.ANTHROPIC_MODEL = "claude-sonnet-5";
const config = loadConfig();
expect(config.routing?.forceInherit).toBe(false);
});
+1 -1
View File
@@ -344,7 +344,7 @@ describe('resolveClaudeFamily() — Bedrock inference profile IDs', () => {
});
it('resolves bare Anthropic model IDs', () => {
expect(resolveClaudeFamily('claude-sonnet-4-6')).toBe('SONNET');
expect(resolveClaudeFamily('claude-sonnet-5')).toBe('SONNET');
expect(resolveClaudeFamily('claude-opus-4-6')).toBe('OPUS');
expect(resolveClaudeFamily('claude-haiku-4-5')).toBe('HAIKU');
expect(resolveClaudeFamily('claude-fable-5')).toBe('FABLE');
+3 -3
View File
@@ -31,7 +31,7 @@ const TIER_ENV_KEYS: Record<ModelTier, readonly string[]> = {
*/
export const CLAUDE_FAMILY_DEFAULTS: Record<ClaudeModelFamily, string> = {
HAIKU: 'claude-haiku-4-5',
SONNET: 'claude-sonnet-4-6',
SONNET: 'claude-sonnet-5',
OPUS: 'claude-opus-4-8',
FABLE: 'claude-fable-5',
};
@@ -269,7 +269,7 @@ export function isBedrock(): boolean {
*
* These IDs must be passed through to the CLI as-is because normalizing them
* to aliases like "sonnet" causes Claude Code to expand them to Anthropic API
* model names (e.g. claude-sonnet-4-6) which are invalid on Bedrock/Vertex.
* model names (e.g. claude-sonnet-5) which are invalid on Bedrock/Vertex.
*/
export function isProviderSpecificModelId(modelId: string): boolean {
// Bedrock prefixed formats (region.anthropic.claude-*, anthropic.claude-*)
@@ -294,7 +294,7 @@ export function isProviderSpecificModelId(modelId: string): boolean {
* The `[1m]` suffix is a Claude Code internal annotation for the 1M context
* window variant. It is valid for the parent session's API path but is
* rejected by the sub-agent spawning runtime, which strips it to a bare
* Anthropic model ID (e.g., `claude-sonnet-4-6`) that is invalid on Bedrock.
* Anthropic model ID (e.g., `claude-sonnet-5`) that is invalid on Bedrock.
*/
export function hasExtendedContextSuffix(modelId: string): boolean {
return /\[\d+[mk]\]$/i.test(modelId);
+1 -1
View File
@@ -166,7 +166,7 @@ export function enforceModel(agentInput: AgentInput): EnforcementResult {
}
// If model is already specified, normalize it to CC-supported aliases
// before passing through. Full IDs like 'claude-sonnet-4-6' cause 400
// before passing through. Full IDs like 'claude-sonnet-5' cause 400
// errors on Bedrock/Vertex. (issue #1415)
if (agentInput.model) {
const normalizedModel = normalizeToCcAlias(agentInput.model);
+6 -6
View File
@@ -190,8 +190,8 @@ World`);
describe('switcher - getHighVariant', () => {
describe('Claude models', () => {
it('should return high variant for claude-sonnet-4-6', () => {
expect(getHighVariant('claude-sonnet-4-6')).toBe('claude-sonnet-4-6-high');
it('should return high variant for claude-sonnet-5', () => {
expect(getHighVariant('claude-sonnet-5')).toBe('claude-sonnet-5-high');
});
it('should return high variant for claude-opus-4-8', () => {
@@ -199,7 +199,7 @@ World`);
});
it('should return high variant for claude-3-5-sonnet', () => {
expect(getHighVariant('claude-3-5-sonnet')).toBe('claude-sonnet-4-6-high');
expect(getHighVariant('claude-3-5-sonnet')).toBe('claude-sonnet-5-high');
});
it('should return high variant for claude-3-opus', () => {
@@ -207,7 +207,7 @@ World`);
});
it('should handle version with dot notation', () => {
expect(getHighVariant('claude-sonnet-4.5')).toBe('claude-sonnet-4-6-high');
expect(getHighVariant('claude-sonnet-4.5')).toBe('claude-sonnet-5-high');
});
});
@@ -255,7 +255,7 @@ World`);
describe('Prefixed models', () => {
it('should preserve prefix in high variant', () => {
expect(getHighVariant('vertex_ai/claude-sonnet-4-5')).toBe('vertex_ai/claude-sonnet-4-6-high');
expect(getHighVariant('vertex_ai/claude-sonnet-4-5')).toBe('vertex_ai/claude-sonnet-5-high');
});
it('should handle openai/ prefix', () => {
@@ -539,7 +539,7 @@ World`);
};
const state = hook.processChatParams('test-session', input);
expect(state.modelSwitched).toBe(true);
expect(input.message.model?.modelId).toBe('claude-sonnet-4-6-high');
expect(input.message.model?.modelId).toBe('claude-sonnet-5-high');
});
it('should not switch already high variant', () => {
+2 -2
View File
@@ -193,7 +193,7 @@ const CONTRACTS: Record<CliAgentType, CliAgentContract> = {
if (model) {
// Provider-specific model IDs (Bedrock, Vertex) must be passed as-is.
// Normalizing them to aliases like "sonnet" causes Claude Code to expand
// them to Anthropic API names (claude-sonnet-4-6) which are invalid on
// them to Anthropic API names (claude-sonnet-5) which are invalid on
// these providers. (issue #1695)
const resolved = isProviderSpecificModelId(model) ? model : normalizeToCcAlias(model);
args.push('--model', resolved);
@@ -474,7 +474,7 @@ export function isPromptModeAgent(agentType: CliAgentType): boolean {
*
* When running on a non-standard provider (Bedrock, Vertex), workers need
* the provider-specific model ID passed explicitly via --model. Without it,
* Claude Code falls back to its built-in default (claude-sonnet-4-6) which
* Claude Code falls back to its built-in default (claude-sonnet-5) which
* is invalid on these providers.
*
* Resolution order: