From a1645ad6ee6cd1a0bbf1a0670554f5c73f80ba93 Mon Sep 17 00:00:00 2001 From: ismeth Date: Wed, 18 Feb 2026 19:26:46 +0100 Subject: [PATCH] fix(prompts): normalize agent names for continuation injections --- src/features/background-agent/manager.test.ts | 2 +- src/features/background-agent/manager.ts | 6 ++++-- src/hooks/atlas/index.test.ts | 2 +- src/hooks/ralph-loop/continuation-prompt-injector.ts | 4 +++- src/hooks/session-recovery/resume.ts | 5 ++++- .../unstable-agent-babysitter-hook.ts | 3 ++- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/features/background-agent/manager.test.ts b/src/features/background-agent/manager.test.ts index 7ad6fea39..e8cf3daff 100644 --- a/src/features/background-agent/manager.test.ts +++ b/src/features/background-agent/manager.test.ts @@ -889,7 +889,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () => .notifyParentSession(task) //#then - expect(capturedBody?.agent).toBe("sisyphus") + expect(capturedBody?.agent).toBe("Sisyphus (Ultraworker)") expect(capturedBody?.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4.6" }) manager.shutdown() diff --git a/src/features/background-agent/manager.ts b/src/features/background-agent/manager.ts index a59ea9530..86d641968 100644 --- a/src/features/background-agent/manager.ts +++ b/src/features/background-agent/manager.ts @@ -17,6 +17,7 @@ import { createInternalAgentTextPart, } from "../../shared" import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers" +import { normalizeAgentForPrompt } from "../../shared/agent-display-names" import { setSessionTools } from "../../shared/session-tools-store" import { SessionCategoryRegistry } from "../../shared/session-category-registry" import { ConcurrencyManager } from "./concurrency" @@ -1829,10 +1830,11 @@ export class BackgroundManager { } const resolvedTools = resolveInheritedPromptTools(task.parentSessionID, tools) + const promptAgent = normalizeAgentForPrompt(agent) log("[background-agent] notifyParentSession context:", { taskId: task.id, - resolvedAgent: agent, + resolvedAgent: promptAgent, resolvedModel: model, }) @@ -1846,7 +1848,7 @@ export class BackgroundManager { path: { id: task.parentSessionID }, body: { noReply: !shouldReply, - ...(agent !== undefined ? { agent } : {}), + ...(promptAgent !== undefined ? { agent: promptAgent } : {}), ...(model !== undefined ? { model } : {}), ...(variant !== undefined ? { variant } : {}), ...(resolvedTools ? { tools: resolvedTools } : {}), diff --git a/src/hooks/atlas/index.test.ts b/src/hooks/atlas/index.test.ts index 0f9e7d605..e9ad5557d 100644 --- a/src/hooks/atlas/index.test.ts +++ b/src/hooks/atlas/index.test.ts @@ -1804,7 +1804,7 @@ session_id: ses_untrusted_999 // then - should call prompt for sisyphus expect(mockInput._promptMock).toHaveBeenCalled() const callArgs = mockInput._promptMock.mock.calls[0][0] - expect(callArgs.body.agent).toBe("sisyphus") + expect(callArgs.body.agent).toBe("Sisyphus (Ultraworker)") }) test("should preserve display-name agent in continuation prompt when boulder agent uses display form", async () => { diff --git a/src/hooks/ralph-loop/continuation-prompt-injector.ts b/src/hooks/ralph-loop/continuation-prompt-injector.ts index 94df8debf..05e69e3fb 100644 --- a/src/hooks/ralph-loop/continuation-prompt-injector.ts +++ b/src/hooks/ralph-loop/continuation-prompt-injector.ts @@ -8,6 +8,7 @@ import { normalizeSDKResponse, resolveInheritedPromptTools, } from "../../shared" +import { normalizeAgentForPrompt } from "../../shared/agent-display-names" type MessageInfo = { agent?: string @@ -69,6 +70,7 @@ export async function injectContinuationPrompt( } const inheritedTools = resolveInheritedPromptTools(sourceSessionID, tools) + const promptAgent = normalizeAgentForPrompt(agent) const launchModel = model ? { providerID: model.providerID, modelID: model.modelID } @@ -78,7 +80,7 @@ export async function injectContinuationPrompt( await ctx.client.session.promptAsync({ path: { id: options.sessionID }, body: { - ...(agent !== undefined ? { agent } : {}), + ...(promptAgent ? { agent: promptAgent } : {}), ...(launchModel ? { model: launchModel } : {}), ...(launchVariant ? { variant: launchVariant } : {}), ...(inheritedTools ? { tools: inheritedTools } : {}), diff --git a/src/hooks/session-recovery/resume.ts b/src/hooks/session-recovery/resume.ts index 6c42b6315..2a80b3c8b 100644 --- a/src/hooks/session-recovery/resume.ts +++ b/src/hooks/session-recovery/resume.ts @@ -1,4 +1,5 @@ import type { createOpencodeClient } from "@opencode-ai/sdk" +import { normalizeAgentForPrompt } from "../../shared/agent-display-names" import type { MessageData, ResumeConfig } from "./types" import { createInternalAgentTextPart, resolveInheritedPromptTools } from "../../shared" @@ -25,6 +26,8 @@ export function extractResumeConfig(userMessage: MessageData | undefined, sessio } export async function resumeSession(client: Client, config: ResumeConfig): Promise { + const promptAgent = normalizeAgentForPrompt(config.agent) + try { const inheritedTools = resolveInheritedPromptTools(config.sessionID, config.tools) const launchModel = config.model @@ -36,7 +39,7 @@ export async function resumeSession(client: Client, config: ResumeConfig): Promi path: { id: config.sessionID }, body: { parts: [createInternalAgentTextPart(RECOVERY_RESUME_TEXT)], - agent: config.agent, + ...(promptAgent ? { agent: promptAgent } : {}), ...(launchModel ? { model: launchModel } : {}), ...(launchVariant ? { variant: launchVariant } : {}), ...(inheritedTools ? { tools: inheritedTools } : {}), diff --git a/src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts b/src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts index 5821a1738..9390bfb83 100644 --- a/src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts +++ b/src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts @@ -1,5 +1,6 @@ import type { BackgroundManager } from "../../features/background-agent" import { getMainSessionID, getSessionAgent } from "../../features/claude-code-session-state" +import { normalizeAgentForPrompt } from "../../shared/agent-display-names" import { log } from "../../shared/logger" import { createInternalAgentTextPart, resolveInheritedPromptTools } from "../../shared" import { isAbortError } from "../../shared/is-abort-error" @@ -83,7 +84,7 @@ async function resolveMainSessionTarget( log(`[${HOOK_NAME}] Failed to resolve main session agent`, { sessionID, error: String(error) }) } - return { agent, model, tools: resolveInheritedPromptTools(sessionID, tools) } + return { agent: normalizeAgentForPrompt(agent), model, tools: resolveInheritedPromptTools(sessionID, tools) } } async function getThinkingSummary(ctx: BabysitterContext, sessionID: string): Promise {