Merge pull request #4435 from code-yeongyu/fix/issue-4429-team-mode-model-override

fix(agents): preserve user model overrides when team_mode enabled
This commit is contained in:
YeonGyu-Kim
2026-05-25 11:18:18 +09:00
committed by GitHub
3 changed files with 184 additions and 27 deletions
+98
View File
@@ -30,6 +30,104 @@ afterEach(() => {
})
describe("createBuiltinAgents with model overrides", () => {
test("user config models take priority when team_mode is enabled", async () => {
// #given
const providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue(null)
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set())
const overrides = {
sisyphus: { model: "openai/gpt-5.5" },
explore: { model: "minimax-cn-coding-plan/MiniMax-M2.5-highspeed" },
atlas: { model: "google/antigravity-claude-opus-4-5-thinking" },
hephaestus: { model: "github-copilot/gpt-5.5" },
}
try {
// #when
const agentsWithTeamMode = await createBuiltinAgents(
[],
overrides,
undefined,
TEST_DEFAULT_MODEL,
undefined,
undefined,
[],
undefined,
undefined,
undefined,
undefined,
false,
false,
true
)
// #then
expect(agentsWithTeamMode.sisyphus.model).toBe("openai/gpt-5.5")
expect(agentsWithTeamMode.explore.model).toBe("minimax-cn-coding-plan/MiniMax-M2.5-highspeed")
expect(agentsWithTeamMode.atlas.model).toBe("google/antigravity-claude-opus-4-5-thinking")
expect(agentsWithTeamMode.hephaestus.model).toBe("github-copilot/gpt-5.5")
} finally {
providerModelsSpy.mockRestore()
fetchSpy.mockRestore()
}
})
test("team_mode does not change resolved models for user overrides", async () => {
// #given
const providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue(null)
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set())
const overrides = {
sisyphus: { model: "openai/gpt-5.5" },
explore: { model: "minimax-cn-coding-plan/MiniMax-M2.5-highspeed" },
atlas: { model: "google/antigravity-claude-opus-4-5-thinking" },
hephaestus: { model: "github-copilot/gpt-5.5" },
}
try {
// #when
const agentsWithoutTeamMode = await createBuiltinAgents(
[],
overrides,
undefined,
TEST_DEFAULT_MODEL,
undefined,
undefined,
[],
undefined,
undefined,
undefined,
undefined,
false,
false,
false
)
const agentsWithTeamMode = await createBuiltinAgents(
[],
overrides,
undefined,
TEST_DEFAULT_MODEL,
undefined,
undefined,
[],
undefined,
undefined,
undefined,
undefined,
false,
false,
true
)
// #then
expect(agentsWithTeamMode.sisyphus.model).toBe(agentsWithoutTeamMode.sisyphus.model)
expect(agentsWithTeamMode.explore.model).toBe(agentsWithoutTeamMode.explore.model)
expect(agentsWithTeamMode.atlas.model).toBe(agentsWithoutTeamMode.atlas.model)
expect(agentsWithTeamMode.hephaestus.model).toBe(agentsWithoutTeamMode.hephaestus.model)
} finally {
providerModelsSpy.mockRestore()
fetchSpy.mockRestore()
}
})
test("Sisyphus with default model has thinking config when all models available", async () => {
// #given
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
@@ -310,6 +310,62 @@ describe("applyAgentConfig builtin override protection", () => {
expect(result.SiSyPhUs).toBeUndefined()
})
test("filters host config agent display-name aliases before they override resolved builtin models", async () => {
// given
createBuiltinAgentsSpy.mockResolvedValue({
sisyphus: {
name: "sisyphus",
prompt: "resolved sisyphus prompt",
mode: "primary",
model: "openai/gpt-5.5",
},
explore: {
name: "explore",
prompt: "resolved explore prompt",
mode: "subagent",
model: "minimax-cn-coding-plan/MiniMax-M2.5-highspeed",
},
atlas: builtinAtlasConfig,
})
const config = createBaseConfig()
config.agent = {
[getAgentListDisplayName("sisyphus")]: {
name: getAgentListDisplayName("sisyphus"),
prompt: "stale sisyphus prompt",
mode: "primary",
model: "anthropic/claude-opus-4-7",
},
[getAgentListDisplayName("explore")]: {
name: getAgentListDisplayName("explore"),
prompt: "stale explore prompt",
mode: "subagent",
model: "openai/gpt-5.4",
},
}
const pluginConfig = {
...createPluginConfig(),
team_mode: { enabled: true },
agents: {
sisyphus: { model: "openai/gpt-5.5" },
explore: { model: "minimax-cn-coding-plan/MiniMax-M2.5-highspeed" },
},
} as OhMyOpenCodeConfig
// when
const result = await applyAgentConfig({
config,
pluginConfig,
ctx: { directory: "/tmp" },
pluginComponents: createPluginComponents(),
})
// then
expect((result[getAgentListDisplayName("sisyphus")] as AgentConfig).model).toBe("openai/gpt-5.5")
expect((result[getAgentListDisplayName("explore")] as AgentConfig).model).toBe(
"minimax-cn-coding-plan/MiniMax-M2.5-highspeed"
)
})
test("filters plugin agents whose key matches the builtin display-name alias", async () => {
// given
const pluginComponents = createPluginComponents()
+30 -27
View File
@@ -259,24 +259,6 @@ export async function applyAgentConfig(params: {
agentConfig["OpenCode-Builder"] = override ? { ...base, ...override } : base;
}
const filteredConfigAgents = configAgent
? Object.fromEntries(
Object.entries(configAgent)
.filter(([key]) => {
if (key === "build") return false;
if (key === "plan" && shouldDemotePlan) return false;
if (key in builtinAgents) return false;
return true;
})
.map(([key, value]) => {
if (!value) return [key, value];
const migrated = migrateAgentConfig(value as Record<string, unknown>);
if (!migrated.mode) migrated.mode = "subagent";
return [key, migrated];
}),
)
: {};
const migratedBuild = configAgent?.build
? migrateAgentConfig(configAgent.build as Record<string, unknown>)
: {};
@@ -292,6 +274,26 @@ export async function applyAgentConfig(params: {
...Object.keys(agentConfig),
...Object.keys(builtinAgents),
]);
const filteredConfigAgentSource = configAgent
? filterProtectedAgentOverrides(
Object.fromEntries(
Object.entries(configAgent).filter(([key]) => {
if (key === "build") return false;
if (key === "plan" && shouldDemotePlan) return false;
return true;
}),
),
protectedBuiltinAgentNames,
)
: {};
const filteredConfigAgents = Object.fromEntries(
Object.entries(filteredConfigAgentSource).map(([key, value]) => {
if (!value) return [key, value];
const migrated = migrateAgentConfig(value as Record<string, unknown>);
if (!migrated.mode) migrated.mode = "subagent";
return [key, migrated];
}),
);
const filteredUserAgents = filterProtectedAgentOverrides(
userAgents,
protectedBuiltinAgentNames,
@@ -373,16 +375,17 @@ export async function applyAgentConfig(params: {
protectedBuiltinAgentNames,
);
const defaultedConfigAgents = configAgent
? Object.fromEntries(
Object.entries(configAgent).map(([key, value]) => {
if (!value) return [key, value];
const migrated = migrateAgentConfig(value as Record<string, unknown>);
if (!migrated.mode) migrated.mode = "subagent";
return [key, migrated];
}),
)
const filteredConfigAgentSource = configAgent
? filterProtectedAgentOverrides(configAgent, protectedBuiltinAgentNames)
: {};
const defaultedConfigAgents = Object.fromEntries(
Object.entries(filteredConfigAgentSource).map(([key, value]) => {
if (!value) return [key, value];
const migrated = migrateAgentConfig(value as Record<string, unknown>);
if (!migrated.mode) migrated.mode = "subagent";
return [key, migrated];
}),
);
params.config.agent = {
...builtinAgents,