fix(#2731): skip unauthenticated providers when resolving subagent model

Background subagents (explore/librarian) failed with auth errors because
resolveModelForDelegateTask() always picked the first fallback entry when
availableModels was empty — often an unauthenticated provider like xai
or opencode-go.

Fix: when connectedProvidersCache is populated, iterate fallback chain
and pick the first entry whose provider is in the connected set.
Legacy behavior preserved when cache is null (not yet populated).

- model-selection.ts: use readConnectedProvidersCache to filter fallback chain
- test: 4 new tests for connected-provider-aware resolution
This commit is contained in:
YeonGyu-Kim
2026-03-27 14:37:49 +09:00
parent e65a0ed10d
commit 5765168af4
2 changed files with 107 additions and 6 deletions
+24 -6
View File
@@ -2,7 +2,7 @@ import type { FallbackEntry } from "../../shared/model-requirements"
import { normalizeModel } from "../../shared/model-normalization"
import { fuzzyMatchModel } from "../../shared/model-availability"
import { transformModelForProvider } from "../../shared/provider-model-id-transform"
import { hasConnectedProvidersCache, hasProviderModelsCache } from "../../shared/connected-providers-cache"
import { hasConnectedProvidersCache, hasProviderModelsCache, readConnectedProvidersCache } from "../../shared/connected-providers-cache"
import { log } from "../../shared/logger"
import { parseModelString, parseVariantFromModelID } from "./model-string-parser"
@@ -115,11 +115,29 @@ export function resolveModelForDelegateTask(input: {
const fallbackChain = input.fallbackChain
if (fallbackChain && fallbackChain.length > 0) {
if (input.availableModels.size === 0) {
const first = fallbackChain[0]
const provider = first?.providers?.[0]
if (provider) {
const transformedModelId = transformModelForProvider(provider, first.model)
return { model: `${provider}/${transformedModelId}`, variant: first.variant, fallbackEntry: first, matchedFallback: true }
const connectedProviders = readConnectedProvidersCache()
if (connectedProviders) {
const connectedSet = new Set(connectedProviders)
for (const entry of fallbackChain) {
for (const provider of entry.providers) {
if (connectedSet.has(provider)) {
const transformedModelId = transformModelForProvider(provider, entry.model)
log("[resolveModelForDelegateTask] fallback chain resolved via connected provider", {
provider,
model: entry.model,
})
return { model: `${provider}/${transformedModelId}`, variant: entry.variant, fallbackEntry: entry, matchedFallback: true }
}
}
}
log("[resolveModelForDelegateTask] no connected provider found in fallback chain")
} else {
const first = fallbackChain[0]
const provider = first?.providers?.[0]
if (provider) {
const transformedModelId = transformModelForProvider(provider, first.model)
return { model: `${provider}/${transformedModelId}`, variant: first.variant, fallbackEntry: first, matchedFallback: true }
}
}
} else {
for (const entry of fallbackChain) {