Skip to content

Memory

Memory middleware for session persistence and context window management.

Persists session state and conversation history to an external store. Loads on session start, saves after all turns complete. Falls back to in-memory on backend failure — user-facing functionality is never blocked.

function memoryStore(config: MemoryStoreConfig): Middleware
import { memory } from "agent-express"
import { sqliteStore } from "@agent-express/session-sqlite"
agent.use(memory.store({
backend: sqliteStore({ path: "./sessions.db" }),
}))

Config options:

OptionTypeDefaultDescription
backendSessionStorerequiredSession store implementation
ttlnumberSession TTL in seconds (backend-dependent)

Hooks: session — loads state/history before next(), saves in finally.

All store adapters implement this interface:

interface SessionStore {
load(sessionId: string): Promise<SessionData | null>
save(sessionId: string, data: SessionData): Promise<void>
delete(sessionId: string): Promise<void>
add(sessionId: string, message: Message): Promise<void>
list(sessionId: string, opts?: { limit?: number; offset?: number; order?: "asc" | "desc" }): Promise<Message[]>
}

add() appends a single message without rewriting full history. list() supports pagination for large conversation histories.

AdapterPackageBest for
SQLite@agent-express/session-sqliteLocal dev, single-server deployments
Redis@agent-express/session-redisDistributed/multi-server, fast access
PostgreSQL@agent-express/session-postgresLong-term persistence, audit trails
OpenAI@agent-express/session-openaiOpenAI Conversation API (messages only, no state)
CustomNone neededbackend: { load, save, delete, add, list }

All adapters support env var fallback for connection config:

// Explicit config
sqliteStore({ path: "./sessions.db" })
redisStore({ url: "redis://localhost:6379" })
postgresStore({ connectionString: "postgresql://..." })
// Or use env vars: DATABASE_URL, REDIS_URL
postgresStore() // reads DATABASE_URL
redisStore() // reads REDIS_URL, falls back to localhost
const agent = new Agent({ model: "openai/gpt-4o", instructions: "..." })
agent.use(memory.store({ backend: sqliteStore({ path: "./db.sqlite" }) }))
await agent.init()
// Session 1: first conversation
const s1 = agent.session({ id: "user-123" })
await s1.run("My name is Alice").result
await s1.close()
// Session 2: resumes with full history
const s2 = agent.session({ id: "user-123" })
const { text } = await s2.run("What's my name?").result
// text → "Your name is Alice"
await s2.close()
await agent.dispose()

Automatically compacts messages when the token count exceeds a configured limit. Only modifies ModelContext.messagesSessionContext.history is never touched.

function memoryCompaction(config?: CompactionConfig): Middleware
// Simple truncation (default, zero cost)
agent.use(memory.compaction({ maxTokens: 8192 }))
// Hybrid: summarize old + keep recent verbatim (best quality)
agent.use(memory.compaction({
maxTokens: 8192,
strategy: "hybrid",
summaryModel: mySummaryModel, // LanguageModelV3 instance
keepRecentMessages: 10,
}))

Hooks: model — checks token count and compacts before next().

Five strategies (gentlest to most aggressive):

StrategyDescriptionCost
clear-tool-resultsReplace old tool results with placeholdersFree
truncate (default)Drop oldest messagesFree
windowKeep last N messagesFree
summarizeLLM summarizes old messages1 LLM call
hybridSummarize old + keep recent verbatim1 LLM call

Config options:

OptionTypeDefaultDescription
maxTokensnumber8192Token limit for context window
strategyCompactionStrategy"truncate"Compaction strategy
keepLastnumber20For window: keep last N messages
keepLastToolResultsnumber3For clear-tool-results: keep N recent results
keepRecentMessagesnumber10For summarize/hybrid: keep N recent messages
summaryModelLanguageModelV3For summarize/hybrid: model for summaries
tokenCounterTokenCounterchars/4Token estimation function