Persistent Memory for
Node.js AI Agents
The @kronvex/sdk is the fastest way to add persistent, semantic memory to any Node.js or TypeScript AI agent. Install in 30 seconds, get full TypeScript types, and make any LLM remember your users — regardless of which framework or model you use.
Install and configure
The Kronvex Node.js SDK is published as @kronvex/sdk on npm. It ships as ESM and CJS bundles with full TypeScript declarations. No peer dependencies required.
npm install @kronvex/sdk # or: yarn add @kronvex/sdk # or: pnpm add @kronvex/sdk
import Kronvex from '@kronvex/sdk' const kv = new Kronvex({ apiKey: process.env.KV_API_KEY!, // kv-... agentId: process.env.KV_AGENT_ID!, // UUID from dashboard })
const { default: Kronvex } = require('@kronvex/sdk') const kv = new Kronvex({ apiKey: process.env.KV_API_KEY, agentId: process.env.KV_AGENT_ID })
Core API: remember, recall, injectContext
The entire Kronvex SDK is built around three methods. Master these and you have everything you need to add persistent memory to any agent.
await kv.remember({ content: 'User prefers TypeScript and clean architecture', memoryType: 'semantic', // 'episodic' | 'semantic' | 'procedural' sessionId: 'user-alice', // scope to a user or session ttlDays: 90, // optional: auto-expire after N days })
const memories = await kv.recall({ query: 'architecture preferences', topK: 5, // number of memories to return sessionId: 'user-alice', threshold: 0.35, // minimum confidence score (0–1) }) // memories: Memory[] // [{ id, content, memoryType, score, createdAt, sessionId }] for (const m of memories) { console.log(`[${m.score.toFixed(2)}] ${m.content}`) }
const context = await kv.injectContext({ message: userMessage, sessionId: userId, }) // context: string — pre-formatted, ready for system prompt injection // Example output: // ## Relevant memories: // - User prefers TypeScript and clean architecture (0.87) // - User is building a SaaS for logistics (0.74)
TypeScript types and interfaces
The SDK ships complete TypeScript declarations. All methods are fully typed — you get autocomplete on parameters and type-safe return values without any additional @types packages.
import Kronvex, { Memory, RememberOptions, RecallOptions } from '@kronvex/sdk' type MemoryType = 'episodic' | 'semantic' | 'procedural' interface Memory { id: string content: string memoryType: MemoryType score: number // confidence: 0–1 sessionId?: string createdAt: string // ISO 8601 expiresAt?: string // if ttlDays was set } interface RememberOptions { content: string memoryType?: MemoryType sessionId?: string ttlDays?: number } interface RecallOptions { query: string topK?: number // default: 5 sessionId?: string threshold?: number // default: 0.3 memoryType?: MemoryType // optional filter }
Memory types: episodic, semantic, procedural
Choosing the right memory type improves retrieval precision because you can filter by type in recall queries.
| Type | Use for | Example |
|---|---|---|
| episodic | Things that happened — conversation turns, events, actions | "User asked about PostgreSQL indexes on 2026-04-12" |
| semantic | Facts about the user, domain knowledge, stable preferences | "User is a senior backend engineer at a Series B startup" |
| procedural | Recurring patterns, workflows the agent should remember | "User always wants code examples in TypeScript with async/await" |
memoryType: 'semantic' to recall() to retrieve only stable facts — useful when you want user profile information without recent conversation noise.
Integration: plain OpenAI SDK
The most common Node.js pattern: wrap any OpenAI API call with Kronvex memory recall before and memory storage after.
import OpenAI from 'openai' import Kronvex from '@kronvex/sdk' const openai = new OpenAI() const kv = new Kronvex({ apiKey: process.env.KV_API_KEY!, agentId: process.env.KV_AGENT_ID! }) async function chat(userMessage: string, userId: string): Promise<string> { // 1. Recall const context = await kv.injectContext({ message: userMessage, sessionId: userId }) // 2. Call LLM const completion = await openai.chat.completions.create({ model: 'gpt-4o', messages: [ { role: 'system', content: `You are a helpful assistant.\n\n${context}` }, { role: 'user', content: userMessage }, ], }) const reply = completion.choices[0].message.content ?? '' // 3. Store await Promise.all([ kv.remember({ content: `User: ${userMessage}`, memoryType: 'episodic', sessionId: userId }), kv.remember({ content: `Assistant: ${reply.slice(0, 500)}`, memoryType: 'episodic', sessionId: userId }), ]) return reply }
Integration: Express.js chat API
Here is a production-ready Express.js route that wraps Kronvex around any LLM call. The user identity comes from the JWT auth middleware so it's secure.
import express from 'express' import Kronvex from '@kronvex/sdk' import OpenAI from 'openai' const router = express.Router() const kv = new Kronvex({ apiKey: process.env.KV_API_KEY!, agentId: process.env.KV_AGENT_ID! }) const openai = new OpenAI() router.post('/chat', async (req, res) => { const { message } = req.body const userId = (req as any).user.id // from auth middleware try { // Recall before LLM call const memCtx = await kv.injectContext({ message, sessionId: userId }).catch(() => '') const completion = await openai.chat.completions.create({ model: 'gpt-4o', messages: [ { role: 'system', content: `You are a helpful assistant.\n\n${memCtx}` }, { role: 'user', content: message }, ], }) const reply = completion.choices[0].message.content ?? '' // Store after (non-blocking) Promise.all([ kv.remember({ content: `User: ${message}`, memoryType: 'episodic', sessionId: userId }), kv.remember({ content: `Assistant: ${reply.slice(0, 500)}`, memoryType: 'episodic', sessionId: userId }), ]).catch(console.error) res.json({ reply }) } catch (err) { res.status(500).json({ error: 'Chat failed' }) } })
Integration: LangChain.js
If you're using LangChain.js, you can integrate Kronvex as a custom memory class or as a simple before/after wrapper around your chain invocations. The simplest approach is the wrapper pattern — no custom class needed.
import { ChatOpenAI } from '@langchain/openai' import { HumanMessage, SystemMessage } from '@langchain/core/messages' import Kronvex from '@kronvex/sdk' const llm = new ChatOpenAI({ model: 'gpt-4o' }) const kv = new Kronvex({ apiKey: process.env.KV_API_KEY!, agentId: process.env.KV_AGENT_ID! }) async function runWithMemory(input: string, userId: string) { const context = await kv.injectContext({ message: input, sessionId: userId }) const response = await llm.invoke([ new SystemMessage(`You are a helpful assistant.\n\n${context}`), new HumanMessage(input), ]) const reply = response.content as string await kv.remember({ content: `User: ${input}\nAssistant: ${reply.slice(0, 400)}`, memoryType: 'episodic', sessionId: userId }) return reply }
Full SDK method reference
| Method | Description | Returns |
|---|---|---|
| remember(opts) | Store a memory. content required, others optional. | Promise<{ id: string }> |
| recall(opts) | Semantic search. query required. | Promise<Memory[]> |
| injectContext(opts) | Recall + format for system prompt. message required. | Promise<string> |
| deleteMemory(id) | Delete a memory by ID. | Promise<void> |
| listMemories(opts) | List all memories. Optional sessionId, memoryType filters. | Promise<Memory[]> |
Best practices
Wrap all Kronvex calls in try/catch
Memory recall failure should never block an LLM response. Always wrap Kronvex calls in try/catch and treat failures as graceful degradation — the agent responds without memory context rather than returning a 500 error.
Parallel storage with Promise.all
When storing the user message and assistant response, use Promise.all() to store both in parallel rather than sequentially. This halves the storage time and never blocks the response that's already being sent to the client.
- Never trust client-supplied userId — always derive from server-side auth (JWT, session cookie)
- Keep content under 1000 characters — longer memories embed with lower precision; summarise before storing
- Use
ttlDaysfor session context — set 7–30 days for ephemeral conversation memories, leave unset for permanent facts - Separate agents for dev/prod — create distinct Kronvex agents per environment; test data pollutes production memory scores
- Batch recall and LLM in parallel — if you have other async setup work to do before calling the LLM, run
kv.injectContext()in parallel with it usingPromise.all()
Add memory to your Node.js AI agent
Free plan — 1 agent, 100 memories. No credit card. Install @kronvex/sdk and you're live in under 5 minutes.
Or read the npm package documentation