From 370045b310d0854bfe7bfa1bb910be807db2bb67 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 23 Jul 2026 11:11:35 +0200 Subject: [PATCH] fix(sdk): always await in-flight runtime discovery in deferred --model retry The getDiscoverableProviders() guard skipped awaiting runtimeDiscoveryPromise when no config-discovery providers exist, so a cold deferred selector backed only by runtime model managers (extension fetchDynamicModels) with implicit local discovery disabled still resolved against the offline cache. Awaiting unconditionally is free when no runtime managers are registered (refreshRuntimeProviders early-returns); the full refresh fallback stays gated on discoverable providers. --- packages/coding-agent/src/sdk.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/coding-agent/src/sdk.ts b/packages/coding-agent/src/sdk.ts index 37ebd3196..dbe7e082a 100644 --- a/packages/coding-agent/src/sdk.ts +++ b/packages/coding-agent/src/sdk.ts @@ -2084,23 +2084,23 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} // hadn't populated yet. Await the in-flight runtime discovery // already kicked off above (stash + reuse avoids a second concurrent // `#refreshRuntimeDiscoveries` pass for the same runtime model - // managers). `refreshRuntimeProviders()` only covers runtime model + // managers; it resolves instantly when no runtime managers are + // registered). `refreshRuntimeProviders()` only covers runtime model // managers, not config-discovery providers (e.g. user-configured // ollama); fall back to a full cache-aware refresh only when the - // runtime pass didn't surface a match. By then runtime managers - // short-circuit on the fresh cache written by the awaited pass, - // closing the double-fetch window. - if (modelRegistry.getDiscoverableProviders().length > 0) { - await logger.time("resolveModelDiscoveryDeferredRetry", () => runtimeDiscoveryPromise); - const availableModelsAfterRuntime = modelRegistry.getAll(); - const runtimeResolved = deferredModelPatterns.some(pattern => - availableModelsAfterRuntime.some(m => `${m.provider}/${m.id}` === pattern), + // runtime pass didn't surface a match AND config-discovery providers + // exist to fetch from. By then runtime managers short-circuit on the + // fresh cache written by the awaited pass, closing the double-fetch + // window. + await logger.time("resolveModelDiscoveryDeferredRetry", () => runtimeDiscoveryPromise); + const availableModelsAfterRuntime = modelRegistry.getAll(); + const runtimeResolved = deferredModelPatterns.some(pattern => + availableModelsAfterRuntime.some(m => `${m.provider}/${m.id}` === pattern), + ); + if (!runtimeResolved && modelRegistry.getDiscoverableProviders().length > 0) { + await logger.time("resolveModelDiscoveryFallbackNonRuntime", () => + modelRegistry.refresh("online-if-uncached"), ); - if (!runtimeResolved) { - await logger.time("resolveModelDiscoveryFallbackNonRuntime", () => - modelRegistry.refresh("online-if-uncached"), - ); - } } const availableModels = modelRegistry.getAll(); const matchPreferences = getModelMatchPreferences(settings);