From 88280d0e4dda39b5418ac9757c95b4bc84233230 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 6 Apr 2026 17:49:17 +0900 Subject: [PATCH] fix(agents): keep prometheus and atlas in canonical core order --- src/plugin-handlers/agent-config-handler.ts | 36 +++++---- .../agent-priority-order.test.ts | 80 +++++++++++++++++++ src/plugin-handlers/config-handler.test.ts | 51 ++++++++++++ 3 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 src/plugin-handlers/agent-priority-order.test.ts diff --git a/src/plugin-handlers/agent-config-handler.ts b/src/plugin-handlers/agent-config-handler.ts index 38e576692..fffa845e3 100644 --- a/src/plugin-handlers/agent-config-handler.ts +++ b/src/plugin-handlers/agent-config-handler.ts @@ -172,6 +172,24 @@ export async function applyAgentConfig(params: { agentConfig["hephaestus"] = builtinAgents.hephaestus; } + if (plannerEnabled) { + const prometheusOverride = params.pluginConfig.agents?.["prometheus"] as + | (Record & { prompt_append?: string }) + | undefined; + + agentConfig["prometheus"] = await buildPrometheusAgentConfig({ + configAgentPlan: configAgent?.plan, + pluginPrometheusOverride: prometheusOverride, + userCategories: params.pluginConfig.categories, + currentModel, + disabledTools: params.pluginConfig.disabled_tools, + }); + } + + if (builtinAgents.atlas) { + agentConfig["atlas"] = builtinAgents.atlas; + } + agentConfig["sisyphus-junior"] = createSisyphusJuniorAgentWithOverrides( params.pluginConfig.agents?.["sisyphus-junior"], (builtinAgents.atlas as { model?: string } | undefined)?.model, @@ -192,20 +210,6 @@ export async function applyAgentConfig(params: { agentConfig["OpenCode-Builder"] = override ? { ...base, ...override } : base; } - if (plannerEnabled) { - const prometheusOverride = params.pluginConfig.agents?.["prometheus"] as - | (Record & { prompt_append?: string }) - | undefined; - - agentConfig["prometheus"] = await buildPrometheusAgentConfig({ - configAgentPlan: configAgent?.plan, - pluginPrometheusOverride: prometheusOverride, - userCategories: params.pluginConfig.categories, - currentModel, - disabledTools: params.pluginConfig.disabled_tools, - }); - } - const filteredConfigAgents = configAgent ? Object.fromEntries( Object.entries(configAgent) @@ -253,7 +257,9 @@ export async function applyAgentConfig(params: { params.config.agent = { ...agentConfig, ...Object.fromEntries( - Object.entries(builtinAgents).filter(([key]) => key !== "sisyphus" && key !== "hephaestus"), + Object.entries(builtinAgents).filter( + ([key]) => key !== "sisyphus" && key !== "hephaestus" && key !== "atlas", + ), ), ...filterDisabledAgents(filteredUserAgents), ...filterDisabledAgents(filteredProjectAgents), diff --git a/src/plugin-handlers/agent-priority-order.test.ts b/src/plugin-handlers/agent-priority-order.test.ts new file mode 100644 index 000000000..2e48f0053 --- /dev/null +++ b/src/plugin-handlers/agent-priority-order.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, test } from "bun:test" + +import { reorderAgentsByPriority } from "./agent-priority-order" +import { getAgentListDisplayName } from "../shared/agent-display-names" + +describe("reorderAgentsByPriority", () => { + test("moves core agents to canonical order and injects runtime order fields", () => { + // given + const sisyphus = getAgentListDisplayName("sisyphus") + const hephaestus = getAgentListDisplayName("hephaestus") + const prometheus = getAgentListDisplayName("prometheus") + const atlas = getAgentListDisplayName("atlas") + const oracle = getAgentListDisplayName("oracle") + + const agents: Record = { + [oracle]: { name: "oracle", mode: "subagent" }, + [atlas]: { name: "atlas", mode: "primary" }, + [prometheus]: { name: "prometheus", mode: "all" }, + [hephaestus]: { name: "hephaestus", mode: "primary" }, + [sisyphus]: { name: "sisyphus", mode: "primary" }, + } + + // when + const result = reorderAgentsByPriority(agents) + + // then + expect(Object.keys(result)).toEqual([ + sisyphus, + hephaestus, + prometheus, + atlas, + oracle, + ]) + expect(result[sisyphus]).toEqual({ + name: "sisyphus", + mode: "primary", + order: 1, + }) + expect(result[hephaestus]).toEqual({ + name: "hephaestus", + mode: "primary", + order: 2, + }) + expect(result[prometheus]).toEqual({ + name: "prometheus", + mode: "all", + order: 3, + }) + expect(result[atlas]).toEqual({ + name: "atlas", + mode: "primary", + order: 4, + }) + expect(result[oracle]).toEqual({ + name: "oracle", + mode: "subagent", + }) + }) + + test("leaves non-object agent configs untouched while still reordering keys", () => { + // given + const sisyphus = getAgentListDisplayName("sisyphus") + const atlas = getAgentListDisplayName("atlas") + + const agents: Record = { + [atlas]: "atlas-config", + custom: "custom-config", + [sisyphus]: "sisyphus-config", + } + + // when + const result = reorderAgentsByPriority(agents) + + // then + expect(Object.keys(result)).toEqual([sisyphus, atlas, "custom"]) + expect(result[sisyphus]).toBe("sisyphus-config") + expect(result[atlas]).toBe("atlas-config") + expect(result.custom).toBe("custom-config") + }) +}) diff --git a/src/plugin-handlers/config-handler.test.ts b/src/plugin-handlers/config-handler.test.ts index 72681de0f..9c6cc5a34 100644 --- a/src/plugin-handlers/config-handler.test.ts +++ b/src/plugin-handlers/config-handler.test.ts @@ -19,6 +19,7 @@ import * as shared from "../shared" import * as configDir from "../shared/opencode-config-dir" import * as permissionCompat from "../shared/permission-compat" import * as modelResolver from "../shared/model-resolver" +import * as agentPriorityOrder from "./agent-priority-order" function createPluginConfig(overrides: Partial = {}): OhMyOpenCodeConfig { return { @@ -115,6 +116,7 @@ afterEach(() => { ;(configDir.getOpenCodeConfigPaths as any)?.mockRestore?.() ;(permissionCompat.migrateAgentConfig as any)?.mockRestore?.() ;(modelResolver.resolveModelWithFallback as any)?.mockRestore?.() + ;(agentPriorityOrder.reorderAgentsByPriority as any)?.mockRestore?.() }) describe("Sisyphus-Junior model inheritance", () => { @@ -212,6 +214,7 @@ describe("Plan agent demote behavior", () => { // #given const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { mockResolvedValue: (value: Record) => void + mock: { calls: unknown[][] } } createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, @@ -252,6 +255,52 @@ describe("Plan agent demote behavior", () => { expect(ordered).toEqual(coreAgents) }) + test("assembles core agents first before priority reorder runs", async () => { + // #given + const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + mockResolvedValue: (value: Record) => void + mock: { calls: unknown[][] } + } + createBuiltinAgentsMock.mockResolvedValue({ + sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, + hephaestus: { name: "hephaestus", prompt: "test", mode: "primary" }, + oracle: { name: "oracle", prompt: "test", mode: "subagent" }, + atlas: { name: "atlas", prompt: "test", mode: "primary" }, + }) + const reorderSpy = spyOn(agentPriorityOrder, "reorderAgentsByPriority") as any + const pluginConfig = createPluginConfig({ + sisyphus_agent: { + planner_enabled: true, + }, + }) + const config: Record = { + model: "anthropic/claude-opus-4-6", + agent: {}, + } + const handler = createConfigHandler({ + ctx: { directory: "/tmp" }, + pluginConfig, + modelCacheState: { + anthropicContext1MEnabled: false, + modelContextLimitsCache: new Map(), + }, + }) + + // #when + await handler(config) + + // #then + const assembledAgentKeys = Object.keys( + reorderSpy.mock.calls.at(0)?.[0] as Record + ) + expect(assembledAgentKeys.slice(0, 4)).toEqual([ + getAgentListDisplayName("sisyphus"), + getAgentListDisplayName("hephaestus"), + getAgentListDisplayName("prometheus"), + getAgentListDisplayName("atlas"), + ]) + }) + test("plan agent should be demoted to subagent without inheriting prometheus prompt", async () => { // #given const pluginConfig = createPluginConfig({ @@ -1255,6 +1304,7 @@ describe("per-agent todowrite/todoread deny when task_system enabled", () => { //#given const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { mockResolvedValue: (value: Record) => void + mock: { calls: unknown[][] } } createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, @@ -1296,6 +1346,7 @@ describe("per-agent todowrite/todoread deny when task_system enabled", () => { //#given const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { mockResolvedValue: (value: Record) => void + mock: { calls: unknown[][] } } createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" },