Menu
popagent
publicLatest change b9b19bd567bc0d15e47b6ff4bffc41698d116de7 - Fix orchistrator's own run ending on a dangling assistant tool-call message 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` would abort before the assistant's pending tool calls are
// resolved, leaving the conversation ending on an unresolved assistant tool-call
// message; strict Claude-family models reject that as invalid prefill on the next
// call. Let the tool calls execute (continue) and steer via feedback instead; the
// native maxSteps ceiling still forces a text-only final response on the next step.
return { 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 }) }
: {}),
};
}