resolveEffectiveCliArgs function
Returns the effective CliArgs, the resolved provider kind — the same
value, the explicit field saves the caller a re-derivation — the
FA_PROVIDER_* declaration when one is active (the caller needs it
for the roles pinning, the key decision and the extra redaction), the
saved provider id when it was unrecognizable, the saved provider id
when the persisted provider/baseUrl pair was unservable (endpoint-locked
kind, foreign baseUrl — the caller degrades it with a named warning),
and the revalidated binding's warnings when a --model/--base-url
flag authored an unverified dialect mismatch (gh-1624 review r2 — the
flag path renders them like the FA_PROVIDER notice does; warnings the
active preconfig binding already carries are deduped). Both report
fields are null otherwise. See EffectiveCliArgs.
Implementation
EffectiveCliArgs resolveEffectiveCliArgs(
CliArgs parsed,
CliConfig saved, {
required Map<String, String> env,
}) {
final faPreconfig = faProviderPreconfig(parsed, saved, env: env);
final restore = _judgeSavedRestore(saved);
// issue #1973: while the saved slot is the untouched factory triple, the
// first custom provider is the boot default — a config that only ever
// gained custom providers must not start new folders on a provider the
// user never configured. Explicit flags and FA_PROVIDER_* preconfigs keep
// winning below; the fresh-install wizard needs NO custom provider, so it
// can never be short-circuited by this branch.
final customDefault = _customProviderBootDefault(saved);
final restoredKind = restore.endpointConflict
? null
: (customDefault?.kind ?? restore.spec?.kind);
final provider = _bootProvider(
parsed: parsed,
faPreconfig: faPreconfig,
restoredKind: restoredKind,
);
final explicitOverride = parsed.providerExplicit || faPreconfig != null;
final reports = _savedRestoreReports(
savedRaw: restore.raw,
savedSpec: restore.spec,
endpointConflict: restore.endpointConflict,
explicitOverride: explicitOverride,
);
// gh-1570 (L9/E2): an endpoint-conflict DEGRADE must not inherit the
// stale pair — the saved kind was refused over its baseUrl. "Stale" is
// the binding's verdict, not the URL's spelling: the pair rides the
// degraded boot only when it still resolves through the one validating
// constructor (E2 "rebuilds a valid binding") — the gh-760
// mock-endpoint partial write keeps serving, the codex-over-openrouter
// default spelling keeps working, and a pair the degraded kind cannot
// bind (a claude id pinned over api.openai.com) is dropped.
final degraded = restore.endpointConflict && !explicitOverride;
final degradedDefault = resolveCliProviderSpec(provider)?.defaultBaseUrl;
final keepSavedPair = !degraded || _degradedPairResolves(provider, saved);
final modelId =
parsed.model ??
faPreconfig?.modelId ??
customDefault?.modelId ??
saved.modelId;
final baseUrl =
parsed.baseUrl ??
faPreconfig?.baseUrl ??
customDefault?.baseUrl ??
saved.baseUrl;
final effective = CliArgs(
model: keepSavedPair ? modelId : (parsed.model ?? faPreconfig?.modelId),
provider: provider,
baseUrl: keepSavedPair ? baseUrl : (parsed.baseUrl ?? faPreconfig?.baseUrl),
visionModel: parsed.visionModel,
visionBaseUrl: parsed.visionBaseUrl,
transcribeModel: parsed.transcribeModel,
transcribeBaseUrl: parsed.transcribeBaseUrl,
plugins: parsed.plugins,
promptTemplateDirs: parsed.promptTemplateDirs,
mode: parsed.mode ?? saved.mode,
tools: parsed.tools,
redact: parsed.redact ?? saved.redact,
cwd: parsed.cwd,
sessionRoot: parsed.sessionRoot,
session: parsed.session,
);
// gh-1570 (AC3/H2): an explicit `--model`/`--base-url` over a restored
// or preconfigured binding is a PARTIAL override — the effective
// binding (flag slot winning, everything else from the current source)
// is revalidated through the one constructor and a mismatch is a
// boot-time rejection naming both sides, never a silent cross-provider
// ride (`--model gpt-5` over a saved/active anthropic binding dies
// here, before any network call). An ACTIVE FA_PROVIDER_* preconfig is
// a binding too (AC3/H2 review): its kind survives and the flag wins
// its slot, so the same joint check applies.
final bindingWarnings = _revalidatePartialOverride(
parsed,
faPreconfig,
saved,
provider: provider,
baseUrl: baseUrl,
keepSavedPair: keepSavedPair,
degradedDefault: degradedDefault,
);
return EffectiveCliArgs(
args: effective,
provider: provider,
faPreconfig: faPreconfig,
unknownSavedProvider: reports.unknownSavedProvider,
incompatibleSavedEndpoint: reports.incompatibleSavedEndpoint,
bindingWarnings: bindingWarnings,
);
}