import type { MemorySyncCursor } from "@sema-agent/core"; import type { PgQueryFn } from "./pg-query.js"; import { type IndexSpec } from "./ensure-index.js"; /** Table names (single source). SAME names as the TiDB twin (the two dialects never share one DB). */ export declare const PG_MEMORY_SYNC_TABLES: { readonly syncCursor: "agent_memory_engine_sync_cursor"; readonly pushQueue: "agent_memory_engine_push_queue"; readonly history: "agent_memory_engine_history"; }; /** What the caller hands `enqueue` — the transport-neutral unit (§3 传输中立包:payload = the full * entry, opaque to this store; this queue never interprets it). */ export interface PushQueueItem { /** uuidv7 minted by the caller (time-ordered id = queue order tiebreak). */ id: string; scope: string; entryId: string; /** JSON-serializable entry payload — stored verbatim, returned parsed. */ payload: unknown; /** Epoch ms at/after which the item is due (caller-injected clock, core precedent). */ nextAttemptAtMs: number; } /** A row handed back by `claimDue` — `attempts`/`lastError` reflect the state BEFORE this claim * (`fail` is what increments), so the consumer can enforce its attempt cap fail-loud. */ export interface ClaimedPushQueueItem { id: string; scope: string; entryId: string; payload: unknown; attempts: number; lastError: string | null; /** The lease deadline this claim holds the row until (nowMs + claimLeaseMs). */ leasedUntilMs: number; } /** The dialect-neutral contract both twins implement (anti-drift: one shared integration suite, * test/memory-sync-store-db-integration.test.ts, runs the SAME scenarios against BOTH engines). */ export interface MemorySyncStore { getCursor(scope: string, peer: string): Promise; /** Whole-row replace (round semantics — baseRevs is swapped as one map, never merged). */ putCursor(cursor: MemorySyncCursor): Promise; enqueue(item: PushQueueItem): Promise; /** Lease-claim up to `limit` due rows (next_attempt_at_ms <= nowMs), oldest-due first. Claimed rows * are invisible to other claimers until `complete`/`fail` or lease expiry (crash self-heal). */ claimDue(nowMs: number, limit: number): Promise; /** Success = delete the row (outbox posture: the queue only holds outstanding work). */ complete(id: string): Promise; /** Transient failure: attempts+1 (SQL-atomic), record the error, schedule the next attempt. */ fail(id: string, error: string, nextAttemptAtMs: number): Promise; } /** S-287:push queue 的索引**声明**(MySQL 孪生 = `memory-sync-store-tidb.ts` 的内联 `KEY`)。 */ export declare const MEMORY_SYNC_INDEXES: readonly IndexSpec[]; /** Idempotent DDL for the sync plane (PG dialect). Call once at startup. */ export declare function ensurePgMemorySyncSchema(query: PgQueryFn): Promise; /** Options for {@link PgMemorySyncStore}. */ export interface PgMemorySyncStoreOptions { /** How long a `claimDue` lease holds a row invisible before a crashed worker's claim self-heals. * Must exceed the consumer's worst-case push round-trip. Default 60s. */ claimLeaseMs?: number; } export declare class PgMemorySyncStore implements MemorySyncStore { private readonly query; private readonly claimLeaseMs; constructor(query: PgQueryFn, opts?: PgMemorySyncStoreOptions); getCursor(scope: string, peer: string): Promise; putCursor(cursor: MemorySyncCursor): Promise; enqueue(item: PushQueueItem): Promise; claimDue(nowMs: number, limit: number): Promise; complete(id: string): Promise; fail(id: string, error: string, nextAttemptAtMs: number): Promise; } /** One committed entry version, as the caller hands it to `appendHistory`. `prevRev` links the rev * chain (chain head = absent); a `delete` row is the tombstone (no snapshot). */ export interface MemoryHistoryAppendRow { /** Entry id (NOT unique alone — one id has many versions; PK is (id, rev)). */ id: string; /** This version's committed rev (content hash — the same rev the entry plane CAS'd on). */ rev: string; /** The rev this version replaced; absent on the chain head (first add). */ prevRev?: string; op: "add" | "update" | "delete"; /** ⚠️ DESIGN-DOC AMENDMENT (142-S4 §1.3 has no scope column): the §1.4 export face and the * countByScope ops read NEED per-scope addressing, so the column is added here — denormalized * from the entry at write time (a deleted entry's row can't be joined back to the entries table). */ scope: string; /** Full entry snapshot for add/update; absent (→ SQL NULL) on delete tombstones. */ snapshot?: unknown; /** Caller-injected clock (core precedent) — the append point stamps the commit time. */ writtenAtMs: number; } /** A history row read back (`historyOf`) — same shape with SQL NULLs made explicit. */ export interface MemoryHistoryRow { id: string; rev: string; prevRev: string | null; op: "add" | "update" | "delete"; scope: string; snapshot: unknown | null; writtenAtMs: number; } /** The dialect-neutral history contract both twins implement (append-only: no update/delete face). */ export interface MemoryHistoryStore { /** Batch append. A duplicate (id, rev) is IGNORED idempotently (PG ON CONFLICT DO NOTHING / * TiDB no-op ON DUPLICATE KEY) — replaying a batch after a partial failure must not error and * must not rewrite committed history rows. Empty input = no-op. */ appendHistory(rows: readonly MemoryHistoryAppendRow[]): Promise; /** All versions of one entry, oldest-first (written_at_ms; the authoritative ORDER within a same-ms * burst is the prev_rev chain itself — the sort is the read convenience, rev tie-break keeps it * deterministic). */ historyOf(id: string): Promise; /** Ops read face (§1.3 保留策略: v1 无界 + 运维计数 — quota/pruning waits for a demand signal). */ countByScope(scope: string): Promise; } /** S-287:history 表的索引**声明**(MySQL 孪生 = `memory-sync-store-tidb.ts` 的内联 `KEY`)。 */ export declare const MEMORY_HISTORY_INDEXES: readonly IndexSpec[]; /** Idempotent DDL for the history table (PG dialect). Separate from the sync-plane ensure so S5.1 * can wire the write point independently of the S4 sync wiring. */ export declare function ensurePgMemoryHistorySchema(query: PgQueryFn): Promise; export declare class PgMemoryHistoryStore implements MemoryHistoryStore { private readonly query; constructor(query: PgQueryFn); appendHistory(rows: readonly MemoryHistoryAppendRow[]): Promise; historyOf(id: string): Promise; countByScope(scope: string): Promise; } /** Shared row mapper (both dialects' SELECTs project the same column set). */ export declare function historyRowFrom(r: Record): MemoryHistoryRow; //# sourceMappingURL=memory-sync-store-pg.d.ts.map