/**
 * defineInstruction — sugar for rule-based system-prompt Injections.
 *
 * The most flexible Instruction-style flavor: a predicate decides
 * activation each iteration. Use for "if condition X is true, give
 * the LLM this guidance". Compared to:
 *   - Steering (always-on, no predicate)
 *   - Skill (LLM-activated via `read_skill`)
 *   - on-tool-return (specific tool just ran — Dynamic ReAct)
 *
 * Produces an `Injection` with:
 *   - flavor: `'instructions'`
 *   - trigger: `{ kind: 'rule', activeWhen }` (or `'always'` if omitted)
 *   - inject: `{ systemPrompt: prompt }`, or `{ messages: [{ role, content }] }`
 *     when `slot: 'messages'` names the role it speaks as
 *
 * @example
 *   const calmTone = defineInstruction({
 *     id: 'calm-tone',
 *     description: 'Use a calm, empathetic tone with frustrated users.',
 *     activeWhen: (ctx) => /upset|angry|frustrated/.test(ctx.userMessage),
 *     prompt: 'Acknowledge feelings before facts. Avoid corporate jargon.',
 *   });
 *
 *   const piiAfterRedact = defineInstruction({
 *     id: 'pii-after-redact',
 *     activeWhen: (ctx) => ctx.lastToolResult?.toolName === 'redact_pii',
 *     prompt: 'PII has been redacted. Do not include emails or phone numbers.',
 *   });
 */
import type { WireRole } from '../../provider-contract/types.js';
import type { Injection, InjectionContext } from '../types.js';
import type { CachePolicy } from '../../cache/types.js';
interface DefineInstructionBase {
    readonly id: string;
    readonly description?: string;
    /**
     * Predicate to decide activation. Synchronous; side-effect free.
     * If omitted, the instruction is always active (effectively a
     * Steering doc, but tagged with `'instructions'` flavor for
     * narrative semantics — use `defineSteering` for clearer intent).
     * Predicates that throw are skipped (fail-open) and reported via
     * `agentfootprint.context.evaluated`.
     */
    readonly activeWhen?: (ctx: InjectionContext) => boolean;
    /**
     * Cache policy for this instruction. Defaults to `'never'` —
     * instructions are typically rule-based (volatile per-iter
     * `activeWhen` predicates, on-tool-return reminders). Override to
     * `'always'` only for instructions you know are stable per-turn
     * (e.g., a static safety rule wrapped as `defineInstruction` for
     * narrative tagging — though `defineSteering` is the cleaner choice
     * for that case).
     */
    readonly cache?: CachePolicy;
}
/**
 * `slot` and `role` are ONE decision, so the type makes them one choice —
 * see {@link DefineFactOptions} for the full reasoning. In short:
 * `slot: 'messages'` requires a `role` with no default (who appears to speak
 * is the app's meaning to set), and `slot: 'system-prompt'` forbids one.
 */
/**
 * The instruction's words — a fixed string, or a template that names run-time
 * facts. Exactly one, because they are the same decision spelled two ways and
 * a definition carrying both would leave the library picking one.
 */
export type DefineInstructionWords = {
    /** Instruction text, exactly as the model will read it. */
    readonly prompt: string;
    readonly promptTemplate?: never;
} | {
    readonly prompt?: never;
    /**
     * Instruction text with named run-time facts in it (9.57.0), rendered
     * fresh on every action:
     *
     * ```ts
     * promptTemplate:
     *   'You are on action {{action}} of {{actionBudget}}; ' +
     *   '{{actionsRemaining}} remain. Finish what you have rather than start something new.',
     * ```
     *
     * The vocabulary is CLOSED and has three words — `{{action}}`,
     * `{{actionBudget}}`, `{{actionsRemaining}}` — and a name outside it is
     * refused here, at define time, rather than rendering as a literal in
     * front of the model. It is closed rather than a `(ctx) => string`
     * because a function makes ABSENCE the author's problem: given one, an
     * author writes `${ctx.maxIterations}` and ships "23 of undefined", or
     * writes `?? 0` and ships a fabricated denominator nothing can tell
     * from a real zero. With named slots the library owns absence and
     * applies one rule — if any named fact is unavailable the whole
     * instruction is skipped, by name, on the record.
     *
     * Two things a template may not be, both refused at define time:
     * `slot: 'messages'` (the delivery ledger keys by content, so a
     * re-render would deliver a NEW message on every action), and any
     * `cache` other than `'never'` (a marker asserting a stable prefix
     * through bytes that change every call is a guaranteed miss plus the
     * cache-write premium, with the trace claiming a stable prefix).
     *
     * Declare templated instructions LAST: a never-cacheable injection
     * truncates the cached prefix at its declaration position.
     */
    readonly promptTemplate: string;
};
export type DefineInstructionOptions = DefineInstructionBase & DefineInstructionWords & ({
    /**
     * The default. Appended to the system prompt — the one placement
     * every provider delivers. An `on-tool-return` instruction already
     * lands on exactly the turn it matters; this is a system prompt that
     * says it on that one turn, rather than on every turn.
     */
    readonly slot?: 'system-prompt';
    readonly role?: never;
} | {
    /**
     * Deliver the instruction into the conversation itself — it enters
     * `scope.history` at the injection-engine boundary, at the END of the
     * window, so the model reads it at the recency this option is
     * reaching for. If its role would collide with the turn already at
     * the end, delivery defers to the next boundary and says so on
     * `messagesDelivery.deferred` rather than reordering anything.
     *
     * In a tool-using loop the window ends on the user's turn or on tool
     * results, so a `'user'` role typically never gets a slot. Use
     * `'assistant'`, use `'system'` on a provider that carries it, or
     * return the words from the tool whose result they are about.
     */
    readonly slot: 'messages';
    /**
     * Who appears to say it. Required — no default. Refused at run start
     * when the attached provider does not carry it inside `messages`.
     */
    readonly role: WireRole;
});
export declare function defineInstruction(opts: DefineInstructionOptions): Injection;
export {};
