Merge pull request #1798 from code-yeongyu/feat/subagent-metadata-on-resume
This commit is contained in:
commit
283c7e6cb7
95
src/tools/delegate-task/background-continuation.test.ts
Normal file
95
src/tools/delegate-task/background-continuation.test.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
const { describe, test, expect, mock } = require("bun:test")
|
||||||
|
|
||||||
|
describe("executeBackgroundContinuation - subagent metadata", () => {
|
||||||
|
test("includes subagent in task_metadata when task has agent", async () => {
|
||||||
|
//#given - mock manager.resume returning task with agent info
|
||||||
|
const mockManager = {
|
||||||
|
resume: async () => ({
|
||||||
|
id: "bg_task_001",
|
||||||
|
description: "oracle consultation",
|
||||||
|
agent: "oracle",
|
||||||
|
status: "running",
|
||||||
|
sessionID: "ses_resumed_123",
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockCtx = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
callID: "call-456",
|
||||||
|
metadata: mock(() => Promise.resolve()),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockExecutorCtx = {
|
||||||
|
manager: mockManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentContext = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
messageID: "msg-parent",
|
||||||
|
agent: "sisyphus",
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
session_id: "ses_resumed_123",
|
||||||
|
prompt: "continue working",
|
||||||
|
description: "resume oracle",
|
||||||
|
load_skills: [],
|
||||||
|
run_in_background: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when - executeBackgroundContinuation completes
|
||||||
|
const { executeBackgroundContinuation } = require("./background-continuation")
|
||||||
|
const result = await executeBackgroundContinuation(args, mockCtx, mockExecutorCtx, parentContext)
|
||||||
|
|
||||||
|
//#then - task_metadata should contain subagent field
|
||||||
|
expect(result).toContain("<task_metadata>")
|
||||||
|
expect(result).toContain("subagent: oracle")
|
||||||
|
expect(result).toContain("session_id: ses_resumed_123")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("omits subagent from task_metadata when task agent is undefined", async () => {
|
||||||
|
//#given - mock manager.resume returning task without agent
|
||||||
|
const mockManager = {
|
||||||
|
resume: async () => ({
|
||||||
|
id: "bg_task_002",
|
||||||
|
description: "unknown task",
|
||||||
|
agent: undefined,
|
||||||
|
status: "running",
|
||||||
|
sessionID: "ses_resumed_456",
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockCtx = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
callID: "call-789",
|
||||||
|
metadata: mock(() => Promise.resolve()),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockExecutorCtx = {
|
||||||
|
manager: mockManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentContext = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
messageID: "msg-parent",
|
||||||
|
agent: "sisyphus",
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
session_id: "ses_resumed_456",
|
||||||
|
prompt: "continue",
|
||||||
|
description: "resume task",
|
||||||
|
load_skills: [],
|
||||||
|
run_in_background: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when - executeBackgroundContinuation completes without agent
|
||||||
|
const { executeBackgroundContinuation } = require("./background-continuation")
|
||||||
|
const result = await executeBackgroundContinuation(args, mockCtx, mockExecutorCtx, parentContext)
|
||||||
|
|
||||||
|
//#then - task_metadata should NOT contain subagent field
|
||||||
|
expect(result).toContain("<task_metadata>")
|
||||||
|
expect(result).toContain("session_id: ses_resumed_456")
|
||||||
|
expect(result).not.toContain("subagent:")
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -50,7 +50,7 @@ Use \`background_output\` with task_id="${task.id}" to check progress.
|
|||||||
|
|
||||||
<task_metadata>
|
<task_metadata>
|
||||||
session_id: ${task.sessionID}
|
session_id: ${task.sessionID}
|
||||||
</task_metadata>`
|
${task.agent ? `subagent: ${task.agent}\n` : ""}</task_metadata>`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return formatDetailedError(error, {
|
return formatDetailedError(error, {
|
||||||
operation: "Continue background task",
|
operation: "Continue background task",
|
||||||
|
|||||||
@ -356,4 +356,112 @@ describe("executeSyncContinuation - toast cleanup error paths", () => {
|
|||||||
expect(addTaskCalls.length).toBe(0)
|
expect(addTaskCalls.length).toBe(0)
|
||||||
expect(removeTaskCalls.length).toBe(0)
|
expect(removeTaskCalls.length).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("includes subagent in task_metadata when agent info is present in session messages", async () => {
|
||||||
|
//#given - mock session messages with agent info on the last assistant message
|
||||||
|
const mockClient = {
|
||||||
|
session: {
|
||||||
|
messages: async () => ({
|
||||||
|
data: [
|
||||||
|
{ info: { id: "msg_001", role: "user", time: { created: 1000 }, agent: "oracle" } },
|
||||||
|
{
|
||||||
|
info: { id: "msg_002", role: "assistant", time: { created: 2000 }, finish: "end_turn", agent: "oracle", providerID: "openai", modelID: "gpt-5.2" },
|
||||||
|
parts: [{ type: "text", text: "Response" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
|
status: async () => ({
|
||||||
|
data: { ses_test: { type: "idle" } },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { executeSyncContinuation } = require("./sync-continuation")
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
pollSyncSession: async () => null,
|
||||||
|
fetchSyncResult: async () => ({ ok: true as const, textContent: "Result" }),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockCtx = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
callID: "call-123",
|
||||||
|
metadata: () => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockExecutorCtx = {
|
||||||
|
client: mockClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
session_id: "ses_test_12345678",
|
||||||
|
prompt: "continue working",
|
||||||
|
description: "resume oracle task",
|
||||||
|
load_skills: [],
|
||||||
|
run_in_background: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when - executeSyncContinuation completes with agent info in messages
|
||||||
|
const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps)
|
||||||
|
|
||||||
|
//#then - task_metadata should contain subagent field with the agent name
|
||||||
|
expect(result).toContain("<task_metadata>")
|
||||||
|
expect(result).toContain("subagent: oracle")
|
||||||
|
expect(result).toContain("session_id: ses_test_12345678")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("omits subagent from task_metadata when no agent info in session messages", async () => {
|
||||||
|
//#given - mock session messages without any agent info
|
||||||
|
const mockClient = {
|
||||||
|
session: {
|
||||||
|
messages: async () => ({
|
||||||
|
data: [
|
||||||
|
{ info: { id: "msg_001", role: "user", time: { created: 1000 } } },
|
||||||
|
{
|
||||||
|
info: { id: "msg_002", role: "assistant", time: { created: 2000 }, finish: "end_turn" },
|
||||||
|
parts: [{ type: "text", text: "Response" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
|
status: async () => ({
|
||||||
|
data: { ses_test: { type: "idle" } },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { executeSyncContinuation } = require("./sync-continuation")
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
pollSyncSession: async () => null,
|
||||||
|
fetchSyncResult: async () => ({ ok: true as const, textContent: "Result" }),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockCtx = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
callID: "call-123",
|
||||||
|
metadata: () => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockExecutorCtx = {
|
||||||
|
client: mockClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
session_id: "ses_test_12345678",
|
||||||
|
prompt: "continue working",
|
||||||
|
description: "resume task",
|
||||||
|
load_skills: [],
|
||||||
|
run_in_background: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when - executeSyncContinuation completes without agent info
|
||||||
|
const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps)
|
||||||
|
|
||||||
|
//#then - task_metadata should NOT contain subagent field
|
||||||
|
expect(result).toContain("<task_metadata>")
|
||||||
|
expect(result).toContain("session_id: ses_test_12345678")
|
||||||
|
expect(result).not.toContain("subagent:")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -128,7 +128,7 @@ ${result.textContent || "(No text output)"}
|
|||||||
|
|
||||||
<task_metadata>
|
<task_metadata>
|
||||||
session_id: ${args.session_id}
|
session_id: ${args.session_id}
|
||||||
</task_metadata>`
|
${resumeAgent ? `subagent: ${resumeAgent}\n` : ""}</task_metadata>`
|
||||||
} finally {
|
} finally {
|
||||||
if (toastManager) {
|
if (toastManager) {
|
||||||
toastManager.removeTask(taskId)
|
toastManager.removeTask(taskId)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user