All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
- Deferred embedding generation via the new
defer_embeddingsoption. When enabled, content is stored immediately without computing embeddings or FTS entries, so ingestion needs no embedding model and returns instantly. Deferred content is invisible tomemory_searchuntil it is processed. Requiressave_content=1. memory_embed_pending([limit])generates the embeddings and FTS entries for deferred content, either all at once or in batches oflimitrows. Each row is processed in its own SAVEPOINT, so an interrupted call can simply be retried and other connections observe per-file progress while a batch runs.memory_pending_count()returns how many rows are still waiting for embedding generation, for progress reporting during amemory_embed_pending()loop.
- File nodes returned by
memory_list_files()now include anindexedboolean, which isfalsewhile content is waiting for embedding generation.
memory_rename_file()withpreserve_duplicate_paths=1now recomputes the path-scoped hash and updates the related embedding and FTS rows in a single transaction. Previously a renamed entry kept its old hash and its embeddings and FTS rows were left orphaned, so the renamed content stopped matching correctly in search.
- Renaming a
preserve_duplicate_pathsrow that was created withsave_content=0now returns a clear error instead of silently corrupting the entry, because the path-scoped hash cannot be recomputed without the stored content.
- Explicit empty directory markers. With
preserve_duplicate_paths=1,memory_add_content('dirname/', '')records a directory that contains no files. Markers appear as directories inmemory_list_files(), are recreated as real directories bymemory_materialize_files(), are excluded from search, and can be deleted withmemory_delete_file()using eitherdirnameordirname/(which removes only the marker, never its children).
- Provider and model settings are now picked up automatically by new connections. The embedding engine is initialized lazily on first use from the settings persisted in
dbmem_settings, somemory_set_model()no longer has to be called on every connection. Calling it explicitly still initializes the engine immediately, which is useful to preload and validate a model. API keys remain connection-scoped:memory_set_apikey()must still be called per connection for remote providers. memory_materialize_files()now counts directory markers in its return value.
- Re-adding the same path with
preserve_duplicate_paths=1is now idempotent instead of churning rows on every call.
preserve_duplicate_pathsoption keeps separate entries for distinct logical paths even when their content is identical or empty. With it enabled,dbmem_content.hashbecomes path-scoped and identifies an entry rather than only its raw content. Intended for virtual-file and editor-style workflows where two files may legitimately hold the same text.
- Error messages returned by many
memory_*functions were truncated or garbled because of a wrong length argument. Errors now arrive intact and readable.
memory_add_content(path, content [, context])indexes content supplied by the caller without reading the filesystem, so applications that already hold the text (uploads, editors, generated documents) can index it directly. Available even inDBMEM_OMIT_IObuilds.memory_rename_file(old_path, new_path)renames an indexed path without re-reading or re-embedding its content.memory_delete_file(path)deletes a single indexed entry by logical path or by exact local source path.memory_list_files()returns the indexed content as a JSON directory and file tree.memory_materialize_files([root_path])writes stored content back to disk, creating parent directories as needed. Paths containing..are rejected, and files that already match are left untouched.memory_is_enabled()reports whether the current database already has the sqlite-memory schema.
- File paths are now portable. Absolute paths are stored as a logical suffix (
/Users/me/docs/readme.mdbecomesdocs/readme.md) andmemory_add_directory()stores paths relative to the scanned root, so an indexed database can be synced between machines. The original local path moves todbmem_content_source, a local-only table that is never synchronized. - Local path collisions are resolved automatically. When a logical suffix would collide, it is extended until unique. Importing a local file whose logical path already exists without local provenance — for example after a sync — updates that entry and attaches provenance to it instead of creating a duplicate.
memory_add_directory()now returns the number of files scanned successfully, rather than only the number of newly processed files.memory_clear()also clears the localdbmem_content_sourcetable.
- Adding content before configuring a model no longer crashes.
memory_add_text(),memory_add_file()andmemory_add_directory()now return "memory_set_model must be called before adding content". - Searching an empty database now says to add content first, instead of reporting the misleading "embedding dimension is not specified".
- Local embedding crashes on certain content. Whitespace-only chunks are no longer sent to the encoder, and the llama.cpp context is sized from the configured chunk window with batch limits kept in step, which removes encoder assertion failures during indexing.
- Changing
max_tokens,overlay_tokensorchars_per_tokensnow rebuilds the local engine and invalidates the cached local embeddings, so stale embeddings computed with the old token window are no longer reused. - Local engine error messages are now per-thread, so errors from one connection are no longer reported on another.
- Token usage and truncation are now recorded for every embedding, in the new
n_tokensandtruncatedcolumns ofdbmem_vaultanddbmem_cache. Existing databases are migrated automatically on open. Cached embeddings restore this metadata instead of reporting zeros, making it possible to see which content was truncated by the model.
- C API (breaking): in
dbmem_embedding_result_t, then_tokens_truncatedinteger is replaced by atruncatedboolean, andn_tokensnow means processed tokens (0when unknown). Custom providers registered withsqlite3_memory_register_providermust be updated.
sqlmemcommand line tool for managing sqlite-memory projects from the terminal. It creates and manages the database, downloads and loads the required extensions, configures the embedding model, indexes Markdown sources, runs searches, and can watch files for changes.sqlmem mcpexposes the memory tools to agents over MCP (stdio or HTTP), and optional PDF indexing can be enabled withsqlmem config set pdf.enabled true. Seecli/README.md.- MDX support. Files with an
.mdxextension now have theirimport/exportstatements and{...}JSX expressions stripped before indexing, so MDX documents are indexed as clean prose instead of polluting search results with JavaScript scaffolding.
- Remote providers that omit
output_dimensionin their response no longer fail with "Missing embedding data in API response"; the dimension is inferred from the returned embedding.
- Content hashes are now TEXT (a 16-character hex string such as
'9e3779b97f4a7c15') acrossdbmem_content,dbmem_vaultanddbmem_cache, and thehashcolumn ofmemory_searchreturns that value.memory_delete()now takes the hash as TEXT. Databases created with earlier versions must be rebuilt. memory_set_model()is now atomic. The engine switch, settings update and reindex all happen in one transaction; if any step fails the previous provider, model and engine are restored, instead of leaving the connection half-configured.memory_set_apikey()now takes effect immediately on an already-initialized remote engine, so it no longer has to be called beforememory_set_model().- Switching between provider classes (custom, local, remote) frees the previous engine right away instead of keeping it resident until the connection closes, which matters for large local models holding RAM or VRAM.
memory_set_option()now accepts0formax_results,vector_weight,text_weightandmin_score; previously zero was silently ignored and the old value kept.memory_add_directory()runs its cleanup pass transactionally and then processes each file in its own transaction, so one failing file rolls back only itself and previously indexed files remain valid.
memory_add_directory()could delete entries from unrelated directories sharing a name prefix — syncing/docscould remove entries belonging to/docs-old. Paths are now matched on real directory boundaries.- Markdown headings did not start a new chunk, so sections were merged into the preceding one and chunk boundaries did not follow the document structure as documented.
memory_reindex()is now transactional, aborts on the first failing row instead of silently swallowing errors, and no longer fails when a leftover temporary table exists.- Repeated
memory_searchqueries on the same statement could reuse stale results; the cursor is now fully reset between queries. - Fixed a potential crash when the local embedding engine reported a tokenize or encode failure.
- Memory synchronization between agents.
memory_enable_sync([context, ...])enables CRDT-based sync ofdbmem_contentthrough sqlite-sync, using block-level LWW on the content so concurrent line-level edits from different agents merge without conflicts. Called with no arguments it syncs everything; with one or more context names it replicates only those contexts.memory_disable_sync()removes the sync infrastructure while preserving the data. memory_reindex()generates embeddings and FTS entries for content that has none — the situation after pulling content from other agents, since embeddings are always local and never synchronized. It also repairs hashes left stale by a CRDT merge.
- Context-filtered searches returned wrong or empty results.
memory_searchwithcontext = ...referenced a nonexistent column in the vector branch (#2). - Combining
queryandcontextwithoutmax_entriesassigned the values to the wrong search parameters. - A large
max_resultscombined withsearch_oversamplecould overflow and under-allocate; the query now fails cleanly instead.
- macOS builds now resolve SQLite symbols from the host process at load time, so the extension loads correctly into arbitrary SQLite builds and alongside other extensions.
- Extension loading is now failure-safe: if any function fails to register, the extension cleans up instead of leaking.
- Hardened the remote embedding engine against over-long provider and model names and against invalid or empty embedding arrays in the API response.
memory_add_*now reports oversized content as an error instead of silently truncating it, and error messages from the file, directory and search paths are correctly bounded and formatted.
API.mdnow documentsmemory_searchcorrectly:query,max_entriesandcontextare hidden filter columns used inWHERE(contextwas previously listed as an output column), and the output columns arehash,seq,ranking,pathandsnippet. It also gained a C API section forsqlite3_memory_register_provider.
- The macOS slice of the shipped
memory.xcframeworknow uses the versioned bundle layout required by Xcode 26, fixing framework validation and embedding failures for Apple developers. The xcframework release asset is also published correctly again.
- Renamed internal exported symbols to a
dbmemory_*prefix, resolving the symbol conflicts that prevented sqlite-memory from being built or statically linked alongside sqlite-sync.
- C API (breaking):
dbmem_provider_tgained anxdatauser pointer, and all three provider callbacks now receive it. Providers written against 0.8.0 must be updated.
- Custom embedding providers.
sqlite3_memory_register_provider()lets a host application plug in its own embedding engine, which is then selected from SQL withmemory_set_model('<provider_name>', '<model>'). Custom providers work regardless of theDBMEM_OMIT_LOCAL_ENGINEandDBMEM_OMIT_REMOTE_ENGINEbuild options, and are used for both indexing and search.
OMIT_CURL=1build option. On Apple platforms the remote embedding engine is built onNSURLSessioninstead of bundling libcurl and mbedTLS, producing a considerably smaller binary with no third-party TLS dependency. Remote embeddings continue to work unchanged.
- sqlite-memory is now bundled into
sqlite-wasmreleases.
Initial public release.
- Hybrid semantic search through the
memory_searchvirtual table, combining vector similarity with FTS5 full-text search. Results exposehash,seq,ranking,pathandsnippet, and queries can be filtered bycontextor capped per query withmax_entries. - Markdown-aware indexing with
memory_add_text(),memory_add_file()andmemory_add_directory(). Content is chunked along semantic boundaries, and content-hash change detection means unchanged files are skipped, modified files are atomically replaced, and deleted files are cleaned up — so sync functions are cheap to call repeatedly. - Local and remote embeddings, selected with
memory_set_model(): the built-in llama.cpp engine for local GGUF models, or the vectors.space service for remote providers (withmemory_set_apikey()). Changing the provider or model automatically re-embeds existing content. - Embedding cache so re-indexing the same text skips redundant computation and API calls, with optional size-based eviction (
embedding_cache,cache_max_entries) andmemory_cache_clear(). - Deletion functions:
memory_delete(),memory_delete_context()andmemory_clear(). - Configuration via
memory_set_option()andmemory_get_option(), covering chunking (max_tokens,overlay_tokens), parsing (skip_semantic,skip_html,extensions), search behaviour (max_results,fts_enabled,vector_weight,text_weight,min_score,search_oversample) and storage (save_content,update_access). - Transactional safety: every ingest runs inside a SAVEPOINT, so content is never left partially indexed.
- Prebuilt binaries for macOS, iOS and iOS Simulator (XCFramework), Linux (including musl), Windows, Android and WASM, plus the
DBMEM_OMIT_IO,DBMEM_OMIT_LOCAL_ENGINEandDBMEM_OMIT_REMOTE_ENGINEbuild options for trimming the extension.