diff --git a/src/hooks/start-work/index.test.ts b/src/hooks/start-work/index.test.ts index e51973a4e..c2a4fb09a 100644 --- a/src/hooks/start-work/index.test.ts +++ b/src/hooks/start-work/index.test.ts @@ -7,7 +7,6 @@ import { tmpdir } from "node:os" import { randomUUID } from "node:crypto" import { createStartWorkHook } from "./index" import { createAtlasHook } from "../atlas" -import { getAgentDisplayName, getAgentListDisplayName } from "../../shared/agent-display-names" import { writeBoulderState, clearBoulderState, @@ -467,7 +466,7 @@ You are starting a Sisyphus work session. updateSpy.mockRestore() }) - test("should stamp the outgoing message with Atlas list key so follow-up events keep the handoff", async () => { + test("should stamp the outgoing message with Atlas config key so OpenCode can resolve the agent", async () => { // given const hook = createStartWorkHook(createMockPluginInput()) const output = { @@ -481,8 +480,8 @@ You are starting a Sisyphus work session. output ) - // then - expect(output.message.agent).toBe(getAgentDisplayName("atlas")) + // then - config key, not display name (matches no-sisyphus-gpt / boulder-continuation-injector convention) + expect(output.message.agent).toBe("atlas") }) test("should switch to Atlas even when current session is Sisyphus (regression: #3155)", async () => { @@ -502,7 +501,7 @@ You are starting a Sisyphus work session. ) // atlas is registered in beforeEach, so it must be selected - expect(output.message.agent).toBe(getAgentDisplayName("atlas")) + expect(output.message.agent).toBe("atlas") expect(sessionState.getSessionAgent("ses-sisyphus-to-atlas")).toBe("atlas") }) @@ -525,7 +524,7 @@ You are starting a Sisyphus work session. ) // then - expect(output.message.agent).toBe("Sisyphus - Ultraworker") + expect(output.message.agent).toBe("sisyphus") expect(sessionState.getSessionAgent("ses-prometheus-to-sisyphus")).toBe("sisyphus") }) @@ -553,7 +552,7 @@ You are starting a Sisyphus work session. ) // then - expect(output.message.agent).toBe("Sisyphus - Ultraworker") + expect(output.message.agent).toBe("sisyphus") expect(sessionState.getSessionAgent("ses-prometheus-to-worker")).toBe("sisyphus") expect(readBoulderState(testDir)?.agent).toBe("sisyphus") }) @@ -588,7 +587,7 @@ You are starting a Sisyphus work session. ) // then - expect(output.message.agent).toBe("Sisyphus - Ultraworker") + expect(output.message.agent).toBe("sisyphus") expect(readBoulderState(testDir)?.agent).toBe("sisyphus") }) @@ -623,7 +622,7 @@ You are starting a Sisyphus work session. await atlasHook.handler({ event: { type: "session.idle", properties: { sessionID: "session-123" } } }) // then - expect(output.message.agent).toBe(getAgentDisplayName("atlas")) + expect(output.message.agent).toBe("atlas") expect(readBoulderState(testDir)?.session_ids).toContain("session-123") expect(readBoulderState(testDir)?.agent).toBe("atlas") expect(promptAsyncMock).toHaveBeenCalledTimes(1) @@ -713,7 +712,7 @@ You are starting a Sisyphus work session. await firePendingTimers() // then - expect(output.message.agent).toBe(getAgentDisplayName("atlas")) + expect(output.message.agent).toBe("atlas") expect(readBoulderState(testDir)?.session_ids).toContain("session-123") expect(readBoulderState(testDir)?.agent).toBe("atlas") expect(promptAsyncMock).toHaveBeenCalledTimes(1) diff --git a/src/hooks/start-work/start-work-hook.ts b/src/hooks/start-work/start-work-hook.ts index 7a85491df..b8ad853aa 100644 --- a/src/hooks/start-work/start-work-hook.ts +++ b/src/hooks/start-work/start-work-hook.ts @@ -11,11 +11,6 @@ import { clearBoulderState, } from "../../features/boulder-state" import { log } from "../../shared/logger" -import { - getAgentDisplayName, - getAgentListDisplayName, - stripAgentListSortPrefix, -} from "../../shared/agent-display-names" import { isAgentRegistered, updateSessionAgent, @@ -86,12 +81,9 @@ export function createStartWorkHook(ctx: PluginInput) { const activeAgent = isAgentRegistered("atlas") ? "atlas" : "sisyphus" - const activeAgentDisplayName = activeAgent === "atlas" - ? getAgentListDisplayName(activeAgent) - : getAgentDisplayName(activeAgent) updateSessionAgent(input.sessionID, activeAgent) if (output.message) { - output.message["agent"] = stripAgentListSortPrefix(activeAgentDisplayName) + output.message["agent"] = activeAgent } const existingState = readBoulderState(ctx.directory) diff --git a/src/plugin-handlers/command-config-handler.test.ts b/src/plugin-handlers/command-config-handler.test.ts index 19f31f1e3..0b95395b2 100644 --- a/src/plugin-handlers/command-config-handler.test.ts +++ b/src/plugin-handlers/command-config-handler.test.ts @@ -97,7 +97,7 @@ describe("applyCommandConfig", () => { expect(commandConfig["agents-global-skill"]?.description).toContain("Agents global skill"); }); - test("remaps Atlas command agents to the list display name used by runtime agent lookup", async () => { + test("normalizes Atlas command agents to the config key OpenCode expects for native routing", async () => { // given loadBuiltinCommandsSpy.mockReturnValue({ "start-work": { @@ -119,6 +119,31 @@ describe("applyCommandConfig", () => { // then const commandConfig = config.command as Record; - expect(commandConfig["start-work"]?.agent).toBe(getAgentDisplayName("atlas")); + expect(commandConfig["start-work"]?.agent).toBe("atlas"); + }); + + test("normalizes legacy display-name command agents back to config keys", async () => { + // given + loadBuiltinCommandsSpy.mockReturnValue({ + "start-work": { + name: "start-work", + description: "(builtin) Start work", + template: "template", + agent: getAgentDisplayName("atlas"), + }, + }); + const config: Record = { command: {} }; + + // when + await applyCommandConfig({ + config, + pluginConfig: createPluginConfig(), + ctx: { directory: "/tmp" }, + pluginComponents: createPluginComponents(), + }); + + // then + const commandConfig = config.command as Record; + expect(commandConfig["start-work"]?.agent).toBe("atlas"); }); }); diff --git a/src/plugin-handlers/command-config-handler.ts b/src/plugin-handlers/command-config-handler.ts index f45ff6531..5cb129291 100644 --- a/src/plugin-handlers/command-config-handler.ts +++ b/src/plugin-handlers/command-config-handler.ts @@ -1,5 +1,5 @@ import type { OhMyOpenCodeConfig } from "../config"; -import { getAgentDisplayName } from "../shared/agent-display-names"; +import { getAgentConfigKey } from "../shared/agent-display-names"; import { loadUserCommands, loadProjectCommands, @@ -96,7 +96,7 @@ export async function applyCommandConfig(params: { function remapCommandAgentFields(commands: Record>): void { for (const cmd of Object.values(commands)) { if (cmd?.agent && typeof cmd.agent === "string") { - cmd.agent = getAgentDisplayName(cmd.agent); + cmd.agent = getAgentConfigKey(cmd.agent); } } } diff --git a/src/plugin-interface.test.ts b/src/plugin-interface.test.ts index 86f509de1..4dac3f7be 100644 --- a/src/plugin-interface.test.ts +++ b/src/plugin-interface.test.ts @@ -165,7 +165,7 @@ describe("createPluginInterface - command.execute.before", () => { ) // then - expect(output.message.agent).toBe("Atlas - Plan Executor") + expect(output.message.agent).toBe("atlas") expect(getSessionAgent("ses-command-atlas")).toBe("atlas") expect(readBoulderState(testDir)?.agent).toBe("atlas") }) diff --git a/src/plugin/chat-message.test.ts b/src/plugin/chat-message.test.ts index 4c2757d23..6dd7a0397 100644 --- a/src/plugin/chat-message.test.ts +++ b/src/plugin/chat-message.test.ts @@ -87,7 +87,7 @@ describe("createChatMessageHandler - /start-work integration", () => { await handler(input, output) // then - expect(output.message["agent"]).toBe("Sisyphus - Ultraworker") + expect(output.message["agent"]).toBe("sisyphus") expect(output.parts[0].text).toContain("") expect(output.parts[0].text).toContain("Auto-Selected Plan") expect(output.parts[0].text).toContain("boulder.json has been created") @@ -116,7 +116,7 @@ describe("createChatMessageHandler - /start-work integration", () => { await handler(input, output) // then - expect(output.message["agent"]).toBe("Sisyphus - Ultraworker") + expect(output.message["agent"]).toBe("sisyphus") expect(output.parts[0].text).toContain("") expect(output.parts[0].text).toContain("Auto-Selected Plan") expect(output.parts[0].text).toContain("my-feature-plan")