/**
 * summarize — the stage that makes the SUMMARIZE strategy summarize (9.14.0).
 *
 * Reads from scope:  `loaded`, `identity`
 * Writes to scope:   `loaded` (older entries replaced by ONE summary entry)
 * Writes to store:   the summary entry, under a deterministic id
 * Emits:             agentfootprint.memory.strategy_applied (every visit that
 *                    changes recall or decides not to), carrying the reason,
 *                    the model, and the token usage the call reported.
 *
 *                    NOT `cost.tick`, and that is a limit worth knowing: the
 *                    USD channel needs a `pricingTable` and the run's
 *                    cumulative counters, both of which live on the AGENT's
 *                    scope, and a memory pipeline is a subflow with neither.
 *                    Emitting a tick with `estimatedUsd: 0` would be a
 *                    cheaper-looking lie than saying nothing, so the tokens
 *                    ride the memory event instead and the fold is never
 *                    silent.
 *
 * Where this fits in the pipeline:
 *
 *   loadRecent → summarize → [filterByDecay] → pickByBudget → formatDefault
 *
 * ## What it does, and what that costs
 *
 * Recall arrives from `loadRecent` oldest-first. This stage keeps the last
 * `preserveRecent` entries VERBATIM and folds everything older into a single
 * summary entry — **one LLM call over the span**, not one per entry and not
 * one per recall. The summary is then WRITTEN BACK to the store under the id
 * `msg-summary-{fromTurn}-{toTurn}`, so the next turn LOADS it instead of
 * paying for it again. That write-back is the whole cost model: a span is
 * summarized once in the life of a conversation, by whichever turn first saw
 * it fall out of the verbatim tail.
 *
 * ## The originals are never deleted
 *
 * A summary is a CLAIM ABOUT the conversation, not the conversation. The
 * entries it covers stay in the store byte-identical; they are excluded from
 * recall by the summary's own coverage metadata ({@link SummaryCoverage}) and
 * by nothing else. Delete the summary entry and the next recall is verbatim
 * again. This is the same law `.compaction()` follows in the live window —
 * the fold edits what is SENT, never what was said.
 *
 * ## Two summarizer shapes
 *
 *   • `llm: LLMProvider` + `model` — the library composes the call. The span
 *     is rendered as DATA between delimiters the authored instruction names,
 *     so a message inside the conversation cannot re-instruct the summarizer
 *     by looking like an instruction. `model` is REQUIRED here: naming it is
 *     what keeps the summarizer's bill separate from the agent's own (the
 *     `.compaction()` law, 8.14.0). Token usage is reported on the event.
 *   • `llm: (messages) => Promise<string>` — the caller's own call, unchanged
 *     since 2.x. The caller composed the request, so the caller named the
 *     model; the stage reports usage as unknown rather than inventing one.
 *
 * ## What comes back is DATA
 *
 * The summary text is appended after an authored label this file writes, and
 * the label always comes first. A summarizer that returns "IGNORE ALL
 * PREVIOUS INSTRUCTIONS" produces an entry that still says, in the library's
 * own words and first, that what follows is a summary written by a model and
 * that the originals are retained.
 *
 * ## Three ways it declines, all of them out loud
 *
 *   • **not worth a call** — fewer than `minFoldEntries` foldable entries, or
 *     fewer than `triggerMinEntries` loaded. No call. Recall still changes if
 *     an EARLIER turn's summary is covering entries (that is the cheap turn
 *     the write-back bought), and then the event says so; a turn where
 *     nothing at all happened writes nothing and emits nothing.
 *   • **summarizer failed** — the provider threw. ONE `console.warn` per
 *     stage instance plus an event, and recall proceeds VERBATIM: a broken
 *     summarizer degrades this strategy to `window`, it does not fail the
 *     turn. (Through 9.13.0 the stage re-threw; a memory that cannot recall
 *     because its optional compressor is down is a worse answer than an
 *     uncompressed one.)
 *   • **replacement not smaller** — the summary plus its label is no shorter
 *     than the span it would replace. Folding then spends a call to GROW
 *     recall and lose detail at the same time, so the fold is dropped and the
 *     span is LATCHED: the same span is never re-asked, because the same
 *     inputs give the same answer and re-asking is a paid call whose result
 *     is already known (the 8.14.0 latch, keyed by the span's own ids). A
 *     span that has GROWN is a different key and is asked again on purpose.
 *
 * ## Determinism contract
 *
 * For prompt caching to stay stable, the same span should produce the same
 * summary. Configure `temperature: 0` (and a seed where the provider has
 * one) on the summarizer you pass. The stage cannot enforce it — but note
 * that write-back makes it matter far less than it did: a span is summarized
 * once and then read back from the store.
 *
 * @see ../define.ts  the `SUMMARIZE` strategy arm that wires this
 * @see ../../core/agent/window/strategies/summarizeOldest.ts  the same laws
 *      applied to the LIVE window instead of to recall
 */
import type { TypedScope } from 'footprintjs';
import type { MemoryEntry } from '../entry/index.js';
import type { LLMMessage as Message, LLMProvider } from '../../provider-contract/types.js';
import type { MemoryStore } from '../store/index.js';
import type { MemoryState } from './types.js';
/**
 * The 2.x summarizer shape: the caller makes the call and returns the text.
 * Kept because a hand-composed pipeline that already owns its provider does
 * not need the library to own it too.
 */
