/**
 * Chat V2 SQLite bootstrap.
 *
 * Opens (or creates) the chat database at the configured path, applies
 * the Phase 1 migration idempotently, and returns a ready-to-use
 * `better-sqlite3` Database instance.
 *
 * Schema matches tech-spec §3.2. All tables use `INTEGER ms-since-epoch (UTC)`
 * for timestamps and raw UUID strings for IDs.
 *
 * @module services/chat-v2/sqlite/chat-db
 */
import { type ComponentLogger } from '../../core/logger.service.js';
/**
 * F-CYCLE7-1: install a test-only stub for the better-sqlite3 loader.
 *
 * Pass a function to override; pass `null` to restore the real loader
 * AND clear the cached `better-sqlite3` reference so the next call
 * exercises the stub. Tests MUST call this with `null` in `afterEach`
 * to avoid leaking state across cases.
 *
 * @param fn - Stub `require`-like function or `null` to restore real loader
 */
export declare function setBetterSqlite3LoaderForTesting(fn: ((name: string) => unknown) | null): void;
/** Type alias for a `better-sqlite3` Database instance. */
export type ChatDatabase = import('better-sqlite3').Database;
/**
 * Idempotent base DDL — Phase 1 schema with Phase A column additions baked in.
 *
 * Safe to run every boot.
 *
 * Differences vs. the literal spec §3.2:
 * - Added `IF NOT EXISTS` on every CREATE so reboots don't fail.
 * - PRAGMAs set outside the transaction (SQLite disallows PRAGMA in txn).
 *
 * Phase A additions (SEALED design 2026-04-25 §3.1 + §3.2) are reflected
 * directly in the CREATE TABLE bodies so a fresh-install database lands on
 * the new schema in one shot. Pre-existing databases (created under the
 * Phase 1 schema) get the same columns added by `applyPhaseAColumnUpgrades`
 * below, which uses `ALTER TABLE ADD COLUMN` guarded by `PRAGMA table_info`.
 */
