Skip to content
Dakera-AIPublic

About

TypeScript/JavaScript SDK for Dakera AI agent memory — self-hosted, 88.2% LoCoMo. Vectors, hybrid search, knowledge graphs, sessions.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Dakera AI

dakera-js

TypeScript/JavaScript SDK for Dakera AI — the memory engine for AI agents

CI npm Downloads License: MIT Docs LoCoMo 88.2% Playground


Why Dakera?

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


Run Dakera

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 -d

Full deployment guide (Docker Compose, Kubernetes, Helm): dakera-deploy


Install

npm install @dakera-ai/dakera

Works with Node.js (20+), Deno, Bun, Cloudflare Workers, and modern browsers. Ships ESM + CJS with full TypeScript declarations.


Quick Start

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);
}

SSE Streaming

// 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.


What's new for Dakera server v0.12.3

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_mode on recall() — 'inline' | 'separate' | 'off' (omit = server default DAKERA_NEIGHBORHOOD_MODE, default inline; neighborhood: false still means off and wins). Under inline, neighbourhood context is appended to memories after the top_k ranked results, each marked retrieval: "neighborhood" (its score is a raw similarity, not comparable). Use separate when top_k must be the maximum: memories.length <= top_k and the context comes in context_memories, each entry with context_of (the ranked memory it belongs to). ChatMemorySession.recall() asks for separate.
  • include_summaries on recall() — keep session summaries out of (or in) turn-level recall; omit for the server default.
  • Results (recall() and searchMemories()) may carry matched_text / matched_ids when the server collapses derived sentence sub-memories into their source (DAKERA_RECALL_COLLAPSE_DERIVED).
  • RecallResponse now also returns the server's query_embedding_time_ms, search_time_ms, rerank_report and late_interaction_first_stage; fusion defaults to minmax, and the rerank pool is 10x the ranking depth max(top_k, 10) on hybrid routing.
  • rank_score on recall results — the server's ranking field, non-increasing over memories in response order: sort or threshold on it. RecalledMemory.score is rank_score when the server sends it.
  • event_time on storeMemory() and on each storeMemoriesBatch() item — when the memory's event happened, as Unix seconds, an RFC 3339 string or a Date (sent as its ISO string). The server records it as metadata _dakera_event_time; recency, since / until and the temporal graph edges read it instead of the ingest time (created_at stays 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() and updateMemory() answer truncated: true with content_tokens, embedded_tokens, max_tokens, windows (and tokens_estimated); storeMemoriesBatch() lists such memories in truncated: [{ id, ... }].
  • warnings on store, batch store, recall and batch recall responses: request fields the server ignored. The SDK sends only fields the server knows (the deprecated valid_from now goes to metadata.valid_from, where the server reads it; the deprecated embedding, 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 (NotFoundError without a restorable copy, ConflictError when the agent holds the id again). Audit events (listAuditEvents()) and memory events carry reason; the new audit event types are deduplicated, consolidated and restored.
  • Autopilot — status and autopilotUpdateConfig() gain dedup_mode (off | report | merge; the server default is now off), dedup_cross_session, consolidate_cross_session and trash_retention_hours; the trigger's dedup result gains duplicates_found and mode; getConfig() shows the autopilot_* 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 → ValidationError asking for the agent). getMemory(agentId, memoryId) is unchanged.
  • Behaviour change: recall(), searchMemories() and batchRecall() for an agent that has stored nothing answer with no memories (an older server answered 404 NAMESPACE_NOT_FOUND). Session ids may contain : (capabilities().naming.session_id_pattern). New error codes HOST_NOT_ALLOWED (421, a Host the server does not answer for while authentication is off), UNPROCESSABLE_CONTENT (422) and EXTRACTION_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/backups and POST /admin/encryption/rotate-key answer 422 to an unknown field. createKey(), createNamespaceKey(), adminCreateBackup() and rotateEncryptionKey() send only the fields the server knows, never undefined ones, and throw a ValidationError for any other field before sending.
  • opsShutdown() needs a global super_admin key on a v0.12.3 server (admin → 403).
  • ODE sidecar — requests to odeUrl currently 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);

