import type { MemeshDatabase } from './sqlite.js'; export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS entities (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL UNIQUE,\n type TEXT NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n metadata JSON\n);\n\nCREATE TABLE IF NOT EXISTS observations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n entity_id INTEGER NOT NULL,\n content TEXT NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS relations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_entity_id INTEGER NOT NULL,\n to_entity_id INTEGER NOT NULL,\n relation_type TEXT NOT NULL,\n metadata JSON,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (from_entity_id) REFERENCES entities(id) ON DELETE CASCADE,\n FOREIGN KEY (to_entity_id) REFERENCES entities(id) ON DELETE CASCADE,\n UNIQUE(from_entity_id, to_entity_id, relation_type)\n);\n\nCREATE TABLE IF NOT EXISTS tags (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n entity_id INTEGER NOT NULL,\n tag TEXT NOT NULL,\n FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_tags_entity ON tags(entity_id);\nCREATE INDEX IF NOT EXISTS idx_tags_tag ON tags(tag);\n-- The tags dedup DELETE + idx_tags_entity_tag_unique creation live in\n-- ensureTagsUniqueIndex(), AFTER this exec \u2014 the DELETE is a one-time\n-- migration, and a DML statement in this string made every open start a\n-- write transaction even when it deleted nothing (same reader-breaking\n-- pattern as the hook_runs_since note below).\nCREATE INDEX IF NOT EXISTS idx_observations_entity ON observations(entity_id);\nCREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_entity_id);\nCREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_entity_id);\nCREATE INDEX IF NOT EXISTS idx_entities_type_created ON entities(type, created_at);\n\n-- Migration markers and small bits of persistent state.\n--\n-- This used to be created ad hoc by each helper that needed it \u2014 four inline\n-- CREATE TABLE IF NOT EXISTS copies in src/db.ts, none of them visible to\n-- schema checks. A column added to one copy would not have been caught. It\n-- also meant the hook-side schema had no metadata table at all, so hooks\n-- could not participate in migrations even in principle.\nCREATE TABLE IF NOT EXISTS memesh_metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\n-- Local agent messaging core. Messages are immutable, deliveries identify the\n-- authorized recipient, events are the wakeup/catch-up surface, cursors keep\n-- the internal sequence opaque, and receipts are append-only facts that remain\n-- separate from delivery existence.\nCREATE TABLE IF NOT EXISTS agent_messages (\n message_id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n sender TEXT NOT NULL,\n sender_host TEXT,\n recipient TEXT NOT NULL,\n content_type TEXT NOT NULL,\n correlation_id TEXT,\n reply_to_message_id TEXT,\n privacy TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n -- A tombstone replaces only a terminal payload. The original JSON bytes\n -- are never reconstructed from these fields; they retain enough identity to\n -- audit the erasure while the routing and lifecycle tables remain intact.\n payload_sha256 TEXT,\n payload_original_bytes INTEGER,\n payload_tombstoned_at TIMESTAMP,\n provenance_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS agent_message_deliveries (\n delivery_id TEXT PRIMARY KEY,\n message_id TEXT NOT NULL,\n project TEXT NOT NULL,\n recipient TEXT NOT NULL,\n target_kind TEXT NOT NULL DEFAULT 'principal' CHECK (target_kind IN ('principal', 'session')),\n -- #497: the one session a principal delivery is meant for (NULL: every\n -- session of that principal). Added by migrateEntitiesSchema on older files.\n intended_session TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (message_id) REFERENCES agent_messages(message_id) ON DELETE CASCADE,\n UNIQUE(message_id, project, recipient)\n);\n\nCREATE TABLE IF NOT EXISTS agent_message_events (\n event_sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n message_id TEXT NOT NULL,\n delivery_id TEXT NOT NULL,\n project TEXT NOT NULL,\n recipient TEXT NOT NULL,\n event_kind TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (message_id) REFERENCES agent_messages(message_id) ON DELETE CASCADE,\n FOREIGN KEY (delivery_id) REFERENCES agent_message_deliveries(delivery_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS agent_message_idempotency (\n project TEXT NOT NULL,\n sender TEXT NOT NULL,\n idempotency_key TEXT NOT NULL,\n request_hash TEXT NOT NULL,\n message_id TEXT NOT NULL UNIQUE,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (message_id) REFERENCES agent_messages(message_id) ON DELETE CASCADE,\n PRIMARY KEY(project, sender, idempotency_key)\n);\n\nCREATE TABLE IF NOT EXISTS agent_message_cursors (\n cursor_token TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n recipient TEXT NOT NULL,\n event_sequence INTEGER NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS agent_message_receipts (\n receipt_id TEXT PRIMARY KEY,\n message_id TEXT NOT NULL,\n project TEXT NOT NULL,\n recipient TEXT NOT NULL,\n receipt_kind TEXT NOT NULL,\n actor TEXT NOT NULL,\n idempotency_key TEXT NOT NULL,\n request_hash TEXT NOT NULL,\n detail_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (message_id) REFERENCES agent_messages(message_id) ON DELETE CASCADE,\n UNIQUE(project, recipient, message_id, receipt_kind, idempotency_key)\n);\n\n-- Host-native push identity and lifecycle. A principal is stable, a session\n-- instance is ephemeral, and every connection to that session gets a strictly\n-- increasing generation. The activation checkpoint is intentionally created\n-- at first principal registration: principal-targeted history at or before it\n-- is durable inbox history, but is never replayed as a first-time host push.\nCREATE TABLE IF NOT EXISTS agent_principals (\n project TEXT NOT NULL,\n principal_id TEXT NOT NULL,\n activation_event_sequence INTEGER NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY(project, principal_id)\n);\n\nCREATE TABLE IF NOT EXISTS agent_session_instances (\n project TEXT NOT NULL,\n session_instance_id TEXT NOT NULL,\n principal_id TEXT NOT NULL,\n adapter_kind TEXT NOT NULL,\n last_generation INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY(project, session_instance_id),\n FOREIGN KEY (project, principal_id) REFERENCES agent_principals(project, principal_id)\n);\n\nCREATE TABLE IF NOT EXISTS agent_session_connections (\n connection_id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n principal_id TEXT NOT NULL,\n session_instance_id TEXT NOT NULL,\n generation INTEGER NOT NULL,\n adapter_kind TEXT NOT NULL,\n router_instance_id TEXT NOT NULL,\n lease_expires_at_ms INTEGER NOT NULL,\n connected_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n disconnected_at TIMESTAMP,\n disconnect_reason TEXT,\n UNIQUE(project, session_instance_id, generation),\n FOREIGN KEY (project, session_instance_id)\n REFERENCES agent_session_instances(project, session_instance_id)\n);\n\nCREATE TABLE IF NOT EXISTS agent_presence_facts (\n presence_fact_id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n principal_id TEXT NOT NULL,\n session_instance_id TEXT NOT NULL,\n connection_id TEXT NOT NULL,\n generation INTEGER NOT NULL,\n presence_kind TEXT NOT NULL CHECK (presence_kind IN ('connected', 'heartbeat', 'disconnected', 'superseded')),\n detail_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (connection_id) REFERENCES agent_session_connections(connection_id)\n);\n\n-- Dispatch is at-least-once across a crash boundary. delivery_id is the stable\n-- adapter dispatch key; attempt_id identifies one invocation. A host_accept is\n-- a separate fact and never implies an agent acknowledgement or workflow\n-- outcome.\nCREATE TABLE IF NOT EXISTS agent_dispatch_attempts (\n attempt_id TEXT PRIMARY KEY,\n delivery_id TEXT NOT NULL,\n project TEXT NOT NULL,\n principal_id TEXT NOT NULL,\n session_instance_id TEXT NOT NULL,\n connection_id TEXT NOT NULL,\n generation INTEGER NOT NULL,\n router_instance_id TEXT NOT NULL,\n attempt_number INTEGER NOT NULL,\n result TEXT NOT NULL DEFAULT 'started' CHECK (result IN ('started', 'adapter_returned', 'adapter_rejected', 'adapter_failed', 'stale_generation')),\n failure_code TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n completed_at TIMESTAMP,\n UNIQUE(delivery_id, attempt_number),\n FOREIGN KEY (delivery_id) REFERENCES agent_message_deliveries(delivery_id) ON DELETE CASCADE,\n FOREIGN KEY (connection_id) REFERENCES agent_session_connections(connection_id)\n);\n\nCREATE TABLE IF NOT EXISTS agent_host_accepts (\n host_accept_id TEXT PRIMARY KEY,\n attempt_id TEXT NOT NULL UNIQUE,\n delivery_id TEXT NOT NULL UNIQUE,\n adapter_kind TEXT NOT NULL,\n receipt_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (attempt_id) REFERENCES agent_dispatch_attempts(attempt_id),\n FOREIGN KEY (delivery_id) REFERENCES agent_message_deliveries(delivery_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS agent_ack_facts (\n ack_fact_id TEXT PRIMARY KEY,\n delivery_id TEXT NOT NULL,\n host_accept_id TEXT NOT NULL,\n actor TEXT NOT NULL,\n idempotency_key TEXT NOT NULL,\n request_hash TEXT NOT NULL,\n detail_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(delivery_id, actor, idempotency_key),\n FOREIGN KEY (delivery_id) REFERENCES agent_message_deliveries(delivery_id) ON DELETE CASCADE,\n FOREIGN KEY (host_accept_id) REFERENCES agent_host_accepts(host_accept_id)\n);\n\nCREATE TABLE IF NOT EXISTS agent_workflow_facts (\n workflow_fact_id TEXT PRIMARY KEY,\n delivery_id TEXT NOT NULL,\n actor TEXT NOT NULL,\n workflow_state TEXT NOT NULL,\n idempotency_key TEXT NOT NULL,\n request_hash TEXT NOT NULL,\n detail_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(delivery_id, actor, idempotency_key),\n FOREIGN KEY (delivery_id) REFERENCES agent_message_deliveries(delivery_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS agent_retention_facts (\n retention_fact_id TEXT PRIMARY KEY,\n message_id TEXT NOT NULL,\n actor TEXT NOT NULL,\n retention_state TEXT NOT NULL,\n idempotency_key TEXT NOT NULL,\n request_hash TEXT NOT NULL,\n detail_json TEXT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(message_id, actor, idempotency_key),\n FOREIGN KEY (message_id) REFERENCES agent_messages(message_id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_agent_message_events_recipient_sequence\n ON agent_message_events(project, recipient, event_sequence);\nCREATE INDEX IF NOT EXISTS idx_agent_message_deliveries_scope\n ON agent_message_deliveries(project, recipient, message_id);\nCREATE INDEX IF NOT EXISTS idx_agent_message_receipts_scope\n ON agent_message_receipts(project, recipient, message_id, created_at);\nCREATE UNIQUE INDEX IF NOT EXISTS idx_agent_message_cursors_unique_scope_sequence\n ON agent_message_cursors(project, recipient, event_sequence);\nCREATE INDEX IF NOT EXISTS idx_agent_principals_activation\n ON agent_principals(project, activation_event_sequence);\nCREATE INDEX IF NOT EXISTS idx_agent_session_connections_active\n ON agent_session_connections(project, principal_id, session_instance_id, lease_expires_at_ms)\n WHERE disconnected_at IS NULL;\nCREATE INDEX IF NOT EXISTS idx_agent_dispatch_attempts_delivery\n ON agent_dispatch_attempts(delivery_id, attempt_number);\nCREATE INDEX IF NOT EXISTS idx_agent_workflow_facts_delivery_created\n ON agent_workflow_facts(delivery_id, created_at, workflow_fact_id);\n\n-- Proof that a capture hook actually RAN. Nothing else in this schema can\n-- give it: every other signal is \"a row was written\", and \"the hook ran and\n-- found nothing worth saving\" is the healthy case that produces no row at\n-- all. So a quiet day and a dead capture loop were byte-identical in the\n-- database, and the one doctor message that had to cover both cried wolf on\n-- the first and stayed silent on the second.\n--\n-- Written by the three hooks that hold a read-write handle (Stop, PreCompact,\n-- PostToolUse), each calling recordHookRun() at its own SUCCESSFUL exit \u2014\n-- after capture, not at open. Stamping at open certified the wrong thing: a\n-- hook that opened the database and then died mid-capture looked alive for a\n-- day. \"Successful\" is precise: a completed capture, or a well-formed\n-- payload the hook correctly decided not to capture (dedup, low-signal\n-- session). A malformed payload (schema-flip shapes) and a write that did\n-- not land both leave no stamp \u2014 either would make the heartbeat mask the\n-- exact dropout it exists to expose. The recall-side hooks open read-only\n-- and deliberately do not appear here: their liveness answers a different\n-- question, and giving them a write handle would put a lock acquisition on\n-- the SessionStart hot path.\n--\n-- One row per hook, upserted. It does not grow.\nCREATE TABLE IF NOT EXISTS hook_runs (\n hook TEXT PRIMARY KEY,\n last_run_at TIMESTAMP NOT NULL,\n run_count INTEGER NOT NULL DEFAULT 0\n);\n\n-- The 'hook_runs_since' metadata key (when this database first became able\n-- to record hook runs) is stamped by ensureHookRunsSince() AFTER this exec,\n-- NOT here. It used to be an INSERT OR IGNORE in this string, and that made\n-- every open \u2014 including opens that only ever read \u2014 start a write\n-- transaction: an INSERT statement takes the WAL writer lock even when\n-- OR IGNORE ends up changing nothing. Two states regressed from \"reads work\"\n-- to \"open throws\": a peer holding the writer lock past busy_timeout, and a\n-- read-only database FILE (measured: \"attempt to write a readonly database\"\n-- killed recall along with capture). The helper SELECTs first and writes\n-- only when the key is genuinely absent \u2014 once per database lifetime.\n"; export declare const FTS_SQL = "\nCREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(\n name, observations, content='',\n tokenize='unicode61 remove_diacritics 1'\n);\n\n-- Term -> document-count view over the index above. Stores nothing of its own;\n-- it exists so search() can drop query terms that appear in most of the corpus,\n-- which are the ones BM25 already scores near zero. See dropUbiquitousTerms().\nCREATE VIRTUAL TABLE IF NOT EXISTS fts_vocab USING fts5vocab(entities_fts, 'row');\n"; export declare function safeAlter(db: MemeshDatabase, sql: string): void; export declare function migrateEntitiesSchema(db: MemeshDatabase): void; export declare function ensureTagsUniqueIndex(db: MemeshDatabase): void; export declare function ensureHookRunsSince(db: MemeshDatabase): void; export declare function isTransientDbError(err: unknown): boolean; export declare const MIGRATION_RETRY_BACKOFF_MS: number; export declare function runOnceMigration(db: MemeshDatabase, opts: { key: string; version: number; describe: string; retryCommand?: string; migrate: (db: MemeshDatabase, fromVersion: number) => void; }): boolean; export declare const FTS_SEGMENTATION_VERSION = 3; export declare const FTS_REBUILD_PAGE_SIZE = 500; export declare function rebuildFtsIndex(db: MemeshDatabase): void; export declare function ensureFtsSegmentation(db: MemeshDatabase): void; export declare function ftsIndexIsCurrent(db: MemeshDatabase): boolean; //# sourceMappingURL=schema.d.ts.map