import { isProviderUsingOAuth, log, normalizeModelID } from "../../shared" const OPUS_PATTERN = /claude-.*opus/i const EFFORT_UNSUPPORTED_PATTERN = /claude-.*haiku/i const INTERNAL_SKIP_AGENTS = new Set(["title", "summary", "compaction"]) function isClaudeProvider(providerID: string, modelID: string): boolean { if (["anthropic", "google-vertex-anthropic", "opencode"].includes(providerID)) return true if (providerID === "github-copilot" && modelID.toLowerCase().includes("claude")) return true return false } function isOpusModel(modelID: string): boolean { const normalized = normalizeModelID(modelID) return OPUS_PATTERN.test(normalized) } function isEffortUnsupportedModel(modelID: string): boolean { const normalized = normalizeModelID(modelID) return EFFORT_UNSUPPORTED_PATTERN.test(normalized) } function shouldSkipForInternalAgent(agentName: string | undefined): boolean { if (!agentName) return false return INTERNAL_SKIP_AGENTS.has(agentName.trim().toLowerCase()) } /** * Providers that expose constrained APIs rejecting `output_config.effort: "max"` * (supported values: low | medium | high). Includes: * - Anthropic OAuth (Claude Pro/Max via third-party clients) * - GitHub Copilot (proxied Anthropic, doesn't support "max") */ function isConstrainedProvider(providerID: string): boolean { if (providerID === "github-copilot") return true if (providerID === "anthropic") return isProviderUsingOAuth(providerID) return false } interface ChatParamsInput { sessionID: string agent: { name?: string } model: { providerID: string; modelID: string } provider: { id: string } message: { variant?: string } } interface ChatParamsOutput { temperature?: number topP?: number topK?: number options: Record } /** * Valid thinking budget levels per model tier. * Opus supports "max"; all other Claude models cap at "high". */ const MAX_VARIANT_BY_TIER: Record = { opus: "max", default: "high", } function clampVariant(variant: string, isOpus: boolean, isConstrained: boolean): string { if (variant !== "max") return variant if (isConstrained) return MAX_VARIANT_BY_TIER.default return isOpus ? MAX_VARIANT_BY_TIER.opus : MAX_VARIANT_BY_TIER.default } export function createAnthropicEffortHook() { return { "chat.params": async ( input: ChatParamsInput, output: ChatParamsOutput ): Promise => { const { agent, model, message } = input if (!model?.modelID || !model?.providerID) return if (isEffortUnsupportedModel(model.modelID)) return if (!isClaudeProvider(model.providerID, model.modelID)) return if (shouldSkipForInternalAgent(agent?.name)) return const opus = isOpusModel(model.modelID) const constrained = isConstrainedProvider(model.providerID) if (output.options.effort !== undefined) { if (output.options.effort === "max" && constrained) { const clamped = clampVariant("max", opus, constrained) output.options.effort = clamped ;(message as { variant?: string }).variant = clamped log("anthropic-effort: clamped pre-set effort max→high", { sessionID: input.sessionID, provider: model.providerID, model: model.modelID, reason: "constrained-provider", }) } return } if (message.variant !== "max") return const clamped = clampVariant(message.variant, opus, constrained) output.options.effort = clamped const shouldOverrideMessageVariant = !opus || constrained if (shouldOverrideMessageVariant) { ;(message as { variant?: string }).variant = clamped log("anthropic-effort: clamped variant max→high", { sessionID: input.sessionID, provider: model.providerID, model: model.modelID, reason: constrained ? "constrained-provider" : "non-opus", }) } else { log("anthropic-effort: injected effort=max", { sessionID: input.sessionID, provider: model.providerID, model: model.modelID, }) } }, } }