Merge pull request #4045 from code-yeongyu/supersede/3901-call-omo-agent-display-name

fix(call-omo-agent): translate config-key subagent_type to display name before SDK dispatch (supersedes #3901)
This commit is contained in:
YeonGyu-Kim
2026-05-15 19:34:21 +09:00
committed by GitHub
6 changed files with 142 additions and 5 deletions
@@ -152,4 +152,32 @@ describe("executeBackgroundAgent", () => {
expect(secondResult).toContain("Task ID: task-2")
expect(secondResult).not.toContain("interrupt")
})
test("#given subagent_type is the lowercase config key 'hephaestus' #when executeBackgroundAgent runs #then BackgroundManager.launch receives the registered display name 'Hephaestus - Deep Agent'", async () => {
//#given
launchMock.mockClear()
launchMock.mockResolvedValueOnce({
id: "task-heph",
sessionId: "ses-heph",
description: "task",
agent: "Hephaestus - Deep Agent",
status: "pending",
})
getTaskMock.mockReturnValueOnce({
id: "task-heph",
sessionId: "ses-heph",
description: "task",
agent: "Hephaestus - Deep Agent",
status: "pending",
})
const args = { ...testArgs, subagent_type: "hephaestus" }
//#when
await executeBackgroundAgent(args, testContext, mockManager, mockClient)
//#then
const launchCall = launchMock.mock.calls.find(([input]) => (input as { agent: string }).agent !== undefined)
expect(launchCall).toBeDefined()
expect((launchCall![0] as { agent: string }).agent).toBe("Hephaestus - Deep Agent")
})
})
@@ -7,6 +7,7 @@ import type { CallOmoAgentArgs } from "./types"
import type { ToolContextWithMetadata } from "./tool-context-with-metadata"
import { getMessageDir } from "./message-storage-directory"
import { getSessionTools } from "../../shared/session-tools-store"
import { getAgentDisplayName, stripAgentListSortPrefix } from "../../shared/agent-display-names"
export async function executeBackgroundAgent(
args: CallOmoAgentArgs,
@@ -39,7 +40,7 @@ export async function executeBackgroundAgent(
const task = await manager.launch({
description: args.description,
prompt: args.prompt,
agent: args.subagent_type,
agent: getAgentDisplayName(stripAgentListSortPrefix(args.subagent_type)),
parentSessionId: toolContext.sessionID,
parentMessageId: toolContext.messageID,
parentAgent,
@@ -126,7 +126,7 @@ describe("executeBackground", () => {
if (!launchArgs) {
throw new Error("Expected launch arguments")
}
expect(launchArgs.agent).toBe("hephaestus")
expect(launchArgs.agent).toBe("Hephaestus - Deep Agent")
})
test("keeps launched background task alive when parent aborts before session id resolves", async () => {
@@ -210,4 +210,48 @@ describe("executeBackground", () => {
expect(secondResult).toContain("Task ID: task-2")
expect(secondResult).not.toContain("interrupt")
})
test("#given subagent_type is the lowercase config key 'hephaestus' #when executeBackground runs #then BackgroundManager.launch receives the registered display name", async () => {
//#given
launchMock.mockClear()
launchMock.mockResolvedValueOnce({
id: "test-task-id",
sessionId: "sub-session",
description: "Test task",
agent: "Hephaestus - Deep Agent",
status: "pending",
})
//#when
await executeBackground({ ...testArgs, subagent_type: "hephaestus" }, testContext, mockManager, mockClient)
//#then
const latestCall = [...launchMock.mock.calls].pop()
if (!latestCall) throw new Error("Expected background manager launch to be called")
const launchArgs = latestCall[0]
if (!launchArgs) throw new Error("Expected launch arguments")
expect(launchArgs.agent).toBe("Hephaestus - Deep Agent")
})
test("#given subagent_type is a same-keyed agent 'explore' #when executeBackground runs #then BackgroundManager.launch receives the unchanged key (regression guard)", async () => {
//#given
launchMock.mockClear()
launchMock.mockResolvedValueOnce({
id: "test-task-id",
sessionId: "sub-session",
description: "Test task",
agent: "explore",
status: "pending",
})
//#when
await executeBackground({ ...testArgs, subagent_type: "explore" }, testContext, mockManager, mockClient)
//#then
const latestCall = [...launchMock.mock.calls].pop()
if (!latestCall) throw new Error("Expected background manager launch to be called")
const launchArgs = latestCall[0]
if (!launchArgs) throw new Error("Expected launch arguments")
expect(launchArgs.agent).toBe("explore")
})
})
@@ -9,6 +9,7 @@ import { getSessionAgent } from "../../features/claude-code-session-state"
import { getMessageDir } from "./message-dir"
import { getSessionTools } from "../../shared/session-tools-store"
import { sanitizeSubagentType } from "../delegate-task/subagent-discovery"
import { getAgentDisplayName, stripAgentListSortPrefix } from "../../shared/agent-display-names"
export async function executeBackground(
args: CallOmoAgentArgs,
@@ -48,7 +49,7 @@ export async function executeBackground(
const task = await manager.launch({
description: args.description,
prompt: args.prompt,
agent: sanitizeSubagentType(args.subagent_type),
agent: getAgentDisplayName(stripAgentListSortPrefix(sanitizeSubagentType(args.subagent_type))),
parentSessionId: toolContext.sessionID,
parentMessageId: toolContext.messageID,
parentAgent,
@@ -141,6 +141,69 @@ describe("executeSync", () => {
expect(promptInput?.body.agent).toBe("Sisyphus - Ultraworker")
})
test("#given subagent_type is the lowercase config key 'hephaestus' #when executeSync runs #then promptAsync receives the registered display name 'Hephaestus - Deep Agent'", async () => {
//#given
const executeSync = await importExecuteSync()
const deps = createDependencies()
const toolContext = createToolContext()
const recorder = createPromptAsyncRecorder()
const args = {
subagent_type: "hephaestus",
description: "task",
prompt: "do the thing",
run_in_background: false,
}
//#when
await executeSync(args, toolContext, createContext(recorder.promptAsync) as never, deps)
//#then — SDK rejects raw config keys with UnknownError; the dispatch must translate
const promptInput = recorder.getCapturedInput()
expect(promptInput?.body.agent).toBe("Hephaestus - Deep Agent")
})
test("#given subagent_type is the lowercase config key 'sisyphus-junior' #when executeSync runs #then promptAsync receives the registered display name 'Sisyphus-Junior'", async () => {
//#given
const executeSync = await importExecuteSync()
const deps = createDependencies()
const toolContext = createToolContext()
const recorder = createPromptAsyncRecorder()
const args = {
subagent_type: "sisyphus-junior",
description: "task",
prompt: "do the thing",
run_in_background: false,
}
//#when
await executeSync(args, toolContext, createContext(recorder.promptAsync) as never, deps)
//#then
const promptInput = recorder.getCapturedInput()
expect(promptInput?.body.agent).toBe("Sisyphus-Junior")
})
test("#given subagent_type is already a display name like 'explore' (config key == display name) #when executeSync runs #then promptAsync receives 'explore' unchanged", async () => {
//#given a same-keyed agent must not be double-translated
const executeSync = await importExecuteSync()
const deps = createDependencies()
const toolContext = createToolContext()
const recorder = createPromptAsyncRecorder()
const args = {
subagent_type: "explore",
description: "task",
prompt: "do the thing",
run_in_background: false,
}
//#when
await executeSync(args, toolContext, createContext(recorder.promptAsync) as never, deps)
//#then
const promptInput = recorder.getCapturedInput()
expect(promptInput?.body.agent).toBe("explore")
})
test("returns processed response with task metadata footer", async () => {
//#given
const executeSync = await importExecuteSync()
+2 -2
View File
@@ -5,7 +5,7 @@ import { getAgentToolRestrictions, log } from "../../shared"
import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers"
import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
import type { FallbackEntry } from "../../shared/model-requirements"
import { stripAgentListSortPrefix } from "../../shared/agent-display-names"
import { getAgentDisplayName, stripAgentListSortPrefix } from "../../shared/agent-display-names"
import { promptAsyncAfterSessionIdle } from "../../hooks/shared/prompt-async-gate"
import { waitForCompletion } from "./completion-poller"
import { processMessages } from "./message-processor"
@@ -119,7 +119,7 @@ export async function executeSync(
input: {
path: { id: sessionID },
body: {
agent: normalizedSubagentType,
agent: getAgentDisplayName(normalizedSubagentType),
tools: {
...getAgentToolRestrictions(normalizedSubagentType),
task: false,