fix(server): apply provider model overrides to agent default resolution

Built-in provider clients bypassed mergeModels in listModels(), so
configured profileModels/additionalModels were honored by the catalog
but ignored when AgentManager picked a default for new agents.

Wrap the inner client whenever overrides exist (not only when the
provider id differs) and route listModels through mergeModels.

Fixes #579
This commit is contained in:
Mohamed Boudra
2026-04-28 13:41:04 +07:00
parent b58e62136e
commit 01728e7c22
2 changed files with 81 additions and 3 deletions
@@ -905,4 +905,72 @@ describe("model merging", () => {
},
]);
});
test("built-in createClient().listModels() honors profile model replacement (issue #579)", async () => {
mockState.runtimeModels.set("claude", [
{
provider: "claude",
id: "runtime-default",
label: "Runtime Default",
isDefault: true,
},
]);
const registry = buildProviderRegistry(logger, {
providerOverrides: {
claude: {
models: [
{
id: "profile-fast",
label: "Profile Fast",
isDefault: true,
},
],
},
},
});
const client = registry.claude.createClient(logger);
const models = await client.listModels({
cwd: "/tmp/registry-models",
force: false,
});
expect(models.map((model) => model.id)).toEqual(["profile-fast"]);
expect(models.find((model) => model.isDefault)?.id).toBe("profile-fast");
});
test("built-in createClient().listModels() honors additionalModels default (issue #579)", async () => {
mockState.runtimeModels.set("claude", [
{
provider: "claude",
id: "runtime-default",
label: "Runtime Default",
isDefault: true,
},
]);
const registry = buildProviderRegistry(logger, {
providerOverrides: {
claude: {
additionalModels: [
{
id: "profile-default",
label: "Profile Default",
isDefault: true,
},
],
},
},
});
const client = registry.claude.createClient(logger);
const models = await client.listModels({
cwd: "/tmp/registry-models",
force: false,
});
const defaultModel = models.find((model) => model.isDefault) ?? models[0];
expect(defaultModel?.id).toBe("profile-default");
});
});
@@ -308,7 +308,12 @@ function wrapSessionProvider(provider: AgentProvider, inner: AgentSession): Agen
};
}
function wrapClientProvider(provider: AgentProvider, inner: AgentClient): AgentClient {
function wrapClientProvider(
provider: AgentProvider,
inner: AgentClient,
profileModels: ProviderProfileModel[],
additionalModels: ProviderProfileModel[],
): AgentClient {
const listPersistedAgents = inner.listPersistedAgents?.bind(inner);
return {
@@ -343,7 +348,7 @@ function wrapClientProvider(provider: AgentProvider, inner: AgentClient): AgentC
),
),
listModels: async (options) =>
(await inner.listModels(options)).map((model) => mapModel(provider, model)),
mergeModels(provider, profileModels, additionalModels, await inner.listModels(options)),
listModes: inner.listModes?.bind(inner),
listPersistedAgents: listPersistedAgents
? async (options?: ListPersistedAgentsOptions) =>
@@ -398,7 +403,12 @@ function createResolvedProviderClient(
resolved: ResolvedProvider,
): AgentClient {
const inner = resolved.createBaseClient(logger);
return inner.provider === provider ? inner : wrapClientProvider(provider, inner);
const hasModelOverrides =
resolved.profileModels.length > 0 || resolved.additionalModels.length > 0;
if (inner.provider === provider && !hasModelOverrides) {
return inner;
}
return wrapClientProvider(provider, inner, resolved.profileModels, resolved.additionalModels);
}
function buildResolvedBuiltinProviders(