/** * Store layer types and interfaces. * Defines StorePort (port interface) and all data types for persistence. * * @module src/store/types */ import type { ChunkingParams } from "../config/chunking"; import type { Collection, Context, EgressPolicy, EgressPolicySource, FtsTokenizer, } from "../config/types"; import type { RecordAnchor, RecordMetadata } from "../converters/types"; import type { EgressLineage } from "../core/egress-provenance"; import type { FileRefactorJournalAdvance, FileRefactorRecoveryReceipt, FileRefactorRecoveryReceiptDraft, } from "../core/file-refactor-journal"; import type { MetadataPredicate, TypedMetadata } from "../core/typed-metadata"; import type { ChunkingPolicyToken, ChunkingStatus, PendingChunkingMirror, } from "./chunking"; // ───────────────────────────────────────────────────────────────────────────── // Error Types // ───────────────────────────────────────────────────────────────────────────── /** * Fixed service default maximum for watcher active source-path queries. * Callers (watcher fallback later) pass this explicitly; methods never invent * an unbounded success path. */ export const WATCHER_ACTIVE_SOURCE_PATH_MAX = 100_000; /** Store error codes */ export type StoreErrorCode = | "NOT_FOUND" | "ALREADY_EXISTS" | "CONSTRAINT_VIOLATION" | "MIGRATION_FAILED" | "CONNECTION_FAILED" | "EXTENSION_LOAD_FAILED" | "QUERY_FAILED" | "TRANSACTION_FAILED" | "INVALID_INPUT" | "CHUNKING_POLICY_CONFLICT" | "IO_ERROR" | "INTERNAL" | "EGRESS_DENIED" /** Result set exceeded the caller-supplied positive maximum (never truncated). */ | "OVERFLOW" // Vector-specific error codes (EPIC 7) | "VECTOR_WRITE_FAILED" | "VECTOR_DELETE_FAILED" | "VEC_SEARCH_UNAVAILABLE" | "VEC_SEARCH_FAILED" | "VEC_REBUILD_FAILED" | "VEC_SYNC_FAILED"; /** Store error with structured details */ export interface StoreError { code: StoreErrorCode; message: string; cause?: unknown; details?: Record; } /** Result type for store operations */ export type StoreResult = | { ok: true; value: T } | { ok: false; error: StoreError }; /** Create a success result */ export function ok(value: T): StoreResult { return { ok: true, value }; } /** Create an error result */ export function err( code: StoreErrorCode, message: string, cause?: unknown ): StoreResult { return { ok: false, error: { code, message, cause } }; } // ───────────────────────────────────────────────────────────────────────────── // Row Types (DB representations) // ───────────────────────────────────────────────────────────────────────────── /** Collection row from DB (mirrors config) */ export interface CollectionRow { name: string; path: string; pattern: string; include: string[] | null; exclude: string[] | null; updateCmd: string | null; languageHint: string | null; /** Effective fail-closed content transfer boundary. */ egressPolicy: EgressPolicy; /** Whether policy was explicit or supplied by a safe default. */ egressPolicySource: EgressPolicySource; syncedAt: string; } /** Context row from DB (mirrors config) */ export interface ContextRow { scopeType: "global" | "collection" | "prefix"; scopeKey: string; text: string; syncedAt: string; } /** Document row from DB */ export interface DocumentRow { typedMetadata?: TypedMetadata | null; metadataError?: string | null; id: number; collection: string; relPath: string; // Source metadata sourceHash: string; sourceMime: string; sourceExt: string; sourceSize: number; sourceMtime: string; sourceCtime?: string | null; // Derived identifiers docid: string; uri: string; // Conversion output title: string | null; mirrorHash: string | null; converterId: string | null; converterVersion: string | null; languageHint: string | null; contentType?: string | null; contentTypeSource?: string | null; categories?: string[] | null; author?: string | null; frontmatterDate?: string | null; dateFields?: Record | null; recordKey?: string | null; recordSourcePath?: string | null; recordSourceLocator?: string | null; recordMetadata?: RecordMetadata | null; recordAnchors?: RecordAnchor[] | null; recordAdapterFingerprint?: string | null; indexedAt?: string | null; // Status active: boolean; /** Ingest schema version for backfill detection */ ingestVersion: number | null; /** Fingerprint of metadata-affecting content type rules used for derivation. */ contentTypeRulesFingerprint?: string | null; // Error tracking lastErrorCode: string | null; lastErrorMessage: string | null; lastErrorAt: string | null; // Timestamps createdAt: string; updatedAt: string; } /** Minimal persisted record identity used by export snapshot reconciliation. */ export interface StoredRecordState { recordKey: string; sourceHash: string; adapterVersion: string; adapterFingerprint: string; active: boolean; relativePath: string; } /** Chunk row from DB */ export interface ChunkRow { mirrorHash: string; seq: number; pos: number; text: string; startLine: number; endLine: number; language: string | null; tokenCount: number | null; createdAt: string; } /** Ingest error row from DB */ export interface IngestErrorRow { id: number; collection: string; relPath: string; occurredAt: string; code: string; message: string; detailsJson: string | null; } /** Tag row from DB */ export interface TagRow { /** Normalized tag text */ tag: string; /** Source: 'frontmatter' (auto-extracted) or 'user' (manually applied) */ source: "frontmatter" | "user"; } /** Tag count for aggregation */ export interface TagCount { /** Normalized tag text */ tag: string; /** Number of documents with this tag */ count: number; } /** Tag source type */ export type TagSource = "frontmatter" | "user"; /** Link source type */ export type DocLinkSource = "parsed" | "user" | "suggested"; /** Link type */ export type DocLinkType = "wiki" | "markdown"; /** Semantic edge type / relationship name. Lowercase snake_case after validation. */ export type DocEdgeType = string; export type RelationType = DocEdgeType; /** Semantic edge confidence. Distinct from GraphEdgeConfidence. */ export type DocEdgeConfidence = "parsed" | "configured" | "manual" | "inferred"; /** Semantic edge source / provenance. */ export type DocEdgeSource = | "wikilink" | "markdown-link" | "frontmatter-relation"; /** Document link row from DB */ export interface DocLinkRow { /** Raw path or wiki name (no anchor) */ targetRef: string; /** Normalized key for matching */ targetRefNorm: string; /** Anchor/fragment without # */ targetAnchor: string | null; /** Explicit collection prefix */ targetCollection: string | null; /** Link type */ linkType: DocLinkType; /** Display text (truncated 256 graphemes) */ linkText: string | null; /** 1-based line number */ startLine: number; /** 1-based column */ startCol: number; /** 1-based end line */ endLine: number; /** 1-based end column */ endCol: number; /** Source of the link */ source: DocLinkSource; } /** * Bounded read snapshot for reference-safe rename/move planning. * Content covers indexed backlinks unioned with a conservative content * prefilter (opaque embeds/HTML/code/malformed may not be in doc_links). * Never logged by callers. */ export interface FileRefactorResolutionCatalogDocument { id: number; uri: string; relPath: string; collection: string; title: string | null; } export interface FileRefactorResolutionReferrerDocument extends FileRefactorResolutionCatalogDocument { /** Markdown body when available; null when mirror content is missing. */ content: string | null; contentTruncated: boolean; /** True when a potentially relevant document had no loadable mirror. */ contentMissing: boolean; editable: boolean; editableReason?: string; sourceExt: string; sourceMime: string; recordKey: string | null; } export interface FileRefactorResolutionSnapshot { source: FileRefactorResolutionCatalogDocument & { mirrorHash: string | null; sourceExt: string; sourceMime: string; recordKey: string | null; content: string | null; contentTruncated: boolean; editable: boolean; editableReason?: string; }; catalog: FileRefactorResolutionCatalogDocument[]; referrers: FileRefactorResolutionReferrerDocument[]; occupiedRelPaths: string[]; truncated: boolean; /** Content-free truncation reason codes. */ truncationReasons: string[]; } /** Semantic document edge row from DB. */ export interface DocEdgeRow { sourceDocId: number; sourceDocid: string; sourceUri: string; sourceTitle: string | null; targetDocId: number; targetDocid: string; targetUri: string; targetTitle: string | null; edgeType: DocEdgeType; relationType: RelationType; confidence: DocEdgeConfidence; edgeSource: DocEdgeSource; } /** Backlink row from DB (document linking TO target) */ export interface BacklinkRow { /** Source document internal ID */ sourceDocId: number; /** Source document docid (#hex) */ sourceDocid: string; /** Source document URI */ sourceDocUri: string; /** Source document title */ sourceDocTitle: string | null; /** Link display text */ linkText: string | null; /** 1-based line number in source */ startLine: number; /** 1-based column in source */ startCol: number; } /** Input for setting document links */ export interface DocLinkInput { targetRef: string; targetRefNorm: string; targetAnchor?: string; targetCollection?: string; linkType: DocLinkType; linkText?: string; startLine: number; startCol: number; endLine: number; endCol: number; } /** Input for setting semantic document edges for one source document. */ export interface DocEdgeInput { targetDocId: number; edgeType: DocEdgeType; confidence: DocEdgeConfidence; } // ───────────────────────────────────────────────────────────────────────────── // Input Types (for upsert operations) // ───────────────────────────────────────────────────────────────────────────── /** Input for upserting a document */ export interface DocumentInput { typedMetadata?: TypedMetadata; metadataError?: string; collection: string; relPath: string; sourceHash: string; sourceMime: string; sourceExt: string; sourceSize: number; sourceMtime: string; sourceCtime?: string; title?: string; mirrorHash?: string; converterId?: string; converterVersion?: string; languageHint?: string; contentType?: string; contentTypeSource?: string; categories?: string[]; author?: string; frontmatterDate?: string; dateFields?: Record; recordKey?: string; recordSourcePath?: string; recordSourceLocator?: string; recordMetadata?: RecordMetadata; recordAnchors?: RecordAnchor[]; recordAdapterFingerprint?: string; lastErrorCode?: string; lastErrorMessage?: string; /** Ingest schema version for backfill detection */ ingestVersion?: number; /** Fingerprint of metadata-affecting content type rules used for derivation. */ contentTypeRulesFingerprint?: string; /** * Change-journal metadata for a source lifecycle write. Conversion failures * that change source/evidence identity are lifecycle writes; set false only * for bookkeeping or repair that must not represent a source change. */ changeJournal?: | false | { observedAtMs?: number; structureDelta?: Partial; }; } /** Result of upserting a document */ export interface UpsertDocumentResult { /** Database row ID */ id: number; /** Content-derived document ID (#hex) */ docid: string; } /** Lifecycle transition persisted in the metadata-only document journal. */ export type DocumentChangeKind = | "create" | "update" | "rename" | "inactivate" | "reactivate"; /** Bounded normalized additions/removals for one structural dimension. */ export interface DocumentChangeSet { added: string[]; removed: string[]; } /** Bounded normalized date-field changes. */ export interface DocumentChangeDateDelta extends DocumentChangeSet { changed: string[]; } /** Reserved structural summaries populated by the sync delta pipeline. */ export interface DocumentChangeStructureDelta { headings: DocumentChangeSet; links: DocumentChangeSet; typedEdges: DocumentChangeSet; dates: DocumentChangeDateDelta; truncated: boolean; } /** One committed metadata-only document lifecycle transition. */ export interface DocumentChangeRow { sequence: number; documentId: number; collection: string; kind: DocumentChangeKind; oldRelPath: string | null; newRelPath: string | null; oldDocid: string | null; newDocid: string | null; oldUri: string | null; newUri: string | null; oldSourceHash: string | null; newSourceHash: string | null; oldMirrorHash: string | null; newMirrorHash: string | null; oldActive: boolean | null; newActive: boolean | null; structureDelta: DocumentChangeStructureDelta; egressLineage: EgressLineage; observedAtMs: number; byteSize: number; } /** Stable cursor page over retained document changes. */ export interface DocumentChangePage { changes: DocumentChangeRow[]; nextCursor: string | null; earliestCursor: string; latestCursor: string; cursorExpired: boolean; truncated: boolean; } export interface DocumentChangeListOptions { cursor?: string; collection?: string; documentId?: number; /** Inclusive observed-time lower bound in Unix milliseconds. */ observedAfterMs?: number; limit?: number; } /** Prefix-retention limits; all three are enforced together. */ export interface DocumentChangeRetentionPolicy { maxAgeDays: number; maxEntries: number; maxBytes: number; } export interface DocumentChangeRetentionResult { deleted: number; remainingEntries: number; remainingBytes: number; earliestCursor: string; } export interface DocumentChangePurgeResult { deleted: number; earliestCursor: string; } export type SavedCapsuleNotificationPreference = "none" | "local"; export type SavedCapsuleTriggerKind = "manual" | "journal"; export type SavedCapsuleOperationStatus = "completed" | "failed"; export type SavedCapsuleAffectedQuestionState = | "unaffected" | "affected" | "unknown"; export interface SavedCapsuleEvidenceReference { evidenceId: string; canonicalUri: string; collection: string; sourceHash: string; mirrorHash: string; passageHash: string; } export interface SavedCapsuleRegistration { registrationId: string; filePath: string; fileHash: string; capsuleId: string; indexName: string; question: string | null; label: string | null; notificationPreference: SavedCapsuleNotificationPreference; registeredAtMs: number; updatedAtMs: number; lastAttemptedSequence: number; } export interface SavedCapsuleRegistrationRecord extends SavedCapsuleRegistration { evidence: SavedCapsuleEvidenceReference[]; verification: SavedCapsuleVerificationRecord | null; } export interface SavedCapsuleVerificationRecord { registrationId: string; triggerKind: SavedCapsuleTriggerKind; fromSequence: number; throughSequence: number; operationStatus: SavedCapsuleOperationStatus; affectedQuestionState: SavedCapsuleAffectedQuestionState; affectedReasons: string[]; receiptJson: string | null; receiptHash: string | null; errorCode: string | null; errorMessage: string | null; verifiedAtMs: number; } export interface SavedCapsuleVerificationExpectation { registrationGeneration: number; } export interface SavedCapsuleReverificationState { lastProcessedSequence: number; registrationEpoch: number; } export interface SavedCapsuleRegistrationSnapshot { registration: SavedCapsuleRegistrationRecord; registrationGeneration: number; } export interface SavedCapsuleRegistrationInput extends Omit< SavedCapsuleRegistration, "registeredAtMs" | "updatedAtMs" > { registeredAtMs: number; updatedAtMs: number; evidence: SavedCapsuleEvidenceReference[]; } export interface RenameDocumentOptions { observedAtMs?: number; structureDelta?: Partial; } /** Input for a single chunk */ export interface ChunkInput { seq: number; pos: number; text: string; startLine: number; endLine: number; language?: string; tokenCount?: number; } /** Input for recording an ingest error */ export interface IngestErrorInput { collection: string; relPath: string; code: string; message: string; details?: Record; } // ───────────────────────────────────────────────────────────────────────────── // Search Types // ───────────────────────────────────────────────────────────────────────────── /** Existing SQL owner filters shared by lexical and chunk candidate selection. */ export interface GraphReferenceDocument { documentId: number; collection: string; relPath: string; docid: string; uri: string; title: string | null; mirrorHash: string | null; sourceHash: string; contentType: string | null; } export interface GraphFrontmatterReference { edgeType: string; target: string; } export interface GraphReferenceInventory { document: GraphReferenceDocument; references: GraphFrontmatterReference[]; } export interface GraphProjectionState { epoch: number; version: number | null; configFingerprint: string | null; dirty: boolean; /** Interrupted projection requires full recovery; ordinary input dirtiness does not. */ inProgress: boolean; complete: boolean; } /** Synchronous internal operations compose with the adapter transaction. */ export interface GraphReferenceStore { state(version: number, configFingerprint: string): GraphProjectionState; begin(version: number, configFingerprint: string): number; readInventory(): GraphReferenceInventory[]; /** Active parsed-link sources matching any old/new target candidate. */ incomingLinkSources(identities: GraphReferenceDocument[]): number[]; writeInventory(inventory: GraphReferenceInventory): void; complete(expectedEpoch: number): void; } export interface DocumentEligibilityOptions { filter?: MetadataPredicate; /** Internal owner eligibility; public lexical language remains reserved. */ chunkLanguage?: string; /** Include author, content type and categories in whole-owner exclusion. */ excludeMetadata?: boolean; /** Internal vector/hybrid JavaScript metadata matching semantics. */ semanticMetadata?: boolean; /** Internal caller allowlist; undefined is unrestricted, empty denies all. */ allowedMirrorHashes?: string[]; /** Whole-document title/path/chunk exclusions, applied before the budget. */ exclude?: string[]; /** Filter by collection */ collection?: string; /** Internal exact relative-path boundary applied before ranking and LIMIT. */ relPathPrefix?: string; /** Filter to docs with ANY of these tags */ tagsAny?: string[]; /** Filter to docs with ALL of these tags */ tagsAll?: string[]; /** Filter by modified time lower bound (ISO 8601) */ since?: string; /** Filter by modified time upper bound (ISO 8601) */ until?: string; /** Filter to docs matching ANY category */ categories?: string[]; /** Filter by author field (case-insensitive contains) */ author?: string; /** * Managed-memory scope filter: keep only documents carrying at least one of * these normalized scopes. Applied inside the FTS candidate subquery, before * any LIMIT, so an out-of-scope document never occupies the window. */ memoryScopesAny?: string[]; /** * Exclude documents that an active document supersedes (typed edge * `supersedes` pointing at them). Applied inside the candidate subquery. */ excludeSuperseded?: boolean; } export interface FtsSearchOptions extends DocumentEligibilityOptions { /** Max eligible ranked results to return (filters run before this budget) */ limit?: number; /** * Language hint (reserved for future use). * Note: FTS5 snowball tokenizer is language-aware at index time, * so runtime language filtering is not currently implemented. */ language?: string; /** Include snippet with highlights */ snippet?: boolean; /** Match documents containing ANY positive term instead of ALL of them. */ anyTerm?: boolean; } /** Managed-memory eligibility query (unbounded, executed in one SQL query). */ export interface MemoryEligibleDocumentsOptions { collection: string; /** Normalized scopes; any-intersection semantics. */ scopes: string[]; excludeSuperseded?: boolean; } /** Minimal identity of a memory record eligible for managed recall. */ export interface MemoryEligibleDocument { id: number; docid: string; uri: string; mirrorHash: string; } /** Single FTS search result */ export interface FtsResult { mirrorHash: string; seq: number; score: number; snippet?: string; // Joined from documents table docid?: string; uri?: string; title?: string; collection?: string; relPath?: string; // Source metadata (optional for backward compat) sourceMime?: string; sourceExt?: string; sourceMtime?: string; frontmatterDate?: string; sourceSize?: number; sourceHash?: string; contentType?: string; contentTypeSource?: string; categories?: string[]; converterId?: string; converterVersion?: string; recordKey?: string; recordSourcePath?: string; recordSourceLocator?: string; recordMetadata?: RecordMetadata; recordAnchors?: RecordAnchor[]; recordAdapterFingerprint?: string; } // ───────────────────────────────────────────────────────────────────────────── // Status Types // ───────────────────────────────────────────────────────────────────────────── /** Per-collection status */ export interface CollectionStatus { name: string; path: string; /** Effective fail-closed content transfer boundary. */ egressPolicy: EgressPolicy; /** Whether policy was explicit or supplied by a safe default. */ egressPolicySource: EgressPolicySource; totalDocuments: number; activeDocuments: number; errorDocuments: number; chunkedDocuments: number; /** Total chunks for this collection */ totalChunks: number; /** Chunks with embeddings (EPIC 7) */ embeddedChunks: number; } /** Index-level status */ export interface IndexStatus { typedMetadata?: { pending: number; invalid: number }; /** Config version string */ version: string; /** Index name (from dbPath) */ indexName: string; /** Full path to config file */ configPath: string; /** Full path to database file */ dbPath: string; /** FTS tokenizer in use */ ftsTokenizer: FtsTokenizer; /** Per-collection status */ collections: CollectionStatus[]; /** Total documents across all collections */ totalDocuments: number; /** Active (non-deleted) documents */ activeDocuments: number; /** Total chunks across all collections */ totalChunks: number; /** Chunks without embeddings */ embeddingBacklog: number; /** Configuration and applied cached layouts; separate from source freshness. */ chunking?: ChunkingStatus; /** Recent ingest errors (last 24h) */ recentErrors: number; /** Last successful update timestamp (ISO 8601) */ lastUpdatedAt: string | null; /** Overall health status */ healthy: boolean; } /** Cleanup operation stats */ export interface CleanupStats { orphanedContent: number; orphanedChunks: number; orphanedVectors: number; expiredCache: number; } export interface EmbeddingCleanupStats { collection: string; deletedVectors: number; deletedModels: string[]; mode: "stale" | "all"; protectedSharedVectors: number; } // ───────────────────────────────────────────────────────────────────────────── // Graph Types // ───────────────────────────────────────────────────────────────────────────── /** Graph link type (wiki, markdown, or similarity) */ export type GraphLinkType = "wiki" | "markdown" | "similar"; /** Trust classification for graph edges */ export type GraphEdgeConfidence = | "explicit" | "inferred" | "ambiguous" | "similarity"; /** Audit metadata explaining how an edge was derived */ export interface GraphEdgeAudit { /** Resolution path used to create the edge */ resolution: | "exact-title" | "exact-path" | "path-fallback" | "ambiguous-fallback" | "similarity"; /** Number of equally ranked target candidates, when applicable */ matchCount?: number; /** Similarity score copied from weight for similarity edges */ score?: number; } /** Graph node representing a document */ export interface GraphNode { /** Document ID (#hex) - primary identifier */ id: string; /** Document URI (gno://collection/path) */ uri: string; /** Document title */ title: string | null; /** Collection name */ collection: string; /** Relative path within collection */ relPath: string; /** Total degree (in + out unique neighbors) */ degree: number; /** Optional deterministic community id from graph analysis */ communityId?: string; /** Typed graph hints from the node's configured content type */ graphHints?: string[]; } /** Compact graph report node summary */ export interface GraphReportNode { /** Document ID (#hex) */ id: string; /** Document URI (gno://collection/path) */ uri: string; /** Document title */ title: string | null; /** Collection name */ collection: string; /** Relative path within collection */ relPath: string; /** Total degree (in + out unique neighbors) */ degree: number; /** Optional deterministic community id from graph analysis */ communityId?: string; } /** Deterministic graph community summary */ export interface GraphCommunity { /** Stable community id within this graph response */ id: string; /** Human-readable label derived from the highest-degree member */ label: string; /** Number of returned nodes assigned to this community */ size: number; /** Internal edge count before edge-limit truncation */ edgeCount: number; /** Internal density, 0-1 */ density: number; /** Highest-degree example nodes in the community */ topNodes: GraphReportNode[]; } /** Graph link (edge) between two nodes */ export interface GraphLink { /** Source node ID (docid) */ source: string; /** Target node ID (docid) */ target: string; /** Link type */ type: GraphLinkType; /** Edge weight (link count for wiki/md, similarity score for similar) */ weight: number; /** Trust classification for retrieval and agent audit */ confidence: GraphEdgeConfidence; /** Audit metadata for how this edge was resolved */ audit: GraphEdgeAudit; } /** Graph report summary over the current graph result */ export interface GraphReport { /** Highest-degree documents */ hubs: GraphReportNode[]; /** Bridge-like documents with both incoming and outgoing links */ bridgeCandidates: GraphReportNode[]; /** Isolated documents with no resolved explicit graph links */ isolated: { /** Total isolated active documents in scope */ total: number; /** First isolated documents by stable document id order */ examples: GraphReportNode[]; }; /** Unresolved explicit links */ unresolvedLinks: { /** Total unresolved wiki/markdown links in scope */ total: number; /** Unresolved links by type */ byType: Record, number>; }; /** Edge breakdown by type before edge-limit truncation */ edgeTypes: Record; /** Edge confidence breakdown before edge-limit truncation */ edgeConfidence: Record; /** Explicit links resolved through fallback or ambiguous matching */ audit: { inferredEdges: number; ambiguousEdges: number; similarityEdges: number; }; /** Optional deterministic community/cluster analysis over returned nodes */ communities: { /** Number of detected communities */ total: number; /** Algorithm used for deterministic cluster labels */ algorithm: "deterministic-label-propagation"; /** Whether community detection was skipped for graph size */ skipped: boolean; /** Node docid to community id map */ assignments: Record; /** Top communities by size */ top: GraphCommunity[]; }; } /** Graph metadata with truncation info */ export interface GraphMeta { /** Collection filter applied (null = all) */ collection: string | null; /** Node limit applied */ nodeLimit: number; /** Edge limit applied */ edgeLimit: number; /** Total nodes before truncation */ totalNodes: number; /** Total edges before truncation */ totalEdges: number; /** Edges dropped due to unresolved targets */ totalEdgesUnresolved: number; /** Nodes actually returned */ returnedNodes: number; /** Edges actually returned */ returnedEdges: number; /** Whether results were truncated */ truncated: boolean; /** Whether linkedOnly filter was applied */ linkedOnly: boolean; /** Whether similarity edges were included */ includedSimilar: boolean; /** Whether similarity search is available */ similarAvailable: boolean; /** Similar top-K value used */ similarTopK: number; /** Whether similarity was truncated by compute budget */ similarTruncatedByComputeBudget: boolean; /** Warning messages */ warnings: string[]; } /** Graph query result */ export interface GraphResult { nodes: GraphNode[]; links: GraphLink[]; report: GraphReport; meta: GraphMeta; } /** Options for getGraph query */ export interface GetGraphOptions { /** Filter to single collection */ collection?: string; /** Max nodes to return (default 2000) */ limitNodes?: number; /** Max edges to return (default 10000) */ limitEdges?: number; /** Include similarity edges (default false) */ includeSimilar?: boolean; /** Similarity threshold (default 0.7) */ threshold?: number; /** Exclude isolated nodes (default true) */ linkedOnly?: boolean; /** Top-K similar docs per node (default 5, clamped 1-20) */ similarTopK?: number; } /** Options for seed-scoped one-hop graph neighbor lookup (query-time expansion). */ export interface GetGraphNeighborsOptions { /** Seed document primary keys; implementations clamp to a small bound (≤5). */ seedDocumentIds: number[]; /** Filter neighbors to a single collection */ collection?: string; /** Max edges to return (default 10000) */ limitEdges?: number; } /** Result of seed-scoped one-hop graph neighbor lookup. */ export interface GraphNeighborsResult { /** One-hop wiki/markdown edges touching the seeds (no similarity edges). */ links: GraphLink[]; meta: { /** Seeds that were actually resolved (active docs only). */ seedDocumentIds: number[]; /** Link rows examined during scoped resolution (for latency regressions). */ examinedLinkRows: number; /** Edges returned after merge/cap. */ returnedEdges: number; }; } /** Direction for bounded typed-edge graph traversal. */ export type GraphQueryDirection = "out" | "in" | "both"; /** Node returned by graph query traversal. */ export interface GraphQueryNode { id: string; uri: string; title: string | null; collection: string; relPath: string; depth: number; graphHints: string[]; } /** Edge returned by graph query traversal. */ export interface GraphQueryEdge { source: string; target: string; edgeType: DocEdgeType; relationType: RelationType; confidence: DocEdgeConfidence; edgeSource: DocEdgeSource; depth: number; } /** Options for bounded graph query traversal. */ export interface GraphQueryOptions { direction?: GraphQueryDirection; edgeType?: DocEdgeType; maxDepth?: number; maxNodes?: number; frontierLimit?: number; visitedLimit?: number; } /** Bounded graph query traversal result. */ export interface GraphQueryResult { schemaVersion: "1.0"; root: GraphQueryNode; nodes: GraphQueryNode[]; edges: GraphQueryEdge[]; meta: { direction: GraphQueryDirection; edgeType: DocEdgeType | null; maxDepth: number; maxNodes: number; frontierLimit: number; visitedLimit: number; returnedNodes: number; returnedEdges: number; truncated: boolean; warnings: string[]; }; } export interface GraphQueryTraversalRows { nodes: Array<{ doc: DocumentRow; depth: number }>; edges: Array<{ edge: DocEdgeRow; depth: number }>; truncated: boolean; warnings: string[]; } // ───────────────────────────────────────────────────────────────────────────── // Migration Types // ───────────────────────────────────────────────────────────────────────────── /** Migration result */ export interface MigrationResult { applied: number[]; currentVersion: number; ftsTokenizer: FtsTokenizer; } // ───────────────────────────────────────────────────────────────────────────── // Activation Verification // ───────────────────────────────────────────────────────────────────────────── export type ActivationStageName = | "index" | "lexical" | "semantic" | "connector"; export type ActivationStageStatus = "passed" | "pending" | "failed" | "skipped"; export type ActivationVerificationCode = | "no_documents" | "index_out_of_sync" | "no_probe_term" | "index_query_failed" | "retrieval_mismatch" | "semantic_not_checked" | "connector_not_requested" | "connector_not_configured" | "connector_probe_unavailable" | "connector_unsupported_config" | "connector_start_failed" | "connector_timeout" | "connector_missing_tools" | "connector_status_failed" | "connector_search_failed" | "connector_result_mismatch" | "target_runtime_unverifiable"; export interface ActivationStageReceipt { status: ActivationStageStatus; startedAt: string | null; completedAt: string | null; latencyMs: number | null; code?: ActivationVerificationCode; } /** Privacy-bounded, per-collection proof that the local index can retrieve. */ export interface ActivationVerificationReceipt { schemaVersion: "1.0"; collection: string; fingerprint: string; ready: boolean; generatedAt: string; stages: Record; evidence: { /** Corpus-keyed SHA-256 probe digest. The term/key are never persisted. */ probeHash?: string; resultUri?: string; resultSourceHash?: string; connectorTarget?: string; }; } /** Stable index inputs included in activation fingerprints. */ export interface ActivationIndexIdentity { indexName: string; schemaVersion: number; ftsTokenizer: FtsTokenizer; /** Hash of collection-scoped active FTS synchronization state. */ ftsStateHash: string; /** Number of active documents represented by the snapshot. */ activeDocumentCount: number; /** True only when every active document has a current owned FTS row. */ ftsSynchronized: boolean; } /** Content-free document identity used by passive activation fingerprints. */ export interface ActivationIndexDocument { id: number; uri: string; sourceHash: string; mirrorHash: string | null; active: boolean; } /** One metadata-only snapshot; never contains source or indexed body text. */ export interface ActivationIndexSnapshot { identity: ActivationIndexIdentity; documents: ActivationIndexDocument[]; } // ───────────────────────────────────────────────────────────────────────────── // Private Retrieval Trace Receipts // ───────────────────────────────────────────────────────────────────────────── export type RetrievalTraceRedactionMode = "metadata" | "replay"; export type RetrievalTraceTerminalStatus = | "completed" | "partial" | "failed" | "cancelled"; export type RetrievalTraceStatus = "open" | RetrievalTraceTerminalStatus; export type RetrievalTraceJudgmentLabel = | "relevant" | "irrelevant" | "missing_expected"; export type RetrievalTraceJudgmentTargetKind = | "document" | "chunk" | "span" | "query"; export type RetrievalTraceAppendResult = "inserted" | "duplicate"; export type RetrievalTraceRunKind = "retrieval" | "context" | "get"; export type RetrievalTraceEventKind = | "query" | "retrieval" | "context" | "get" | "open" | "cite" | "pin" | "capability" | "complete"; export type RetrievalTraceExportFormat = "agentic-receipt" | "qrels"; export interface RetrievalTraceFingerprints { pipeline: string; model: string; config: string; index: string; } /** Already-redacted trace header persisted by StorePort implementations. */ export interface RetrievalTraceInput { traceId: string; schemaVersion: "1.0"; redactionMode: RetrievalTraceRedactionMode; replayCapable: boolean; queryText: string | null; queryDigest: string | null; queryShape: { characters: number; terms: number; }; goalText: string | null; goalDigest: string | null; goalShape: { characters: number; terms: number; }; filters: Record; /** Trusted collection ownership, independent from privacy-redacted filters. */ egressLineage: EgressLineage; fingerprints: RetrievalTraceFingerprints; status: "open"; createdAtMs: number; updatedAtMs: number; expiresAtMs: number; } export type RetrievalTraceRow = Omit< RetrievalTraceInput, "status" | "updatedAtMs" > & { status: RetrievalTraceStatus; updatedAtMs: number; byteSize: number; /** Digest of immutable creation fields; terminal status/time are excluded. */ creationDigest: string; }; export interface RetrievalTraceRunInput { runId: string; traceId: string; idempotencyKey: string; kind: RetrievalTraceRunKind; payload: Record; createdAtMs: number; } export type RetrievalTraceRunRow = RetrievalTraceRunInput & { payloadBytes: number; canonicalDigest: string; }; export interface RetrievalTraceEventInput { eventId: string; traceId: string; runId: string | null; idempotencyKey: string; kind: RetrievalTraceEventKind; payload: Record; createdAtMs: number; } export type RetrievalTraceEventRow = RetrievalTraceEventInput & { payloadBytes: number; canonicalDigest: string; }; export interface RetrievalTraceJudgmentInput { judgmentId: string; traceId: string; runId: string | null; idempotencyKey: string; label: RetrievalTraceJudgmentLabel; targetKind: RetrievalTraceJudgmentTargetKind; targetRef: string; target: Record; createdAtMs: number; } export type RetrievalTraceJudgmentRow = RetrievalTraceJudgmentInput & { targetBytes: number; canonicalDigest: string; }; export interface RetrievalTraceExportInput { exportId: string; traceId: string; format: RetrievalTraceExportFormat; artifactHash: string; createdAtMs: number; } export interface RetrievalTraceExportRow extends RetrievalTraceExportInput {} export interface RetrievalTraceCursor { createdAtMs: number; traceId: string; } export interface RetrievalTraceExportManifestInput { exportId: string; traceIds: string[]; format: RetrievalTraceExportFormat; artifactHash: string; egressLineage: EgressLineage; createdAtMs: number; } export interface RetrievalTraceExportManifestRow extends Omit< RetrievalTraceExportManifestInput, "traceIds" > { traceIds: string[]; } export type EgressAuditDecision = "allow" | "deny"; export interface EgressAuditReceiptInput { auditId: string; decision: EgressAuditDecision; action: import("../core/egress-policy").EgressAction; destinationZone: import("../core/egress-policy").EgressDestinationZone; contentClass: import("../core/egress-policy").EgressContentClass; effectivePolicy: EgressPolicy; reasonCode: import("../core/egress-policy").EgressReasonCode; lineageDigest: string; createdAtMs: number; expiresAtMs: number; } export interface EgressAuditReceiptRow extends EgressAuditReceiptInput { byteSize: number; } export interface EgressAuditCursor { createdAtMs: number; auditId: string; } export interface EgressAuditPage { receipts: EgressAuditReceiptRow[]; nextCursor: EgressAuditCursor | null; } export interface EgressAuditRetentionPolicy { maxAgeDays: number; maxReceipts: number; maxBytes: number; } export interface EgressAuditRetentionResult { deleted: number; remainingReceipts: number; remainingBytes: number; } export interface EgressAuditPurgeResult { deleted: number; physicalCleanup: RetrievalTracePhysicalCleanupStatus; checkpointedFrames: number; remainingWalFrames: number; } export interface EgressAuditDeleteResult extends EgressAuditPurgeResult { auditId: string; } export interface EgressAuditStatusResult { receipts: number; bytes: number; oldestCreatedAtMs: number | null; newestCreatedAtMs: number | null; } export interface RetrievalTraceBundle { trace: RetrievalTraceRow; runs: RetrievalTraceRunRow[]; events: RetrievalTraceEventRow[]; judgments: RetrievalTraceJudgmentRow[]; exports: RetrievalTraceExportRow[]; } /** One aggregate export identity and every complete linked trace bundle. */ export interface RetrievalTraceExportBundle { manifest: RetrievalTraceExportManifestRow; traces: RetrievalTraceBundle[]; } export interface RetrievalTraceBundleTotals { runs: number; events: number; judgments: number; exports: number; } export interface RetrievalTraceBoundedBundle { bundle: RetrievalTraceBundle; totals: RetrievalTraceBundleTotals; } export interface RetrievalTraceRetentionPolicy { maxAgeDays: number; maxTraces: number; maxRecordsPerTrace: number; maxBytes: number; } export interface RetrievalTraceDeleteCounts { traces: number; runs: number; events: number; judgments: number; exports: number; exportLinks: number; } export interface RetrievalTraceRetentionResult { deleted: RetrievalTraceDeleteCounts; deletedTraceIds: string[]; remainingTraces: number; remainingBytes: number; } export type RetrievalTracePhysicalCleanupStatus = | "completed" | "wal_busy" | "failed"; export interface RetrievalTracePurgeResult extends RetrievalTraceDeleteCounts { physicalCleanup: RetrievalTracePhysicalCleanupStatus; checkpointedFrames: number; remainingWalFrames: number; } // ───────────────────────────────────────────────────────────────────────────── // Transaction Types // ───────────────────────────────────────────────────────────────────────────── /** * Optional transaction wrapper capability. * Store implementations that support batching multiple writes into a single * durable commit should implement this. */ export type WithTransaction = ( fn: () => Promise ) => Promise>; // ───────────────────────────────────────────────────────────────────────────── // StorePort Interface // ───────────────────────────────────────────────────────────────────────────── /** * StorePort - Port interface for data persistence. * Implementations: SQLite adapter (src/store/sqlite/adapter.ts) */ export interface StorePort { getTypedMetadataCoverage?( options: DocumentEligibilityOptions ): Promise>; // ───────────────────────────────────────────────────────────────────────── // Lifecycle // ───────────────────────────────────────────────────────────────────────── /** * Open database connection and run migrations. * Creates DB file if it doesn't exist. */ open( dbPath: string, ftsTokenizer: FtsTokenizer ): Promise>; /** * Close database connection and cleanup resources. */ close(): Promise; /** * Check if database is open. */ isOpen(): boolean; /** * Run an async function within a single transaction. * Optional - implementations without transactional support can omit this. * Used by SyncService to batch document writes for better Windows performance. */ withTransaction?: WithTransaction; // ───────────────────────────────────────────────────────────────────────── // Config Sync (YAML -> DB) // ───────────────────────────────────────────────────────────────────────── /** * Sync collections from config to DB. * Adds new, updates existing, removes deleted. */ syncCollections(collections: Collection[]): Promise>; /** * Sync contexts from config to DB. * Adds new, updates existing, removes deleted. */ syncContexts(contexts: Context[]): Promise>; /** * Monotonic in-process generation for the persisted context snapshot. * Changes after a successful context sync or when the store is reopened. */ getContextGeneration(): number; /** * Get all collections from DB. */ getCollections(): Promise>; /** * Get all contexts from DB. */ getContexts(): Promise>; /** Schema/tokenizer identity used to invalidate activation receipts. */ getActivationIndexIdentity( collection: string ): Promise>; /** Metadata/hash-only identity and documents for passive activation checks. */ getActivationIndexSnapshot( collection: string ): Promise>; /** * Load the current activation receipt. A row with a different fingerprint is * deleted and returned as null so stale readiness cannot escape the store. */ getActivationReceipt( collection: string, expectedFingerprint: string, connectorTarget?: string ): Promise>; /** Persist a strictly projected, privacy-bounded activation receipt. */ upsertActivationReceipt( receipt: ActivationVerificationReceipt ): Promise>; /** Persist an already-redacted, versioned retrieval trace header. */ createRetrievalTrace( trace: RetrievalTraceInput ): Promise>; /** Widen one trace to the canonical union of observed policy ownership. */ mergeRetrievalTraceEgressLineage?( traceId: string, lineage: EgressLineage ): Promise>; /** Return a trace and all of its locally stored subordinate records. */ getRetrievalTrace( traceId: string ): Promise>; /** Read bounded subordinate detail while returning exact aggregate counts. */ getBoundedRetrievalTrace( traceId: string, detailLimit: number ): Promise>; /** List trace headers newest-first with a bounded caller-selected limit. */ listRetrievalTraces( limit: number, cursor?: RetrievalTraceCursor ): Promise>; /** Transition an open trace to one explicit terminal outcome idempotently. */ finalizeRetrievalTrace( traceId: string, status: RetrievalTraceTerminalStatus, updatedAtMs: number ): Promise>; /** Append a run once per trace-scoped idempotency key. */ appendRetrievalTraceRun( run: RetrievalTraceRunInput ): Promise>; /** Append an event once per trace-scoped idempotency key. */ appendRetrievalTraceEvent( event: RetrievalTraceEventInput ): Promise>; /** Append an explicit judgment once per trace-scoped idempotency key. */ appendRetrievalTraceJudgment( judgment: RetrievalTraceJudgmentInput ): Promise>; /** Record a locally produced explicit export receipt idempotently. */ appendRetrievalTraceExport( traceExport: RetrievalTraceExportInput ): Promise>; /** Record one export manifest and all sorted trace memberships atomically. */ appendRetrievalTraceExportManifest( manifest: RetrievalTraceExportManifestInput ): Promise>; /** Read one aggregate export manifest with stable sorted membership. */ getRetrievalTraceExportManifest( exportId: string ): Promise>; /** Read one export and all linked traces without bounded-detail truncation. */ getRetrievalTraceExportBundle( exportId: string ): Promise>; /** Durable random index-local secret used only to redact metadata labels. */ getOrCreateRetrievalTraceRedactionSecret(): Promise>; /** Delete one trace and report exact cascade counts. */ deleteRetrievalTrace( traceId: string ): Promise>; /** Delete every trace and subordinate record in one transaction. */ purgeRetrievalTraces(): Promise>; /** Apply deterministic time, row, and byte retention limits. */ enforceRetrievalTraceRetention( policy: RetrievalTraceRetentionPolicy, nowMs: number ): Promise>; /** Append one content-free egress decision receipt. */ appendEgressAuditReceipt( receipt: EgressAuditReceiptInput ): Promise>; /** Append one receipt and enforce bounds in one atomic store transaction. */ appendEgressAuditReceiptWithRetention?( receipt: EgressAuditReceiptInput, policy: EgressAuditRetentionPolicy, nowMs: number ): Promise>; /** Inspect bounded receipts newest-first through an opaque cursor. */ listEgressAuditReceipts( limit: number, cursor?: EgressAuditCursor ): Promise>; /** Inspect one content-free receipt by stable local identifier. */ getEgressAuditReceipt( auditId: string ): Promise>; /** Delete exactly one audit receipt with truthful physical cleanup status. */ deleteEgressAuditReceipt( auditId: string ): Promise>; /** Return content-free local audit storage and retention facts. */ getEgressAuditStatus(): Promise>; /** Enforce the audit domain's independent age/count/byte policy. */ enforceEgressAuditRetention( policy: EgressAuditRetentionPolicy, nowMs: number ): Promise>; /** Purge only audit receipts with truthful SQLite physical cleanup status. */ purgeEgressAuditReceipts(): Promise>; // ───────────────────────────────────────────────────────────────────────── // Documents // ───────────────────────────────────────────────────────────────────────── /** * Upsert a document. Returns id and docid. * Creates new or updates existing by (collection, relPath). */ upsertDocument( doc: DocumentInput ): Promise>; /** * Explicitly rename one document while preserving its stable database id. * External move inference deliberately remains outside this contract. */ renameDocument( collection: string, oldRelPath: string, newRelPath: string, options?: RenameDocumentOptions ): Promise>; /** * Get document by collection and relative path. */ getDocument( collection: string, relPath: string ): Promise>; /** * Get document by docid (#hex). */ getDocumentByDocid(docid: string): Promise>; /** * Get document by URI (gno://collection/path). */ getDocumentByUri(uri: string): Promise>; /** * List all documents, optionally filtered by collection. */ listDocuments(collection?: string): Promise>; /** * List logical record documents produced from one source container. * Uses the record-source index so targeted syncs do not scan a collection. */ listRecordDocuments( collection: string, sourcePath: string ): Promise>; /** * List distinct effective source paths of ACTIVE documents that are direct * children of `dirRelPath` within `collection`. * * Effective source path is `COALESCE(NULLIF(record_source_path, ''), rel_path)` * so record-container logical documents resolve to their physical container * path. Deeper descendants, inactive rows, and other collections are excluded. * * `dirRelPath` is collection-relative and POSIX-style; the collection root is * `""`. Paths that escape the collection root are rejected with * `INVALID_INPUT`. An empty successful result is distinct from query failure. * * `max` must be a positive integer (see `WATCHER_ACTIVE_SOURCE_PATH_MAX` for * the fixed service default the watcher will pass). Matching rows beyond * `max` yield `OVERFLOW` — never a truncated successful list. */ listActiveDirectChildSourcePaths( collection: string, dirRelPath: string, max: number ): Promise>; /** * List distinct effective source paths of ACTIVE documents anywhere beneath * `dirRelPath` (direct children and deeper descendants). * * Used when a directory is gone from disk so the whole removed subtree can be * reconciled. Prefix containment is exact (`dir1` never matches `dir10/x.md`). * * `dirRelPath` must name a directory below the collection root: `""` is * rejected with `INVALID_INPUT` because a root-wide scan is intentionally out * of scope for this bounded seam. Escaping paths are also `INVALID_INPUT`. * * `max` must be a positive integer (see `WATCHER_ACTIVE_SOURCE_PATH_MAX` for * the fixed service default the watcher will pass). Matching rows beyond * `max` yield `OVERFLOW` — never a truncated successful list. */ listActiveDescendantSourcePaths( collection: string, dirRelPath: string, max: number ): Promise>; /** * Root-wide bounded DISTINCT active physical source paths for one collection. * * Effective source path is `COALESCE(NULLIF(record_source_path, ''), rel_path)`. * Overflow is decided after DISTINCT collapse (`LIMIT max+1`), never on raw * logical document row counts. Inactive rows and other collections are excluded. * Results are ordered ascending. Matching unique sources beyond `max` yield * `OVERFLOW` — never a truncated successful list. */ listActiveSourcePaths( collection: string, max: number ): Promise>; /** * Fetch documents by mirror hashes in batch. * Useful for retrieval pipelines to avoid full document scans. */ getDocumentsByMirrorHashes( mirrorHashes: string[], options?: { collection?: string; activeOnly?: boolean; } ): Promise>; /** * Fetch documents by docids in batch. * Useful for graph traversal pipelines to avoid per-node document lookups. */ getDocumentsByDocids( docids: string[], options?: { collection?: string; activeOnly?: boolean; eligibility?: DocumentEligibilityOptions; } ): Promise>; /** * List documents with pagination support. * Returns documents and total count for efficient browsing. */ listDocumentsPaginated(options: { collection?: string; limit: number; offset: number; /** Filter to docs having ALL these tags (AND) */ tagsAll?: string[]; /** Filter to docs having ANY of these tags (OR) */ tagsAny?: string[]; /** Filter by modified time lower bound (ISO 8601) */ since?: string; /** Filter by modified time upper bound (ISO 8601) */ until?: string; /** Filter to docs matching ANY category */ categories?: string[]; /** Filter by author field (case-insensitive contains) */ author?: string; /** Sort field: "modified" or frontmatter date key */ sortField?: string; /** Sort direction */ sortOrder?: "asc" | "desc"; }): Promise>; /** * Mark documents as inactive (soft delete). * Returns count of affected documents. */ markInactive( collection: string, relPaths: string[] ): Promise>; /** List retained document changes using an opaque monotonic cursor. */ listDocumentChanges( options?: DocumentChangeListOptions ): Promise>; /** Enforce age, entry-count, and byte limits by deleting an oldest prefix. */ enforceDocumentChangeRetention( policy: DocumentChangeRetentionPolicy, nowMs: number ): Promise>; /** Purge the journal while retaining a cursor-expiry boundary. */ purgeDocumentChanges(): Promise>; /** Create a content-free prepared file-refactor recovery receipt. */ createFileRefactorPreparedReceipt( draft: FileRefactorRecoveryReceiptDraft ): Promise>; /** Advance phase/state on a file-refactor recovery receipt. */ advanceFileRefactorReceipt( journalId: string, update: FileRefactorJournalAdvance ): Promise>; /** Load one file-refactor recovery receipt by journal id. */ getFileRefactorReceiptById( journalId: string ): Promise>; /** Latest receipt for a plan digest (deterministic retry/recovery lookup). */ getLatestFileRefactorReceiptByPlanDigest( planDigest: string ): Promise>; /** Register one user-owned Capsule file and its metadata-only evidence refs. */ upsertSavedCapsuleRegistration( input: SavedCapsuleRegistrationInput ): Promise>; /** List saved Capsule registrations in deterministic identity order. */ listSavedCapsuleRegistrations(): Promise< StoreResult >; /** Read one saved Capsule registration and its latest verification. */ getSavedCapsuleRegistration( registrationId: string ): Promise>; /** Read one registration with its internal CAS generation atomically. */ getSavedCapsuleRegistrationSnapshot( registrationId: string ): Promise>; /** Remove one saved Capsule registration and its subordinate metadata. */ deleteSavedCapsuleRegistration( registrationId: string ): Promise>; /** Find registrations whose evidence intersects a retained journal range. */ listSavedCapsuleIdsAffectedByChanges( afterSequence: number, throughSequence: number, limit: number ): Promise>; /** * Persist a receipt/failure only for the expected registration identity. * Returns false without advancing sequence state when the snapshot is stale. */ upsertSavedCapsuleVerification( verification: SavedCapsuleVerificationRecord, expectedRegistration: SavedCapsuleVerificationExpectation ): Promise>; /** Read the resident scheduler's durable journal high-water sequence. */ getSavedCapsuleReverificationSequence(): Promise>; /** Atomically read the high-water sequence and registration generation. */ getSavedCapsuleReverificationState(): Promise< StoreResult >; /** * Advance the high-water sequence only when no registration changed during * the scheduler drain. Returns false when the caller must retry. */ setSavedCapsuleReverificationSequence( sequence: number, expectedRegistrationEpoch: number ): Promise>; // ───────────────────────────────────────────────────────────────────────── // Content (content-addressed) // ───────────────────────────────────────────────────────────────────────── /** * Store markdown content by mirror hash. * Idempotent - no-op if hash exists. */ upsertContent( mirrorHash: string, markdown: string ): Promise>; /** * Get markdown content by mirror hash. */ getContent(mirrorHash: string): Promise>; /** * Read at most maxChars from stored markdown. Used by bounded activation * probes so readiness checks never materialize whole documents. */ getContentPrefix( mirrorHash: string, maxChars: number ): Promise>; /** * Batch fetch markdown content for multiple mirror hashes. * Returns a map of mirrorHash -> markdown for hashes that exist. */ getContentBatch?( mirrorHashes: string[] ): Promise>>; // ───────────────────────────────────────────────────────────────────────── // Chunks // ───────────────────────────────────────────────────────────────────────── /** * Store chunks for a mirror hash. * Replaces existing chunks for this hash. */ upsertChunks( mirrorHash: string, chunks: ChunkInput[], policy?: ChunkingPolicyToken ): Promise>; /** Claim an index-wide policy against the generation observed at open. */ claimChunkingPolicy?( params: ChunkingParams ): Promise>; /** Page cached mirrors whose applied policy differs from the claimed target. */ listPendingChunkingMirrors?( policy: ChunkingPolicyToken, afterHash?: string ): Promise>; /** Atomically replace a layout, refresh FTS and mark its actual policy. */ applyChunkLayout?( mirrorHash: string, chunks: ChunkInput[], policy: ChunkingPolicyToken, sourcePath: string, languageHint?: string ): Promise>; /** * Get all chunks for a mirror hash. */ getChunks(mirrorHash: string): Promise>; /** * Batch fetch chunks for multiple mirror hashes. * Returns Map where each ChunkRow[] is sorted by seq ascending. * Missing hashes are not present in the returned Map. * Note: Map is not JSON-serializable; internal pipeline optimization only. */ getChunksBatch( mirrorHashes: string[] ): Promise>>; /** * Optional exact (mirrorHash, seq) batch hydration. Same mapping as * getChunksBatch, but only requested sequences; missing pairs are omitted. * Callers fall back to whole-hash batching when this capability is absent. */ getChunksBySequenceBatch?( keys: { mirrorHash: string; seq: number }[] ): Promise>>; // ───────────────────────────────────────────────────────────────────────── // FTS Search // ───────────────────────────────────────────────────────────────────────── /** * Search documents using FTS5 (document-level). */ searchFts( query: string, options?: FtsSearchOptions ): Promise>; /** * Sync a document to documents_fts for full-text search. * Must be called after document and content are both upserted. */ syncDocumentFts( collection: string, relPath: string ): Promise>; /** * Rebuild entire documents_fts index from scratch. * Use after migration or for recovery. Returns count of indexed docs. */ rebuildAllDocumentsFts(): Promise>; /** * @deprecated Use syncDocumentFts for document-level FTS. * Rebuild FTS index for a mirror hash. */ rebuildFtsForHash(mirrorHash: string): Promise>; // ───────────────────────────────────────────────────────────────────────── // Tags // ───────────────────────────────────────────────────────────────────────── /** * Set tags for a document. * Replaces tags from the given source (frontmatter or user). * User tags are never overwritten by frontmatter updates. */ setDocTags( documentId: number, tags: string[], source: TagSource ): Promise>; /** * Get all tags for a document. */ getTagsForDoc(documentId: number): Promise>; // ───────────────────────────────────────────────────────────────────────── // Memory scopes (managed memory records) // ───────────────────────────────────────────────────────────────────────── /** * Replace the indexed scope set of one document. An empty list clears it, * which removes the document from managed recall. */ setDocMemoryScopes( documentId: number, scopes: string[] ): Promise>; /** Indexed scopes for one document (sorted). */ getDocMemoryScopes(documentId: number): Promise>; /** * Every active document in the collection carrying at least one requested * scope, optionally excluding superseded records. One unbounded query, so * the result is the exact eligible set rather than a candidate window. */ listMemoryEligibleDocuments( options: MemoryEligibleDocumentsOptions ): Promise>; /** * Get tags for multiple documents in a single query. * Returns a map of documentId -> TagRow[]. */ getTagsBatch( documentIds: number[] ): Promise>>; /** * Get tag counts across all active documents. * Optionally filter by collection or tag prefix. */ getTagCounts(options?: { collection?: string; prefix?: string; }): Promise>; // ───────────────────────────────────────────────────────────────────────── // Links // ───────────────────────────────────────────────────────────────────────── /** * Set links for a document. * Replaces links from the given source (parsed, user, or suggested). */ setDocLinks( documentId: number, links: DocLinkInput[], source: DocLinkSource ): Promise>; /** * Get all outgoing links for a document. */ getLinksForDoc(documentId: number): Promise>; /** Missing capability or stale state requires full graph reconciliation. */ graphReferenceStore?(): GraphReferenceStore; /** * Get backlinks pointing to a document. * Uses target_ref_norm for matching (wiki=normalized title with path fallbacks, markdown=rel_path). * Only returns links from active source documents. */ getBacklinksForDoc( documentId: number, options?: { collection?: string } ): Promise>; /** * Resolve link targets to their documents. * Returns array of resolved docs (or null for unresolved) matching input order. */ resolveLinks( targets: Array<{ targetRefNorm: string; targetCollection: string; linkType: "wiki" | "markdown"; }> ): Promise< StoreResult< Array<{ docid: string; uri: string; title: string | null } | null> > >; /** * Bounded resolution/inventory snapshot for reference-safe rename/move planning. * Read-only: never mutates documents, links, or content. */ getFileRefactorResolutionSnapshot?(input: { sourceUri: string; maxCatalogDocuments?: number; maxReferrerDocuments?: number; maxContentCharsPerDocument?: number; maxTotalContentChars?: number; }): Promise>; /** * Set semantic edges for a document. * Replaces edges from the given source. */ setDocEdges( documentId: number, edges: DocEdgeInput[], source: DocEdgeSource ): Promise>; /** * Get outgoing semantic edges for a document. */ getEdgesForDoc( documentId: number, options?: { edgeType?: DocEdgeType } ): Promise>; /** * Get semantic backlinks pointing to a document. */ getEdgeBacklinksForDoc( documentId: number, options?: { collection?: string; edgeType?: DocEdgeType } ): Promise>; /** * Bounded recursive traversal over typed document edges. */ queryGraphTraversal( rootDocumentId: number, options?: GraphQueryOptions ): Promise>; /** * Rebuild derived semantic edges from currently indexed links. */ backfillDocEdges( sourceDocumentIds?: number[] ): Promise>; // ───────────────────────────────────────────────────────────────────────── // Graph // ───────────────────────────────────────────────────────────────────────── /** * Get knowledge graph of document links. * Two-phase SQL: compute degrees, then fetch edges for top N nodes. */ getGraph(options?: GetGraphOptions): Promise>; /** * Seed-scoped one-hop graph neighbors for query-time expansion. * Resolves only outgoing/backlink edges for ≤5 seed document IDs. * Optional: mocks may omit this and fall back to getGraph. */ getGraphNeighborsForSeeds?( options: GetGraphNeighborsOptions ): Promise>; // ───────────────────────────────────────────────────────────────────────── // Status // ───────────────────────────────────────────────────────────────────────── /** * Get index status with counts and health info. */ getStatus(options?: { embedModel?: string; embedFingerprint?: string; chunking?: Partial; }): Promise>; // ───────────────────────────────────────────────────────────────────────── // Errors // ───────────────────────────────────────────────────────────────────────── /** * Record an ingest error. */ recordError(error: IngestErrorInput): Promise>; /** * Get recent ingest errors. */ getRecentErrors(limit?: number): Promise>; // ───────────────────────────────────────────────────────────────────────── // Cleanup // ───────────────────────────────────────────────────────────────────────── /** * Remove orphaned content, chunks, vectors, and expired cache. */ cleanupOrphans(): Promise>; /** * Remove embeddings for a collection. */ clearEmbeddingsForCollection( collection: string, options: { mode: "stale" | "all"; activeModel?: string } ): Promise>; }