diff --git a/packages/workflow-agent-builtin/src/loop.ts b/packages/workflow-agent-builtin/src/loop.ts index a8127bd..df0f411 100644 --- a/packages/workflow-agent-builtin/src/loop.ts +++ b/packages/workflow-agent-builtin/src/loop.ts @@ -48,7 +48,7 @@ async function appendTurn( await appendSessionTurn(storageRoot, sessionId, payload); } -async function executeTurnTools( +export async function executeTurnTools( calls: Array<{ id: string; name: string; arguments: string }>, toolCtx: ToolContext, messages: ChatMessage[], @@ -70,6 +70,17 @@ async function executeTurnTools( return turnCount; } +export type ShouldNudgeOptions = { + noTools: boolean; + text: string; + turn: number; + maxTurns: number; +}; + +export function shouldNudge({ noTools, text, turn, maxTurns }: ShouldNudgeOptions): boolean { + return !noTools && !text.trimStart().startsWith("---") && turn < maxTurns - 1; +} + /** Agent run loop: LLM ↔ tools until no tool_calls or maxTurns. */ export async function runBuiltinLoop( options: RunBuiltinLoopOptions, @@ -88,7 +99,6 @@ export async function runBuiltinLoop( ); // 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 = { @@ -108,9 +118,7 @@ export async function runBuiltinLoop( }); turnCount += 1; - // If tools are available but LLM stopped calling them without producing - // frontmatter, nudge it to continue working or output frontmatter. - if (!options.noTools && !text.trimStart().startsWith("---") && turn < options.maxTurns - 1) { + if (shouldNudge({ noTools: options.noTools, text, turn, maxTurns: options.maxTurns })) { log("7FXQM2KN", "text-only turn without frontmatter, nudging LLM to continue"); const nudge = "You stopped calling tools but your response does not start with the required `---` YAML frontmatter. " + diff --git a/packages/workflow-agent-builtin/src/prompt.ts b/packages/workflow-agent-builtin/src/prompt.ts index 68be81c..d091e90 100644 --- a/packages/workflow-agent-builtin/src/prompt.ts +++ b/packages/workflow-agent-builtin/src/prompt.ts @@ -70,6 +70,7 @@ export function buildBuiltinMessages(ctx: AgentContext): ChatMessage[] { "Your task is described in the user message below — do NOT use uwf or workflow CLI commands to discover your task. " + "When you are done, output your final response with the YAML frontmatter block as specified above. " + "Do NOT output the frontmatter until you have completed all necessary work. " + + "If you are running low on turns and cannot finish, output the frontmatter with `status: failed` and explain what remains in the body. " + "CRITICAL: Your final output MUST start with the `---` fence on the very first line — " + "no preamble text, no explanation before it. The parser requires `---` at position 0.", );