fix(task): preserved auth fallback for deferred models

Preserve the subagent parent-model auth fallback when an explicit selector resolves only after child runtime provider loading.\n\nFixes #4421
This commit is contained in:
roboomp
2026-07-03 09:29:11 +00:00
parent ddd6ae1182
commit 17163a2b9f
4 changed files with 84 additions and 15 deletions
@@ -21,6 +21,7 @@ describe("createAgentSession deferred model pattern resolution", () => {
});
afterEach(() => {
vi.restoreAllMocks();
for (const authStorage of authStoragesToClose) {
authStorage.close();
}
@@ -162,6 +163,49 @@ describe("createAgentSession deferred model pattern resolution", () => {
expect(modelFallbackMessage).toBe('Model "missing-provider/missing-model" not found');
});
test("uses auth fallback when deferred subagent modelPattern resolves without working credentials", async () => {
const parentModel = getBundledModel("anthropic", "claude-sonnet-4-5");
if (!parentModel) {
throw new Error("Expected bundled anthropic parent model");
}
const authStorage = await AuthStorage.create(path.join(tempDir, "fallback-auth.db"));
authStoragesToClose.push(authStorage);
authStorage.setRuntimeApiKey(parentModel.provider, "test-key");
const modelRegistry = new ModelRegistry(authStorage, path.join(tempDir, "fallback-models.yml"));
const getApiKeySpy = vi.spyOn(modelRegistry, "getApiKey").mockImplementation(async requested => {
if (requested.provider === "runtime-provider") return undefined;
if (requested.provider === parentModel.provider) return "test-key";
return undefined;
});
const { session, modelFallbackMessage } = await createAgentSession({
cwd: tempDir,
agentDir: tempDir,
authStorage,
modelRegistry,
sessionManager: SessionManager.inMemory(),
disableExtensionDiscovery: true,
extensions: [providerExtension],
skills: [],
contextFiles: [],
promptTemplates: [],
slashCommands: [],
enableMCP: false,
enableLsp: false,
skipPythonPreflight: true,
modelPattern: "runtime-provider/runtime-model",
modelPatternAuthFallback: `${parentModel.provider}/${parentModel.id}`,
});
try {
expect(session.model?.provider).toBe(parentModel.provider);
expect(session.model?.id).toBe(parentModel.id);
expect(modelFallbackMessage).toBeUndefined();
} finally {
await session.dispose();
getApiKeySpy.mockRestore();
}
});
test("does not apply default role thinking override when modelPattern is explicit", async () => {
const settings = Settings.isolated({ defaultThinkingLevel: "off" });
settings.setModelRole("smol", "runtime-provider/runtime-reasoning-model");