import { MemoryFile } from './types.js';

/**
 * codex-materialize.ts — Codex CLI native memory artifact materialization (#378)
 *
 * Periodically writes Remnic memories into the file layout that Codex CLI's
 * phase-2 consolidation reads directly under `<codex_home>/memories/`:
 *
 *   memory_summary.md                — always-loaded at session start (tight budget)
 *   MEMORY.md                        — searchable handbook (task-group schema)
 *   raw_memories.md                  — mechanical merge of raw memories, latest first
 *   rollout_summaries/<slug>.md      — per-session recaps
 *
 * Codex's own read path is agnostic to which producer wrote these files — it
 * tags reads by `memory_md` / `memory_summary` / `raw_memories` /
 * `rollout_summaries` / `skills`. By materializing Remnic content into this
 * exact layout we let Codex pick up Remnic memories without a single MCP call.
 *
 * Safety invariants
 * ─────────────────
 *  - **Atomic writes.** Every file is rendered under `.remnic-tmp/` and then
 *    `rename()`d into place so Codex never observes a half-written file.
 *  - **Sentinel-based opt-in.** If `<codex_home>/memories/.remnic-managed` is
 *    missing, we SKIP materialization entirely and log a warning. This honors
 *    user hand-edits to the directory — a user who manually curated their
 *    Codex memory layout will never have those edits overwritten.
 *  - **Schema validation.** `MEMORY.md` content is validated against the
 *    task-group schema before write. Invalid content throws and nothing is
 *    written.
 *  - **Idempotent no-ops.** A content hash is written into the sentinel. If
 *    the re-rendered hash matches the previous run, we skip writes entirely.
 *  - **Token budget.** `memory_summary.md` is truncated to fit under the
 *    configured token budget (whitespace-tokenized approximation), leaving
 *    headroom under Codex's 5000-token summary cap.
 *
 * Privacy
 * ───────
 * This module does not persist any user content outside `<codex_home>/memories`
 * — it only mirrors the memories that Remnic already wrote. It does not log
 * memory content to stdout; it logs file names, counts, and hashes.
 */

/**
 * Input for {@link materializeForNamespace}. Prefer passing pre-loaded
 * `memories` so this module stays I/O-agnostic and trivially testable.
 */
interface MaterializeOptions {
    /** Pre-loaded Remnic memories for this namespace (required). */
    memories: MemoryFile[];
    /** Override `<codex_home>`. Defaults to `$CODEX_HOME` or `~/.codex`. */
    codexHome?: string;
    /** Maximum whitespace-tokenized size of memory_summary.md. Default 4500. */
    maxSummaryTokens?: number;
    /** Maximum age of rollout_summaries/*.md in days. Default 30. */
    rolloutRetentionDays?: number;
    /** Per-session rollout summaries to render. */
    rolloutSummaries?: RolloutSummaryInput[];
    /** Current time, injected for deterministic tests. */
    now?: Date;
    /** Optional logger override for tests. */
    logger?: {
        info: (msg: string) => void;
        warn: (msg: string) => void;
        debug?: (msg: string) => void;
    };
}
/** Input describing one Codex rollout summary file. */
interface RolloutSummaryInput {
    /** Stable slug for the file (becomes `<slug>.md`). */
    slug: string;
    /** Working directory used during the rollout. */
    cwd?: string;
    /** Path to the raw Codex rollout log, if known. */
    rolloutPath?: string;
    /** ISO-8601 timestamp of the last update. */
    updatedAt?: string;
    /** Opaque thread / session id. */
    threadId?: string;
    /** Markdown body for the recap. */
    body: string;
    /** Freeform keywords / search hints. */
    keywords?: string[];
}
/** Result of a materialization run. */
interface MaterializeResult {
    /** Namespace that was materialized. */
    namespace: string;
    /** `<codex_home>/memories` path this run targeted. */
    memoriesDir: string;
    /** Was anything actually written (vs. skipped / idempotent no-op)? */
    wrote: boolean;
    /** True if the sentinel was missing and we skipped with a warning. */
    skippedNoSentinel: boolean;
    /** True if the hash matched the previous run and we short-circuited. */
    skippedIdempotent: boolean;
    /** Files that were written this run (relative to `memoriesDir`). */
    filesWritten: string[];
    /** Content hash computed for this run. */
    contentHash: string;
}
/** On-disk shape of the `.remnic-managed` sentinel. */
interface SentinelFile {
    version: number;
    namespace: string;
    updated_at: string;
    content_hash: string;
}
/** Bump when the on-disk layout or semantics change. */
declare const MATERIALIZE_VERSION = 1;
/** Sentinel file name at the root of the materialized memories dir. */
declare const SENTINEL_FILE = ".remnic-managed";
/**
 * Materialize a Remnic namespace into Codex's native memory layout.
 *
 * Returns a {@link MaterializeResult} describing what happened. Callers
 * should treat "skipped" as success — the sentinel / idempotent cases are
 * expected and intentional.
 *
 * @throws if `MEMORY.md` fails schema validation (we do not write garbage).
 */
declare function materializeForNamespace(namespace: string, options: MaterializeOptions): MaterializeResult;
/**
 * Create (or refresh) the `.remnic-managed` sentinel. Callers must do this
 * explicitly the first time they want Remnic to start managing a directory —
 * we never write it implicitly, because its presence is the user's opt-in.
 */
declare function ensureSentinel(memoriesDir: string, namespace: string, now?: Date): void;
/**
 * Return basic stats about a materialized memories dir. Useful for tests and
 * debug CLI output. Returns `null` if the dir does not exist.
 */
declare function describeMemoriesDir(memoriesDir: string): {
    exists: boolean;
    hasSentinel: boolean;
    files: string[];
    sentinel: SentinelFile | null;
} | null;

export { type MaterializeResult as M, type RolloutSummaryInput as R, SENTINEL_FILE as S, MATERIALIZE_VERSION as a, type MaterializeOptions as b, describeMemoriesDir as d, ensureSentinel as e, materializeForNamespace as m };
