@agentskit/memory
v0.12.0
Published
Persistent and vector memory backends for AgentsKit.
Maintainers
Readme
@agentskit/memory
Profile: major-package
Persist conversations and add vector search to your agents — swap backends without changing agent code.
Tags: ai · agents · llm · agentskit · ai-agents · memory · vector-db · embeddings · rag · sqlite · redis · vector-search
Verified proof
- Package metadata and tests live under
packages/memory/. - Package guide: https://www.agentskit.io/docs/reference/packages/memory
- Stability map: docs/STABILITY.md
How this fits the ecosystem
@agentskit/memory gives agents continuity: chat history, vector memory, graph memory, encrypted stores, and local or hosted backends.
- AgentsKit: compose it with the other packages in this repo to build agents from small, swappable parts.
- Registry: look for ready agents and templates that already use this layer at registry.agentskit.io.
- Playbook: learn the production patterns behind this layer at playbook.agentskit.io.
Docs: package guide · agent handoff
Why memory
- Conversations that survive restarts — SQLite for local development, Redis for production; your agent remembers context across sessions with zero code changes
- RAG-ready vector search — store and retrieve embeddings with
fileVectorMemory(pure JS, no native deps) or Redis vector search for scale - Plug any backend — the
VectorStoreinterface is 3 methods; bring LanceDB, Pinecone, or any custom store in minutes - One interface, every deployment target — swap from
inMemorytosqlitetorediswithout touching agent code
Install
npm install @agentskit/memory better-sqlite3
# For production: npm install redis
# For vectors: npm install vectraQuick example
import { createRuntime } from '@agentskit/runtime'
import { anthropic } from '@agentskit/adapters'
import { sqliteChatMemory } from '@agentskit/memory'
const runtime = createRuntime({
adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, model: 'claude-sonnet-4-6' }),
memory: sqliteChatMemory({ path: './chat.db' }),
})
// Agent now remembers previous conversations across process restarts
const result = await runtime.run('What did we discuss yesterday?')
console.log(result.content)With RAG
Use a vector backend with @agentskit/rag createRAG({ embed, store }) — fileVectorMemory and redisVectorMemory implement VectorMemory for chunk storage and search.
Features
Chat memory (4)
fileChatMemory({ path })— JSON on disk; zero infra.sqliteChatMemory({ path })— WAL-mode SQLite; indexed by session.redisChatMemory({ client, keyPrefix })— distributed, serverless-friendly.tursoChatMemory({ url, authToken? })— hosted or local libSQL/Turso.
All on top of createInMemoryMemory / createLocalStorageMemory from
@agentskit/core.
Browser Web Storage
Use the browser-safe subpath when a host needs validated, bounded storage or
sessionStorage instead of the legacy Core localStorage helper:
import { createWebStorageMemory } from '@agentskit/memory/web-storage'
const memory = createWebStorageMemory({
key: 'app:chat',
getStorage: () => typeof sessionStorage === 'undefined' ? undefined : sessionStorage,
maxMessages: 20,
maxRecordBytes: 1_048_576,
})Canonical records are runtime validated. Oversized saves reject with
AK_MEMORY_SAVE_FAILED before changing storage. migration may supply legacy
keys and a host-owned parser; a legacy key is removed only after canonical
persistence succeeds.
Vector memory (11)
fileVectorMemory— pure-JS, file-persisted (good to ~10k vectors).redisVectorMemory— Redis Stack / Redis 8+ HNSW.pgvector— BYO SQL runner (postgres.js,pg, Drizzle, Prisma, Neon).pinecone— managed; namespaces + metadata filters.qdrant— self-hosted or cloud via HTTP.chroma— Chroma v2 HTTP client with tenant, database, and token support.upstashVector— serverless HTTP.supabaseVectorStore— Supabase-hosted pgvector RPC.weaviateVectorStore— Weaviate Cloud or self-hosted HTTP.milvusVectorStore— Milvus/Zilliz REST API.mongoAtlasVectorStore— MongoDB Atlas Vector Search with an injected collection.
Vector adapters follow ADR 0003: a result is included only when its score is
strictly greater than threshold. Remote HTTP adapters accept timeoutMs and
maxResponseBytes, defaulting to 15 seconds and 2 MiB; both require positive
safe integers. Injected fetch and caller signal are also supported. Timeout
and response-limit failures use AK_MEMORY_REMOTE_HTTP. File KV writes are
atomic and serialized across instances in one process. They are not a
multi-process coordination primitive; use SQLite, Redis, or another external
store when several processes write the same file.
Same 3-method VectorStore contract — swap without touching agent code.
Higher-order wrappers (6)
createHierarchicalMemory— MemGPT-style tiers: working / recall / archival. Recipe.createVirtualizedMemory— hot window + cold retriever for long sessions. Recipe.createAutoSummarizingMemory(via@agentskit/core/auto-summarize) — fold oldest turns into a running summary. Recipe.createEncryptedMemory— AES-GCM-256 envelope over anyChatMemory; keys never leave the caller. Recipe.createInMemoryGraph— knowledge graph (nodes + edges + BFS). Recipe.createInMemoryPersonalization+renderProfileContext— per-user trait profile. Recipe.
Memory contract v1 (ADR 0003) — substitutable across runtime,
useChat, and every framework binding.
Ecosystem
| Package | Role |
|---------|------|
| @agentskit/core | Memory, VectorMemory types |
| @agentskit/rag | Chunking + retrieval on top of vector memory |
| @agentskit/runtime | memory / retriever options |
| @agentskit/adapters | Embeddings for RAG |
Contributors
License
MIT — see LICENSE.
Docs
Maturity and compatibility
- Stability: beta — see docs/STABILITY.md
- Node.js 20+ and TypeScript strict mode
- Published as
@agentskit/memory
Contributing
See CONTRIBUTING.md and the monorepo LICENSE.
PostgreSQL chat memory (Node and Workers)
Install drizzle-orm and pg alongside this package. The optional
@agentskit/memory/postgres subpath exports postgresChatMemory,
postgresChatTable (Drizzle schema), and postgresChatMigrationSql.
Neither peer is imported by the main entry.
import { Pool } from 'pg'
import { drizzle } from 'drizzle-orm/node-postgres'
import { sql } from 'drizzle-orm'
import { postgresChatMemory, postgresChatMigrationSql } from '@agentskit/memory/postgres'
const pool = new Pool() // host owns connection configuration and shutdown
const db = drizzle(pool)
// Run once in your migration tooling, not on every request:
await db.execute(sql.raw(postgresChatMigrationSql))
const memory = postgresChatMemory({ db, tenantId: 'tenant-a', sessionId: 'session-a' })
await memory.save([])
await pool.end()In Workers, enable nodejs_compat; create and connect a pg.Client inside each
request, pass drizzle(client), and close it with ctx.waitUntil(client.end())
after all memory operations (and any stream using them) have finished. The
package never connects, migrates, or closes your client. A Node Pool can be
shared across requests. No Neon, Hyperdrive, or Cloudflare dependency is needed;
the host can supply their connection configuration when appropriate.
Every query uses both tenant and session, which form the primary key. Derive these identifiers from trusted authorization context: caller-supplied tenant names alone are not authorization. Saves atomically replace a versioned JSONB snapshot, preserving parts, tools, metadata, dates, and message order. Concurrent saves use last-writer-wins; this ChatMemory does not implement session CAS or turn leases. Schema migration is explicit; SQL/connection errors propagate and invalid records fail rather than silently discarding history.
No truncation is performed by default (CM1–CM6). Opt into maxMessages: N to
keep roughly the newest N messages on each save, in their original order.
Retention cuts only at a user turn boundary: the kept window always starts at a
user message, so a user question is never separated from its assistant answer
and a tool call is never separated from its results. When the N-newest cutoff
falls inside the latest (possibly in-progress) turn, that whole turn is kept, so
the result can exceed N. Leading system messages are always kept. A history
with no user message is stored unchanged rather than emptied. This permanently
removes older turns and counts messages, not tokens or bytes. For semantic
summarization, prepare the history in the host
instead. A positive safe integer is required; empty saves still persist [].
The supported Drizzle peer range is ^0.44.0 || ^0.45.3. Both lower bounds
passed the real PostgreSQL contract suite and the package typecheck.
signal rejects already-aborted operations before any SQL is issued. Loads
also check after SQL returns. Drizzle/pg does not cancel in-flight SQL here:
a save/clear already submitted can commit after an abort and will report its
actual SQL outcome. Configure query/statement timeouts on the host connection.
Optional defense-in-depth RLS (run as migration owner, access through a non-owner role without BYPASSRLS; set tenant inside a host-managed transaction):
ALTER TABLE agentskit_chat_memory ENABLE ROW LEVEL SECURITY;
ALTER TABLE agentskit_chat_memory FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON agentskit_chat_memory
USING (tenant_id = current_setting('app.tenant_id', true))
WITH CHECK (tenant_id = current_setting('app.tenant_id', true));Do not set a tenant on a shared pool connection outside a transaction. The host
must enforce role permissions and set its trusted tenant with transaction-local
set_config('app.tenant_id', tenant, true).
Real acceptance checks (local disposable PostgreSQL 16, trust auth on loopback; no production accounts):
docker run -d --name ak-memory-test -p 127.0.0.1:55439:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres:16
AK_POSTGRES_TEST_PORT=55439 pnpm --filter @agentskit/memory test:postgres
AK_POSTGRES_TEST_PORT=55439 pnpm --filter @agentskit/memory test:workerd
docker rm -f ak-memory-testCI runs test:postgres against a PostgreSQL 16 service with trust authentication
and an explicit test port; no secrets are required. Locally it is skipped
without its explicit test port. test:workerd
runs wrangler dev --local, uses a Client per request and waitUntil shutdown,
and exercises the same synthetic contract suite as the Node Pool test.