export declare const CHAT_V2_MIGRATION_SQL = "\nCREATE TABLE IF NOT EXISTS chat_channels (\n  id                TEXT PRIMARY KEY,\n  agent_session     TEXT NOT NULL,\n  owner_user_id     TEXT NOT NULL,\n  name              TEXT NOT NULL,\n  purpose           TEXT,\n  created_at        INTEGER NOT NULL,\n  archived_at       INTEGER,\n  last_message_at   INTEGER,\n  -- Phase A (SEALED \u00A73.1): channel-type discriminator. 'dm' preserves the\n  -- Phase 1 1:1 user\u2194agent contract; 'channel' is the Slack-like team\n  -- surface. 'huddle' (Phase B-2, 2026-05-17) is ad-hoc multi-agent\n  -- groups with explicit membership in chat_channel_members.\n  -- Existing rows backfill to 'dm' via applyPhaseAColumnUpgrades.\n  type              TEXT NOT NULL DEFAULT 'dm' CHECK(type IN ('dm','channel','huddle')),\n  -- Phase A (SEALED \u00A73.1): team workspace link. Required at the service\n  -- layer when type='channel'; null for type='dm'.\n  team_id           TEXT,\n  -- Phase A (SEALED \u00A73.1): optional project link for project-scoped channels.\n  project_id        TEXT,\n  -- Phase A (SEALED \u00A73.1): for type='dm', the resolved member-ID being DM'd\n  -- (distinct from agent_session which is the wire-level binding key).\n  target_member_id  TEXT\n);\n\nCREATE INDEX IF NOT EXISTS ix_channels_owner\n  ON chat_channels(owner_user_id, archived_at);\n\n-- Phase A indexes that reference the new columns (type, team_id, thread_id)\n-- live in CHAT_V2_PHASE_A_INDEX_SQL below \u2014 they must be created AFTER\n-- applyPhaseAColumnUpgrades runs so the columns exist on legacy DBs.\n\nCREATE TABLE IF NOT EXISTS chat_messages (\n  id           TEXT PRIMARY KEY,\n  channel_id   TEXT NOT NULL REFERENCES chat_channels(id) ON DELETE CASCADE,\n  seq          INTEGER NOT NULL,\n  sender_type  TEXT NOT NULL CHECK(sender_type IN ('user','agent','system')),\n  sender_id    TEXT NOT NULL,\n  content      TEXT NOT NULL,\n  content_type TEXT NOT NULL CHECK(content_type IN ('text','markdown','image_ref','system_note'))\n               DEFAULT 'markdown',\n  created_at   INTEGER NOT NULL,\n  metadata     TEXT,\n  -- Phase A (SEALED \u00A73.2): JSON-encoded array of mention IDs (member or\n  -- team) referenced inline in the content field. Stored as a JSON string;\n  -- service layer treats null as []. Bounded at insert time; see types.ts.\n  mentions     TEXT,\n  -- Phase A (SEALED \u00A73.2): optional Slack-style thread root. When set,\n  -- this message is a reply within the thread rooted at thread_id.\n  thread_id    TEXT\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS uq_messages_channel_seq\n  ON chat_messages(channel_id, seq);\n\nCREATE INDEX IF NOT EXISTS ix_messages_channel_created\n  ON chat_messages(channel_id, created_at DESC);\n\n-- Phase 1: partial unique index for clientMessageId-based idempotency (spec \u00A74.4)\nCREATE UNIQUE INDEX IF NOT EXISTS uq_messages_client_id\n  ON chat_messages(channel_id, json_extract(metadata, '$.clientMessageId'))\n  WHERE json_extract(metadata, '$.clientMessageId') IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS chat_attachments (\n  id            TEXT PRIMARY KEY,\n  message_id    TEXT NOT NULL REFERENCES chat_messages(id) ON DELETE CASCADE,\n  kind          TEXT NOT NULL CHECK(kind IN ('image')),\n  mime_type     TEXT NOT NULL,\n  size_bytes    INTEGER NOT NULL,\n  local_path    TEXT NOT NULL,\n  original_name TEXT,\n  created_at    INTEGER NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS ix_attachments_message\n  ON chat_attachments(message_id);\n\nCREATE TABLE IF NOT EXISTS chat_offline_queue (\n  id            INTEGER PRIMARY KEY AUTOINCREMENT,\n  channel_id    TEXT NOT NULL,\n  agent_session TEXT NOT NULL,\n  message_id    TEXT NOT NULL REFERENCES chat_messages(id) ON DELETE CASCADE,\n  queued_at     INTEGER NOT NULL,\n  delivered_at  INTEGER,\n  attempts      INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE INDEX IF NOT EXISTS ix_queue_pending\n  ON chat_offline_queue(agent_session, delivered_at)\n  WHERE delivered_at IS NULL;\n\n-- Phase B-2 (2026-05-17): huddle membership roster. One row per\n-- (huddle_channel, agent_session_name). For type='huddle' channels\n-- only; 'dm' and 'channel' rows have no entries here.\n--\n-- Dispatcher uses this list to fan a user message out to every\n-- huddle member; the @-mention subset gets a \"must respond\" prompt\n-- variant while the rest get an \"optional\" one.\nCREATE TABLE IF NOT EXISTS chat_channel_members (\n  channel_id      TEXT NOT NULL REFERENCES chat_channels(id) ON DELETE CASCADE,\n  member_session  TEXT NOT NULL,\n  joined_at       INTEGER NOT NULL,\n  PRIMARY KEY (channel_id, member_session)\n);\n\nCREATE INDEX IF NOT EXISTS ix_channel_members_session\n  ON chat_channel_members(member_session);\n";
/**
 * Phase A indexes that reference Phase A columns. Must run AFTER
 * `applyPhaseAColumnUpgrades` adds those columns on pre-Phase-A databases —
 * otherwise the partial-index `WHERE` clauses fail with "no such column".
 *
 * On a fresh database this runs after `CHAT_V2_MIGRATION_SQL` has already
 * baked the columns into the CREATE TABLE bodies, so each step is a
 * harmless no-op (`CREATE INDEX IF NOT EXISTS`).
 */
