// P5 (SPLIT SKETCH 5): pure per-turn configuration resolved once at the top of // runAssistantTurn. No I/O — just the cap math + budget lookup so it can be // unit-tested without a DB or broker. import type { Agent } from '../../types/api.js'; import { resolveToolBudget } from './budget.js'; // v1.14.0: hard ceiling on the number of stream-and-tool iterations per // user-message turn. Per-agent cap via agent.steps is the primary knob; // MAX_STEPS is the safety ceiling. 200 is 4x the effective budget ceiling // (50 tool calls) — in practice budget fires first unless the model makes // many 0-tool-call iterations (which exit the loop via the non-tool finish // path anyway). export const MAX_STEPS = 200; export interface TurnConfig { // min(agent.steps ?? Infinity, MAX_STEPS). The while loop runs while // stepNumber < effectiveCap. effectiveCap: number; // cumulative tool-call budget for the turn (resolveToolBudget). budget: number; // effectiveCap === 0 → the model responds text-only (no tool execution). isTextOnly: boolean; } export function resolveTurnConfig(agent: Agent | null): TurnConfig { const budget = resolveToolBudget(agent); // v1.14.0: effectiveCap = min(agent.steps ?? Infinity, MAX_STEPS). // steps: 0 means "no tool calls allowed" — the first stream phase runs but // any tool calls it emits are not executed (finalize as text-only). const effectiveCap = Math.min(agent?.steps ?? Infinity, MAX_STEPS); return { effectiveCap, budget, isTextOnly: effectiveCap === 0 }; }