SDK changelog
0.0.0-alpha.17
Section titled “0.0.0-alpha.17”Features
Section titled “Features”-
Atomic memory replacement —
Dimple.writeacceptsreplace: trueand uses one store transaction. The old active unit becomessuperseded, the new unit is inserted together with a confirmed identity edge, and a failed replacement leaves the old memory active. The old id remains readable as history. -
Recency and strict queries —
Dimple.recentreturns the newest memories fromStore.recency. Empty and whitespace-only queries now fail withSdk.InputErrorinstead of reaching FTS5.limitandbudget.maxUnitsare capped at 200, andHybridRetrievalexposescontentTypeandincludeEmbedding. -
Retrieval correctness — hybrid retrieval runs one graph post-pass over the fused rows and re-sorts by the final score with an
idtie-break, sosupportedBy,conflicts, andsupersededBydescribe the rows that are actually returned. A zero-weight profile leg skips its embedder and read, live legs run concurrently, and weighted RRF appliesweight / (k + rank)inside the fusion score. FTS, recency, and metadata reads have deterministic tie-breaks. -
Writes and embeddings are reconcilable —
writeandwriteManyembed before inserting, and a partial vector or route write cleans up the new unit.updateEmbeddingupdates the memory row last, whilefindStalechecks the model vector table and topic membership. Superseding a memory or archiving it by TTL removes its vector and topic membership after the state flip.indexDocsreconciles removed docs, and capped file reads never read past the byte limit. -
Workflow runtime — completion signals are scoped to the runtime instead of a process-global map.
waitconsumes the job’s own signal without hot-spinning,processPendingsurvives one malformed resume, a failed resume offer still resumes in process, completed execution state is pruned, re-enqueueing a completed job returns the stored result, and permanentJobFailures skip retries. -
Effect 4.1 and startup — Effect and the
@effect/*packages are on 4.1. Content hashes useeffect/crypto’s WebCryptoDigestlayer, UUIDs useSecureRandom, and one Platform node selects Bun or Node services at graph build. Lazy graph loading cut the measured SDK entry from about 462 ms / 63.5 MB to about 110 ms / 29 MB.
0.0.0-alpha.16
Section titled “0.0.0-alpha.16”Features
Section titled “Features”-
Incremental index defaults and model backfill —
indexnow re-embeds an unchanged unit when its stored vector is missing or belongs to a different model, so changingembedding.defaultModelbackfills on the next full run.IndexOptions.forcebypasses the content/vector skip and re-embeds every selected unit; use it after changing pooling, dtype, or a model implementation.IndexResult.updatednow includes forced and model-change re-embeds. -
Code-search freshness APIs —
checkIndexreports fresh, stale, missing, and pre-hash file units without writing.indexDocsindexes Markdown, JSON/YAML, changesets, and recent commits askind: "doc"/kind: "commit"units. Indexed file and chunk units carrycontentHash,fileHash, andfileSize. -
Full index after a model change — the synchronous index path no longer skips a unit whose content matches but whose vector is missing or belongs to another model. Deferred mode already queued those units; both paths now converge on the active default model’s vector table and topic tree.
0.0.0-alpha.15
Section titled “0.0.0-alpha.15”-
Quantized search and topic-tree maintenance fixes —
float8andfloat1bitmodels now use the exact scan; the ANNvector_top_kindex is created forfloat32only, so quantized queries no longer fail withfailed to parse vector index parameters.topic.splitjobs carry a power-of-twosizeBucketderived from the routed leaf count, so a rejected split retries after the leaf grows instead of being deduped into the completed rejection.processSplitsattempts each over-limit leaf once per pass, largest first. Routing and topic resolution treatmaxDepthas a soft budget and continue to a real leaf; the old fallback attached memories to the internal__unassigned_active__node. -
Smaller SDK tarball — the two published entries are code-split.
dist/index.jsanddist/errors.jsshare one chunk, so the packed tarball drops from 254.5 KB to 182.4 KB and theerrorsentry is a thin re-export. No API change. -
Latest dependency stack — Effect 4.0.6, AI SDK v4 providers (
@ai-sdk/cohere4,@ai-sdk/google4,ai7.0.150), drizzle-orm 1.0.0-rc.5,@libsql/client0.18.0,web-tree-sitter0.27.1,@huggingface/transformers4.3.1, and TypeScript 7.0.2. The sandbox’s TS guest transpiler uses Bun’s transpiler under Bun andmodule.stripTypeScriptTypesunder Node.
0.0.0-alpha.14
Section titled “0.0.0-alpha.14”Features
Section titled “Features”-
Drain visibility on the status surfaces —
health()now reportsjobs, the outstanding (offered, not yet finished) elements per job queue ({ queue: "DurableQueue/<job name>", pending: n }), so a deferredmemory_indexcan be watched to completion through thedimple healthcommand, thedimple://healthresource, ormaintenance_health. An empty array means everything is drained; the internal resume-signal queue is excluded because its rows outlive the work they point at. The counts come from a newQueueStatsservice in@dimple/workflow— one groupedCOUNT(*)over the queue table on the same libSQL client the queue store uses (the upstream store exposes offer/take/cleanup only), served by that table’s partial covering index. -
MCP: paged search, one-call updates, bounded candidate review, offline docs pages —
memory_searchnow returns at most 25 rows per call (10 by default) withhasMoreand a newoffsetinput, capped at 1000 (deeper pages are refused asinvalid_params), so the ranked set is reachable without unbounded fetches.memory_writetakesreplace: true: the previous active version of the key is forgotten (the journal keeps it) and the new content is written under the same key in one call.graph_candidatescaps at 25 per call withhasMore; reviewing the window frees the next page. Every documentation page from the committed index is served as adimple://docs/<path>resource, anddocs_searchhits andopen_in_dimpleresults carry that URI — following a docs pointer no longer requires leaving MCP for a network fetch. -
Bulk routing no longer overwrites topic-tree member counts —
routeInsertManypersisted the call-local unit count intotopics.member_count, so every bulk pass (synchronous or deferred indexing,writeMany, thememory.embed.batchhandler) replaced each topic’s total with the size of just that pass. The health sweep then reportedleaf __unassigned_active__: memberCount N != topic_memberships count M, and split/merge decisions read the wrong leaf size. Batch routing now accumulates the absolute final state per touched topic (leaf and ancestors), matching sequentialrouteInsertcalls; the regression test covers prior single inserts plus two successive batches. -
MCP: bounded search payloads, recovery hints, a findable update path, and typed dimension errors —
memory_searchrows are ≤400-char snippets cut at a word boundary and ending in… [truncated — read with memory_get], withcontentChars/contentTruncatedmarking the cut (memory_getstays whole), so a search over indexed code can no longer put a whole 64 KB file unit on the wire. An empty search carries a recovery hint instead of a bare[].memory_write’s description and the MCP guide teach the update path (forget + rewrite, or a new key plus a confirmedmemory_supersedesedge).docs_searchdrops question-phrasing stopwords, so filler-only queries find nothing and natural-language intent queries rank their page first.updateEmbeddingvalidates the vector against the model’s declared dimensions and failsSdk.InputError(MCPinvalid_params) instead of leaking the vector index’s rawdimensions are differenterror.
0.0.0-alpha.13
Section titled “0.0.0-alpha.13”Features
Section titled “Features”-
Configurable ONNX Runtime session options:
embedding.sessionOptionsis the new global default and each model’ssessionOptionsoverrides it field by field (built-in defaults → global → model). The struct coversintraOpNumThreads,interOpNumThreads(integers in [0, 1024],0= runtime default),executionMode,graphOptimizationLevel,enableCpuMemArena,enableMemPattern, the CUDA pairgpuMemLimit/arenaExtendStrategy, and a per-modelwasm: { numThreads }block for the WASM backend, which reads its thread count from the ORT env rather than from the session options.@dimple/configexports the puremergeSessionOptions(global, model);@dimple/embedderexports the widenedbuildSessionOptionsand the newapplyWasmOptions. The point is parallel ingest: ONNX Runtime sizes every session’s intra-op pool at one thread per physical core, so two dimple processes on an 8-core box run 16 threads and scale nothing. SetintraOpNumThreads = floor(physicalCores / workers); the configuration reference carries the tuning guide. -
CPU-only session options no longer inject CUDA: any
sessionOptionsobject used to emitexecutionProviders: [{ name: "cuda", device_id: 0 }], pinning CPU-only models to a CUDA provider. The provider is now emitted only whengpuMemLimitorarenaExtendStrategyis present, andenableCpuMemArenastill always lands, defaulting tofalse.
0.0.0-alpha.12
Section titled “0.0.0-alpha.12”Features
Section titled “Features”-
Deferred embedding mode:
index({ deferEmbed: true })writes every unit without a vector and hands the ones that need one to the durable queue, so the call returns once the rows and the lexical index are written.IndexResult.pendingEmbeddingsreports the count. A newmemory.embed.batchjob drains up to 512 ids per execution through one batched store phase: oneensureTopicTree, oneensureVectorTable, one membership query, oneinsertVectors, onerouteInsertMany, one CASE update, and stampsmemories.embedding/embedding_model, so skip detection andfindStalesettle once a drain completes. The CLI exposes it asdimple index --defer-embed; the MCPmemory_indextool defaults to it because the server’s workers drain the queue. The synchronous path is unchanged and stays the default. -
Index throughput: New-unit writes now go through the existing
writeMany+updateEmbeddingManyrecipe: one transaction, one multi-row vector insert, onerouteInsertManytree pass, and the candidate scan off as inwriteMany, instead of one transaction, one DDL pass, one whole-tree routing read, and one candidate scan per unit. Measured on a pinned 235-unit corpus: index plus 20 queries 9,218 ms → 6,944 ms minimum (median 9,659 → 7,717). Updated units keep the per-unit supersede path. -
ORT session options apply: The embedder passed
sessionOptions(camelCase) where transformers.js readssession_options, so every session option was silently dropped, including the CUDA execution-provider settings. The key is fixed,enableCpuMemArena: falsebounds the CPU arena (onnxruntime#11627: a 2 MB model can jump from 200 MB to 6 GB during inference), and CUDA options now reach the runtime.
0.0.0-alpha.11
Section titled “0.0.0-alpha.11”Features
Section titled “Features”-
Typed
LayerNodegraph (breaking): Replace the runtime-only layer-graph helper with a typed port of OpenCode v2’sLayerNode(packages/shared/src/effect/node.ts), and adopt it at both composition sites. Breaking changes, in the packages they occur:@dimple/shared(@dimple/shared/effect):makenow requiresdeps(it used to default to none), acceptsserviceas well asname, and returns a typedProvider<A, E, Tag>instead of the untypedLayerNodeclass;groupandcompileare typed as well, andcompileinfers the compiled layer’s outputs and error channel instead of the caller stating a type. Addedtags,unbound,mapLayer, tag-scoped shared memo maps, and theNode,Provider,Replacements,Tag,Output,Error,Tags, andTagConfigtypes. Fourteen call sites gained an explicitdeps: [].@dimple/shared/effectand@dimple/core:LayerNodeis now the module’s value namespace (LayerNode.make,LayerNode.compile,LayerNode.tags, and the graph types inside it), matching the reference’sexport * as LayerNode from "./layer-node.js". It is no longer a class, soinstanceof LayerNodedoes not apply; the replacement contracts are checked at compile time.compileLayers,groupLayers, andmakeLayerkeep their names.@dimple/retriever: thehybridRetrievalNodeexport is removed, andhybridRetrievalConfigurednow takes its provider nodes:hybridRetrievalConfigured(options, [storeNode, embedderNode]). The retriever package has no default store or embedder, so there is no self-contained default declaration.@dimple/workflow:workersConfigurednow takes its provider nodes:workersConfigured(defs, options, [jobStackNode, registryNode]), and theworkersNodeexport is removed. The embedded default job stack would have built a second provider and bound the worker to it.@dimple/core:layer()’s error channel is now the real union of its nodes’ build errors (CoreConfigError | EmbeddingError | HybridError | RegistryError | SqlError | StoreError) instead of the caller-statedCoreConfigError, which erased failures the graph could already produce. The output union is unchanged, andCoreGraphServicesis derived from the compiled layer rather than hand-written. Boot failures keep the same runtime contract: the SDK catches the raw failure and remaps it to the closedDimpleErrorsurface withremapError.@dimple/logger:layer’s return type dropped its staleScoperequirement (the acquisition isLayer.effect-owned, so it was never part of the layer’s requirements).
Graph composition now rejects at compile time a node whose declared
depsdo not cover its layer’s requirements (naming each missing service), a replacement that drops outputs, widens the error channel, or crosses tags, and acompileresult that is claimed to provide services its graph does not expose. Compilation itself is unchanged: declarations stay lazy and Effect still owns acquisition, memoization, scopes, and finalization. -
Effect 4.0.0 runtime: Upgrade the bundled Effect runtime to the released
4.0.0(theeffect/unstable/*toeffect/*module move, theSchema.TaggedErrorClasstoSchema.TaggedErrorrename, and the queue store API changes), replace the vendored job-queue store with the upstream SQL store plus TTL cleanup, and rebuild the composition root on a declared layer graph. No public API changes.The rc.118 to 4.0.0 step is additive for this codebase: no source change was needed. Two upstream changes in the window are worth naming even though dimple uses neither:
Chunk.separateandChunk.partitionnow return their two halves in the opposite order ([values, errors]: a silent swap, not a type error), andSchema.brand/Schema.fromBrandwere retightened while internalSchemaASThelpers (getCandidatesremoved,getContextOwnergenericized) changed. -
Self-contained published declarations: Fix the published declarations importing private
@dimple/*packages. The internalizer rewrote only bare specifiers, so a declaration underdist/internal/**kept names like@dimple/shared/errorsand@dimple/shared/types/language: subpath exports that the private packages resolve through anexportsmap a consumer does not have. It also emitted a fixed./internal/<pkg>/index.jspath regardless of the importing file’s depth, so every vendored declaration outsidedist/pointed one or two directories too shallow. A consumer typechecking the package withskipLibCheck: falsegot 67TS2307 Cannot find moduleerrors from the SDK’s own.d.tsfiles, 11 of them naming an unpublished package.The rewriter now resolves each specifier through the target package’s
exportsmap: honouringtypesbeforeimport, and subpath and*patterns, so@dimple/shared/effectlands on the declaration that subpath names, and computes the path relative to the importing file, so nesting depth no longer matters. The build then fails loudly, naming the file and the specifier, if any@dimple/module specifier survives or if any relative specifier in the tree points at no shipped declaration. The guard uses a detector written independently of the rewriter’s matcher, so tightening the rewriter cannot disarm it.No public API change: the shipped types are the same declarations, now resolvable without the private workspace packages.
0.0.0-alpha.10
Section titled “0.0.0-alpha.10”Features
Section titled “Features”- CUDA session options —
sessionOptions: { gpuMemLimit, arenaExtendStrategy }on embedding models: ORT’s CUDA arena is unbounded by default (grows by powers of two), so GPUs with limited VRAM (≤8GB) OOM mid-batch and leave the process unable to allocate even a cublas handle afterwards. Capping the arena is the verified fix: ~3.7× faster than CPU on a 4GB card (12.3 ms/turn vs 46.0), stable across write + query
0.0.0-alpha.9
Section titled “0.0.0-alpha.9”Features
Section titled “Features”writeManybatch routing: bulk ingest no longer routes per unit: the topic tree is read once, every insertion is computed in memory (bit-identical to sequential writes), and one transaction + one batched vector upsert apply the batch (per-unit routing re-read the whole tree and committed a transaction per write, ~42 ms/turn). Embeds also run in 64-text sub-batches: large corpora no longer OOM the ONNX pass (results are bit-identical regardless of batch size), and the batch’s embedding updates land as ONE statement- ANN vector search: the vector leg now searches a
libsql_vector_idxindex (vector_top_kwith exact re-ranking of the candidates): the same results as the brute-force scan, dramatically faster on large corpora (the scan remains the fallback when the index or extension is unavailable) - Lighter query results: search results no longer carry the
384-float embedding vector by default; opt back in with
includeEmbedding: true - Faster hybrid queries: graph context (conflicts / supports / superseded-by) is fetched once over the fused result set instead of per-leg over 2× the rows
- One-transaction dream runs: every judgement edge, derived memory, and the watermark advance in a single transaction (was one per write)
graph.enabled: false: now actually disables the write-path candidate scan (the flag existed in the schema but was never consulted)
- Deterministic ranking with tied distances: search results now sort by id on exact distance ties (approximate-neighbor candidate order varied run to run)
- Tree health after merges: a merge that leaves an internal node with a single child now collapses it up the tree, so the childCount invariant always holds
- Faster waits:
Runtime.waitwakes on an in-process completion signal instead of polling alone (the poll remains the cross-process fallback)
0.0.0-alpha.8
Section titled “0.0.0-alpha.8”Features
Section titled “Features”writeManybulk ingest: batch insert + batch embed + topic routing in one pass: about 10 to 30 times faster than per-writewritefor corpus loads (the per-write candidate scan is O(leaf size) and made bulk ingest quadratic). All-or-nothing: a duplicate logical key rejects the whole batch. Bulk content skips near-duplicate/conflict candidate edges: rundreamafterwards if you want them- First-use auto-install: the optional
@huggingface/transformersembedder peer installs itself on first use (project package manager, scripts skipped, concurrent installs deduped, timeout-safe, win32-safe) DIMPLE_CWD: treat any directory as the project root for config discovery, relativefile:database URLs, andindex()relative paths. For daemons, systemd units, and tests running from a different working directory
- Keyword search matches again: FTS5 treated the whole query as a
verbatim phrase, so multi-word queries (e.g.
import helper main) returned nothing unless the exact phrase appeared contiguously. Queries are now AND over tokens: every word must appear, anywhere.
0.0.0-alpha.7
Section titled “0.0.0-alpha.7”Features
Section titled “Features”- MCP server:
dimple mcpexposes dimple to any MCP client over the 2026-07-28 protocol: asearchtool to discover the API surface, and a sandboxedcode.exectool with the full dimple API available inside the sandbox (dimple.write,dimple.search,dimple.graph.*, …), plus resources, prompts, and change notifications - Code-mode execution: run TS/JS in a secure, fresh-per-run sandbox
with the whole API as a
dimplenamespace: one line per operation, native control flow, andPromise.allfor parallel calls (deny-by-default permissions;secure-execis an optional peer) - API discovery: the
searchtool returns matching methods with signatures, when-to-use scenarios, and examples, so agents pick the right operation before writing code - Daemon lifecycle:
dimple mcp start/stop/reload/statusplus--printfor client config
semantic/keywordsearch profiles now actually switch the retrieval legs (previously accepted but ignored in ranking)updateEmbeddingis idempotent: re-embedding an existing memory replaces it in place instead of failing- MCP results exclude embedding vectors (~1.5 KB of JSON noise per result): smaller agent context, faster round-trips
- Prompt arguments with options (
limit,includePending) work with standard MCP clients (the 2026 spec passes them as strings) - TypeScript guests run on plain Node (typescript ^5; newer majors are rejected with a clear pin hint)
- The packed CLI runs on plain Node (no Bun-only runtime APIs) and installs cleanly in consumer projects
0.0.0-alpha.6
Section titled “0.0.0-alpha.6”dimple initproduced an invalid config template (baseUrlvsbaseURL): every command after a fresh init failed; the template now boots as-is with zero env vars- First-boot races when the jobs database / log files’ directories did not exist yet
0.0.0-alpha.5
Section titled “0.0.0-alpha.5”Features
Section titled “Features”- Runtime config reload: swap config (models, DB, settings) without restarting; failed reloads leave the current stack untouched
- Indexing lifecycle: re-indexing a changed file updates its memory in place; deleted files and removed definitions are demoted; renames surface as review candidates; code units never expire
- Call-graph edges: indexed code links callers to callees, same-file and across imports
- Query budgets: cap results by units, characters, or time
- Edge review: confirm / reject / expire candidate edges and explain a memory’s graph neighborhood
- Optional local models: transformers is auto-installed on first use; hosted embedding providers need nothing installed
- Remote databases: Turso (
libsql://) for store + jobs with env-var token resolution - CLI polish: stdout is data-only (logs go to stderr), compact
--json,dimple db-schema, fully annotateddimple init - Search profiles:
semantic/keywordmodes plus content-type filtering - Worker-ready builds: runs on edge runtimes without a filesystem service
- Confirming a supersede edge now flips the old memory in the same
transaction;
get/forget/updateEmbeddingaccept logical keys
0.0.0-alpha.4
Section titled “0.0.0-alpha.4”Features
Section titled “Features”- Hosted embedding providers: OpenAI-compatible endpoints (OpenAI, ollama, vLLM, org gateways), Google, Cohere, and Amazon Bedrock
- Per-model runtime options: device, precision/dtype, pooling, normalization, cache directory
- Smaller embeddings on disk: quantized vector types (float8 is 4× smaller)
- Multiple models side by side: per-model trees; switching models backfills embeddings idempotently
- Tag-based publishing:
v<version>tags trigger verified npm publishes - Packaging: self-contained type declarations, READMEs, changelog in tarballs
--versionnow reports the real package version
0.0.0-alpha.3
Section titled “0.0.0-alpha.3”--versionnow reports the real package version (was hardcoded)
0.0.0-alpha.2
Section titled “0.0.0-alpha.2”Features
Section titled “Features”- Per-model embedding options (device/dtype/pooling/normalize/cacheDir),
full transformers dtype list, DB-level vector quantization
(
vectorType), package READMEs