Memory Stores
How memory blocks use DataStore for persistence, recall, and production storage.
Memory does not define its own storage system. Every memory block persists through DataStore.
That means the same data store adapter can back memory records, indexing metadata, plans, flows, blackboards, and workspace metadata.
import { memory, workingState } from '@crux/core/memory'
import { inMemoryDataStore } from '@crux/core/storage'
const store = inMemoryDataStore()
const mem = memory({
id: 'assistant',
namespace: 'thread:123',
store,
blocks: [workingState({ id: 'scratch', schema })],
})For general store APIs, TTL, adapters, subscriptions, and custom database implementations, use the DataStore guide. For vector search, use the VectorStore guide. For workspace files and generated binary outputs, use BlobStore.
What Memory Stores
Memory blocks write JSON records to the configured DataStore.
const mem = memory({
id: 'support-agent',
namespace: `user:${userId}`,
store,
blocks: [workingState({ id: 'profile', schema: profileSchema }), episodes({ id: 'history', embed: dense })],
})The id identifies the memory collection. The namespace scopes records to a user, thread, tenant, session, or agent. Use stable namespaces so memory can be recalled on later turns.
Defaults
If you do not pass a store, memory uses in-memory storage. That is useful for tests and local demos, but it is not durable.
const mem = memory({
id: 'assistant',
namespace: 'demo',
blocks: [workingState({ id: 'scratch', schema })],
})For production, pass a durable DataStore. Convex apps use cruxConvexStore(); see the Convex guide for setup and boundary rules.
Vector Recall
Some memory blocks can use vector search when configured with a vector-capable store path.
const history = episodes({
id: 'history',
embed: dense,
})Rules:
- If the store supports vectors, embedding-backed blocks can recall semantically relevant entries.
- If the store does not support vectors, blocks that can degrade safely fall back to non-vector behavior such as recency or listing.
- Stores should throw for unsupported sparse or hybrid query modes instead of silently degrading.
For dense, sparse, and hybrid vector capabilities, see VectorStore.
TTL And Cache-Like Memory
Memory records can use stores that support TTL. TTL is a store feature, not a memory-only feature.
await store.set(
'memory:temporary-note',
{
content: 'Remember this only briefly.',
},
{ ttl: 300_000 },
)Use TTL for short-lived scratch records, resolver caches, or temporary agent state. Do not use TTL for facts, episodes, or user preferences that should survive future sessions.
Reactive Memory UI
Memory records can be rendered reactively through the same transport layer as plans and tasks.
Use:
- Convex transport for Convex apps.
- SSE transport when the store implements
subscribe(). - Polling transport for universal fallback.
import { CruxProvider, createPollingTransport, useWorkingMemory } from '@crux/react'
const transport = createPollingTransport(store)
function MemoryPanel() {
const profile = useWorkingMemory<{ tone: string }>('profile')
return <pre>{JSON.stringify(profile, null, 2)}</pre>
}
;<CruxProvider transport={transport}>
<MemoryPanel />
</CruxProvider>See React reference for exact hook and transport APIs.
Production Choices
For memory, choose storage based on the behavior you need:
| Need | Use |
|---|---|
| Tests and examples | inMemoryDataStore() |
| Convex app state and reactivity | cruxConvexStore() as a DataStore; see Convex |
| Redis-backed key-value records | cruxRedisStore() as a DataStore |
| Dense/sparse/hybrid vector recall | Pair memory data with a VectorStore such as Upstash Vector |
| Workspace files and generated outputs | BlobStore, not memory storage |