/**
 * window/folded — join a summary sitting in a conversation back to what it
 * stands for.
 *
 * Pattern: Pure lookup over recorded facts. No I/O, no inference.
 * Role:    core/ layer, and the ONE public door onto retained originals.
 * Emits:   N/A.
 *
 * ── Why a fingerprint and not an index ──────────────────────────────────────
 * The obvious join key is "the summary is at `history[3]`". It is also wrong.
 * A later fold swallows the span that CONTAINS an earlier summary, and every
 * index after it moves; a conversation that folded four times would need its
 * stored indices rewritten on every fold, and any reader holding an old one
 * would silently be pointing at somebody else's message.
 *
 * So the join key is a fingerprint of the summary message's full content —
 * authored frame included. Three things follow, and all three are the point:
 *
 *   • it costs NO extra bytes on the wire (no id token planted in the frame);
 *   • it survives JSON, storage, and being read by a runtime that has never
 *     heard of this function;
 *   • it cannot be FORGED. A model that writes a message opening with the
 *     frame's own words produces different content, therefore a different
 *     fingerprint, therefore no match — `isCompactedSummary` alone says "this
 *     LOOKS like a frame", and this says "this IS the frame of a fold that was
 *     recorded". The two answers are different questions and the gap between
 *     them is exactly where a forgery would live.
 *
 * ── Absent is an answer ─────────────────────────────────────────────────────
 * `undefined` means "no fold was recorded for this message" and never "there
 * were no originals". A conversation stored by 8.1 has summaries and no spans
 * at all; one folded under `retain: 'discard'` has a span whose `messages` is
 * absent. Those are three different facts and the caller can tell them apart:
 * no span, a span with no messages, a span with messages.
 */
import type { LLMMessage } from '../../../provider-contract/types.js';
import type { FoldedSpan } from './types.js';
/**
 * The join key for one summary message: a fingerprint of its whole content.
 *
 * @internal Consumers join through {@link foldedSpanFor}; this is exported for
 *   the strategy that stamps the span and for tests that pin the two agree.
 */
export declare function summaryFingerprint(content: string): string;
/**
 * The join key for one SPAN — the messages a fold would replace.
 *
 * Same hash family as {@link summaryFingerprint}, different subject: that one
 * identifies the claim, this one identifies what the claim would be about.
 * `summarizeOldest` uses it to remember which spans it has already been
 * refused on, so a deterministic "no" is paid for once instead of once per
 * iteration for the rest of the run.
 *
 * Role and content, both — a span whose messages are identical but whose roles
 * differ is a different transcript and deserves to be asked about again.
 *
 * @internal
 */
export declare function spanFingerprint(messages: readonly LLMMessage[]): string;
/**
 * The smallest thing that can answer "what did this conversation fold?".
 *
 * Structural rather than `AgentRunCheckpoint` on purpose: it also fits a
 * paused run's `conversation`, a hand-built fixture, and anything a consumer
 * pulled out of their own store — and it keeps this file from importing the
 * checkpoint module, which imports this one's sibling types.
 */
export interface FoldedConversation {
    readonly folded?: readonly FoldedSpan[];
}
/**
 * What one summary message in a conversation stands for.
 *
 * Hand it the conversation you stored and a message out of its `history`; get
 * back the fold that produced that message, with the original messages when
 * the policy retained them.
 *
 * @returns the span, or `undefined` when this conversation recorded no fold
 *   for this message — see "Absent is an answer" above.
 *
 * @example  Show a user what the agent actually saw last week
 * ```ts
 * import { foldedSpanFor, isCompactedSummary } from 'agentfootprint';
 *
 * const conversation = readEnvelope(await sessions.hydrate(sessionId));
 * for (const message of conversation.history) {
 *   if (!isCompactedSummary(message)) continue;
 *   const span = foldedSpanFor(conversation, message);
 *   console.log(`summary of ${span?.messageCount ?? '?'} messages`);
 *   for (const original of span?.messages ?? []) {
 *     console.log(`  ${original.role}: ${original.content}`);
 *   }
 * }
 * ```
 */
export declare function foldedSpanFor(conversation: FoldedConversation, message: LLMMessage): FoldedSpan | undefined;
/**
 * Every message this conversation ever folded, oldest fold first, flattened.
 *
 * The transcript-shaped answer to `foldedSpanFor`'s message-shaped one: what
 * a support view prints when somebody asks what the agent was told before the
 * summaries. Spans that were discarded contribute nothing — they have nothing
 * to contribute, and `foldedSpanFor` is where you find out that they existed.
 */
export declare function foldedMessages(conversation: FoldedConversation): readonly LLMMessage[];
