source dump of claude code
at main 157 lines 5.6 kB view raw
1import type { PermissionMode } from '../permissions/PermissionMode.js' 2import { capitalize } from '../stringUtils.js' 3import { MODEL_ALIASES, type ModelAlias } from './aliases.js' 4import { applyBedrockRegionPrefix, getBedrockRegionPrefix } from './bedrock.js' 5import { 6 getCanonicalName, 7 getRuntimeMainLoopModel, 8 parseUserSpecifiedModel, 9} from './model.js' 10import { getAPIProvider } from './providers.js' 11 12export const AGENT_MODEL_OPTIONS = [...MODEL_ALIASES, 'inherit'] as const 13export type AgentModelAlias = (typeof AGENT_MODEL_OPTIONS)[number] 14 15export type AgentModelOption = { 16 value: AgentModelAlias 17 label: string 18 description: string 19} 20 21/** 22 * Get the default subagent model. Returns 'inherit' so subagents inherit 23 * the model from the parent thread. 24 */ 25export function getDefaultSubagentModel(): string { 26 return 'inherit' 27} 28 29/** 30 * Get the effective model string for an agent. 31 * 32 * For Bedrock, if the parent model uses a cross-region inference prefix (e.g., "eu.", "us."), 33 * that prefix is inherited by subagents using alias models (e.g., "sonnet", "haiku", "opus"). 34 * This ensures subagents use the same region as the parent, which is necessary when 35 * IAM permissions are scoped to specific cross-region inference profiles. 36 */ 37export function getAgentModel( 38 agentModel: string | undefined, 39 parentModel: string, 40 toolSpecifiedModel?: ModelAlias, 41 permissionMode?: PermissionMode, 42): string { 43 if (process.env.CLAUDE_CODE_SUBAGENT_MODEL) { 44 return parseUserSpecifiedModel(process.env.CLAUDE_CODE_SUBAGENT_MODEL) 45 } 46 47 // Extract Bedrock region prefix from parent model to inherit for subagents. 48 // This ensures subagents use the same cross-region inference profile (e.g., "eu.", "us.") 49 // as the parent, which is required when IAM permissions only allow specific regions. 50 const parentRegionPrefix = getBedrockRegionPrefix(parentModel) 51 52 // Helper to apply parent region prefix for Bedrock models. 53 // `originalSpec` is the raw model string before resolution (alias or full ID). 54 // If the user explicitly specified a full model ID that already carries its own 55 // region prefix (e.g., "eu.anthropic.…"), we preserve it instead of overwriting 56 // with the parent's prefix. This prevents silent data-residency violations when 57 // an agent config intentionally pins to a different region than the parent. 58 const applyParentRegionPrefix = ( 59 resolvedModel: string, 60 originalSpec: string, 61 ): string => { 62 if (parentRegionPrefix && getAPIProvider() === 'bedrock') { 63 if (getBedrockRegionPrefix(originalSpec)) return resolvedModel 64 return applyBedrockRegionPrefix(resolvedModel, parentRegionPrefix) 65 } 66 return resolvedModel 67 } 68 69 // Prioritize tool-specified model if provided 70 if (toolSpecifiedModel) { 71 if (aliasMatchesParentTier(toolSpecifiedModel, parentModel)) { 72 return parentModel 73 } 74 const model = parseUserSpecifiedModel(toolSpecifiedModel) 75 return applyParentRegionPrefix(model, toolSpecifiedModel) 76 } 77 78 const agentModelWithExp = agentModel ?? getDefaultSubagentModel() 79 80 if (agentModelWithExp === 'inherit') { 81 // Apply runtime model resolution for inherit to get the effective model 82 // This ensures agents using 'inherit' get opusplan→Opus resolution in plan mode 83 return getRuntimeMainLoopModel({ 84 permissionMode: permissionMode ?? 'default', 85 mainLoopModel: parentModel, 86 exceeds200kTokens: false, 87 }) 88 } 89 90 if (aliasMatchesParentTier(agentModelWithExp, parentModel)) { 91 return parentModel 92 } 93 const model = parseUserSpecifiedModel(agentModelWithExp) 94 return applyParentRegionPrefix(model, agentModelWithExp) 95} 96 97/** 98 * Check if a bare family alias (opus/sonnet/haiku) matches the parent model's 99 * tier. When it does, the subagent inherits the parent's exact model string 100 * instead of resolving the alias to a provider default. 101 * 102 * Prevents surprising downgrades: a Vertex user on Opus 4.6 (via /model) who 103 * spawns a subagent with `model: opus` should get Opus 4.6, not whatever 104 * getDefaultOpusModel() returns for 3P. 105 * See https://github.com/anthropics/claude-code/issues/30815. 106 * 107 * Only bare family aliases match. `opus[1m]`, `best`, `opusplan` fall through 108 * since they carry semantics beyond "same tier as parent". 109 */ 110function aliasMatchesParentTier(alias: string, parentModel: string): boolean { 111 const canonical = getCanonicalName(parentModel) 112 switch (alias.toLowerCase()) { 113 case 'opus': 114 return canonical.includes('opus') 115 case 'sonnet': 116 return canonical.includes('sonnet') 117 case 'haiku': 118 return canonical.includes('haiku') 119 default: 120 return false 121 } 122} 123 124export function getAgentModelDisplay(model: string | undefined): string { 125 // When model is omitted, getDefaultSubagentModel() returns 'inherit' at runtime 126 if (!model) return 'Inherit from parent (default)' 127 if (model === 'inherit') return 'Inherit from parent' 128 return capitalize(model) 129} 130 131/** 132 * Get available model options for agents 133 */ 134export function getAgentModelOptions(): AgentModelOption[] { 135 return [ 136 { 137 value: 'sonnet', 138 label: 'Sonnet', 139 description: 'Balanced performance - best for most agents', 140 }, 141 { 142 value: 'opus', 143 label: 'Opus', 144 description: 'Most capable for complex reasoning tasks', 145 }, 146 { 147 value: 'haiku', 148 label: 'Haiku', 149 description: 'Fast and efficient for simple tasks', 150 }, 151 { 152 value: 'inherit', 153 label: 'Inherit from parent', 154 description: 'Use the same model as the main conversation', 155 }, 156 ] 157}