LIVE DEMO → Home Product
FeaturesUse Cases
Docs
DocumentationQuickstartIntegrations
PricingBlog DASHBOARD → LOG IN →
Tutorial Node.js SDK May 4, 2026 · 7 min read

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.

In this article
  1. Install and configure
  2. Core API: remember, recall, injectContext
  3. TypeScript types and interfaces
  4. Memory types: episodic, semantic, procedural
  5. Integration: plain OpenAI SDK
  6. Integration: Express.js chat API
  7. Integration: LangChain.js
  8. Full SDK method reference
  9. Best practices

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.

Install
npm install @kronvex/sdk
# or: yarn add @kronvex/sdk
# or: pnpm add @kronvex/sdk
Initialize (TypeScript)
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
})
Initialize (CommonJS)
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.

remember — store a memory
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
})
recall — semantic search over memories
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}`)
}
injectContext — recall + format in one call
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.

SDK types
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.

TypeUse forExample
episodicThings that happened — conversation turns, events, actions"User asked about PostgreSQL indexes on 2026-04-12"
semanticFacts about the user, domain knowledge, stable preferences"User is a senior backend engineer at a Series B startup"
proceduralRecurring patterns, workflows the agent should remember"User always wants code examples in TypeScript with async/await"
Filter by type in recall. Pass 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.

OpenAI + Kronvex
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.

Express.js chat route
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.

LangChain.js + Kronvex wrapper
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

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

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.

Get your free API key →

Or read the npm package documentation

Integration guide
Node.js SDK Persistent Memory — Setup Guide
Step-by-step setup · code snippets · 2 min
Read the guide →
Related articles
Free API Key
Get started free
No credit card required. 1 agent, 100 memories on the demo plan.