/**
 * outputEnforcement — what the `outputSchema` contract does INSIDE the run.
 *
 * Pattern: an authored envelope around an untrusted payload (the same shape
 *          `window/summarize` uses), plus one writer for one committed key
 *          (the same rule `middleware/ledger` follows).
 * Role:    core/ layer. Until 7.26 the schema was checked once, after the run
 *          was over, by `runTyped()`. That is a fine place to JUDGE an answer
 *          and a useless place to FIX one: the loop has stopped, the model is
 *          gone, and all the caller can do is throw. This module holds the
 *          pieces that let the loop ask again — the corrective message, the
 *          ledger row, and the synthetic tool the `'tool-forced'` strategy
 *          puts on the wire.
 * Emits:   N/A — the Route decider and the retry stage emit; this file only
 *          builds values and commits rows.
 *
 * ## Why the failed answer is put into the conversation
 *
 * Nothing writes the answering turn back into `scope.history` — the loop
 * appends an assistant turn only when it carries tool calls. So a corrective
 * message sent on its own would arrive at a model that cannot see what it
 * said, and "your answer failed the schema" would be teaching into the void.
 * The retry stage appends BOTH: the answer that failed, then the correction.
 * That is also what makes the retry legible afterwards — the conversation
 * says what was answered, what was wrong with it, and what came back.
 *
 * ## Why the frame comes first and nothing follows the error
 *
 * The validator's message is DATA. A schema authored elsewhere (or a parser
 * that formats an error out of model output) can put anything in it,
 * including text shaped like an instruction. So the library's own words come
 * FIRST, say what the following text is, and nothing authored is appended
 * after it — there is no trailing sentence for injected text to pre-empt.
 * Identical reasoning, identical shape, to the compaction frame.
 */
import type { LLMMessage, LLMRequest, LLMResponse, LLMToolSchema } from '../../provider-contract/types.js';
import { type OutputSchemaParser } from '../../answer-validation/outputSchema.js';
/**
 * One row per final-answer attempt an enforcing agent made, in order.
 *
 * Rows exist ONLY on an agent that opted into `retries`. Without the option
 * the schema is judged after the run as it always was, nothing in the loop
 * looks at it, and this key is never written.
 */
export interface OutputAttempt {
    /** 1-based attempt number within this run. */
    readonly attempt: number;
    /** The ReAct iteration that produced the answer. */
    readonly iteration: number;
    /**
     * What became of this attempt:
     *   • `'passed'`    — the answer satisfied the schema; the run returns it.
     *   • `'retried'`   — it failed and a corrective turn was sent.
     *   • `'exhausted'` — it failed with no retries left; this answer stands,
     *                     and `runTyped()` throws on it exactly as it always did.
     */
    readonly outcome: 'passed' | 'retried' | 'exhausted';
    /** Which half of validation failed. Absent on a passing row. */
    readonly stage?: 'json-parse' | 'schema-validate';
    /** The validator's own message, verbatim. Absent on a passing row. */
    readonly error?: string;
    /** Failing field path when the parser exposes one (Zod-style issues). */
    readonly path?: string;
    /** `fnv1a` of the corrective message this row's failure produced. Present
     *  only on a `'retried'` row — it is the join back to the message in the
     *  conversation and to the `output_schema_retry` event's payload. */
    readonly correctiveMessageHash?: string;
    /**
     * Set on an `'exhausted'` row when the answer the MODEL produced satisfied
     * the schema and an `act({ output })` middleware's rewrite broke it — the
     * name of that middleware (8.18.0).
     *
     * Its presence is also why the row is `'exhausted'` with retries still on
     * the clock: a rule that turns a valid answer into an invalid one will do it
     * to the next answer too, so the run stops paying for re-asks that cannot
     * converge.
     */
    readonly brokenBy?: string;
    /**
     * Set when the attempt failed because the provider STOPPED, not because the
     * text failed the parser: a `strategy: 'native'` answer that ended with a
     * {@link NativeStopReason}, where the structured-output format does not
     * hold. Such an answer fails even when it parses; a `'refusal'` is never
     * re-asked (the row is `'exhausted'` at once).
     */
    readonly stopReason?: NativeStopReason;
}
/**
 * The stops under which a structured-output format does not hold: a refusal,
 * and the two truncations — the answer's own cap (`'max_tokens'`) and the
 * context window (`'model_context_window_exceeded'`).
 */
export type NativeStopReason = 'refusal' | 'max_tokens' | 'model_context_window_exceeded';
/** The scope surface this file needs. Structurally a `TypedScope<AgentState>`. */
export interface OutputLedgerScope {
    outputAttempts?: readonly OutputAttempt[];
}
/**
 * Append one row to the run's output-attempt ledger.
 *
 * The ONLY writer of `outputAttempts`, so the decider (which files the
 * terminal outcomes) and the retry stage (which files its own, carrying the
 * message it wrote) cannot record the same fact two different ways.
 */
export declare function recordOutputAttempt(scope: OutputLedgerScope, row: OutputAttempt): void;
/** A validation failure, flattened to the plain strings a row and an event
 *  payload both need. */
export interface OutputFailure {
    readonly stage: 'json-parse' | 'schema-validate';
    /** The validator's message. Prefer the underlying cause's message (Zod and
     *  friends attach the real error there); fall back to the wrapper's. */
    readonly error: string;
    readonly path?: string;
    /** The provider's stop that failed the attempt — see `OutputAttempt.stopReason`. */
    readonly stopReason?: NativeStopReason;
}
/**
 * Fail a `strategy: 'native'` answer by its stop reason, before the parser.
 *
 * Anthropic's structured-outputs page: on `stop_reason: "refusal"` "the output
 * may not match your schema because the refusal message takes precedence",
 * and on `"max_tokens"` "the output may be incomplete and not match your
 * schema". So the format was not in force for this text, and an answer that
 * happens to parse is still not one the format produced — delivering it as
 * constrained would be a silent pass. Returns `undefined` under any other
 * strategy or stop. The words are the library's own, naming the stop; the
 * failure then takes the ordinary path (retry, or exhausted and unmet).
 */
