import { SqliteConnection } from "./connection.js"; import { SqliteConflictStore } from "./conflict-store.js"; import { SqliteConsolidatorStateStore } from "./consolidator-store.js"; import { EmbeddingMetaRepository } from "./embedding-meta-repo.js"; import { VectorTableManager } from "./vector-table-mgr.js"; import { EpisodicMemoryStore, MemoryStoreExt, ProceduralMemoryStore, SemanticMemoryStore, SessionMemoryStore, SharedMemoryStore, WorkingMemoryStore } from "@graphorin/core/contracts"; import { EntityRole, Fact, GraphEntity, Insight, MemoryHit, MemoryStatus, SessionScope } from "@graphorin/core"; //#region src/memory-store.d.ts /** * Optional embedding payload attached to a memory write. The * `embedder_id` must already be registered in `embedding_meta`; the * `vector` length must match the registered `dim`. * * @stable */ interface EmbeddingPayload { readonly embedderId: string; readonly vector: Float32Array; } /** * Extended write surface for fact / episode / message writes. The base * `SemanticMemoryStore.remember(...)` / `EpisodicMemoryStore.put(...)` * methods leave embeddings out - {@link SqliteMemoryStore} accepts an * optional embedding through these helpers. * * @stable */ interface SqliteMemoryWriteOptions { readonly embedding?: EmbeddingPayload; /** * Contextual-retrieval index text. When supplied, the FTS5 row * is indexed against this (context-prepended) text instead of the * canonical `fact.text`, so a terse fact stays findable by a * vaguely-worded query. The persisted `facts.text` column - the value * shown to the user / audit trail - is always the canonical text; only * the lexical index is affected. The caller's `embedding.vector` should * be computed from the same index text so the vector and FTS surfaces * agree. Absent ⇒ the FTS row uses `fact.text` (the historical * behaviour). */ readonly indexText?: string; } /** * Default `MemoryStore` implementation backed by SQLite + sqlite-vec. * * @stable */ declare class SqliteMemoryStore implements MemoryStoreExt { #private; readonly working: WorkingMemoryStore; readonly session: SessionMemoryStore; readonly episodic: EpisodicMemoryStore; readonly semantic: SemanticMemoryStore; readonly procedural: ProceduralMemoryStore; readonly shared: SharedMemoryStore; readonly conflicts: SqliteConflictStore; readonly consolidator: SqliteConsolidatorStateStore; /** Reflection insight surface. FTS-only; no per-embedder vec0 table. */ readonly insights: SqliteInsightStore; /** Lightweight relation-graph surface: entities + one-hop CTE. */ readonly graph: SqliteGraphStore; constructor(conn: SqliteConnection, embeddings: EmbeddingMetaRepository); init(): Promise; close(): Promise; /** Surfaced for tests and the consolidator. */ vectorTableManager(): VectorTableManager; /** Surfaced for tests and the consolidator. */ embeddingMetaRepository(): EmbeddingMetaRepository; /** * Retention prune for the `memory_history` audit trail - * without one the table grows unboundedly (every supersede / * quarantine transition appends). Deletes rows older than * `olderThanMs`; returns the number pruned. Operators call this from * their own maintenance schedule; nothing prunes automatically. * * @stable */ pruneHistory(olderThanMs: number): Promise; } /** * `SqliteInsightStore` - owns the `insights` + `insights_fts` tables * shipped in migration 014. Implements the structural * `InsightMemoryStoreExt` surface defined in * `@graphorin/memory/internal/storage-adapter.ts`. * * Search is FTS5-only - insights are a soft, rank-capped inspector * surface, not primary recall, so no per-embedder vec0 table is * created. Pruning (the ExpeL forgetting step) is a soft-delete * (`deleted_at`), never a hard purge, so pruned insights remain * auditable. * * @stable */ declare class SqliteInsightStore { #private; constructor(conn: SqliteConnection); insert(insight: Insight): Promise; list(scope: SessionScope, opts?: { readonly limit?: number; readonly includeQuarantined?: boolean; }): Promise>; search(scope: SessionScope, query: string, opts?: { readonly topK?: number; readonly includeQuarantined?: boolean; }): Promise>>; get(id: string): Promise; /** * Promote / demote an insight's retrieval-trust `status` and write a * `memory_history` audit row. Mirrors `setStatus` on facts - a retrieval gate * only. Powers `InsightMemory.validate` so a quarantined (reflection) * insight can be promoted out of quarantine. */ setStatus(id: string, status: MemoryStatus, reason?: string, scope?: SessionScope): Promise; /** * Adjust an insight's ExpeL salience by `delta`, clamped at 0 (the * floor at which `prune` removes it). Never touches content / cites - * salience is the only mutable field. */ bumpSalience(id: string, delta: number, reason?: string): Promise; /** * Soft-delete every salience-0 insight for the scope (the ExpeL * forgetting step). Returns the number pruned. Tombstone only - the * row stays for audit. */ prune(scope: SessionScope): Promise; } /** Find-or-create payload for {@link SqliteGraphStore.upsertEntity}. */ /** * Input row accepted by {@link SqliteGraphStore.upsertEntity}. * * @stable */ interface SqliteEntityUpsertInput { readonly name: string; readonly normalizedName: string; readonly vector?: Float32Array; readonly embedderId?: string; } /** A canonical entity returned with its name embedding for dedup. */ /** * Entity row (plus optional embedding) returned by the graph store reads. * * @stable */ interface SqliteEntityWithEmbedding extends GraphEntity { readonly vector: Float32Array | null; readonly embedderId: string | null; } /** One row of the append-only merge / unmerge audit ledger. */ /** * Merge-audit row returned by {@link SqliteGraphStore.listMerges}. * * @stable */ interface SqliteEntityMergeRecord { readonly id: string; readonly userId: string; readonly kind: 'merge' | 'unmerge'; readonly fromEntityId: string; readonly intoEntityId: string | null; readonly reason?: string; readonly createdAt: string; } /** * Lightweight in-SQLite relation-graph store. Owns the canonical * `entities` table, the `fact_entities` mapping, and the append-only * `entity_merges` ledger. Entity *resolution* (lexical + embedding dedup, * optional LLM adjudication) lives in `@graphorin/memory`; this class is * the pure persistence + the one-hop recursive-CTE traversal. Exposed on * {@link SqliteMemoryStore.graph} and picked up structurally as the * memory adapter's optional `graph` capability. * * @stable */ declare class SqliteGraphStore { #private; constructor(conn: SqliteConnection); /** * Find-or-create the canonical (root) entity for `normalizedName` in * the scope. Returns the existing root's id when one exists (back-filling * its embedding if it had none), else inserts and returns a new root. */ upsertEntity(scope: SessionScope, input: SqliteEntityUpsertInput): Promise; /** Link a fact's subject / object to a canonical entity (idempotent). */ linkFactEntity(factId: string, entityId: string, role: EntityRole): Promise; /** Candidate entities for the resolver (roots only unless `includeMerged`). */ listEntities(scope: SessionScope, opts?: { readonly includeMerged?: boolean; readonly limit?: number; }): Promise>; /** * Uncapped indexed lookup of the canonical root for an exact normalized * name. Backed by the partial-unique index on `(scope_user_id, * normalized_name) WHERE merged_into IS NULL`, so the resolver dedups an * exact alias of an arbitrarily-old entity without paging the bounded * {@link listEntities} candidate window or deserializing its BLOBs. */ findEntityByNormalizedName(scope: SessionScope, normalizedName: string): Promise; /** Lookup one entity by id (any merge state). */ getEntity(scope: SessionScope, id: string): Promise; /** Follow `merged_into` to the canonical root id (cycle-guarded). */ resolveCanonical(scope: SessionScope, id: string): Promise; /** * Merge `fromId` into `intoId` (resolved to its root). Sets * `from.merged_into` and re-points `from`'s children to keep the * pointer single-level, then records a `'merge'` audit row. * `fact_entities` are never rewritten - reads canonicalise via * `merged_into`. A self-merge is a no-op. */ mergeEntities(scope: SessionScope, fromId: string, intoId: string, reason?: string): Promise; /** * Reverse a merge: clear `id.merged_into` (making it a root again) and * record an `'unmerge'` audit row. Restores the entity as a root; the * pre-merge child topology is not reconstructed. */ unmergeEntity(scope: SessionScope, id: string, reason?: string): Promise; /** The append-only merge / unmerge audit ledger, newest first. */ listMerges(scope: SessionScope, opts?: { readonly limit?: number; }): Promise>; /** * Expand `seedFactIds` to neighbouring facts that share a canonical * entity, up to `maxHops` (default 1), via a recursive CTE over * `fact_entities`. Entities are canonicalised through `merged_into` in * the join, so a merge transparently connects both sides. Excludes the * seeds themselves and honours soft-delete / archive / quarantine / * `asOf` exactly like {@link SemanticMemoryStore.search}. */ expandOneHop(scope: SessionScope, seedFactIds: ReadonlyArray, opts?: { readonly maxHops?: number; readonly limit?: number; readonly includeQuarantined?: boolean; readonly asOf?: string; readonly includeSuperseded?: boolean; }): Promise>; /** * PPR-lite graded expansion: the same recursive entity-graph walk * as {@link expandOneHop}, but returns each reachable fact with its * MINIMUM hop distance from the seed set, so callers can weight * neighbours by damped spreading activation instead of a flat score. */ expandActivation(scope: SessionScope, seedFactIds: ReadonlyArray, opts?: { readonly maxHops?: number; readonly limit?: number; readonly includeQuarantined?: boolean; readonly asOf?: string; readonly includeSuperseded?: boolean; }): Promise>; /** * Exact entity-match retriever: facts linked to the entity whose * normalized name equals `normalizedName` (canonicalising merges). * Powers a precise "facts about " candidate leg distinct from * the fuzzy vector/FTS legs. */ factsForEntityName(scope: SessionScope, normalizedName: string, opts?: { readonly limit?: number; readonly includeQuarantined?: boolean; readonly asOf?: string; readonly includeSuperseded?: boolean; }): Promise>; } //#endregion export { EmbeddingPayload, SqliteEntityMergeRecord, SqliteEntityUpsertInput, SqliteEntityWithEmbedding, SqliteGraphStore, SqliteInsightStore, SqliteMemoryStore, SqliteMemoryWriteOptions }; //# sourceMappingURL=memory-store.d.ts.map