/**
 * Agent validators — pure helper functions extracted from Agent.ts.
 *
 * These run at Agent construction time (eagerly, so misconfiguration
 * fails fast at `.build()`) and during stage execution (safeStringify
 * for tool-result formatting).
 *
 * Pure functions, no class state — extracted for readability and
 * isolated testability. The Agent class imports + invokes these in
 * its constructor and stage handlers.
 */
import type { MemoryDefinition } from '../../memory/define.types.js';
import type { Injection } from '../../injection-engine/types.js';
import type { ToolRegistryEntry } from '../../tools/tools.js';
/**
 * Validate that every memory definition has a unique id. Each memory
 * writes to its own scope key (`memoryInjection_${id}`); duplicates
 * silently overwrite, leading to data loss that's hard to debug.
 *
 * Throws on collision so `Agent.build()` fails fast at construction.
 */
export declare function validateMemoryIdUniqueness(memories: readonly MemoryDefinition[]): void;
/**
 * Validate `maxIterations`. The historical silent clamp to 50 existed because
 * footprintjs's recursive traversal hit its depth wall around iteration 71 —
 * footprintjs 9.0.0's trampoline removed that wall (loops run on a flat
 * stack), so the cap is gone. The lower bound (1) still prevents a
 * 0-iteration agent; large values are the consumer's COST choice (each
 * iteration is an LLM call) — a dev-mode warning flags budgets above 100.
 * The Agent passes matching headroom to the engine's own loop-iteration
 * limit so it can never fire below the agent's budget.
 */
export declare function clampIterations(n: number): number;
/**
 * Validate tool-name uniqueness across `.tool()`-registered tools +
 * every Skill's `inject.tools[]`. The LLM dispatches by `tool.schema.name`
 * (the wire format), so any collision silently shadows execution.
 *
 * Called eagerly in the Agent constructor so `Agent.build()` throws
 * immediately, not on first `run()`.
 *
 * `read_skill` is reserved when ≥1 Skill is registered — collisions
 * with consumer tools throw.
 */
export declare function validateToolNameUniqueness(registry: readonly ToolRegistryEntry[], injections: readonly Injection[]): void;
/**
 * What a tool that returned nothing puts in the conversation.
 *
 * Self-describing on purpose: the model reads this string as the tool's
 * result, and it has to be able to tell "this tool does not report a value"
 * apart from "this tool returned the word undefined". `''` would say neither.
 */
export declare const NO_TOOL_VALUE = "(this tool returned no value)";
/**
 * JSON.stringify with circular-ref protection. Tool results are untrusted —
 * a hostile/buggy tool returning a cyclic object must not crash the run.
 * Falls back to '[unstringifiable: <reason>]' so the LLM still sees that
 * the tool ran and produced something unusable.
 *
 * **Total, since 8.18.0.** `JSON.stringify` returns `undefined` — not a
 * string — for `undefined`, a function and a symbol, so the old body's
 * `: string` was a promise it could not keep. An `async execute()` that did
 * its work and forgot to `return` produced a `role: 'tool'` message with
 * `content: undefined`, which crashed the next turn's messages slot with an
 * anonymous `TypeError` five frames inside the engine. A tool is allowed to
 * do something and report nothing; the conversation just has to say so.
 *
 * The truth is unaffected: `agentfootprint.stream.tool_end` still carries
 * the tool's REAL return value, `undefined` included (every serializing sink
 * renders an Error in it by the same wire rule).
 */
export declare function safeStringify(value: unknown): string;
