/** * v1.3.2 §9.4 — domain-agnostic guardrail core. * * Three brakes recur across every autonomous loop SoloSquad runs — a workflow * `mode:agentic` stage, the goal pipeline, and agent delegation depth: * * 1. iteration cap — stop after N turns * 2. budget cap — stop at a cost ceiling (with an early warning band) * 3. loop detection — stop when the last few outputs are identical (the * agent is spinning, producing the same thing each turn) * * `src/engine/guards.ts` already enforces these for the *goal* runtime, but * coupled to `GoalSpec`/`PersistentGuide`. This module is the pure, type-free * kernel those three call-sites can share: the workflow validator uses * {@link GUARDRAIL_KEYS}/{@link hasAnyGuardrail} to check a stage *declares* a * brake; a runtime can use the detectors below to *enforce* one. */ /** The guardrail fields a `mode:agentic` stage may declare (§6). */ export declare const GUARDRAIL_KEYS: readonly ["max_iterations", "budget_usd", "loop_detection"]; export type GuardrailKey = (typeof GUARDRAIL_KEYS)[number]; /** Does this (loosely-typed, e.g. from YAML) spec declare at least one guardrail? */ export declare function hasAnyGuardrail(spec: unknown): boolean; /** True once `iteration` (0-based) has reached/exceeded the cap. */ export declare function iterationCapReached(iteration: number, max: number): boolean; export interface BudgetStatus { /** Spend has met/exceeded the ceiling — stop now. */ exceeded: boolean; /** Spend has crossed the warning band (default 90%) but not the ceiling. */ warning: boolean; } /** Evaluate spend against a ceiling. `warnPct` is the fraction (0..1) at which * to raise the early warning. A non-positive `total` disables the cap. */ export declare function budgetStatus(spent: number, total: number, warnPct?: number): BudgetStatus; /** * Flags when the last `window` recorded outputs are all identical — the * canonical "the agent is stuck repeating itself" signal. Outputs are compared * after trimming + whitespace collapse so trivially-different reformatting of * the same content still trips it. Stateful and cheap (keeps only the window). */ export declare class LoopDetector { private readonly window; private readonly recent; constructor(window?: number); /** Record an output; returns true iff the last `window` outputs are identical. */ record(output: string): boolean; reset(): void; }