/**
 * canonicalHistoryRead — the UNION read source for `threadline_history` (D-C).
 *
 * The read is `canonical log ∪ bounded best-effort backfill`, so post-upgrade
 * history can ONLY GAIN, never regress (`guard-bypass-carries-its-own-cap`). On the
 * first read of a thread whose canonical log predates the upgrade, a ONE-TIME,
 * MEMOIZED, BOUNDED backfill reconstructs entries through the idempotent funnel:
 *   - Outbound legs from a TAIL-BOUNDED scan of `outbox.jsonl.active` (a cap on
 *     lines scanned — NOT the whole shared file).
 *   - Inbound legs from the per-thread derived `threads/{id}.json` aggregate
 *     (O(thread)) — NEVER a full-store scan.
 * All backfilled entries are marked `backfilled:true` and EXCLUDED from the
 * symmetry accumulator (a backfilled leg cannot reproduce the live projection —
 * the outbox lacks a distinct message `createdAt`/`contentDigest`).
 *
 * Restore-correctness (SI2/D1): the one-time `backfilled` memo lives on the
 * conversation record, which a restore-from-backup brings back EMPTY of logs — so
 * the read IGNORES a set memo when the log is ABSENT and re-runs backfill (else a
 * restored thread would stay permanently empty).
 */
import type { ThreadLog, ThreadLogEntry } from './ThreadLog.js';
import type { ThreadMessageRecorder } from './recordThreadMessage.js';
import type { ConversationStore } from './ConversationStore.js';
/** A minimal inbound leg shape pulled from the derived aggregate. */
export interface AggregateMessage {
    id: string;
    body: string;
    createdAt: string;
    /** The sender — used only to decide inbound vs. our own outbound. */
    fromAgent?: string;
}
export interface CanonicalReadDeps {
    threadLog: ThreadLog;
    threadMessageRecorder: ThreadMessageRecorder;
    conversationStore?: ConversationStore;
    /** Path to the canonical outbox (`{stateDir}/threadline/outbox.jsonl.active`). */
    outboxPath?: string;
    /** Returns the inbound legs for a thread from the derived aggregate (O(thread)). */
    getAggregateMessages?: (threadId: string) => Promise<AggregateMessage[]> | AggregateMessage[];
    /** Our own agent name/fingerprint — to classify aggregate legs as inbound. */
    selfName?: string;
    /** Tail-line cap on the outbox scan (FD-config backfillOutboxTailLines). */
    backfillOutboxTailLines?: number;
}
/**
 * Read a thread's canonical history as the UNION (log ∪ one-time bounded
 * backfill). Returns the live-segment entries (post-backfill). Idempotent: the
 * backfill runs at most once per thread (memoized), and re-runs only if the memo
 * is set but the log is absent (restore case).
 */
export declare function readThreadHistoryUnion(deps: CanonicalReadDeps, threadId: string): Promise<ThreadLogEntry[]>;
/**
 * Reconstruct a thread's pre-upgrade legs into the canonical log through the
 * idempotent funnel (marked `backfilled`). Bounded: outbox is tail-scanned, inbound
 * comes from the per-thread aggregate (never a full-store scan). Honest residual:
 * inbound legs absent from BOTH the aggregate and the envelope store are
 * unrecoverable — the symmetry surface (not backfill) makes that gap visible.
 */
export declare function backfillThread(deps: CanonicalReadDeps, threadId: string): Promise<{
    outbound: number;
    inbound: number;
}>;
//# sourceMappingURL=canonicalHistoryRead.d.ts.map