Document import (server v0.12.3+)

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 chunk

Request 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).


What's new for Dakera server v0.12.2

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: false for 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-*); KeyInfo carries grants_version and inert_namespaces.
  • Sessions — startSession(agentId, metadata, { idle_timeout_secs }), touchSession(id), and last_activity_at / ended_reason / idle_since / idle_timeout_secs on sessions. Store answers say session_state, batch stores list ended_sessions. getConfig() / updateConfig() carry session_idle_timeout_secs.
  • Listings — include_derived on agentMemories() / getWakeUpContext(); content_preview_chars (with content_len / content_truncated on each item) on agentMemories(), sessionMemories(), fullKnowledgeGraph() and crossAgentNetwork().
  • Admin — adminDerivationStatus() / adminDrainDerivations(); unavailable on node-wide answers; duplicates_skipped_changed on deduplicate(), summaries_skipped on compressAgent(); namespace kind; capabilities() v2 auth, naming and sessions blocks.

Behaviour changes you may hit with a v0.12.2 server

  • 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_sessions grant, 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, or touchSession(). Keep a long-lived idle session open with touchSession(), or start it with idle_timeout_secs: 0. Storing into an ended session still succeeds; check session_state / ended_sessions.
  • Stricter validation (400, the message names the field). Key namespaces entries are checked (junk entries, internal namespaces, mixed "*"); agent ids are at most 241 bytes; the dakera-curated tag, _dakera_* metadata keys (except _dakera_content_date / _dakera_lang), ids shaped mem_s + 24 hex and TTLs over 100 years are refused; _dakera_embedding_models is reserved.
  • The memory content limit is in bytes (UTF-8, DAKERA_MAX_MEMORY_CONTENT_BYTES, default 100000), now also on updateMemory() and on the endSession() summary.
  • Listings exclude derived records (sentence sub-memories) unless include_derived: true (agentMemories(), getWakeUpContext(); wake-up total_available counts memories only).
  • Keys created before v0.12.2 keep reading foo* entries as literal names (grants_version: 0, listed in inert_namespaces) until their namespaces are saved with updateKey().
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];

What's new for Dakera server v0.12.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/ready with 503 + Retry-After: that means not ready, never healthy.
  • Errors — every v0.12 error is JSON and every 503 carries Retry-After; the retry loop now waits that long. New typed errors: PayloadTooLargeError (413: isQuota for a full namespace, otherwise an oversize request), NotImplementedError (501: isFeatureDisabled, details names the env var), ConflictError (409); err.details, err.resource (404) and err.retryAfterSeconds.
  • Attachments (server DAKERA_ATTACHMENTS=1) — uploadAttachment, listAttachments, downloadAttachment, deleteAttachment, transcribeAttachment (WAV), indexImageAttachment (PNG, needs DAKERA_VISION=1), job polling with waitForAttachmentJob, and attachment_ref on storeMemory.
  • Records (server DAKERA_RECORDS=1) — upsertRecords / getRecord: one primary vector plus named representations (dense, token_multivector, patch_multivector; stored as f32, f16, i8).
  • Per-request lang on storeMemory, storeMemoriesBatch, updateMemory, recall, searchMemories and extractEntities (see capabilities().query_languages).
  • Namespace entity config — replaceNamespaceEntityConfig() is PUT /v1/namespaces/{ns}/config (full replacement; clears entity_types). configureNamespaceNer() stays a merging PATCH.
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' });

Features

  • 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 — TifScore type and evaluateTif() 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, SessionId for 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

Connect to Dakera

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.


Examples

See the examples/ directory:

  • basic.ts — vectors, namespaces, queries, filters, batch operations
  • memory.ts — store/recall memories, sessions, agent stats
  • advanced.ts — text embedding, full-text, hybrid search, knowledge graph, feedback

Run examples with:

npx tsx examples/basic.ts

Resources

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

Other SDKs

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.

About

TypeScript/JavaScript SDK for Dakera AI agent memory — self-hosted, 88.2% LoCoMo. Vectors, hybrid search, knowledge graphs, sessions.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages