diff --git a/src/core/tools/NewTaskTool.ts b/src/core/tools/NewTaskTool.ts index 827a63c95c3..f406774eae4 100644 --- a/src/core/tools/NewTaskTool.ts +++ b/src/core/tools/NewTaskTool.ts @@ -126,22 +126,31 @@ export class NewTaskTool extends BaseTool<"new_task"> { // Preserve the current mode so we can resume with it later. task.pausedModeSlug = (await provider.getState()).mode ?? defaultModeSlug + // For native protocol, set the pending tool call ID BEFORE starting the subtask. + // This prevents a race condition where the subtask completes (during the delay + // in startSubtask) before we set the ID, which would cause completeSubtask to + // not push the tool_result, breaking the API conversation structure. + if (toolProtocol === "native" && toolCallId) { + task.pendingNewTaskToolCallId = toolCallId + } + const newTask = await task.startSubtask(unescapedMessage, todoItems, mode) if (!newTask) { + // Clear the pending ID since the subtask wasn't created + if (toolProtocol === "native" && toolCallId) { + task.pendingNewTaskToolCallId = undefined + } pushToolResult(t("tools:newTask.errors.policy_restriction")) return } - // For native protocol, defer the tool_result until the subtask completes. + // For native protocol with toolCallId, don't push tool_result here. // The actual result (including what the subtask accomplished) will be pushed // by completeSubtask. This gives the parent task useful information about // what the subtask actually did. - if (toolProtocol === "native" && toolCallId) { - task.pendingNewTaskToolCallId = toolCallId - // Don't push tool_result here - it will come from completeSubtask with the actual result. - // The task loop will stay alive because isPaused is true (see Task.ts stack push condition). - } else { + // The task loop will stay alive because isPaused is true (see Task.ts stack push condition). + if (toolProtocol !== "native" || !toolCallId) { // For XML protocol, push the result immediately (existing behavior) pushToolResult( `Successfully created new task in ${targetMode.name} mode with message: ${unescapedMessage} and ${todoItems.length} todo items`,