From a0c72844d855dd1242f73603a5807be091d224b3 Mon Sep 17 00:00:00 2001 From: Benjamin Davis Date: Wed, 14 Oct 2026 20:46:15 -0700 Subject: [PATCH 1/2] fix(server): stop shipping delegated task results twice When the latest terminal run was the task's own run, task_status and delegate_task returned the full result text in both summary and latestTerminalSummary (20k wasted chars on one measured call, every sampled call duplicated). Return null for the latestTerminal* copies when they would repeat summary and resultContextTransferId; latestTerminalRunId/Status still identify the latest run, and genuinely later follow-up results still populate latestTerminalSummary. --- .../src/mcp/OrchestratorMcpService.test.ts | 114 ++++++++++++++++++ apps/server/src/mcp/OrchestratorMcpService.ts | 21 ++-- ...OrchestratorMcpToolkit.integration.test.ts | 14 +-- .../src/mcp/toolkits/orchestrator/tools.ts | 2 +- 4 files changed, 135 insertions(+), 16 deletions(-) diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index 64f2d86eda945..8f1a555fc2f22 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -182,6 +182,120 @@ describe("OrchestratorMcpService", () => { }), ); + it.effect("omits latestTerminalSummary when it repeats the task summary", () => + Effect.gen(function* () { + const parentThreadId = ThreadId.make("thread:mcp-dedupe-parent"); + const childThreadId = ThreadId.make("thread:mcp-dedupe-child"); + const taskId = NodeId.make("node:mcp-dedupe-task"); + const taskRunId = RunId.make("run:mcp-dedupe-task"); + const followupRunId = RunId.make("run:mcp-dedupe-followup"); + const taskResult = "Delegated result. ".repeat(1_000); + const parentProjection = { + thread: { id: parentThreadId }, + runs: [], + contextTransfers: [], + subagents: [ + { + id: taskId, + threadId: parentThreadId, + origin: "app_owned", + childThreadId, + driver: "codex", + model: "gpt-5.6-terra", + status: "completed", + result: taskResult, + completionDelivery: { state: "acknowledged" }, + }, + ], + } as unknown as OrchestrationV2ThreadProjection; + const taskRun = { + id: taskRunId, + ordinal: 1, + status: "completed", + delegatedTaskId: taskId, + startedAt: DateTime.makeUnsafe("2026-10-03T10:00:00Z"), + completedAt: DateTime.makeUnsafe("2026-10-03T10:10:00Z"), + }; + const followupRun = { + id: followupRunId, + ordinal: 2, + status: "completed", + startedAt: DateTime.makeUnsafe("2026-10-03T10:20:00Z"), + completedAt: DateTime.makeUnsafe("2026-10-03T10:30:00Z"), + }; + let followupText: string | null = null; + const childProjection = () => + ({ + thread: { id: childThreadId }, + runs: followupText === null ? [taskRun] : [taskRun, followupRun], + contextTransfers: [], + messages: + followupText === null + ? [] + : [ + { + id: "message:mcp-dedupe-followup", + runId: followupRunId, + role: "assistant", + text: followupText, + updatedAt: followupRun.completedAt, + }, + ], + turnItems: [], + subagents: [], + providerThreads: [], + }) as unknown as OrchestrationV2ThreadProjection; + const layerDependencies = Layer.mergeAll( + NodeServices.layer, + Layer.mock(ThreadManagementService.ThreadManagementService)({ + getThreadRecords: (threadId) => + Effect.succeed(threadId === parentThreadId ? parentProjection : childProjection()), + }), + ServerSettings.layerTest(), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ + list: () => Effect.succeed([]), + }), + Layer.mock(ProjectService.ProjectService)({}), + Layer.mock(SecretRequests.SecretRequests)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), + ); + const scope: McpInvocationScope = { + environmentId: EnvironmentId.make("environment:mcp-dedupe"), + requestNamespace: "provider-session:mcp-dedupe", + thread: { + threadId: parentThreadId, + providerSessionId: "provider-session:mcp-dedupe", + providerInstanceId: ProviderInstanceId.make("codex"), + }, + client: undefined, + capabilities: new Set(["orchestration"]), + issuedAt: 1, + }; + + yield* Effect.gen(function* () { + const service = yield* OrchestratorMcpService.OrchestratorMcpService; + const own = yield* service.taskStatus(scope, taskId); + assert.equal(own.summary, taskResult); + assert.equal(own.latestTerminalRunId, taskRunId); + assert.equal(own.latestTerminalStatus, "completed"); + assert.isNull(own.latestTerminalSummary); + + followupText = taskResult; + const identical = yield* service.taskStatus(scope, taskId); + assert.equal(identical.summary, taskResult); + assert.equal(identical.latestTerminalRunId, followupRunId); + assert.isNull(identical.latestTerminalSummary); + + followupText = "Follow-up result."; + const distinct = yield* service.taskStatus(scope, taskId); + assert.equal(distinct.summary, taskResult); + assert.equal(distinct.latestTerminalRunId, followupRunId); + assert.equal(distinct.latestTerminalSummary, "Follow-up result."); + }).pipe(Effect.provide(layerOrchestratorService.pipe(Layer.provide(layerDependencies)))); + }), + ); + it.effect("reports a restart-cut child as working until its continuation settles", () => Effect.gen(function* () { const parentThreadId = ThreadId.make("thread:mcp-restart-parent"); diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 5c0a93cb1c0fc..ace1123129dce 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -1443,6 +1443,13 @@ const make = Effect.gen(function* () { null); const resultTransfer = taskResultTransfer(parentProjection, childProjection, task) ?? null; const terminalStatus = terminalRun === undefined ? null : taskStatusForRun(terminalRun); + const latestTerminalSummary = canExposeTaskRunResult(terminalRun) + ? terminalRun.id === childRun?.id + ? derivedResult + : subagentResultForRun(childProjection, terminalRun).text + : null; + const latestTerminalTransferId = resultTransferForRun(terminalRun)?.id ?? null; + const resultContextTransferId = resultTransfer?.id ?? null; const response = { taskId: task.id, childThreadId: task.childThreadId, @@ -1454,16 +1461,16 @@ const make = Effect.gen(function* () { providerInstanceId: task.providerInstanceId, model: task.model, summary: derivedResult, - resultContextTransferId: resultTransfer?.id ?? null, + resultContextTransferId, latestTerminalRunId: terminalRun?.id ?? null, latestTerminalStatus: terminalStatus !== null && isTerminalTaskStatus(terminalStatus) ? terminalStatus : null, - latestTerminalSummary: canExposeTaskRunResult(terminalRun) - ? terminalRun.id === childRun?.id - ? derivedResult - : subagentResultForRun(childProjection, terminalRun).text - : null, - latestTerminalResultContextTransferId: resultTransferForRun(terminalRun)?.id ?? null, + // Null when it would repeat summary/resultContextTransferId, so the + // task's own result is not shipped twice. + latestTerminalSummary: + latestTerminalSummary === derivedResult ? null : latestTerminalSummary, + latestTerminalResultContextTransferId: + latestTerminalTransferId === resultContextTransferId ? null : latestTerminalTransferId, // A finished task has no question of its own; later child runs belong to other tasks. hasPendingQuestion: !isTerminalTaskStatus(status) && diff --git a/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts b/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts index c518fd4594814..ebaef8a94a10e 100644 --- a/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts @@ -1801,10 +1801,10 @@ describe("orchestrator MCP toolkit", () => { hasPendingChildRuns: false, latestTerminalRunId: delegated.childRunId, latestTerminalStatus: "completed", - latestTerminalSummary: delegatedResult, + latestTerminalSummary: null, + latestTerminalResultContextTransferId: null, }); expect(delegatedStatus.resultContextTransferId).not.toBeNull(); - expect(delegatedStatus.latestTerminalResultContextTransferId).not.toBeNull(); // Wait-mode completion already returned its result while the parent was live. yield* expectOffersToStay(0); @@ -4045,12 +4045,10 @@ describe("orchestrator MCP toolkit", () => { hasPendingChildRuns: false, latestTerminalRunId: delegated.childRunId, latestTerminalStatus: "completed", - latestTerminalSummary: delegatedResult, + latestTerminalSummary: null, + latestTerminalResultContextTransferId: null, }); expect(delegated.resultContextTransferId).not.toBeNull(); - expect(delegated.latestTerminalResultContextTransferId).toBe( - delegated.resultContextTransferId, - ); // Delegated children are subagent threads too, but T3 owns them, so // they keep taking follow-ups (provider-native children do not). @@ -4146,8 +4144,8 @@ describe("orchestrator MCP toolkit", () => { hasPendingChildRuns: true, latestTerminalRunId: delegated.childRunId, latestTerminalStatus: "completed", - latestTerminalSummary: delegatedResult, - latestTerminalResultContextTransferId: delegated.resultContextTransferId, + latestTerminalSummary: null, + latestTerminalResultContextTransferId: null, }); const finalSequence = yield* orchestrator.getThreadEventSequence(delegated.childThreadId); diff --git a/apps/server/src/mcp/toolkits/orchestrator/tools.ts b/apps/server/src/mcp/toolkits/orchestrator/tools.ts index 36c4d78723c06..9407069dfd088 100644 --- a/apps/server/src/mcp/toolkits/orchestrator/tools.ts +++ b/apps/server/src/mcp/toolkits/orchestrator/tools.ts @@ -78,7 +78,7 @@ export const DelegateTaskTool = Tool.make("delegate_task", { const TaskStatusTool = Tool.make("task_status", { description: - "Needs an agent running inside a T3 thread. Read a T3-owned delegated task created by this parent thread. childRunId identifies the original delegated run. workState distinguishes working, waiting_for_children, and result_available; a completed turn with live nested work is not a completed task. summary is the final task result, including provider errors on failure, and remains stable after publication. hasPendingChildRuns reports later queued or executing turns in the backing child thread, even after the task is terminal; it does not reopen the task, and task_cancel stops those turns too. Turns held in a stopped queue wait for the user and do not count. latestTerminal* provides later non-monitor turn results. Reading a terminal result acknowledges its automatic parent delivery.", + "Needs an agent running inside a T3 thread. Read a T3-owned delegated task created by this parent thread. childRunId identifies the original delegated run. workState distinguishes working, waiting_for_children, and result_available; a completed turn with live nested work is not a completed task. summary is the final task result, including provider errors on failure, and remains stable after publication. hasPendingChildRuns reports later queued or executing turns in the backing child thread, even after the task is terminal; it does not reopen the task, and task_cancel stops those turns too. Turns held in a stopped queue wait for the user and do not count. latestTerminal* provides later non-monitor turn results; latestTerminalSummary and latestTerminalResultContextTransferId are null when they would repeat summary and resultContextTransferId. Reading a terminal result acknowledges its automatic parent delivery.", parameters: OrchestratorMcpTaskStatusInput, success: OrchestratorMcpDelegateTaskResult, failure: OrchestratorMcpFailure, From d0553b35bbe779df0bc55bfa7e3b109f0e6ad868 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 19 Oct 2026 02:57:49 +0000 Subject: [PATCH 2/2] fix(server): return each delegated result value once --- .../src/mcp/OrchestratorMcpService.test.ts | 114 ------------------ apps/server/src/mcp/OrchestratorMcpService.ts | 21 ++-- ...OrchestratorMcpToolkit.integration.test.ts | 41 ++++--- .../src/mcp/toolkits/orchestrator/tools.ts | 2 +- 4 files changed, 41 insertions(+), 137 deletions(-) diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index 88fd84c9ec300..b524921f02a66 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -182,120 +182,6 @@ describe("OrchestratorMcpService", () => { }), ); - it.effect("omits latestTerminalSummary when it repeats the task summary", () => - Effect.gen(function* () { - const parentThreadId = ThreadId.make("thread:mcp-dedupe-parent"); - const childThreadId = ThreadId.make("thread:mcp-dedupe-child"); - const taskId = NodeId.make("node:mcp-dedupe-task"); - const taskRunId = RunId.make("run:mcp-dedupe-task"); - const followupRunId = RunId.make("run:mcp-dedupe-followup"); - const taskResult = "Delegated result. ".repeat(1_000); - const parentProjection = { - thread: { id: parentThreadId }, - runs: [], - contextTransfers: [], - subagents: [ - { - id: taskId, - threadId: parentThreadId, - origin: "app_owned", - childThreadId, - driver: "codex", - model: "gpt-5.6-terra", - status: "completed", - result: taskResult, - completionDelivery: { state: "acknowledged" }, - }, - ], - } as unknown as OrchestrationV2ThreadProjection; - const taskRun = { - id: taskRunId, - ordinal: 1, - status: "completed", - delegatedTaskId: taskId, - startedAt: DateTime.makeUnsafe("2026-10-03T10:00:00Z"), - completedAt: DateTime.makeUnsafe("2026-10-03T10:10:00Z"), - }; - const followupRun = { - id: followupRunId, - ordinal: 2, - status: "completed", - startedAt: DateTime.makeUnsafe("2026-10-03T10:20:00Z"), - completedAt: DateTime.makeUnsafe("2026-10-03T10:30:00Z"), - }; - let followupText: string | null = null; - const childProjection = () => - ({ - thread: { id: childThreadId }, - runs: followupText === null ? [taskRun] : [taskRun, followupRun], - contextTransfers: [], - messages: - followupText === null - ? [] - : [ - { - id: "message:mcp-dedupe-followup", - runId: followupRunId, - role: "assistant", - text: followupText, - updatedAt: followupRun.completedAt, - }, - ], - turnItems: [], - subagents: [], - providerThreads: [], - }) as unknown as OrchestrationV2ThreadProjection; - const layerDependencies = Layer.mergeAll( - NodeServices.layer, - Layer.mock(ThreadManagementService.ThreadManagementService)({ - getThreadRecords: (threadId) => - Effect.succeed(threadId === parentThreadId ? parentProjection : childProjection()), - }), - ServerSettings.layerTest(), - Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), - Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ - list: () => Effect.succeed([]), - }), - Layer.mock(ProjectService.ProjectService)({}), - Layer.mock(SecretRequests.SecretRequests)({}), - Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), - ); - const scope: McpInvocationScope = { - environmentId: EnvironmentId.make("environment:mcp-dedupe"), - requestNamespace: "provider-session:mcp-dedupe", - thread: { - threadId: parentThreadId, - providerSessionId: "provider-session:mcp-dedupe", - providerInstanceId: ProviderInstanceId.make("codex"), - }, - client: undefined, - capabilities: new Set(["orchestration"]), - issuedAt: 1, - }; - - yield* Effect.gen(function* () { - const service = yield* OrchestratorMcpService.OrchestratorMcpService; - const own = yield* service.taskStatus(scope, taskId); - assert.equal(own.summary, taskResult); - assert.equal(own.latestTerminalRunId, taskRunId); - assert.equal(own.latestTerminalStatus, "completed"); - assert.isNull(own.latestTerminalSummary); - - followupText = taskResult; - const identical = yield* service.taskStatus(scope, taskId); - assert.equal(identical.summary, taskResult); - assert.equal(identical.latestTerminalRunId, followupRunId); - assert.isNull(identical.latestTerminalSummary); - - followupText = "Follow-up result."; - const distinct = yield* service.taskStatus(scope, taskId); - assert.equal(distinct.summary, taskResult); - assert.equal(distinct.latestTerminalRunId, followupRunId); - assert.equal(distinct.latestTerminalSummary, "Follow-up result."); - }).pipe(Effect.provide(layerOrchestratorService.pipe(Layer.provide(layerDependencies)))); - }), - ); - it.effect("reports a restart-cut child as working until its continuation settles", () => Effect.gen(function* () { const parentThreadId = ThreadId.make("thread:mcp-restart-parent"); diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 004f577dc1b40..52280be44d034 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -1453,6 +1453,13 @@ const make = Effect.gen(function* () { null); const resultTransfer = taskResultTransfer(parentProjection, childProjection, task) ?? null; const terminalStatus = terminalRun === undefined ? null : taskStatusForRun(terminalRun); + const latestTerminalSummary = canExposeTaskRunResult(terminalRun) + ? terminalRun.id === childRun?.id + ? derivedResult + : subagentResultForRun(childProjection, terminalRun).text + : null; + const resultContextTransferId = resultTransfer?.id ?? null; + const latestTerminalResultContextTransferId = resultTransferForRun(terminalRun)?.id ?? null; const response = { taskId: task.id, childThreadId: task.childThreadId, @@ -1464,16 +1471,16 @@ const make = Effect.gen(function* () { providerInstanceId: task.providerInstanceId, model: task.model, summary: derivedResult, - resultContextTransferId: resultTransfer?.id ?? null, + resultContextTransferId, latestTerminalRunId: terminalRun?.id ?? null, latestTerminalStatus: terminalStatus !== null && isTerminalTaskStatus(terminalStatus) ? terminalStatus : null, - latestTerminalSummary: canExposeTaskRunResult(terminalRun) - ? terminalRun.id === childRun?.id - ? derivedResult - : subagentResultForRun(childProjection, terminalRun).text - : null, - latestTerminalResultContextTransferId: resultTransferForRun(terminalRun)?.id ?? null, + latestTerminalSummary: + latestTerminalSummary === derivedResult ? null : latestTerminalSummary, + latestTerminalResultContextTransferId: + latestTerminalResultContextTransferId === resultContextTransferId + ? null + : latestTerminalResultContextTransferId, hasPendingQuestion: !isTerminalTaskStatus(status) && childControls.runtimeRequests.some( diff --git a/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts b/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts index be9b78878901b..afa2b3f912c7b 100644 --- a/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts @@ -136,16 +136,15 @@ const delegatedTaskStatusTranscriptFile = new URL( import.meta.url, ); -const readDelegatedTaskStatusTranscript = Effect.fn("readDelegatedTaskStatusTranscript")( - function* () { - const fs = yield* FileSystem.FileSystem; - const text = yield* fs.readFileString( - decodeURIComponent(delegatedTaskStatusTranscriptFile.pathname), - ); - return yield* decodeProviderReplayNdjson(text); - }, - Effect.provide(NodeServices.layer), -); +const readDelegatedTaskStatusTranscript = Effect.fn("readDelegatedTaskStatusTranscript")(function* ( + followupResult: string, +) { + const fs = yield* FileSystem.FileSystem; + const text = yield* fs.readFileString( + decodeURIComponent(delegatedTaskStatusTranscriptFile.pathname), + ); + return yield* decodeProviderReplayNdjson(text.replaceAll(queuedFollowupResult, followupResult)); +}, Effect.provide(NodeServices.layer)); interface CapturedTurn { readonly instanceId: ProviderInstanceId; @@ -3951,11 +3950,14 @@ describe("orchestrator MCP toolkit", () => { ), ); - it.live("reports running and queued child follow-ups from a Codex replay transcript", () => + it.live.each([ + { label: "distinct", result: queuedFollowupResult, latestSummary: queuedFollowupResult }, + { label: "identical", result: delegatedResult, latestSummary: null }, + ])("reports $label child results", ({ result, latestSummary }) => Effect.scoped( Effect.gen(function* () { const cwd = yield* checkpointWorkspace("delegated-task-status"); - const rawTranscript = yield* readDelegatedTaskStatusTranscript(); + const rawTranscript = yield* readDelegatedTaskStatusTranscript(result); const transcript = yield* CodexOrchestratorReplayHarness.decodeTranscript( materializeReplayTranscriptWorkspace(rawTranscript, cwd), ); @@ -4295,7 +4297,7 @@ describe("orchestrator MCP toolkit", () => { finishedParent.subagents.find((task) => task.id === followupTasks[1]!.id), ).toMatchObject({ status: "completed", - result: queuedFollowupResult, + result, }); expect( finishedParent.contextTransfers.filter( @@ -4322,9 +4324,18 @@ describe("orchestrator MCP toolkit", () => { hasPendingChildRuns: false, latestTerminalRunId: queuedFollowup.runId, latestTerminalStatus: "completed", - latestTerminalSummary: queuedFollowupResult, + latestTerminalSummary: latestSummary, }); - expect(finalStatus.latestTerminalResultContextTransferId).not.toBeNull(); + const followupTransfer = finishedParent.contextTransfers.find( + (transfer) => + transfer.type === "subagent_result" && + transfer.sourcePoint.runId === queuedFollowup.runId, + ); + expect(followupTransfer).toBeDefined(); + expect(finalStatus.latestTerminalResultContextTransferId).toBe(followupTransfer?.id); + expect(finalStatus.latestTerminalResultContextTransferId).not.toBe( + delegated.resultContextTransferId, + ); const read = yield* invoke("t3_thread_read", { threadId: delegated.childThreadId, limit: 100, diff --git a/apps/server/src/mcp/toolkits/orchestrator/tools.ts b/apps/server/src/mcp/toolkits/orchestrator/tools.ts index 207f66ab2fc50..c89c0baf22d62 100644 --- a/apps/server/src/mcp/toolkits/orchestrator/tools.ts +++ b/apps/server/src/mcp/toolkits/orchestrator/tools.ts @@ -78,7 +78,7 @@ export const DelegateTaskTool = Tool.make("delegate_task", { const TaskStatusTool = Tool.make("task_status", { description: - "Needs an agent running inside a T3 thread. Read a T3-owned delegated task created by this parent thread. childRunId identifies the original delegated run. workState distinguishes working, waiting_for_children, and result_available; a completed turn with live nested work is not a completed task. summary is the final task result, including provider errors on failure, and remains stable after publication. hasPendingChildRuns reports later queued or executing turns in the backing child thread, even after the task is terminal; it does not reopen the task, and task_cancel stops those turns too. Turns held in a stopped queue wait for the user and do not count. latestTerminal* provides later non-monitor turn results; latestTerminalSummary and latestTerminalResultContextTransferId are null when they would repeat summary and resultContextTransferId. Reading a terminal result acknowledges its automatic parent delivery.", + "Needs an agent running inside a T3 thread. Read a T3-owned delegated task created by this parent thread. childRunId identifies the original delegated run. workState distinguishes working, waiting_for_children, and result_available; a completed turn with live nested work is not a completed task. summary is the final task result, including provider errors on failure, and remains stable after publication. hasPendingChildRuns reports later queued or executing turns in the backing child thread, even after the task is terminal; it does not reopen the task, and task_cancel stops those turns too. Turns held in a stopped queue wait for the user and do not count. latestTerminal* provides later non-monitor turn results; latestTerminalSummary and latestTerminalResultContextTransferId are independently null when they exactly repeat summary and resultContextTransferId, respectively. Reading a terminal result acknowledges its automatic parent delivery.", parameters: OrchestratorMcpTaskStatusInput, success: OrchestratorMcpDelegateTaskResult, failure: OrchestratorMcpFailure,