TypeScript/JavaScript SDK for Dakera AI — the memory engine for AI agents
| Dakera | Others | |
|---|---|---|
| LoCoMo Recall@20 | 88.2% (1,536 Q, LLM-judged retrieval recall) | not directly comparable |
| Deployment | Single binary, Docker one-liner | External vector DB + embedding service required |
| Embeddings | Built-in — no OpenAI key needed | Requires external embedding API |
| Search modes | Vector · BM25 · Hybrid · Knowledge Graph | Usually one or two |
| Bundle | ESM + CJS, browser-compatible | Often Node-only |
→ Try the playground · Full benchmark results · dakera.ai
docker run -d \
--name dakera \
-p 3000:3000 \
-e DAKERA_ROOT_API_KEY=dk-mykey \
ghcr.io/dakera-ai/dakera:latest
curl http://localhost:3000/health # → {"status":"ok"}For persistent storage with Docker Compose:
curl -sSfL https://raw.githubusercontent.com/Dakera-AI/dakera-deploy/main/docker/docker-compose.yml \
-o docker-compose.yml
DAKERA_API_KEY=dk-mykey docker compose up -dFull deployment guide (Docker Compose, Kubernetes, Helm): dakera-deploy
npm install @dakera-ai/dakeraWorks with Node.js (20+), Deno, Bun, Cloudflare Workers, and modern browsers. Ships ESM + CJS with full TypeScript declarations.
import { DakeraClient } from '@dakera-ai/dakera';
const client = new DakeraClient({ baseUrl: 'http://localhost:3000', apiKey: 'dk-mykey' });
await client.storeMemory('my-agent', { content: 'User prefers brevity', importance: 0.9 });Full example — store, recall, upsert, and hybrid search:
import { DakeraClient } from '@dakera-ai/dakera';
const client = new DakeraClient({
baseUrl: 'http://localhost:3000',
apiKey: 'dk-mykey',
});
// Store an agent memory
await client.storeMemory('my-agent', {
content: 'User prefers concise responses with code examples',
importance: 0.9,
memory_type: 'semantic',
});
// Recall memories (semantic search)
const response = await client.recall('my-agent', 'what does the user prefer?', {
top_k: 5,
});
for (const m of response.memories) {
console.log(`[${m.score?.toFixed(2)}] ${m.content}`);
}
// Upsert vectors
await client.upsert('my-namespace', [
{ id: 'vec1', values: [0.1, 0.2, 0.3], metadata: { category: 'docs' } },
]);
// Hybrid search (vector + BM25)
const results = await client.hybridSearch('my-namespace', 'completed task', { topK: 5, vectorWeight: 0.7 });
for (const r of results) {
console.log(r.id, r.score);
}// Subscribe to real-time memory events of one agent
const stream = client.subscribeAgentMemories('my-agent');
for await (const event of stream) {
console.log(event.event_type, event.memory_id);
}The streams send the key in the Authorization header (never in the URL) and close the connection when
you break out of the loop.
This SDK release (0.12.3) targets Dakera server v0.12.3 and stays compatible with v0.12.0–v0.12.2 servers: the new request fields are left out when unset (older servers ignore them) and the new response fields are optional. The features below need a v0.12.3+ server.
neighborhood_modeonrecall()—'inline' | 'separate' | 'off'(omit = server defaultDAKERA_NEIGHBORHOOD_MODE, defaultinline;neighborhood: falsestill means off and wins). Underinline, neighbourhood context is appended tomemoriesafter thetop_kranked results, each markedretrieval: "neighborhood"(itsscoreis a raw similarity, not comparable). Useseparatewhentop_kmust be the maximum:memories.length <= top_kand the context comes incontext_memories, each entry withcontext_of(the ranked memory it belongs to).ChatMemorySession.recall()asks forseparate.include_summariesonrecall()— keep session summaries out of (or in) turn-level recall; omit for the server default.- Results (
recall()andsearchMemories()) may carrymatched_text/matched_idswhen the server collapses derived sentence sub-memories into their source (DAKERA_RECALL_COLLAPSE_DERIVED). RecallResponsenow also returns the server'squery_embedding_time_ms,search_time_ms,rerank_reportandlate_interaction_first_stage;fusiondefaults tominmax, and thererankpool is 10x the ranking depthmax(top_k, 10)on hybrid routing.rank_scoreon recall results — the server's ranking field, non-increasing overmemoriesin response order: sort or threshold on it.RecalledMemory.scoreisrank_scorewhen the server sends it.event_timeonstoreMemory()and on eachstoreMemoriesBatch()item — when the memory's event happened, as Unix seconds, an RFC 3339 string or aDate(sent as its ISO string). The server records it as metadata_dakera_event_time; recency,since/untiland the temporal graph edges read it instead of the ingest time (created_atstays the ingest time). Bounds are checked by the server (400 naming the field).- Truncation report — when a text is longer than the embedding model reads,
storeMemory()andupdateMemory()answertruncated: truewithcontent_tokens,embedded_tokens,max_tokens,windows(andtokens_estimated);storeMemoriesBatch()lists such memories intruncated: [{ id, ... }]. warningson store, batch store, recall and batch recall responses: request fields the server ignored. The SDK sends only fields the server knows (the deprecatedvalid_fromnow goes tometadata.valid_from, where the server reads it; the deprecatedembedding, which no server reads, is no longer sent).- Deduplicated memories —
listDeduplicated(agentId)lists memories deduplication or consolidation deleted that can still be restored (kind,survivor_id,restorable_until);restoreDeduplicated(agentId, memoryId)puts one back (NotFoundErrorwithout a restorable copy,ConflictErrorwhen the agent holds the id again). Audit events (listAuditEvents()) and memory events carryreason; the new audit event types arededuplicated,consolidatedandrestored. - Autopilot — status and
autopilotUpdateConfig()gaindedup_mode(off|report|merge; the server default is nowoff),dedup_cross_session,consolidate_cross_sessionandtrash_retention_hours; the trigger'sdedupresult gainsduplicates_foundandmode;getConfig()shows theautopilot_*overrides once set.updateConfig()is typed as the server's{ success, config, message, warnings }. getMemory(memoryId)without an agent id: the server looks in every agent the key may read (several holders →ValidationErrorasking for the agent).getMemory(agentId, memoryId)is unchanged.- Behaviour change:
recall(),searchMemories()andbatchRecall()for an agent that has stored nothing answer with no memories (an older server answered 404NAMESPACE_NOT_FOUND). Session ids may contain:(capabilities().naming.session_id_pattern). New error codesHOST_NOT_ALLOWED(421, aHostthe server does not answer for while authentication is off),UNPROCESSABLE_CONTENT(422) andEXTRACTION_TIMEOUT(504); a 401 or 429 error keeps the server's own message. - Strict admin bodies —
POST /admin/keys,POST /v1/namespaces/{ns}/keys,POST /admin/backupsandPOST /admin/encryption/rotate-keyanswer 422 to an unknown field.createKey(),createNamespaceKey(),adminCreateBackup()androtateEncryptionKey()send only the fields the server knows, neverundefinedones, and throw aValidationErrorfor any other field before sending. opsShutdown()needs a global super_admin key on a v0.12.3 server (admin→ 403).- ODE sidecar — requests to
odeUrlcurrently carry the Dakera API key; tracked in Dakera-AI/dakera#924.
const res = await client.recall('my-agent', 'deploy port', { top_k: 5, neighborhood_mode: 'separate' });
for (const m of res.memories) console.log(m.id, m.matched_text ?? m.content);
for (const c of res.context_memories ?? []) console.log(` context of ${c.context_of}:`, c.content);A document (text, Markdown, HTML, or an uploaded PDF / DOCX / HTML / Markdown / text attachment) becomes a
set of chunk memories of the agent, each with _dakera_document metadata (document, page range, heading
path). Imports run as jobs. capabilities().documents?.enabled says whether the server has the routes; a
server older than v0.12.3 answers them 404, which the SDK reports as "needs Dakera server v0.12.3+".
import { documentChunkMeta } from '@dakera-ai/dakera';
// Text / Markdown / HTML
const accepted = await client.importDocument('docs-bot', {
content: markdown,
media_type: 'text/markdown',
tags: ['manual'],
chunking: { max_tokens: 300, strategy: 'structure' }, // optional
});
// Bounded wait; pass an AbortSignal to stop it early
const job = await client.waitForDocumentJob(accepted, { timeoutMs: 120_000, signal });
// PDF / DOCX: upload, then import
const { attachment_ref } = await client.uploadAttachment('library', pdfBytes, 'application/pdf');
await client.importAttachmentDocument('library', attachment_ref, 'docs-bot', { document_id: 'manual-v3' });
// Cite the source of a recalled chunk
for (const m of (await client.recall('docs-bot', 'install on linux')).memories) {
const doc = documentChunkMeta(m);
if (doc) console.log(doc.title, doc.page_start, doc.heading_path.join(' › '));
}
// Manage documents
const page = await client.listDocuments('docs-bot', { limit: 50 }); // page.next_cursor → cursor
const detail = await client.getDocument('docs-bot', 'manual-v3'); // the document and its chunks
await client.deleteDocument('docs-bot', 'manual-v3'); // every chunkRequest objects are strict: only the server's fields are sent (content, media_type, title,
document_id, the memory fields memory_type, session_id, importance, tags, metadata, ttl_seconds,
expires_at, lang, plus chunking: {max_tokens, overlap_tokens, strategy} and decompose), and any other
field throws a ValidationError before sending. A failed job throws a DakeraError with the job's status and
code (413 too big / too many chunks, 422 UNPROCESSABLE_CONTENT, 504 EXTRACTION_TIMEOUT).
This SDK release (0.12.2) targets Dakera server v0.12.2 and stays compatible with v0.12.0 and
v0.12.1 servers: every new request field is optional and left out when unset, every new response field
is optional, and the new routes answer 404/405 on an older server. Operator upgrade path: "Upgrading from
v0.12.1 to v0.12.2" in the server's docs/v0.12/UPGRADE.md.
- Agents —
createAgent(agentId)(POST /v1/agents) creates an agent's memory namespace before its first memory; idempotent (created: falsefor an existing agent). - Keys —
updateKey()/updateNamespaceKey()(PATCH) rename a key or replace its grants (namespaces: null= every namespace);rotateKey(id, { grace_secs })keeps the old key working for up to 7 days (old_key_id,old_key_expires_at);whoami()(GET /v1/auth/whoami). Key grants may hold prefix patterns (team-*);KeyInfocarriesgrants_versionandinert_namespaces. - Sessions —
startSession(agentId, metadata, { idle_timeout_secs }),touchSession(id), andlast_activity_at/ended_reason/idle_since/idle_timeout_secson sessions. Store answers saysession_state, batch stores listended_sessions.getConfig()/updateConfig()carrysession_idle_timeout_secs. - Listings —
include_derivedonagentMemories()/getWakeUpContext();content_preview_chars(withcontent_len/content_truncatedon each item) onagentMemories(),sessionMemories(),fullKnowledgeGraph()andcrossAgentNetwork(). - Admin —
adminDerivationStatus()/adminDrainDerivations();unavailableon node-wide answers;duplicates_skipped_changedondeduplicate(),summaries_skippedoncompressAgent(); namespacekind;capabilities()v2auth,namingandsessionsblocks.
- Sessions are authorized by their agent. A key needs access to
_dakera_agent_<agent_id>(Write to start, end and touch; Read to read and list) and no longer a_dakera_sessionsgrant, which is now inert. A key without grants lists no sessions.endSession()with a Read key is a 403 for any id; ending a session of an agent the key cannot reach returns the same empty 200 as an unknown session. - Sessions end after 4 hours idle by default (
ended_reason: "idle"). Activity is a memory stored or updated with the session, a session-scoped recall or search, ortouchSession(). Keep a long-lived idle session open withtouchSession(), or start it withidle_timeout_secs: 0. Storing into an ended session still succeeds; checksession_state/ended_sessions. - Stricter validation (400, the message names the field). Key
namespacesentries are checked (junk entries, internal namespaces, mixed"*"); agent ids are at most 241 bytes; thedakera-curatedtag,_dakera_*metadata keys (except_dakera_content_date/_dakera_lang), ids shapedmem_s+ 24 hex and TTLs over 100 years are refused;_dakera_embedding_modelsis reserved. - The memory content limit is in bytes (UTF-8,
DAKERA_MAX_MEMORY_CONTENT_BYTES, default 100000), now also onupdateMemory()and on theendSession()summary. - Listings exclude derived records (sentence sub-memories) unless
include_derived: true(agentMemories(),getWakeUpContext(); wake-uptotal_availablecounts memories only). - Keys created before v0.12.2 keep reading
foo*entries as literal names (grants_version: 0, listed ininert_namespaces) until theirnamespacesare saved withupdateKey().
const agent = await client.createAgent('mlx-dev'); // 201 created / 200 existing
const session = await client.startSession('mlx-dev', undefined, { idle_timeout_secs: 3600 });
const { session_state } = await client.touchSession(session.id); // 'active' | 'ended'
const page = await client.agentMemories('mlx-dev', { content_preview_chars: 200 });
const full = page[0]?.content_truncated ? await client.getMemory('mlx-dev', page[0].id) : page[0];This SDK release (0.12.0) targets Dakera server v0.12.0 and is compatible with both v0.11.108
and v0.12.0 servers: every addition is opt-in or additive, requests that do not use them are
byte-identical to before, and the v0.12-only routes simply answer 404/501 on an older server.
Upgrade guide for operators: docs/v0.12/UPGRADE.md in the server release (the server repository is private;
see the public Dakera changelog for release notes).
- Capabilities —
client.capabilities()(GET /v1/capabilities): models (bge-m3,colbert-small), index kinds (ivfpq), search modes (rabitq), record kinds/dtypes, query languages, and the attachment / transcription / vision / scoring sections. Unknown values from a newer server never throw. - Health —
healthReady()/healthLive()/waitUntilReady(). A starting v0.12 server answers/health/readywith503+Retry-After: that means not ready, never healthy. - Errors — every v0.12 error is JSON and every
503carriesRetry-After; the retry loop now waits that long. New typed errors:PayloadTooLargeError(413:isQuotafor a full namespace, otherwise an oversize request),NotImplementedError(501:isFeatureDisabled,detailsnames the env var),ConflictError(409);err.details,err.resource(404) anderr.retryAfterSeconds. - Attachments (server
DAKERA_ATTACHMENTS=1) —uploadAttachment,listAttachments,downloadAttachment,deleteAttachment,transcribeAttachment(WAV),indexImageAttachment(PNG, needsDAKERA_VISION=1), job polling withwaitForAttachmentJob, andattachment_refonstoreMemory. - Records (server
DAKERA_RECORDS=1) —upsertRecords/getRecord: one primary vector plus named representations (dense,token_multivector,patch_multivector; stored asf32,f16,i8). - Per-request
langonstoreMemory,storeMemoriesBatch,updateMemory,recall,searchMemoriesandextractEntities(seecapabilities().query_languages). - Namespace entity config —
replaceNamespaceEntityConfig()isPUT /v1/namespaces/{ns}/config(full replacement; clearsentity_types).configureNamespaceNer()stays a mergingPATCH.
await client.waitUntilReady(); // never treats a starting server as healthy
const up = await client.uploadAttachment('uploads', wavBytes, 'audio/wav');
const job = await client.transcribeAttachment('uploads', up.attachment_ref, { agent_id: 'my-agent' });
await client.waitForAttachmentJob(job); // the transcript is now a memory
await client.recall('my-agent', 'was ist gesagt worden?', { lang: 'de' });- Agent Memory — store, recall, search, and forget memories with importance scoring
- Sessions — group memories by conversation with auto-consolidation on session end
- Knowledge Graph — traverse memory relationships, find paths, export graphs
- Vector Search — ANN queries with metadata filters and batch operations
- Full-Text Search — BM25 ranking with stemming and stop-word filtering
- Hybrid Search — combine vector similarity with keyword matching
- Text Auto-Embedding — server-side embedding generation (no local model needed)
- Namespaces — isolated vector stores per project, tenant, or use case
- Feedback Loop — upvote/downvote/flag memories to improve recall quality
- T-I-F Reliability —
TifScoretype andevaluateTif()for Truth-Indeterminacy-Falsity scoring of memory reliability - Entity Extraction — GLiNER NER for automatic entity detection
- Attachments & Records — audio transcription, image indexing, multi-representation records (server v0.12)
- SSE Streaming — async generator event subscriptions, browser-compatible
- Branded Types —
VectorId,AgentId,MemoryId,SessionIdfor compile-time safety - ESM + CJS — dual bundle output, works in Node.js and browsers
- Retry & Rate Limiting — built-in exponential backoff and rate-limit header tracking
- Zero Runtime Deps — uses native
fetch, no external HTTP libraries
import { DakeraClient } from '@dakera-ai/dakera';
// Self-hosted
const client = new DakeraClient({
baseUrl: 'http://your-server:3000',
apiKey: 'your-key',
});
// Cloud (early access)
const client = new DakeraClient({
baseUrl: 'http://<your-server-ip>:3000',
apiKey: 'your-key',
});
// With custom retry config and timeout
const client = new DakeraClient({
baseUrl: 'http://localhost:3000',
apiKey: 'your-key',
timeout: 10_000, // ms per attempt, response body included (default 30 000)
retryBackoff: { maxRetries: 5, baseDelay: 200, maxDelay: 10000 },
});Timeouts and retries. Every attempt runs under an AbortController deadline (timeout, default 30 s)
that covers the response body too, and ends in a TimeoutError. Only safe retries are made: 429 and 503
for every request (the server did not process it), honouring Retry-After (seconds or HTTP-date, capped at
60 s); other 5xx, timeouts and network errors only for idempotent requests (GET, HEAD, PUT, DELETE and
read-only POSTs such as recall, search and query). A write such as storeMemory() that times out is not sent
twice. Other 4xx and 501 are never retried.
Keep API keys out of browsers. A key in browser code is readable by anyone who can open the page. Call
Dakera from your backend, or put a proxy in front of it that adds the key server-side (as the Dakera
dashboard does) and construct the browser client without apiKey. The SDK sends the key only in the
Authorization header; it is kept in a private field, so it is not in JSON.stringify(client),
console.log(client) or any error the SDK throws. sseUrl() (key in a ?api_key= URL, for
EventSource) is deprecated: URLs end up in logs and history.
See the examples/ directory:
basic.ts— vectors, namespaces, queries, filters, batch operationsmemory.ts— store/recall memories, sessions, agent statsadvanced.ts— text embedding, full-text, hybrid search, knowledge graph, feedback
Run examples with:
npx tsx examples/basic.ts| Documentation | Full API reference and guides |
| TypeScript SDK docs | TypeScript-specific reference |
| Benchmark | LoCoMo evaluation results |
| dakera.ai | Website and early access |
| GitHub Org | All public repos |
| dakera-deploy | Self-hosting guide |
| SDK | Package |
|---|---|
| dakera-py | dakera (PyPI) |
| dakera-rs | dakera-client (crates.io) |
| dakera-go | github.com/dakera-ai/dakera-go |
| dakera-cli | CLI tool |
| dakera-mcp | MCP server for Claude/Cursor |
dakera.ai · Docs · Benchmark · Request Early Access
Built with Rust. Single binary. Zero external dependencies.
