From 603018caf2d799d50ba20fc42d4b4788dfda007f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=9C=88?= Date: Sat, 23 May 2026 22:33:02 +0800 Subject: [PATCH] fix(builtin): force-strip tool_calls when noTools is set copilot-api returns tool_calls even when tools field is omitted from the request (infers from message history). Now the loop explicitly nullifies tool_calls when noTools=true. --- packages/workflow-agent-builtin/src/loop.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/workflow-agent-builtin/src/loop.ts b/packages/workflow-agent-builtin/src/loop.ts index 879cb85..a8127bd 100644 --- a/packages/workflow-agent-builtin/src/loop.ts +++ b/packages/workflow-agent-builtin/src/loop.ts @@ -87,14 +87,18 @@ export async function runBuiltinLoop( openAiTools.length > 0 ? openAiTools : null, ); + // When noTools is set, ignore any tool_calls the LLM might still return + // (some providers infer tools from message history even when tools field is omitted) + const effectiveToolCalls = options.noTools ? null : (response.toolCalls ?? null); + const assistantMessage: ChatMessage = { role: "assistant", content: response.content, - tool_calls: response.toolCalls, + tool_calls: effectiveToolCalls, }; messages.push(assistantMessage); - if (response.toolCalls === null || response.toolCalls.length === 0) { + if (effectiveToolCalls === null || effectiveToolCalls.length === 0) { const text = response.content ?? ""; await appendTurn(options.storageRoot, options.sessionId, { role: "assistant", @@ -123,14 +127,14 @@ export async function runBuiltinLoop( await appendTurn(options.storageRoot, options.sessionId, { role: "assistant", content: response.content ?? "", - toolCalls: mapToolCallsForPayload(response.toolCalls), + toolCalls: mapToolCallsForPayload(effectiveToolCalls), reasoning: null, }); turnCount += 1; // Execute tools turnCount += await executeTurnTools( - response.toolCalls, + effectiveToolCalls, options.toolCtx, messages, options.storageRoot,