From 2892ca4adf06c93e4dc34a14aa8887550dc0359b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 17 Apr 2026 14:42:38 +0900 Subject: [PATCH] fix(delegate-task): align metadata with opencode core task tool Match opencode core 'task' tool behavior for metadata consistency: 1. Model fallback: When categoryModel/task.model/resumeModel is undefined, fall back to parentContext.model so subagent metadata always includes model info. Thread parentContext into executeSyncContinuation for parity. 2. Task ID consistency: unstable-agent-task was missing taskId and backgroundTaskId in metadata. background_output used inconsistent snake_case 'task_id' vs camelCase 'taskId' elsewhere. Standardize on camelCase: taskId = sessionID (resume id), backgroundTaskId = bg task id. Update text output blocks to use buildTaskMetadataBlock helper. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../create-background-output.metadata.test.ts | 2 +- .../create-background-output.ts | 4 +- .../delegate-task/background-continuation.ts | 3 +- src/tools/delegate-task/background-task.ts | 4 +- .../metadata-model-unification.test.ts | 177 +++++++++++++- .../metadata-task-id-consistency.test.ts | 218 ++++++++++++++++++ .../delegate-task/sync-continuation.test.ts | 20 +- src/tools/delegate-task/sync-continuation.ts | 6 +- src/tools/delegate-task/sync-task.ts | 3 +- src/tools/delegate-task/tools.ts | 2 +- .../delegate-task/unstable-agent-task.ts | 36 ++- 11 files changed, 445 insertions(+), 30 deletions(-) create mode 100644 src/tools/delegate-task/metadata-task-id-consistency.test.ts diff --git a/src/tools/background-task/create-background-output.metadata.test.ts b/src/tools/background-task/create-background-output.metadata.test.ts index 5111667bb..7b031abee 100644 --- a/src/tools/background-task/create-background-output.metadata.test.ts +++ b/src/tools/background-task/create-background-output.metadata.test.ts @@ -59,7 +59,7 @@ describe("createBackgroundOutput metadata", () => { agent: "test-agent", category: undefined, description: "background task", - task_id: "task-1", + backgroundTaskId: "task-1", }, }) diff --git a/src/tools/background-task/create-background-output.ts b/src/tools/background-task/create-background-output.ts index 7e8ac8f3d..56634b191 100644 --- a/src/tools/background-task/create-background-output.ts +++ b/src/tools/background-task/create-background-output.ts @@ -66,11 +66,11 @@ export function createBackgroundOutput(manager: BackgroundOutputManager, client: const meta = { title: formatResolvedTitle(task), metadata: { - task_id: task.id, + backgroundTaskId: task.id, agent: task.agent, category: task.category, description: task.description, - ...(task.sessionID ? { sessionId: task.sessionID } : {}), + ...(task.sessionID ? { sessionId: task.sessionID, taskId: task.sessionID } : {}), } as Record, } await publishToolMetadata(ctx, meta) diff --git a/src/tools/delegate-task/background-continuation.ts b/src/tools/delegate-task/background-continuation.ts index becf732b9..90ea1398b 100644 --- a/src/tools/delegate-task/background-continuation.ts +++ b/src/tools/delegate-task/background-continuation.ts @@ -4,6 +4,7 @@ import { publishToolMetadata } from "../../features/tool-metadata-store" import { formatDetailedError } from "./error-formatting" import { getSessionTools } from "../../shared/session-tools-store" import { buildTaskMetadataBlock } from "../../features/tool-metadata-store/task-metadata-contract" +import { resolveMetadataModel } from "./resolve-metadata-model" import { getTaskID } from "./task-id" export async function executeBackgroundContinuation( @@ -42,7 +43,7 @@ export async function executeBackgroundContinuation( backgroundTaskId: task.id, sessionId: task.sessionID, command: args.command, - model: task.model ? { providerID: task.model.providerID, modelID: task.model.modelID } : undefined, + model: resolveMetadataModel(task.model, parentContext.model), }, } await publishToolMetadata(ctx, bgContMeta) diff --git a/src/tools/delegate-task/background-task.ts b/src/tools/delegate-task/background-task.ts index 17b3bd632..6d982992b 100644 --- a/src/tools/delegate-task/background-task.ts +++ b/src/tools/delegate-task/background-task.ts @@ -11,6 +11,7 @@ import { QUESTION_DENIED_SESSION_PERMISSION } from "../../shared/question-denied import { setSessionFallbackChain } from "../../hooks/model-fallback/hook" import { stripAgentListSortPrefix } from "../../shared/agent-display-names" import { buildTaskMetadataBlock } from "../../features/tool-metadata-store/task-metadata-contract" +import { resolveMetadataModel } from "./resolve-metadata-model" function continueSessionSetup(args: { taskID: string @@ -118,6 +119,7 @@ export async function executeBackgroundTask( SessionCategoryRegistry.register(sessionId, args.category) } + const resolvedModel = resolveMetadataModel(categoryModel, parentContext.model) const metadata = { prompt: args.prompt, agent: task.agent, @@ -129,7 +131,7 @@ export async function executeBackgroundTask( ...(sessionId ? { taskId: sessionId } : {}), backgroundTaskId: task.id, ...(sessionId ? { sessionId } : {}), - ...(categoryModel ? { model: { providerID: categoryModel.providerID, modelID: categoryModel.modelID } } : {}), + ...(resolvedModel ? { model: resolvedModel } : {}), } const unstableMeta = { diff --git a/src/tools/delegate-task/metadata-model-unification.test.ts b/src/tools/delegate-task/metadata-model-unification.test.ts index fbd407015..799b9537e 100644 --- a/src/tools/delegate-task/metadata-model-unification.test.ts +++ b/src/tools/delegate-task/metadata-model-unification.test.ts @@ -161,7 +161,7 @@ describe("metadata model unification", () => { prompt: async () => ({}), }, }, - } as any, deps) + } as any, parentContext, deps) const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) expect(meta).toBeDefined() @@ -169,4 +169,179 @@ describe("metadata model unification", () => { }) }) }) + + describe("#given categoryModel is undefined but parent.model is set", () => { + describe("#when executors publish metadata", () => { + test("#then sync-task metadata falls back to parent.model", async () => { + const { executeSyncTask } = require("./sync-task") + const ctx = makeMockCtx() + const deps = { + createSyncSession: async () => ({ ok: true, sessionID: "ses_sync" }), + sendSyncPrompt: async () => null, + pollSyncSession: async () => null, + fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }), + } + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + subagent_type: "explore", load_skills: [], run_in_background: false, + } + + await executeSyncTask(args, ctx, { + client: { session: { create: async () => ({ data: { id: "ses_sync" } }) } }, + directory: "/tmp", + onSyncSessionCreated: null, + }, parentContext, "explore", undefined, undefined, undefined, undefined, deps) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.model).toEqual(MODEL) + }) + + test("#then background-task metadata falls back to parent.model", async () => { + const { executeBackgroundTask } = require("./background-task") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + load_skills: [], run_in_background: true, subagent_type: "explore", + } + + await executeBackgroundTask(args, ctx, { + manager: { + launch: async () => ({ + id: "bg_1", description: "test", agent: "explore", + status: "pending", sessionID: "ses_bg", + }), + getTask: () => undefined, + }, + } as any, parentContext, "explore", undefined, undefined) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.model).toEqual(MODEL) + }) + + test("#then unstable-agent-task metadata falls back to parent.model", async () => { + const { executeUnstableAgentTask } = require("./unstable-agent-task") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + category: "quick", load_skills: [], run_in_background: false, + } + + const launchedTask = { + id: "bg_unstable", description: "test", agent: "explore", + status: "completed", sessionID: "ses_unstable", + } + + await executeUnstableAgentTask( + args, ctx, + { + manager: { + launch: async () => launchedTask, + getTask: () => launchedTask, + }, + client: { + session: { + status: async () => ({ data: { ses_unstable: { type: "idle" } } }), + messages: async () => ({ + data: [{ + info: { role: "assistant", time: { created: 1 } }, + parts: [{ type: "text", text: "done" }], + }], + }), + }, + }, + syncPollTimeoutMs: 100, + } as any, + parentContext, "explore", undefined, undefined, "anthropic/claude-sonnet-4-6", + ) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.model).toEqual(MODEL) + }) + + test("#then background-continuation metadata falls back to parent.model when task.model missing", async () => { + const { executeBackgroundContinuation } = require("./background-continuation") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "continue", prompt: "keep going", + load_skills: [], run_in_background: true, task_id: "ses_resumed", + } + + await executeBackgroundContinuation(args, ctx, { + manager: { + resume: async () => ({ + id: "bg_2", description: "continue", agent: "explore", + status: "running", sessionID: "ses_resumed", + }), + }, + } as any, parentContext) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.model).toEqual(MODEL) + }) + + test("#then sync-continuation metadata falls back to parent.model when resume model missing", async () => { + const { executeSyncContinuation } = require("./sync-continuation") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "continue", prompt: "keep going", + load_skills: [], run_in_background: false, task_id: "ses_cont", + } + + const deps = { + pollSyncSession: async () => null, + fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }), + } + + await executeSyncContinuation(args, ctx, { + client: { + session: { + messages: async () => ({ data: [] }), + prompt: async () => ({}), + }, + }, + } as any, parentContext, deps) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.model).toEqual(MODEL) + }) + }) + }) + + describe("#given both categoryModel and parent.model are undefined", () => { + test("#when sync-task runs #then metadata.model is undefined without crashing", async () => { + const { executeSyncTask } = require("./sync-task") + const ctx = makeMockCtx() + const deps = { + createSyncSession: async () => ({ ok: true, sessionID: "ses_sync" }), + sendSyncPrompt: async () => null, + pollSyncSession: async () => null, + fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }), + } + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + subagent_type: "explore", load_skills: [], run_in_background: false, + } + + const parentContextWithoutModel: ParentContext = { + sessionID: "ses_parent", + messageID: "msg_parent", + agent: "sisyphus", + } + + await executeSyncTask(args, ctx, { + client: { session: { create: async () => ({ data: { id: "ses_sync" } }) } }, + directory: "/tmp", + onSyncSessionCreated: null, + }, parentContextWithoutModel, "explore", undefined, undefined, undefined, undefined, deps) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.model).toBeUndefined() + }) + }) }) diff --git a/src/tools/delegate-task/metadata-task-id-consistency.test.ts b/src/tools/delegate-task/metadata-task-id-consistency.test.ts new file mode 100644 index 000000000..c9a2a0b8c --- /dev/null +++ b/src/tools/delegate-task/metadata-task-id-consistency.test.ts @@ -0,0 +1,218 @@ +const { describe, test, expect } = require("bun:test") + +import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types" +import type { ParentContext } from "./executor-types" + +const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" } + +function makeMockCtx(): ToolContextWithMetadata & { captured: any[] } { + const captured: any[] = [] + return { + sessionID: "ses_parent", + messageID: "msg_parent", + agent: "sisyphus", + abort: new AbortController().signal, + callID: "call_001", + metadata: async (input: any) => { captured.push(input) }, + captured, + } +} + +const parentContext: ParentContext = { + sessionID: "ses_parent", + messageID: "msg_parent", + agent: "sisyphus", + model: MODEL, +} + +describe("taskId and backgroundTaskId metadata consistency", () => { + describe("#given sync-task runs", () => { + test("#when publishing metadata #then taskId equals sessionId", async () => { + const { executeSyncTask } = require("./sync-task") + const ctx = makeMockCtx() + const deps = { + createSyncSession: async () => ({ ok: true, sessionID: "ses_sync" }), + sendSyncPrompt: async () => null, + pollSyncSession: async () => null, + fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }), + } + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + category: "quick", load_skills: [], run_in_background: false, + } + + await executeSyncTask(args, ctx, { + client: { session: { create: async () => ({ data: { id: "ses_sync" } }) } }, + directory: "/tmp", + onSyncSessionCreated: null, + }, parentContext, "explore", MODEL, undefined, undefined, undefined, deps) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.taskId).toBe("ses_sync") + expect(meta.metadata.sessionId).toBe("ses_sync") + expect(meta.metadata.taskId).toBe(meta.metadata.sessionId) + }) + }) + + describe("#given background-task runs", () => { + test("#when publishing metadata #then taskId is sessionID and backgroundTaskId is task.id", async () => { + const { executeBackgroundTask } = require("./background-task") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + load_skills: [], run_in_background: true, subagent_type: "explore", + } + + await executeBackgroundTask(args, ctx, { + manager: { + launch: async () => ({ + id: "bg_abc123", description: "test", agent: "explore", + status: "pending", sessionID: "ses_xyz789", + }), + getTask: () => undefined, + }, + } as any, parentContext, "explore", MODEL, undefined) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.taskId).toBe("ses_xyz789") + expect(meta.metadata.sessionId).toBe("ses_xyz789") + expect(meta.metadata.backgroundTaskId).toBe("bg_abc123") + }) + }) + + describe("#given unstable-agent-task runs", () => { + test("#when publishing metadata #then taskId and backgroundTaskId are both included", async () => { + const { executeUnstableAgentTask } = require("./unstable-agent-task") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "test", prompt: "do it", + category: "quick", load_skills: [], run_in_background: false, + } + + const launchedTask = { + id: "bg_unstable_abc", description: "test", agent: "explore", + status: "completed", sessionID: "ses_unstable_xyz", + } + + await executeUnstableAgentTask( + args, ctx, + { + manager: { + launch: async () => launchedTask, + getTask: () => launchedTask, + }, + client: { + session: { + status: async () => ({ data: { ses_unstable_xyz: { type: "idle" } } }), + messages: async () => ({ + data: [{ + info: { role: "assistant", time: { created: 1 } }, + parts: [{ type: "text", text: "done" }], + }], + }), + }, + }, + syncPollTimeoutMs: 100, + } as any, + parentContext, "explore", MODEL, undefined, "anthropic/claude-sonnet-4-6", + ) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.taskId).toBe("ses_unstable_xyz") + expect(meta.metadata.sessionId).toBe("ses_unstable_xyz") + expect(meta.metadata.backgroundTaskId).toBe("bg_unstable_abc") + }) + }) + + describe("#given background-continuation runs", () => { + test("#when publishing metadata #then taskId is sessionID and backgroundTaskId is bg.id", async () => { + const { executeBackgroundContinuation } = require("./background-continuation") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "continue", prompt: "keep going", + load_skills: [], run_in_background: true, task_id: "ses_resumed_x", + } + + await executeBackgroundContinuation(args, ctx, { + manager: { + resume: async () => ({ + id: "bg_resumed_y", description: "continue", agent: "explore", + status: "running", sessionID: "ses_resumed_x", model: MODEL, + }), + }, + } as any, parentContext) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.taskId).toBe("ses_resumed_x") + expect(meta.metadata.sessionId).toBe("ses_resumed_x") + expect(meta.metadata.backgroundTaskId).toBe("bg_resumed_y") + }) + }) + + describe("#given sync-continuation runs", () => { + test("#when publishing metadata #then taskId is sessionID", async () => { + const { executeSyncContinuation } = require("./sync-continuation") + const ctx = makeMockCtx() + const args: DelegateTaskArgs = { + description: "continue", prompt: "keep going", + load_skills: [], run_in_background: false, task_id: "ses_cont_abc", + } + + const deps = { + pollSyncSession: async () => null, + fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }), + } + + await executeSyncContinuation(args, ctx, { + client: { + session: { + messages: async () => ({ + data: [{ info: { agent: "explore", model: MODEL } }], + }), + prompt: async () => ({}), + }, + }, + } as any, parentContext, deps) + + const meta = ctx.captured.find((m: any) => m.metadata?.sessionId) + expect(meta).toBeDefined() + expect(meta.metadata.taskId).toBe("ses_cont_abc") + expect(meta.metadata.sessionId).toBe("ses_cont_abc") + }) + }) + + describe("#given background_output runs", () => { + test("#when publishing metadata #then backgroundTaskId is task.id not task_id", async () => { + const { createBackgroundOutput } = require("../background-task/create-background-output") + const ctx = makeMockCtx() + const manager = { + getTask: (id: string) => ({ + id, + sessionID: "ses_bg_session", + agent: "explore", + category: "deep", + description: "test", + status: "completed" as const, + }), + } + const client = { + session: { + messages: async () => ({ data: [] }), + }, + } + + const bgOutput = createBackgroundOutput(manager as any, client as any) + await bgOutput.execute({ task_id: "bg_output_xyz" } as any, ctx as any) + + const meta = ctx.captured.find((m: any) => m.metadata?.backgroundTaskId) + expect(meta).toBeDefined() + expect(meta.metadata.backgroundTaskId).toBe("bg_output_xyz") + expect(meta.metadata.sessionId).toBe("ses_bg_session") + expect(meta.metadata.taskId).toBe("ses_bg_session") + }) + }) +}) diff --git a/src/tools/delegate-task/sync-continuation.test.ts b/src/tools/delegate-task/sync-continuation.test.ts index 47205ce73..37757dbeb 100644 --- a/src/tools/delegate-task/sync-continuation.test.ts +++ b/src/tools/delegate-task/sync-continuation.test.ts @@ -97,7 +97,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { let error: any = null let result: string | null = null try { - result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) } catch (e) { error = e } @@ -159,7 +159,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { let error: any = null let result: string | null = null try { - result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) } catch (e) { error = e } @@ -222,7 +222,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - executeSyncContinuation completes successfully - const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then - toast should be removed exactly once expect(removeTaskCalls.length).toBe(1) @@ -286,7 +286,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - executeSyncContinuation with abort signal - const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then - removeTask should be called at least once (poller and finally may both call it) expect(removeTaskCalls.length).toBeGreaterThanOrEqual(1) @@ -346,7 +346,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { let error: any = null let result: string | null = null try { - result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) } catch (e) { error = e } @@ -403,7 +403,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - executeSyncContinuation completes with agent info in messages - const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then - task_metadata should contain subagent field with the agent name expect(result).toContain("") @@ -457,7 +457,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - executeSyncContinuation completes without agent info - const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + const result = await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then - task_metadata should NOT contain subagent field expect(result).toContain("") @@ -522,7 +522,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then expect(promptAsyncCalls).toHaveLength(1) @@ -592,7 +592,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then expect(promptAsyncCalls).toHaveLength(1) @@ -662,7 +662,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { } //#when - await executeSyncContinuation(args, mockCtx, mockExecutorCtx, deps) + await executeSyncContinuation(args, mockCtx, mockExecutorCtx, { sessionID: "parent-session", messageID: "parent-message" }, deps) //#then expect(promptAsyncCalls).toHaveLength(1) diff --git a/src/tools/delegate-task/sync-continuation.ts b/src/tools/delegate-task/sync-continuation.ts index 45b06ea88..5ec1406b0 100644 --- a/src/tools/delegate-task/sync-continuation.ts +++ b/src/tools/delegate-task/sync-continuation.ts @@ -1,5 +1,5 @@ import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types" -import type { ExecutorContext, SessionMessage } from "./executor-types" +import type { ExecutorContext, ParentContext, SessionMessage } from "./executor-types" import { isPlanFamily } from "./constants" import { publishToolMetadata } from "../../features/tool-metadata-store" import { getTaskToastManager } from "../../features/task-toast-manager" @@ -14,11 +14,13 @@ import { normalizeSDKResponse } from "../../shared" import { buildTaskPrompt } from "./prompt-builder" import { buildTaskMetadataBlock } from "../../features/tool-metadata-store/task-metadata-contract" import { getTaskID } from "./task-id" +import { resolveMetadataModel } from "./resolve-metadata-model" export async function executeSyncContinuation( args: DelegateTaskArgs, ctx: ToolContextWithMetadata, executorCtx: ExecutorContext, + parentContext: ParentContext, deps: SyncContinuationDeps = syncContinuationDeps ): Promise { const { client, syncPollTimeoutMs, sisyphusAgentConfig } = executorCtx @@ -81,7 +83,7 @@ export async function executeSyncContinuation( sessionId: continuationID, sync: true, command: args.command, - model: resumeModel, + model: resolveMetadataModel(resumeModel, parentContext.model), }, } await publishToolMetadata(ctx, syncContMeta) diff --git a/src/tools/delegate-task/sync-task.ts b/src/tools/delegate-task/sync-task.ts index 6cd42bb93..111371a51 100644 --- a/src/tools/delegate-task/sync-task.ts +++ b/src/tools/delegate-task/sync-task.ts @@ -12,6 +12,7 @@ import { syncTaskDeps, type SyncTaskDeps } from "./sync-task-deps" import { setSessionFallbackChain, clearSessionFallbackChain } from "../../hooks/model-fallback/hook" import { retrySyncPromptWithFallbacks } from "./sync-task-fallback" import { buildTaskMetadataBlock } from "../../features/tool-metadata-store/task-metadata-contract" +import { resolveMetadataModel } from "./resolve-metadata-model" export async function executeSyncTask( args: DelegateTaskArgs, @@ -128,7 +129,7 @@ export async function executeSyncTask( sync: true, spawnDepth: spawnContext.childDepth, command: args.command, - model: categoryModel ? { providerID: categoryModel.providerID, modelID: categoryModel.modelID } : undefined, + model: resolveMetadataModel(categoryModel, parentContext.model), }, } await publishToolMetadata(ctx, syncTaskMeta) diff --git a/src/tools/delegate-task/tools.ts b/src/tools/delegate-task/tools.ts index f53d50fb2..397048aa1 100644 --- a/src/tools/delegate-task/tools.ts +++ b/src/tools/delegate-task/tools.ts @@ -162,7 +162,7 @@ export function createDelegateTask(options: DelegateTaskToolOptions): ToolDefini if (runInBackground) { return executeBackgroundContinuation(args, ctx, options, parentContext) } - return executeSyncContinuation(args, ctx, options) + return executeSyncContinuation(args, ctx, options, parentContext) } if (!args.category && !args.subagent_type) { diff --git a/src/tools/delegate-task/unstable-agent-task.ts b/src/tools/delegate-task/unstable-agent-task.ts index 7a6d46e0d..7afffdeee 100644 --- a/src/tools/delegate-task/unstable-agent-task.ts +++ b/src/tools/delegate-task/unstable-agent-task.ts @@ -9,6 +9,8 @@ import { formatDetailedError } from "./error-formatting" import { getSessionTools } from "../../shared/session-tools-store" import { normalizeSDKResponse } from "../../shared" import { QUESTION_DENIED_SESSION_PERMISSION } from "../../shared/question-denied-session-permission" +import { resolveMetadataModel } from "./resolve-metadata-model" +import { buildTaskMetadataBlock } from "../../features/tool-metadata-store/task-metadata-contract" export async function executeUnstableAgentTask( args: DelegateTaskArgs, @@ -75,9 +77,11 @@ export async function executeUnstableAgentTask( load_skills: args.load_skills, description: args.description, run_in_background: args.run_in_background, + taskId: sessionID, + backgroundTaskId: task.id, sessionId: sessionID, command: args.command, - model: categoryModel ? { providerID: categoryModel.providerID, modelID: categoryModel.modelID } : undefined, + model: resolveMetadataModel(categoryModel, parentContext.model), }, } await publishToolMetadata(ctx, bgTaskMeta) @@ -147,9 +151,13 @@ Model: ${actualModel} The task session may contain partial results. - -session_id: ${sessionID} -` +${buildTaskMetadataBlock({ + sessionId: sessionID, + taskId: sessionID, + backgroundTaskId: task.id, + agent: agentToUse, + category: args.category, + })}` } if (!completedDuringMonitoring) { @@ -167,9 +175,13 @@ Model: ${actualModel} The task session may still contain partial results. - -session_id: ${sessionID} -` +${buildTaskMetadataBlock({ + sessionId: sessionID, + taskId: sessionID, + backgroundTaskId: task.id, + agent: agentToUse, + category: args.category, + })}` } const messagesResult = await client.session.messages({ path: { id: sessionID } }) @@ -217,9 +229,13 @@ RESULT: ${textContent || "(No text output)"} - -session_id: ${sessionID} -` +${buildTaskMetadataBlock({ + sessionId: sessionID, + taskId: sessionID, + backgroundTaskId: task.id, + agent: agentToUse, + category: args.category, + })}` } catch (error) { if (!cleanupReason) { cleanupReason = "exception"