import { getProviderScopedTeamModelLabel } from '@renderer/utils/teamModelCatalog'; import { isDefaultProviderModelSelection } from '@shared/utils/providerModelSelection'; import type { TeamProviderId, TeamProvisioningModelVerificationMode, TeamProvisioningPrepareResult, } from '@shared/types'; export type ProviderPrepareCheckStatus = 'ready' | 'notes' | 'failed'; type PrepareProvisioningFn = ( cwd?: string, providerId?: TeamProviderId, providerIds?: TeamProviderId[], selectedModels?: string[], limitContext?: boolean, modelVerificationMode?: TeamProvisioningModelVerificationMode ) => Promise; interface ProviderPrepareDiagnosticsProgress { status: ProviderPrepareCheckStatus | 'checking'; details: string[]; completedCount: number; totalCount: number; } export interface ProviderPrepareDiagnosticsModelResult { status: 'ready' | 'notes' | 'failed'; line: string; warningLine?: string | null; } export interface ProviderPrepareDiagnosticsCachedSnapshot { status: ProviderPrepareCheckStatus | 'checking'; details: string[]; completedCount: number; totalCount: number; } export interface ProviderPrepareDiagnosticsResult { status: ProviderPrepareCheckStatus; details: string[]; warnings: string[]; modelResultsById: Record; } export function buildReusableProviderPrepareModelResults( modelResultsById: Record ): Record { return Object.fromEntries( Object.entries(modelResultsById).filter(([, result]) => result.status !== 'notes') ); } function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function getModelLabel(providerId: TeamProviderId, modelId: string): string { if (isDefaultProviderModelSelection(modelId)) { return 'Default'; } return getProviderScopedTeamModelLabel(providerId, modelId) ?? modelId; } export function buildProviderPrepareModelCheckingLine( providerId: TeamProviderId, modelId: string ): string { return `${getModelLabel(providerId, modelId)} - checking...`; } function buildModelSuccessLine(providerId: TeamProviderId, modelId: string): string { return `${getModelLabel(providerId, modelId)} - verified`; } function buildModelAvailableLine(providerId: TeamProviderId, modelId: string): string { return `${getModelLabel(providerId, modelId)} - available for launch`; } function buildModelCompatibilityPendingLine(providerId: TeamProviderId, modelId: string): string { return `${getModelLabel(providerId, modelId)} - compatible, deep verification pending...`; } export function getProviderPrepareCachedSnapshot({ providerId, selectedModelIds, cachedModelResultsById, }: { providerId: TeamProviderId; selectedModelIds: string[]; cachedModelResultsById?: Record; }): ProviderPrepareDiagnosticsCachedSnapshot { const reusableModelResultsById = cachedModelResultsById ?? {}; const orderedModelIds = Array.from( new Set(selectedModelIds.map((modelId) => modelId.trim()).filter(Boolean)) ); let completedCount = 0; let hasFailure = false; let hasNotes = false; let hasChecking = false; const details = orderedModelIds.map((modelId) => { const cachedResult = reusableModelResultsById[modelId]; if (!cachedResult) { hasChecking = true; return buildProviderPrepareModelCheckingLine(providerId, modelId); } completedCount += 1; if (cachedResult.status === 'failed') { hasFailure = true; } else if (cachedResult.status === 'notes') { hasNotes = true; } return cachedResult.line; }); return { status: hasChecking ? 'checking' : hasFailure ? 'failed' : hasNotes ? 'notes' : 'ready', details, completedCount, totalCount: orderedModelIds.length, }; } function stripSelectedModelPrefix(modelId: string, message: string): string { const trimmed = message.trim(); if (!trimmed) { return trimmed; } const patterns = [ new RegExp(`^Selected model ${escapeRegExp(modelId)} is unavailable\\.\\s*`, 'i'), new RegExp(`^Selected model ${escapeRegExp(modelId)} could not be verified\\.\\s*`, 'i'), new RegExp(`^Selected model ${escapeRegExp(modelId)} verified for launch\\.\\s*`, 'i'), new RegExp(`^Selected model ${escapeRegExp(modelId)} is available for launch\\.\\s*`, 'i'), new RegExp( `^Selected model ${escapeRegExp(modelId)} is compatible\\. Deep verification pending\\.\\s*`, 'i' ), ]; for (const pattern of patterns) { if (pattern.test(trimmed)) { return trimmed.replace(pattern, '').trim(); } } return trimmed; } function decodeQuotedJsonString(value: string): string { try { return JSON.parse(`"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`) as string; } catch { return value; } } function normalizeModelReason(rawReason: string | null | undefined): string | null { const trimmed = rawReason?.trim() ?? ''; if (!trimmed) { return null; } if ( /The '[^']+' model is not supported when using Codex with a ChatGPT account\./i.test(trimmed) ) { return 'Not available on this Codex native runtime'; } if (/The requested model is not available for your account\./i.test(trimmed)) { return 'Not available for this account'; } if (/token refresh failed:\s*401/i.test(trimmed)) { return 'OpenCode provider authentication failed (token refresh 401)'; } if (/unauthorized|forbidden|\b401\b|\b403\b/i.test(trimmed)) { return 'OpenCode provider authentication failed'; } if ( trimmed.toLowerCase().includes('timeout running:') || trimmed.toLowerCase().includes('timed out') || trimmed.toLowerCase().includes('etimedout') ) { return 'Model verification timed out'; } const detailMatch = /"detail":"((?:\\"|[^"])*)"/i.exec(trimmed); if (detailMatch?.[1]) { return normalizeModelReason(detailMatch[1].replace(/\\"/g, '"').trim()); } const messageMatch = /"message":"((?:\\"|[^"])*)"/i.exec(trimmed); if (messageMatch?.[1]) { const decodedMessage = messageMatch[1].replace(/\\"/g, '"'); const nestedDetailMatch = /"detail":"([^"]+)"/i.exec(decodedMessage); if (nestedDetailMatch?.[1]) { return normalizeModelReason(nestedDetailMatch[1].trim()); } return normalizeModelReason(decodeQuotedJsonString(decodedMessage).trim()); } return trimmed; } function getResultReason(modelId: string, result: TeamProvisioningPrepareResult): string | null { const candidates = [...(result.details ?? []), ...(result.warnings ?? []), result.message] .map((entry) => entry?.trim() ?? '') .filter(Boolean); for (const candidate of candidates) { const stripped = stripSelectedModelPrefix(modelId, candidate); if (stripped) { return normalizeModelReason(stripped); } } return null; } function getModelScopedEntries(modelId: string, result: TeamProvisioningPrepareResult): string[] { const escapedModelId = escapeRegExp(modelId); const scopedPattern = new RegExp(`^Selected model ${escapedModelId}\\b`, 'i'); return [...(result.details ?? []), ...(result.warnings ?? []), result.message] .map((entry) => entry?.trim() ?? '') .filter(Boolean) .filter((entry) => scopedPattern.test(entry)); } function isModelScopedEntryForAnyModel(modelIds: readonly string[], entry: string): boolean { const trimmed = entry.trim(); if (!trimmed) { return false; } return modelIds.some((modelId) => new RegExp(`^Selected model ${escapeRegExp(modelId)}\\b`, 'i').test(trimmed) ); } function looksLikeSingleModelBatchFailure( modelId: string, result: TeamProvisioningPrepareResult ): boolean { const candidates = [...(result.details ?? []), ...(result.warnings ?? []), result.message] .map((entry) => entry?.trim() ?? '') .filter(Boolean); const modelLower = modelId.toLowerCase(); return candidates.some((candidate) => { const lower = candidate.toLowerCase(); return ( lower.includes(modelLower) || lower.includes('requested model') || lower.includes('model is not supported') || lower.includes('model is not available') || lower.includes('selected model') ); }); } function getScopedModelReason(modelId: string, entries: string[]): string | null { for (const entry of entries) { const stripped = stripSelectedModelPrefix(modelId, entry); if (!stripped) { continue; } const normalized = normalizeModelReason(stripped); if (normalized) { return normalized; } } return null; } function buildModelFailureLine( providerId: TeamProviderId, modelId: string, kind: 'unavailable' | 'check failed', reason: string | null ): string { const label = getModelLabel(providerId, modelId); return reason ? `${label} - ${kind} - ${reason}` : `${label} - ${kind}`; } function createRuntimeDetailLines(result: TeamProvisioningPrepareResult): string[] { return [...(result.details ?? []), ...(result.warnings ?? [])]; } function extractTimedOutPreflightProbeModelId(detail: string): string | null { const trimmed = detail.trim(); if (!trimmed) { return null; } if ( !trimmed.toLowerCase().includes('preflight check for `') || !trimmed.toLowerCase().includes('-p` did not complete') ) { return null; } const match = /--model\s+([^\s]+)/i.exec(trimmed); return match?.[1]?.trim() || null; } function isSuppressibleGenericPreflightWarning(detail: string): boolean { const lower = detail.trim().toLowerCase(); if (!lower) { return false; } return ( lower.includes('preflight check failed') || (lower.includes('preflight check for `') && lower.includes('-p` did not complete')) || lower.includes('preflight ping completed but did not return the expected pong') ); } function suppressSupersededRuntimeWarnings(params: { runtimeDetailLines: string[]; runtimeWarnings: string[]; modelResultsById: Map; }): { runtimeDetailLines: string[]; runtimeWarnings: string[]; } { const suppressedEntries = new Set(); const allSelectedModelsReady = params.modelResultsById.size > 0 && Array.from(params.modelResultsById.values()).every((result) => result.status === 'ready'); for (const warning of params.runtimeWarnings) { const probedModelId = extractTimedOutPreflightProbeModelId(warning); if (probedModelId) { if (params.modelResultsById.get(probedModelId)?.status !== 'ready') { continue; } suppressedEntries.add(warning); continue; } if (allSelectedModelsReady && isSuppressibleGenericPreflightWarning(warning)) { suppressedEntries.add(warning); } } return { runtimeDetailLines: params.runtimeDetailLines.filter( (detail) => !suppressedEntries.has(detail) ), runtimeWarnings: params.runtimeWarnings.filter((warning) => !suppressedEntries.has(warning)), }; } function getProgressStatus(params: { completedCount: number; totalCount: number; runtimeWarnings: string[]; modelResultsById: Map; }): ProviderPrepareCheckStatus | 'checking' { if (params.completedCount < params.totalCount) { return 'checking'; } if (Array.from(params.modelResultsById.values()).some((result) => result.status === 'failed')) { return 'failed'; } if ( params.runtimeWarnings.length > 0 || Array.from(params.modelResultsById.values()).some((result) => result.status === 'notes') ) { return 'notes'; } return 'ready'; } function resolveModelResultFromBatch( providerId: TeamProviderId, modelId: string, result: TeamProvisioningPrepareResult, isOnlyModel: boolean ): ProviderPrepareDiagnosticsModelResult { const modelScopedEntries = getModelScopedEntries(modelId, result); const hasModelScopedEntries = modelScopedEntries.length > 0; const scopedReason = getScopedModelReason(modelId, modelScopedEntries); const fallbackBatchReason = isOnlyModel ? (getResultReason(modelId, result) ?? normalizeModelReason(result.message)) : null; const hasVerifiedLine = modelScopedEntries.some((entry) => /selected model .* verified for launch\./i.test(entry) ); if (hasVerifiedLine) { return { status: 'ready', line: buildModelSuccessLine(providerId, modelId), warningLine: null, }; } const hasAvailableLine = modelScopedEntries.some((entry) => /selected model .* is available for launch\./i.test(entry) ); if (hasAvailableLine) { return { status: 'ready', line: buildModelAvailableLine(providerId, modelId), warningLine: null, }; } const hasCompatibilityLine = modelScopedEntries.some((entry) => /selected model .* is compatible\. deep verification pending\./i.test(entry) ); if (hasCompatibilityLine) { return { status: 'notes', line: buildModelCompatibilityPendingLine(providerId, modelId), warningLine: null, }; } const hasUnavailableLine = modelScopedEntries.some((entry) => /selected model .* is unavailable\./i.test(entry) ); if (hasUnavailableLine || (!result.ready && isOnlyModel)) { return { status: 'failed', line: buildModelFailureLine( providerId, modelId, 'unavailable', scopedReason ?? fallbackBatchReason ), warningLine: null, }; } const hasVerificationWarningLine = modelScopedEntries.some((entry) => /selected model .* could not be verified\./i.test(entry) ); if ( hasVerificationWarningLine || ((result.warnings?.length ?? 0) > 0 && isOnlyModel && hasModelScopedEntries) ) { const line = buildModelFailureLine(providerId, modelId, 'check failed', scopedReason); return { status: 'notes', line, warningLine: line, }; } if (result.ready && (result.warnings?.length ?? 0) > 0 && !hasModelScopedEntries) { return { status: 'notes', line: buildModelCompatibilityPendingLine(providerId, modelId), warningLine: null, }; } if (result.ready) { return { status: 'ready', line: buildModelSuccessLine(providerId, modelId), warningLine: null, }; } const line = buildModelFailureLine( providerId, modelId, 'check failed', scopedReason ?? 'Model verification failed' ); return { status: 'notes', line, warningLine: line, }; } function resolveModelResultFromCompatibilityBatch( providerId: TeamProviderId, modelId: string, result: TeamProvisioningPrepareResult, isOnlyModel: boolean ): { kind: 'compatible' } | { kind: 'terminal'; result: ProviderPrepareDiagnosticsModelResult } { const modelScopedEntries = getModelScopedEntries(modelId, result); const scopedReason = getScopedModelReason(modelId, modelScopedEntries); const fallbackBatchReason = isOnlyModel ? (getResultReason(modelId, result) ?? normalizeModelReason(result.message)) : null; const hasVerifiedLine = modelScopedEntries.some((entry) => /selected model .* verified for launch\./i.test(entry) ); if (hasVerifiedLine) { return { kind: 'terminal', result: { status: 'ready', line: buildModelSuccessLine(providerId, modelId), warningLine: null, }, }; } const hasCompatibilityLine = modelScopedEntries.some((entry) => /selected model .* is compatible\. deep verification pending\./i.test(entry) ); if (hasCompatibilityLine || (result.ready && modelScopedEntries.length === 0)) { return { kind: 'compatible' }; } const hasAvailableLine = modelScopedEntries.some((entry) => /selected model .* is available for launch\./i.test(entry) ); if (hasAvailableLine) { return { kind: 'terminal', result: { status: 'ready', line: buildModelAvailableLine(providerId, modelId), warningLine: null, }, }; } const hasUnavailableLine = modelScopedEntries.some((entry) => /selected model .* is unavailable\./i.test(entry) ); if (hasUnavailableLine || (!result.ready && isOnlyModel)) { return { kind: 'terminal', result: { status: 'failed', line: buildModelFailureLine( providerId, modelId, 'unavailable', scopedReason ?? fallbackBatchReason ), warningLine: null, }, }; } const hasVerificationWarningLine = modelScopedEntries.some((entry) => /selected model .* could not be verified\./i.test(entry) ); if (hasVerificationWarningLine) { const line = buildModelFailureLine( providerId, modelId, 'check failed', scopedReason ?? fallbackBatchReason ); return { kind: 'terminal', result: { status: 'notes', line, warningLine: line, }, }; } return { kind: 'terminal', result: { status: 'notes', line: buildModelFailureLine( providerId, modelId, 'check failed', scopedReason ?? fallbackBatchReason ?? 'Model verification failed' ), warningLine: buildModelFailureLine( providerId, modelId, 'check failed', scopedReason ?? fallbackBatchReason ?? 'Model verification failed' ), }, }; } export async function runProviderPrepareDiagnostics({ cwd, providerId, selectedModelIds, prepareProvisioning, limitContext, onModelProgress, cachedModelResultsById, }: { cwd: string; providerId: TeamProviderId; selectedModelIds: string[]; prepareProvisioning: PrepareProvisioningFn; limitContext?: boolean; onModelProgress?: (progress: ProviderPrepareDiagnosticsProgress) => void; cachedModelResultsById?: Record; }): Promise { if (selectedModelIds.length === 0) { const runtimeResult = await prepareProvisioning( cwd, providerId, [providerId], undefined, limitContext ); const runtimeDetailLines = createRuntimeDetailLines(runtimeResult); const runtimeWarnings = [...(runtimeResult.warnings ?? [])]; if (!runtimeResult.ready) { return { status: 'failed', details: [...runtimeDetailLines, ...(runtimeResult.message ? [runtimeResult.message] : [])], warnings: runtimeWarnings, modelResultsById: {}, }; } return { status: runtimeWarnings.length > 0 ? 'notes' : 'ready', details: runtimeDetailLines, warnings: runtimeWarnings, modelResultsById: {}, }; } const orderedModelIds = Array.from( new Set(selectedModelIds.map((modelId) => modelId.trim()).filter(Boolean)) ); const reusableModelResultsById = cachedModelResultsById ?? {}; const modelResultsById = new Map(); const modelLines = new Map(); let runtimeDetailLines: string[] = []; let runtimeWarnings: string[] = []; let completedCount = 0; let hasFailure = false; let hasNotes = false; const modelWarnings: string[] = []; for (const modelId of orderedModelIds) { const cachedResult = reusableModelResultsById[modelId]; if (cachedResult) { modelResultsById.set(modelId, cachedResult); modelLines.set(modelId, cachedResult.line); completedCount += 1; if (cachedResult.status === 'failed') { hasFailure = true; } else if (cachedResult.status === 'notes') { hasNotes = true; } if (cachedResult.warningLine) { modelWarnings.push(cachedResult.warningLine); } continue; } modelLines.set(modelId, buildProviderPrepareModelCheckingLine(providerId, modelId)); } const emitProgress = (): void => { const filteredRuntime = suppressSupersededRuntimeWarnings({ runtimeDetailLines, runtimeWarnings, modelResultsById, }); onModelProgress?.({ status: getProgressStatus({ completedCount, totalCount: orderedModelIds.length, runtimeWarnings: filteredRuntime.runtimeWarnings, modelResultsById, }), details: [ ...filteredRuntime.runtimeDetailLines, ...orderedModelIds.map((modelId) => modelLines.get(modelId) ?? ''), ], completedCount, totalCount: orderedModelIds.length, }); }; emitProgress(); const uncachedModelIds = orderedModelIds.filter((modelId) => !modelResultsById.has(modelId)); if (uncachedModelIds.length === 0) { const runtimeResult = await prepareProvisioning( cwd, providerId, [providerId], undefined, limitContext ); runtimeDetailLines = createRuntimeDetailLines(runtimeResult); runtimeWarnings = [...(runtimeResult.warnings ?? [])]; if (!runtimeResult.ready) { return { status: 'failed', details: [...runtimeDetailLines, ...(runtimeResult.message ? [runtimeResult.message] : [])], warnings: runtimeWarnings, modelResultsById: {}, }; } } else { const recordTerminalModelResult = ( modelId: string, resolvedResult: ProviderPrepareDiagnosticsModelResult ): void => { modelLines.set(modelId, resolvedResult.line); modelResultsById.set(modelId, resolvedResult); completedCount += 1; if (resolvedResult.status === 'failed') { hasFailure = true; } else if (resolvedResult.status === 'notes') { hasNotes = true; } if (resolvedResult.warningLine) { modelWarnings.push(resolvedResult.warningLine); } }; if (providerId === 'opencode') { const compatibilityPassedModelIds: string[] = []; try { const compatibilityResult = await prepareProvisioning( cwd, providerId, [providerId], uncachedModelIds, limitContext, 'compatibility' ); runtimeDetailLines = createRuntimeDetailLines(compatibilityResult).filter( (entry) => !isModelScopedEntryForAnyModel(uncachedModelIds, entry) ); runtimeWarnings = [...(compatibilityResult.warnings ?? [])].filter( (entry) => !isModelScopedEntryForAnyModel(uncachedModelIds, entry) ); const hasModelScopedEntries = uncachedModelIds.some( (modelId) => getModelScopedEntries(modelId, compatibilityResult).length > 0 ); const hasNonModelScopedDiagnostics = runtimeDetailLines.length > 0 || runtimeWarnings.length > 0; const hasSingleModelFallbackReason = uncachedModelIds.length === 1 && looksLikeSingleModelBatchFailure(uncachedModelIds[0], compatibilityResult); if ( !compatibilityResult.ready && !hasModelScopedEntries && (uncachedModelIds.length > 1 || (!hasNonModelScopedDiagnostics && !hasSingleModelFallbackReason)) ) { return { status: 'failed', details: [ ...runtimeDetailLines, ...(compatibilityResult.message ? [compatibilityResult.message] : []), ], warnings: runtimeWarnings, modelResultsById: {}, }; } if (!hasModelScopedEntries && uncachedModelIds.length === 1) { runtimeDetailLines = []; runtimeWarnings = []; } for (const modelId of uncachedModelIds) { const compatibilityResolution = resolveModelResultFromCompatibilityBatch( providerId, modelId, compatibilityResult, uncachedModelIds.length === 1 ); if (compatibilityResolution.kind === 'compatible') { modelLines.set(modelId, buildModelCompatibilityPendingLine(providerId, modelId)); compatibilityPassedModelIds.push(modelId); continue; } recordTerminalModelResult(modelId, compatibilityResolution.result); } } catch (error) { hasNotes = true; const reason = normalizeModelReason( error instanceof Error ? error.message.trim() : String(error).trim() ); for (const modelId of uncachedModelIds) { const line = buildModelFailureLine(providerId, modelId, 'check failed', reason || null); recordTerminalModelResult(modelId, { status: 'notes', line, warningLine: line, }); } } emitProgress(); if (compatibilityPassedModelIds.length === 0) { const filteredRuntime = suppressSupersededRuntimeWarnings({ runtimeDetailLines, runtimeWarnings, modelResultsById, }); const dedupedWarnings = Array.from( new Set([...filteredRuntime.runtimeWarnings, ...modelWarnings]) ); const selectedModelResultsById = Object.fromEntries( orderedModelIds .map((modelId) => [modelId, modelResultsById.get(modelId)] as const) .filter((entry): entry is [string, ProviderPrepareDiagnosticsModelResult] => Boolean(entry[1]) ) ); return { status: hasFailure ? 'failed' : hasNotes || dedupedWarnings.length > 0 ? 'notes' : 'ready', details: [ ...filteredRuntime.runtimeDetailLines, ...orderedModelIds.map((modelId) => modelLines.get(modelId) ?? ''), ], warnings: dedupedWarnings, modelResultsById: selectedModelResultsById, }; } try { const batchedModelResult = await prepareProvisioning( cwd, providerId, [providerId], compatibilityPassedModelIds, limitContext, 'deep' ); runtimeDetailLines = createRuntimeDetailLines(batchedModelResult).filter( (entry) => !isModelScopedEntryForAnyModel(compatibilityPassedModelIds, entry) ); runtimeWarnings = [...(batchedModelResult.warnings ?? [])].filter( (entry) => !isModelScopedEntryForAnyModel(compatibilityPassedModelIds, entry) ); const hasModelScopedEntries = compatibilityPassedModelIds.some( (modelId) => getModelScopedEntries(modelId, batchedModelResult).length > 0 ); const hasNonModelScopedDiagnostics = runtimeDetailLines.length > 0 || runtimeWarnings.length > 0; const hasSingleModelFallbackReason = compatibilityPassedModelIds.length === 1 && looksLikeSingleModelBatchFailure(compatibilityPassedModelIds[0], batchedModelResult); if ( !batchedModelResult.ready && !hasModelScopedEntries && (compatibilityPassedModelIds.length > 1 || (!hasNonModelScopedDiagnostics && !hasSingleModelFallbackReason)) ) { return { status: 'failed', details: [ ...runtimeDetailLines, ...(batchedModelResult.message ? [batchedModelResult.message] : []), ], warnings: runtimeWarnings, modelResultsById: {}, }; } if (!hasModelScopedEntries && compatibilityPassedModelIds.length === 1) { runtimeDetailLines = []; runtimeWarnings = []; } for (const modelId of compatibilityPassedModelIds) { recordTerminalModelResult( modelId, resolveModelResultFromBatch( providerId, modelId, batchedModelResult, compatibilityPassedModelIds.length === 1 ) ); } } catch (error) { hasNotes = true; const reason = normalizeModelReason( error instanceof Error ? error.message.trim() : String(error).trim() ); for (const modelId of compatibilityPassedModelIds) { const line = buildModelFailureLine(providerId, modelId, 'check failed', reason || null); recordTerminalModelResult(modelId, { status: 'notes', line, warningLine: line, }); } } finally { emitProgress(); } } else { try { const compatibilityResult = await prepareProvisioning( cwd, providerId, [providerId], uncachedModelIds, limitContext, 'compatibility' ); runtimeDetailLines = createRuntimeDetailLines(compatibilityResult).filter( (entry) => !isModelScopedEntryForAnyModel(uncachedModelIds, entry) ); runtimeWarnings = [...(compatibilityResult.warnings ?? [])].filter( (entry) => !isModelScopedEntryForAnyModel(uncachedModelIds, entry) ); const hasModelScopedEntries = uncachedModelIds.some( (modelId) => getModelScopedEntries(modelId, compatibilityResult).length > 0 ); const hasNonModelScopedDiagnostics = runtimeDetailLines.length > 0 || runtimeWarnings.length > 0; const hasSingleModelFallbackReason = uncachedModelIds.length === 1 && looksLikeSingleModelBatchFailure(uncachedModelIds[0], compatibilityResult); if ( !compatibilityResult.ready && !hasModelScopedEntries && (uncachedModelIds.length > 1 || (!hasNonModelScopedDiagnostics && !hasSingleModelFallbackReason)) ) { return { status: 'failed', details: [ ...runtimeDetailLines, ...(compatibilityResult.message ? [compatibilityResult.message] : []), ], warnings: runtimeWarnings, modelResultsById: {}, }; } if (!hasModelScopedEntries && uncachedModelIds.length === 1) { runtimeDetailLines = []; runtimeWarnings = []; } for (const modelId of uncachedModelIds) { recordTerminalModelResult( modelId, resolveModelResultFromBatch( providerId, modelId, compatibilityResult, uncachedModelIds.length === 1 ) ); } emitProgress(); if (!hasFailure) { try { const deepResult = await prepareProvisioning( cwd, providerId, [providerId], undefined, limitContext, 'deep' ); runtimeDetailLines = createRuntimeDetailLines(deepResult).filter( (entry) => !isModelScopedEntryForAnyModel(uncachedModelIds, entry) ); runtimeWarnings = [...(deepResult.warnings ?? [])].filter( (entry) => !isModelScopedEntryForAnyModel(uncachedModelIds, entry) ); if ( !deepResult.ready && runtimeDetailLines.length === 0 && runtimeWarnings.length === 0 ) { runtimeWarnings = deepResult.message ? [deepResult.message] : []; } } catch (deepError) { hasNotes = true; runtimeWarnings = [ normalizeModelReason( deepError instanceof Error ? deepError.message.trim() : String(deepError).trim() ) ?? 'One-shot diagnostic failed', ]; } } } catch (error) { hasNotes = true; const reason = normalizeModelReason( error instanceof Error ? error.message.trim() : String(error).trim() ); for (const modelId of uncachedModelIds) { const line = buildModelFailureLine(providerId, modelId, 'check failed', reason || null); recordTerminalModelResult(modelId, { status: 'notes', line, warningLine: line, }); } } finally { emitProgress(); } } } const filteredRuntime = suppressSupersededRuntimeWarnings({ runtimeDetailLines, runtimeWarnings, modelResultsById, }); const dedupedWarnings = Array.from( new Set([...filteredRuntime.runtimeWarnings, ...modelWarnings]) ); const selectedModelResultsById = Object.fromEntries( orderedModelIds .map((modelId) => [modelId, modelResultsById.get(modelId)] as const) .filter((entry): entry is [string, ProviderPrepareDiagnosticsModelResult] => Boolean(entry[1]) ) ); return { status: hasFailure ? 'failed' : hasNotes || dedupedWarnings.length > 0 ? 'notes' : 'ready', details: [ ...filteredRuntime.runtimeDetailLines, ...orderedModelIds.map((modelId) => modelLines.get(modelId) ?? ''), ], warnings: dedupedWarnings, modelResultsById: selectedModelResultsById, }; }