Session & MemoryStore
The Session and MemoryStore primitives in Kikoyu SDK manage multi-turn conversation turn history across multiple separate Runner.run() invocations.
Why Storage-Agnostic Memory?
AI agents in production need to retain user turn history across HTTP requests, server restarts, or distributed serverless instances (Next.js, AWS Lambda, Cloudflare Workers).
Kikoyu SDK provides a storage-agnostic architecture:
Session: The session wrapper representing a unique user conversation turn log.MemoryStore: The pluggable storage engine interface (InMemoryStore, Redis, PostgreSQL, DynamoDB).
Basic Usage with InMemoryStore
import { Agent, InMemoryStore, Runner, Session } from '@kikoyu/core';
// 1. Instantiate a shared memory store
const memoryStore = new InMemoryStore();
// 2. Create or resume a session
const session = new Session({
sessionId: 'session_usr_1042',
store: memoryStore,
});
// 3. Define agent
const agent = Agent.builder()
.setName('Personal Concierge')
.setInstructions('Remember user preferences across multiple turns.')
.build();
const runner = new Runner();
// --- Turn 1 ---
await runner.run(agent, 'My favorite coffee order is an iced oat milk latte with vanilla.', { session });
// --- Turn 2 (Separate request, same session) ---
const response = await runner.run(agent, 'What coffee should I order today?', { session });
console.log(response.text);
// "I recommend your favorite: an iced oat milk latte with vanilla!"Building a Custom Database MemoryStore (Redis / Postgres)
To persist session state in external databases like Redis or PostgreSQL, implement the simple MemoryStore interface:
import { MemoryStore, Message } from '@kikoyu/core';
import { Redis } from 'ioredis';
export class RedisMemoryStore implements MemoryStore {
private redis: Redis;
constructor(redisUrl: string) {
this.redis = new Redis(redisUrl);
}
async getMessages(sessionId: string): Promise<Message[]> {
const raw = await this.redis.get(`session:${sessionId}`);
return raw ? JSON.parse(raw) : [];
}
async saveMessages(sessionId: string, messages: Message[]): Promise<void> {
// Save conversation turn history with a 7-day TTL
await this.redis.set(`session:${sessionId}`, JSON.stringify(messages), 'EX', 604800);
}
async clearSession(sessionId: string): Promise<void> {
await this.redis.del(`session:${sessionId}`);
}
}Managing Turn History & Sliding Windows
To prevent hitting LLM context window limits during long conversations, you can trim or clear turn history using Session helpers:
// Clear all history for a session
await session.clear();
// Get current turn message count
const history = await session.getMessages();
console.log(`Current session contains ${history.length} messages.`);Best Practices
- Use Unique Session IDs: Key session IDs by composite tenant keys (e.g.
tenantId:userId:chatId) to prevent multi-tenant data bleed. - Implement Database TTLs: Automatically expire inactive sessions after 7–30 days in your storage database.
- Pass Session to Runner: Always pass
{ session }inrunner.run(agent, prompt, { session })for continuous multi-turn flows.