@anvia/memory-postgres ​
@anvia/memory-postgres stores Anvia agent memory in PostgreSQL. Use it when several application instances need the same durable conversation history or when memory belongs in an existing operational Postgres environment.
Install ​
pnpm add @anvia/memory-postgres @anvia/coreThe package includes pg and should use a version compatible with its declared @anvia/core dependency range.
Connect a store ​
import { Agent } from '@anvia/core/agent'
import { PostgresMemoryClient } from '@anvia/memory-postgres'
const memoryClient = new PostgresMemoryClient({
connectionString: process.env.DATABASE_URL!,
})
const memory = memoryClient.memoryStore()
await memory.ensure()
const agent = new Agent({
id: 'support',
model: model,
memory: { store: memory, savePolicy: 'turn' },
})You may pass a compatible client or pool instead of connectionString. Pool-like clients let the adapter acquire and release a connection for each transaction.
What it provides ​
- Ordered, transactional message persistence.
- Advisory transaction locking per memory scope by default.
- Failed-run storage and message validation by default.
- Read-only inspection and memory compaction support.
- Custom table prefixes or explicit table names.
- Exportable schema SQL for application-owned migrations.
Own the schema in production ​
Calling store.ensure() creates the pgcrypto extension, sessions, messages, errors, and the unique message-position index.
For controlled deployments, generate the same SQL and apply it through your migration system:
import { createPostgresMemorySchemaSql } from '@anvia/memory-postgres'
const sql = createPostgresMemorySchemaSql({
tablePrefix: 'app_',
})Then connect without DDL at application startup:
const memoryClient = new PostgresMemoryClient({
connectionString: process.env.DATABASE_URL!,
})
const memory = memoryClient.memoryStore({
tablePrefix: 'app_',
})
await memory.validate()Keep the schema options identical in the migration and runtime configuration. Explicit tableNames override the prefix for individual tables.
Scope and concurrency ​
The default scope combines sessionId and userId. Add tenant metadata when needed:
const memoryClient = new PostgresMemoryClient({
connectionString: process.env.DATABASE_URL!,
})
const memory = memoryClient.memoryStore({
scopeKey: {
metadataKeys: ['tenantId'],
},
lock: 'advisory',
})Advisory locking serializes position assignment for concurrent appends to the same scope. Set lock: 'none' only when another layer guarantees that those writes cannot race. Scope isolation does not replace database authorization or tenant access checks.
Production patterns ​
- Reuse an application-managed pool when connection ownership and shutdown are already centralized.
- Apply DDL through migrations and call
store.validate()in runtime processes. - Monitor table growth and decide how long conversation and error histories should remain.
- Keep
validateMessages: trueunless validated messages are guaranteed upstream. - Review custom names as identifiers, not arbitrary SQL fragments; the adapter validates and quotes them.
See Memory sessions for context design and Custom stores for the core contract.