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:
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user