export declare const CHAT_V2_PHASE_A_INDEX_SQL = "\n-- Phase A (SEALED \u00A73.1) used to define uq_channel_agent_dm_active\n-- enforcing 1:1 agent<->DM-channel binding. That constraint was dropped\n-- per the unified-chat-message-store spec (2026-05-14 \u00A7\"Phase 2 design\n-- discovery\" \u2192 Option B): legacy ChatService models \"one conversation\n-- per Slack thread or web chat session\", so an agent participates in\n-- N concurrent DM-style channels. Dropping the index removes the\n-- agent_already_bound (409) failure that would otherwise block\n-- migration of the legacy multi-thread model. Existing databases get\n-- the drop applied in applyPhaseAColumnUpgrades.\n\n-- Phase A (SEALED \u00A73.1): scoped lookup for \"all channels in team T\" and\n-- \"project P channels\". Indexes by team first because team-scoped reads\n-- are the dominant Phase B+ access pattern.\nCREATE INDEX IF NOT EXISTS ix_channels_team\n  ON chat_channels(team_id, archived_at)\n  WHERE team_id IS NOT NULL;\n\n-- Phase A (SEALED \u00A73.2): thread-pane lookup. Filtered to non-null so the\n-- (smaller) index only covers actual threaded replies.\nCREATE INDEX IF NOT EXISTS ix_messages_thread\n  ON chat_messages(thread_id, seq)\n  WHERE thread_id IS NOT NULL;\n";
/**
 * Bring a pre-existing chat database up to Phase A's schema by adding
 * any missing columns and the new indexes / dropping the superseded ones.
 *
 * Safe to run on a fresh database too — every step is no-op idempotent.
 * Specifically:
 *   - `ensureColumn` is a no-op when the column is already present (the
 *     fresh-install case, since the columns are baked into the CREATE
 *     TABLE in `CHAT_V2_MIGRATION_SQL`).
 *   - `DROP INDEX IF EXISTS uq_channel_agent_active` removes the legacy
 *     unconditional partial index so the new dm-scoped one isn't shadowed.
 *     The DROP is metadata-only — no row data is affected.
 *
 * Exported (separately from `openChatDatabase`) so tests can simulate the
 * pre-Phase-A → Phase A migration path explicitly.
 *
 * @param db - The chat database handle
 * @returns A small report describing what changed (for logging)
 */
export declare function applyPhaseAColumnUpgrades(db: ChatDatabase): {
    channelsAdded: string[];
    messagesAdded: string[];
    legacyIndexDropped: boolean;
    phaseADmIndexDropped: boolean;
    /**
     * Phase B-2: whether `chat_channels` was rebuilt to drop the legacy
     * `CHECK(type IN ('dm','channel'))` constraint and allow `'huddle'`.
     * `false` on fresh installs (the new CHECK is already in the
     * baseline DDL) and on subsequent boots after the first rebuild.
     */
    channelsCheckRebuilt: boolean;
};
/**
 * Options accepted by `openChatDatabase`.
 */
export interface OpenChatDatabaseOptions {
    /** Absolute path to the SQLite file. Parent dirs will be created. */
    dbPath: string;
    /**
     * When true, uses SQLite `:memory:` regardless of `dbPath`.
     * Intended for unit tests. Defaults to false.
     */
    inMemory?: boolean;
    /**
     * When true, suppress the integrity-check warning on open. Tests use this
     * to avoid noisy logs. Defaults to false.
     */
    skipIntegrityCheck?: boolean;
    /** Optional logger override — defaults to LoggerService. */
    logger?: ComponentLogger;
}
/**
 * Open (or create) the chat database, apply PRAGMAs, run the Phase 1
 * migration idempotently, and run a boot-time integrity check.
 *
 * @param options - Opener options
 * @returns A ready-to-use better-sqlite3 Database handle
 *
 * @example
 * ```ts
 * const db = openChatDatabase({ dbPath: '/tmp/chat.db' });
 * // ... use db ...
 * db.close();
 * ```
 */
export declare function openChatDatabase(options: OpenChatDatabaseOptions): ChatDatabase;
//# sourceMappingURL=chat-db.d.ts.map