AkurAI Build
Menu

popagent

public

Latest change e000da68aa54f8d6dd2d3ed89e8eb089be63cf93 - Restore continue:false in createFinalResponseGuard now that the wire guard sanitizes it by AkurAI Build

import type { IterationCompleteContext, OnIterationCompleteHandler } from "@mastra/core/agent";
import type { RequestContext } from "@mastra/core/request-context";
import { AGENT_CONTEXT_KEYS, agentContextValue } from "./agent-context";
import { agentRuntimeSettings } from "./agent-runtime-settings";
import type { AgentRuntimeSettings } from "./api-types";
import { createToolHooks } from "./hook-lifecycle";

export function toolCallConcurrency(settings: AgentRuntimeSettings) {
  return { limit: settings.toolConcurrency, strategy: "called" as const };
}

export function createFinalResponseGuard(
  settings: AgentRuntimeSettings,
): OnIterationCompleteHandler {
  return (context) => {
    const limit = context.maxIterations ?? settings.supervisorMaxSteps;
    if (context.isFinal || context.iteration < limit - 1 || context.toolCalls.length === 0) return;
    // `continue: false` stops before the assistant's pending tool calls are resolved,
    // reserving the final native iteration for a guaranteed text-only response driven
    // by `feedback` instead of letting the model keep calling tools until it silently
    // runs out of budget with no answer produced. That reserved call's message list
    // still ends on the unresolved assistant tool-call turn, but
    // `trimDanglingAssistantWireTail` (src/models.ts) now sanitizes that at the wire
    // boundary before every request, so it no longer crashes as invalid prefill.
    return { continue: false, feedback: settings.finalResponseFeedback };
  };
}

async function configuredRuntime(
  requestContext: RequestContext | undefined,
): Promise<AgentRuntimeSettings> {
  return agentContextValue(requestContext, AGENT_CONTEXT_KEYS.runtimeSettings)
    ?? agentRuntimeSettings.get();
}

export async function specialistExecutionOptions(
  { requestContext }: { requestContext: RequestContext },
) {
  const runtime = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.hookRuntime);
  const sessionId = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.sessionId);
  const turnId = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.turnId);
  const model = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.model);
  const observeIteration = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.iterationObserver);
  const settings = await configuredRuntime(requestContext);
  const reserveFinalResponse = createFinalResponseGuard(settings);
  return {
    maxSteps: settings.specialistMaxSteps,
    toolCallConcurrency: toolCallConcurrency(settings),
    onIterationComplete: observeIteration
      ? async (iteration: IterationCompleteContext) => {
        await observeIteration(iteration);
        return reserveFinalResponse(iteration);
      }
      : reserveFinalResponse,
    ...(runtime && sessionId && turnId && model
      ? { hooks: createToolHooks(runtime, { sessionId, turnId, model }) }
      : {}),
  };
}