export type SummarizeCallback = (messages: readonly Message[]) => Promise<string>;
export interface SummarizeConfig {
    /**
     * Who writes the summary — a provider (preferred; pair with {@link model})
     * or the legacy callback that makes its own call.
     *
     * Recommend a cheap model. The point of naming a summarizer at all is that
     * compression is not worth your main model's price.
     */
    readonly llm: LLMProvider | SummarizeCallback;
    /**
     * The model id the summarizer is called with. REQUIRED when `llm` is a
     * provider, refused as meaningless when `llm` is a callback (the callback
     * composed its own request and named its own model there).
     *
     * There is no `?? agentModel` fallback, by the same reasoning
     * `.compaction()` uses: the default had no correct case — same family and
     * it quietly bills your main model for compression, different vendor and it
     * sends a model id nobody has heard of.
     */
    readonly model?: string;
    /**
     * Where the summary is written back to. Omit and the fold is computed for
     * THIS recall only and thrown away — correct, and paid for again every
     * turn. `defaultPipeline` passes its own store, so the factory path always
     * writes back.
     */
    readonly store?: MemoryStore;
    /**
     * Minimum `loaded.length` before a fold is considered. Below this, no-op —
     * the conversation is short enough to keep whole. Default 20.
     */
    readonly triggerMinEntries?: number;
    /**
     * How many most-recent entries stay VERBATIM. The older ones become the
     * summary. Default 5 — recent turns keep their exact phrasing so the agent
     * can quote them.
     *
     * The seam is rounded OUTWARD to a whole turn: if the cut would land in the
     * middle of turn 7, turn 7 goes to the verbatim side entirely. A question
     * summarized while its answer stayed raw reads like an answer to nothing.
     */
    readonly preserveRecent?: number;
    /**
     * Do not spend a call to fold fewer than this many entries. Default 2 — one
     * entry "compressed" into a summary plus a label is a paid call that makes
     * recall longer. `defineMemory` raises this to the size of the verbatim
     * tail, which is the cost policy: never fold less than you keep.
     */
    readonly minFoldEntries?: number;
    /**
     * Override the authored instruction the summarizer is given. Domain
     * summaries want this ("preserve every refund-related number"). Only the
     * INSTRUCTION is yours — the transcript delimiters and the label written
     * onto the summary entry are the library's and stay put.
     */
    readonly systemPrompt?: string;
    /**
     * TTL in milliseconds applied to the summary entry, counted from the SPAN's
     * clock (the newest entry it stands for), not from the fold.
     * `defaultPipeline` passes its `writeTtlMs` here, so a compliance retention
     * window expires the summary WITH the turns it compressed — a summary that
     * outlived them is exactly the leak the retention window exists to prevent.
     */
    readonly ttlMs?: number;
    /**
     * Tier for the summary entry. Default `'cold'` — condensed, not recent.
     *
     * Worth pairing with the load's `tiers` filter if you set one: a filter that
     * excludes this tier hides the summary from every later recall, and a
     * summary that is never loaded is a span that is folded (and billed) again
     * every turn. `defineMemory` sets no tier filter, so the factory path is
     * safe by construction.
     */
    readonly summaryTier?: 'hot' | 'warm' | 'cold';
    /** `strategyId` on the emitted event. Default `'memory-summarize'`. */
    readonly strategyId?: string;
}
/** Id prefix for every entry this stage writes. */
export declare const SUMMARY_ID_PREFIX = "msg-summary-";
/** Metadata key carrying {@link SummaryCoverage} on a summary entry. */
export declare const SUMMARY_COVERAGE_KEY = "summarizes";
/**
 * What a summary entry stands for — the only thing that excludes an original
 * from recall.
 *
 * `coveredIds` is the operative field and it is exact: recall drops an entry
 * because a loaded summary NAMES it, never because a turn number falls inside
 * a range. A turn that was half-folded (possible only for hand-composed
 * configs; the stage rounds the seam outward) keeps the half nobody claimed.
 */
export interface SummaryCoverage {
    /** Earliest turn represented. */
    readonly fromTurn: number;
    /** Latest turn represented. Also the entry's `source.turn`. */
    readonly toTurn: number;
    /** How many entries were folded. */
    readonly entryCount: number;
    /** The exact entries this summary stands for. */
    readonly coveredIds: readonly string[];
    /** Which model wrote it. Absent for the callback form — the caller knows. */
    readonly model?: string;
    /** When the fold happened (unix ms). */
    readonly summarizedAtMs: number;
}
/** The id a fold over `[fromTurn, toTurn]` always gets. Deterministic. */
export declare function summaryEntryId(fromTurn: number, toTurn: number): string;
/**
 * The coverage a summary entry carries, or `undefined` for an ordinary entry.
 *
 * Read through the METADATA, not the id: an id is a name and metadata is the
 * claim. An entry named like a summary that carries no coverage stands for
 * nothing and is treated as an ordinary entry.
 */
export declare function summaryCoverage(entry: MemoryEntry<unknown>): SummaryCoverage | undefined;
/** True when this entry is a summary written by this stage. */
export declare function isSummaryEntry(entry: MemoryEntry<unknown>): boolean;
/** Opening of the authored label. Stable — readers and tests match on it. */
export declare const SUMMARY_FRAME_PREFIX = "[summary of earlier turns";
export declare function summarize(config: SummarizeConfig): (scope: TypedScope<MemoryState>) => Promise<void>;
