import { CLAUDE_CODE_EFFORT_OPTIONS, CODEX_REASONING_EFFORT_OPTIONS, DEFAULT_MODEL_BY_PROVIDER, DEFAULT_REASONING_EFFORT_BY_PROVIDER, MODEL_OPTIONS_BY_PROVIDER, MODEL_SLUG_ALIASES_BY_PROVIDER, REASONING_EFFORT_OPTIONS_BY_PROVIDER, type ClaudeModelOptions, type ClaudeCodeEffort, type CodexModelOptions, type CodexReasoningEffort, type ModelSlug, type ProviderReasoningEffort, type ProviderKind,} from "@t3tools/contracts"; const MODEL_SLUG_SET_BY_PROVIDER: Record<ProviderKind, ReadonlySet<ModelSlug>> = { claudeAgent: new Set(MODEL_OPTIONS_BY_PROVIDER.claudeAgent.map((option) => option.slug)), codex: new Set(MODEL_OPTIONS_BY_PROVIDER.codex.map((option) => option.slug)),}; const CLAUDE_OPUS_4_6_MODEL = "claude-opus-4-6";const CLAUDE_SONNET_4_6_MODEL = "claude-sonnet-4-6";const CLAUDE_HAIKU_4_5_MODEL = "claude-haiku-4-5"; export function getModelOptions(provider: ProviderKind = "codex") { return MODEL_OPTIONS_BY_PROVIDER[provider];} export function getDefaultModel(provider: ProviderKind = "codex"): ModelSlug { return DEFAULT_MODEL_BY_PROVIDER[provider];} export function supportsClaudeFastMode(model: string | null | undefined): boolean { return normalizeModelSlug(model, "claudeAgent") === CLAUDE_OPUS_4_6_MODEL;} export function supportsClaudeAdaptiveReasoning(model: string | null | undefined): boolean { const normalized = normalizeModelSlug(model, "claudeAgent"); return normalized === CLAUDE_OPUS_4_6_MODEL || normalized === CLAUDE_SONNET_4_6_MODEL;} export function supportsClaudeMaxEffort(model: string | null | undefined): boolean { return normalizeModelSlug(model, "claudeAgent") === CLAUDE_OPUS_4_6_MODEL;} export function supportsClaudeUltrathinkKeyword(model: string | null | undefined): boolean { return supportsClaudeAdaptiveReasoning(model);} export function supportsClaudeThinkingToggle(model: string | null | undefined): boolean { return normalizeModelSlug(model, "claudeAgent") === CLAUDE_HAIKU_4_5_MODEL;} export function isClaudeUltrathinkPrompt(text: string | null | undefined): boolean { return typeof text === "string" && /\bultrathink\b/i.test(text);} export function normalizeModelSlug( model: string | null | undefined, provider: ProviderKind = "codex",): ModelSlug | null { if (typeof model !== "string") { return null; } const trimmed = model.trim(); if (!trimmed) { return null; } const aliases = MODEL_SLUG_ALIASES_BY_PROVIDER[provider] as Record<string, ModelSlug>; const aliased = Object.prototype.hasOwnProperty.call(aliases, trimmed) ? aliases[trimmed] : undefined; return typeof aliased === "string" ? aliased : (trimmed as ModelSlug);} export function resolveModelSlug( model: string | null | undefined, provider: ProviderKind = "codex",): ModelSlug { const normalized = normalizeModelSlug(model, provider); if (!normalized) { return DEFAULT_MODEL_BY_PROVIDER[provider]; } return MODEL_SLUG_SET_BY_PROVIDER[provider].has(normalized) ? normalized : DEFAULT_MODEL_BY_PROVIDER[provider];} export function resolveModelSlugForProvider( provider: ProviderKind, model: string | null | undefined,): ModelSlug { return resolveModelSlug(model, provider);} export function inferProviderForModel( model: string | null | undefined, fallback: ProviderKind = "codex",): ProviderKind { const normalizedClaude = normalizeModelSlug(model, "claudeAgent"); if (normalizedClaude && MODEL_SLUG_SET_BY_PROVIDER.claudeAgent.has(normalizedClaude)) { return "claudeAgent"; } const normalizedCodex = normalizeModelSlug(model, "codex"); if (normalizedCodex && MODEL_SLUG_SET_BY_PROVIDER.codex.has(normalizedCodex)) { return "codex"; } return typeof model === "string" && model.trim().startsWith("claude-") ? "claudeAgent" : fallback;} export function getReasoningEffortOptions(provider: "codex"): ReadonlyArray<CodexReasoningEffort>;export function getReasoningEffortOptions( provider: "claudeAgent", model?: string | null | undefined,): ReadonlyArray<ClaudeCodeEffort>;export function getReasoningEffortOptions( provider?: ProviderKind, model?: string | null | undefined,): ReadonlyArray<ProviderReasoningEffort>;export function getReasoningEffortOptions( provider: ProviderKind = "codex", model?: string | null | undefined,): ReadonlyArray<ProviderReasoningEffort> { if (provider === "claudeAgent") { if (supportsClaudeMaxEffort(model)) { return ["low", "medium", "high", "max", "ultrathink"]; } if (supportsClaudeAdaptiveReasoning(model)) { return ["low", "medium", "high", "ultrathink"]; } return []; } return REASONING_EFFORT_OPTIONS_BY_PROVIDER[provider];} export function getDefaultReasoningEffort(provider: "codex"): CodexReasoningEffort;export function getDefaultReasoningEffort(provider: "claudeAgent"): ClaudeCodeEffort;export function getDefaultReasoningEffort(provider?: ProviderKind): ProviderReasoningEffort;export function getDefaultReasoningEffort( provider: ProviderKind = "codex",): ProviderReasoningEffort { return DEFAULT_REASONING_EFFORT_BY_PROVIDER[provider];} export function resolveReasoningEffortForProvider( provider: "codex", effort: string | null | undefined,): CodexReasoningEffort | null;export function resolveReasoningEffortForProvider( provider: "claudeAgent", effort: string | null | undefined,): ClaudeCodeEffort | null;export function resolveReasoningEffortForProvider( provider: ProviderKind, effort: string | null | undefined,): ProviderReasoningEffort | null;export function resolveReasoningEffortForProvider( provider: ProviderKind, effort: string | null | undefined,): ProviderReasoningEffort | null { if (typeof effort !== "string") { return null; } const trimmed = effort.trim(); if (!trimmed) { return null; } const options = REASONING_EFFORT_OPTIONS_BY_PROVIDER[provider] as ReadonlyArray<string>; return options.includes(trimmed) ? (trimmed as ProviderReasoningEffort) : null;} export function getEffectiveClaudeCodeEffort( effort: ClaudeCodeEffort | null | undefined,): Exclude<ClaudeCodeEffort, "ultrathink"> | null { if (!effort) { return null; } return effort === "ultrathink" ? null : effort;} export function normalizeCodexModelOptions( modelOptions: CodexModelOptions | null | undefined,): CodexModelOptions | undefined { const defaultReasoningEffort = getDefaultReasoningEffort("codex"); const reasoningEffort = resolveReasoningEffortForProvider("codex", modelOptions?.reasoningEffort) ?? defaultReasoningEffort; const fastModeEnabled = modelOptions?.fastMode === true; const nextOptions: CodexModelOptions = { ...(reasoningEffort !== defaultReasoningEffort ? { reasoningEffort } : {}), ...(fastModeEnabled ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;} export function normalizeClaudeModelOptions( model: string | null | undefined, modelOptions: ClaudeModelOptions | null | undefined,): ClaudeModelOptions | undefined { const reasoningOptions = getReasoningEffortOptions("claudeAgent", model); const defaultReasoningEffort = getDefaultReasoningEffort("claudeAgent"); const resolvedEffort = resolveReasoningEffortForProvider("claudeAgent", modelOptions?.effort); const effort = resolvedEffort && resolvedEffort !== "ultrathink" && reasoningOptions.includes(resolvedEffort) && resolvedEffort !== defaultReasoningEffort ? resolvedEffort : undefined; const thinking = supportsClaudeThinkingToggle(model) && modelOptions?.thinking === false ? false : undefined; const fastMode = supportsClaudeFastMode(model) && modelOptions?.fastMode === true ? true : undefined; const nextOptions: ClaudeModelOptions = { ...(thinking === false ? { thinking: false } : {}), ...(effort ? { effort } : {}), ...(fastMode ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;} export function applyClaudePromptEffortPrefix( text: string, effort: ClaudeCodeEffort | null | undefined,): string { const trimmed = text.trim(); if (!trimmed) { return trimmed; } if (effort !== "ultrathink") { return trimmed; } if (trimmed.startsWith("Ultrathink:")) { return trimmed; } return `Ultrathink:\n${trimmed}`;} export { CLAUDE_CODE_EFFORT_OPTIONS, CODEX_REASONING_EFFORT_OPTIONS };
import {
CLAUDE_CODE_EFFORT_OPTIONS,
CODEX_REASONING_EFFORT_OPTIONS,
DEFAULT_MODEL_BY_PROVIDER,
DEFAULT_REASONING_EFFORT_BY_PROVIDER,
MODEL_OPTIONS_BY_PROVIDER,
MODEL_SLUG_ALIASES_BY_PROVIDER,
REASONING_EFFORT_OPTIONS_BY_PROVIDER,
type ClaudeModelOptions,
type ClaudeCodeEffort,
type CodexModelOptions,
type CodexReasoningEffort,
type ModelSlug,
type ProviderReasoningEffort,
type ProviderKind,
} from "@t3tools/contracts";
const MODEL_SLUG_SET_BY_PROVIDER: Record<ProviderKind, ReadonlySet<ModelSlug>> = {
claudeAgent: new Set(MODEL_OPTIONS_BY_PROVIDER.claudeAgent.map((option) => option.slug)),
codex: new Set(MODEL_OPTIONS_BY_PROVIDER.codex.map((option) => option.slug)),
};
const CLAUDE_OPUS_4_6_MODEL = "claude-opus-4-6";
const CLAUDE_SONNET_4_6_MODEL = "claude-sonnet-4-6";
const CLAUDE_HAIKU_4_5_MODEL = "claude-haiku-4-5";
export function getModelOptions(provider: ProviderKind = "codex") {
return MODEL_OPTIONS_BY_PROVIDER[provider];
}
export function getDefaultModel(provider: ProviderKind = "codex"): ModelSlug {
return DEFAULT_MODEL_BY_PROVIDER[provider];
}
export function supportsClaudeFastMode(model: string | null | undefined): boolean {
return normalizeModelSlug(model, "claudeAgent") === CLAUDE_OPUS_4_6_MODEL;
}
export function supportsClaudeAdaptiveReasoning(model: string | null | undefined): boolean {
const normalized = normalizeModelSlug(model, "claudeAgent");
return normalized === CLAUDE_OPUS_4_6_MODEL || normalized === CLAUDE_SONNET_4_6_MODEL;
}
export function supportsClaudeMaxEffort(model: string | null | undefined): boolean {
return normalizeModelSlug(model, "claudeAgent") === CLAUDE_OPUS_4_6_MODEL;
}
export function supportsClaudeUltrathinkKeyword(model: string | null | undefined): boolean {
return supportsClaudeAdaptiveReasoning(model);
}
export function supportsClaudeThinkingToggle(model: string | null | undefined): boolean {
return normalizeModelSlug(model, "claudeAgent") === CLAUDE_HAIKU_4_5_MODEL;
}
export function isClaudeUltrathinkPrompt(text: string | null | undefined): boolean {
return typeof text === "string" && /\bultrathink\b/i.test(text);
}
export function normalizeModelSlug(
model: string | null | undefined,
provider: ProviderKind = "codex",
): ModelSlug | null {
if (typeof model !== "string") {
return null;
}
const trimmed = model.trim();
if (!trimmed) {
return null;
}
const aliases = MODEL_SLUG_ALIASES_BY_PROVIDER[provider] as Record<string, ModelSlug>;
const aliased = Object.prototype.hasOwnProperty.call(aliases, trimmed)
? aliases[trimmed]
: undefined;
return typeof aliased === "string" ? aliased : (trimmed as ModelSlug);
}
export function resolveModelSlug(
model: string | null | undefined,
provider: ProviderKind = "codex",
): ModelSlug {
const normalized = normalizeModelSlug(model, provider);
if (!normalized) {
return DEFAULT_MODEL_BY_PROVIDER[provider];
}
return MODEL_SLUG_SET_BY_PROVIDER[provider].has(normalized)
? normalized
: DEFAULT_MODEL_BY_PROVIDER[provider];
}
export function resolveModelSlugForProvider(
provider: ProviderKind,
model: string | null | undefined,
): ModelSlug {
return resolveModelSlug(model, provider);
}
export function inferProviderForModel(
model: string | null | undefined,
fallback: ProviderKind = "codex",
): ProviderKind {
const normalizedClaude = normalizeModelSlug(model, "claudeAgent");
if (normalizedClaude && MODEL_SLUG_SET_BY_PROVIDER.claudeAgent.has(normalizedClaude)) {
return "claudeAgent";
}
const normalizedCodex = normalizeModelSlug(model, "codex");
if (normalizedCodex && MODEL_SLUG_SET_BY_PROVIDER.codex.has(normalizedCodex)) {
return "codex";
}
return typeof model === "string" && model.trim().startsWith("claude-") ? "claudeAgent" : fallback;
}
export function getReasoningEffortOptions(provider: "codex"): ReadonlyArray<CodexReasoningEffort>;
export function getReasoningEffortOptions(
provider: "claudeAgent",
model?: string | null | undefined,
): ReadonlyArray<ClaudeCodeEffort>;
export function getReasoningEffortOptions(
provider?: ProviderKind,
model?: string | null | undefined,
): ReadonlyArray<ProviderReasoningEffort>;
export function getReasoningEffortOptions(
provider: ProviderKind = "codex",
model?: string | null | undefined,
): ReadonlyArray<ProviderReasoningEffort> {
if (provider === "claudeAgent") {
if (supportsClaudeMaxEffort(model)) {
return ["low", "medium", "high", "max", "ultrathink"];
}
if (supportsClaudeAdaptiveReasoning(model)) {
return ["low", "medium", "high", "ultrathink"];
}
return [];
}
return REASONING_EFFORT_OPTIONS_BY_PROVIDER[provider];
}
export function getDefaultReasoningEffort(provider: "codex"): CodexReasoningEffort;
export function getDefaultReasoningEffort(provider: "claudeAgent"): ClaudeCodeEffort;
export function getDefaultReasoningEffort(provider?: ProviderKind): ProviderReasoningEffort;
export function getDefaultReasoningEffort(
provider: ProviderKind = "codex",
): ProviderReasoningEffort {
return DEFAULT_REASONING_EFFORT_BY_PROVIDER[provider];
}
export function resolveReasoningEffortForProvider(
provider: "codex",
effort: string | null | undefined,
): CodexReasoningEffort | null;
export function resolveReasoningEffortForProvider(
provider: "claudeAgent",
effort: string | null | undefined,
): ClaudeCodeEffort | null;
export function resolveReasoningEffortForProvider(
provider: ProviderKind,
effort: string | null | undefined,
): ProviderReasoningEffort | null;
export function resolveReasoningEffortForProvider(
provider: ProviderKind,
effort: string | null | undefined,
): ProviderReasoningEffort | null {
if (typeof effort !== "string") {
return null;
}
const trimmed = effort.trim();
if (!trimmed) {
return null;
}
const options = REASONING_EFFORT_OPTIONS_BY_PROVIDER[provider] as ReadonlyArray<string>;
return options.includes(trimmed) ? (trimmed as ProviderReasoningEffort) : null;
}
export function getEffectiveClaudeCodeEffort(
effort: ClaudeCodeEffort | null | undefined,
): Exclude<ClaudeCodeEffort, "ultrathink"> | null {
if (!effort) {
return null;
}
return effort === "ultrathink" ? null : effort;
}
export function normalizeCodexModelOptions(
modelOptions: CodexModelOptions | null | undefined,
): CodexModelOptions | undefined {
const defaultReasoningEffort = getDefaultReasoningEffort("codex");
const reasoningEffort =
resolveReasoningEffortForProvider("codex", modelOptions?.reasoningEffort) ??
defaultReasoningEffort;
const fastModeEnabled = modelOptions?.fastMode === true;
const nextOptions: CodexModelOptions = {
...(reasoningEffort !== defaultReasoningEffort ? { reasoningEffort } : {}),
...(fastModeEnabled ? { fastMode: true } : {}),
};
return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;
}
export function normalizeClaudeModelOptions(
model: string | null | undefined,
modelOptions: ClaudeModelOptions | null | undefined,
): ClaudeModelOptions | undefined {
const reasoningOptions = getReasoningEffortOptions("claudeAgent", model);
const defaultReasoningEffort = getDefaultReasoningEffort("claudeAgent");
const resolvedEffort = resolveReasoningEffortForProvider("claudeAgent", modelOptions?.effort);
const effort =
resolvedEffort &&
resolvedEffort !== "ultrathink" &&
reasoningOptions.includes(resolvedEffort) &&
resolvedEffort !== defaultReasoningEffort
? resolvedEffort
: undefined;
const thinking =
supportsClaudeThinkingToggle(model) && modelOptions?.thinking === false ? false : undefined;
const fastMode =
supportsClaudeFastMode(model) && modelOptions?.fastMode === true ? true : undefined;
const nextOptions: ClaudeModelOptions = {
...(thinking === false ? { thinking: false } : {}),
...(effort ? { effort } : {}),
...(fastMode ? { fastMode: true } : {}),
};
return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;
}
export function applyClaudePromptEffortPrefix(
text: string,
effort: ClaudeCodeEffort | null | undefined,
): string {
const trimmed = text.trim();
if (!trimmed) {
return trimmed;
}
if (effort !== "ultrathink") {
return trimmed;
}
if (trimmed.startsWith("Ultrathink:")) {
return trimmed;
}
return `Ultrathink:\n${trimmed}`;
}
export { CLAUDE_CODE_EFFORT_OPTIONS, CODEX_REASONING_EFFORT_OPTIONS };