export declare function nativeStopFailure(enforcement: Pick<ResolvedOutputEnforcement, 'outputFormat'>, stopReason: string | undefined): OutputFailure | undefined;
/**
 * Flatten whatever the parser threw into {@link OutputFailure}.
 *
 * Mirrors the extraction `callLLM`'s reliability validator already does, so
 * the two paths report a failure the same way.
 */
export declare function describeFailure(err: unknown): OutputFailure;
export { SCHEMA_CHECK_FRAME_PREFIX } from '../../lib/saidByPerson.js';
/**
 * The two messages a failed attempt adds to the conversation: the answer
 * that failed, and the correction.
 *
 * The frame is authored and comes first; `failure.error` is appended to it
 * verbatim and nothing is written after. The library never edits the
 * validator's words, and it never lets them speak in the library's voice
 * either.
 */
export declare function buildCorrectiveTurn(failedAnswer: string, failure: OutputFailure, facts: {
    readonly attempt: number;
    readonly totalAttempts: number;
}): readonly [LLMMessage, LLMMessage];
/** True when this message is a correction a previous attempt wrote. */
export declare function isSchemaCheckMessage(msg: LLMMessage | undefined): boolean;
/** The join between a corrective message, its ledger row and its event. */
export declare function correctiveMessageHash(content: string): string;
/**
 * Name of the tool the schema is presented as.
 *
 * It is the STRATEGY's mechanism, never the agent's surface: it is built at
 * request-assembly time, so it cannot reach `.tools()`, the tools slot, the
 * `tools.offered` event, an MCP server's served list, or the dispatcher that
 * runs tools and files middleware rows. The only places it exists are the
 * wire and the `llm_start` event — and it belongs in that event, whose whole
 * claim is that it reports what the model actually saw.
 */
export declare const SCHEMA_TOOL_NAME = "respond_with_schema";
/** Build the synthetic tool from the resolved JSON Schema. */
export declare function buildSchemaTool(jsonSchema: Readonly<Record<string, unknown>>, description?: string): LLMToolSchema;
/**
 * Pull the answer out of a forced tool call.
 *
 * Returns the JSON text the rest of the run treats as the final answer, or
 * `undefined` when this response did not answer through the synthetic tool
 * (which is what a still-working turn looks like, and is left alone).
 */
export declare function readSchemaToolAnswer(response: LLMResponse): string | undefined;
/**
 * Resolve the JSON Schema for the synthetic tool.
 *
 * Duck-typed, in the same spirit as the parser itself: a schema object that
 * can render itself as JSON Schema (ArkType's `toJsonSchema()`) is asked to;
 * anything else must be handed the shape explicitly. Returns `undefined`
 * when neither is available — the builder turns that into a refusal naming
 * exactly what to pass.
 */
export declare function resolveJsonSchema(parser: OutputSchemaParser<unknown>, explicit?: Readonly<Record<string, unknown>>): Readonly<Record<string, unknown>> | undefined;
/**
 * What the builder resolved once, and the chart carries for the whole run.
 *
 * `parser` is here (not in scope) for the ordinary reason: scope values must
 * survive `structuredClone`, and a parser is functions.
 *
 * @internal
 */
export interface ResolvedOutputEnforcement {
    readonly parser: OutputSchemaParser<unknown>;
    /**
     * Corrective re-asks allowed. **May be `0` since 8.18.0**, which is the
     * whole of that release's output-contract change: enforcement is mounted
     * whenever a parser exists, and `0` means "judge the answer, do not re-ask"
     * rather than "do not judge". Before, `retries: 0` mounted nothing at all —
     * the chart of an agent with a terminal contract was byte-identical to one
     * without, and a failed contract left no trace in the run's own record.
     *
     * `0` never mounts the retry BRANCH; the chart is unchanged. What changes is
     * which function the Route decider runs.
     */
    readonly retries: number;
    /** The synthetic tool, pre-built. Present only under `'tool-forced'`. */
    readonly schemaTool?: LLMToolSchema;
    /** The structured-output format, pre-built by `nativeOutputFormat`.
     *  Present under `'native'` and schema-capable `'auto'`. */
    readonly outputFormat?: NonNullable<LLMRequest['outputFormat']>;
    /** Auto selects this format only for a capable effective provider/model. */
    readonly nativeOutputAuto?: true;
    /** True when `.outputFallback()` is configured. The decider carries it so
     *  the unmet-contract signal can say that a tier exists which `run()` does
     *  not reach — the fallback itself lives at the caller's boundary. */
    readonly hasFallback: boolean;
    /** An additional delivery gate exists; do not promise raw output on failure. */
    readonly answerValidationEnforced?: boolean;
    /** Typed negatives share one request-only, no-tools correction with evidence repair. */
    readonly negativeClaimsEnforced?: true;
}
/**
 * Validate an answer against the contract, as the loop sees it. Returns
 * `undefined` when the answer passed.
 *
 * Deliberately calls the SAME `applyOutputSchema` the caller-facing
 * `runTyped()` calls — one validator, two call sites, so the loop can never
 * accept an answer the boundary would go on to reject. A parser that throws
 * something other than `OutputSchemaError` still failed the answer, and is
 * reported as a failure rather than allowed to escape and kill the run.
 */
export declare function judgeAnswer(answer: string, parser: OutputSchemaParser<unknown>): OutputFailure | undefined;
