memory

TypeScript Agent SDK

Full-featured TypeScript SDK for building AI agents with persistent memory, local SQLite storage, cloud sync, and offline-first architecture.

@rekall/agent-sdk

Installation

npm install @rekall/agent-sdk

Prerequisites

The Agent SDK requires Node.js 18+ and includes a bundled SQLite binary via better-sqlite3. No additional native dependencies are needed on most platforms.

Initialization

Create a RekallAgent instance with your API key and agent configuration. Call initialize() to set up the local database and establish the cloud connection.

agent.ts
import { RekallAgent } from '@rekall/agent-sdk';
const agent = new RekallAgent({
apiKey: process.env.REKALL_API_KEY!,
agentId: 'my-assistant',
storage: {
path: './data/rekall.db', // Local SQLite path
encryptionKey: process.env.REKALL_ENCRYPTION_KEY, // Optional
},
sync: {
enabled: true, // Enable cloud sync (default: true)
intervalMs: 30_000, // Sync every 30 seconds
strategy: 'last-write-wins', // Conflict resolution
},
baseUrl: 'https://api.rekall.ai/v1', // Default
});
// Initialize local DB and start sync
await agent.initialize();
// Always clean up when done
process.on('SIGTERM', async () => {
await agent.shutdown();
});

Environment variables

Store your API key in an environment variable. Never hard-code secrets in source files. The SDK reads REKALL_API_KEY automatically if no apiKey is provided.

Local SQLite Storage

The Agent SDK stores all memories locally in a SQLite database. This provides instant reads, offline capability, and reduced API calls. The local database is automatically synced with the Rekall cloud.

Storage configuration
const agent = new RekallAgent({
apiKey: process.env.REKALL_API_KEY!,
agentId: 'my-assistant',
storage: {
path: './data/rekall.db',
// Optional: encrypt data at rest
encryptionKey: process.env.REKALL_ENCRYPTION_KEY,
// Optional: set max DB size (default: 500MB)
maxSizeMb: 1000,
// Optional: WAL mode for concurrent reads (default: true)
walMode: true,
},
});

Local queries bypass the network entirely, returning results in microseconds. The SDK maintains a write-ahead log for durability and supports concurrent read access.

Cloud Sync

Cloud sync keeps your local database in sync with the Rekall API. Changes are pushed on a configurable interval and pulled automatically. The SDK tracks a sync cursor to minimize data transfer.

// Trigger an immediate sync
await agent.sync();
// Check sync status
const status = agent.getSyncStatus();
console.log(status);
// {
// lastSyncAt: '2025-01-15T10:30:00Z',
// pendingChanges: 3,
// connected: true,
// strategy: 'last-write-wins'
// }
// Pause and resume sync
agent.pauseSync();
agent.resumeSync();

Creating Memories

Store memories locally and sync them to the cloud. The SDK supports all seven memory types: episodic, semantic, procedural, long-term, short-term, execution, and preferences.

Creating an episodic memory
const memory = await agent.memories.create({
type: 'episodic',
content: 'User asked about quarterly revenue trends for Q3 2024.',
metadata: {
conversationId: 'conv_abc123',
userId: 'user_456',
tags: ['finance', 'revenue', 'Q3'],
},
context: 'agent', // 'agent' | 'user' | 'hive'
importance: 0.8, // 0.0 - 1.0
});
console.log(memory.id); // 'mem_...'
console.log(memory.createdAt); // ISO timestamp
console.log(memory.syncStatus); // 'pending' | 'synced' | 'conflict'
Creating a semantic memory with entities
const memory = await agent.memories.create({
type: 'semantic',
content: 'Acme Corp is a Fortune 500 company headquartered in San Francisco.',
entities: [
{ name: 'Acme Corp', type: 'organization' },
{ name: 'San Francisco', type: 'location' },
],
relationships: [
{
source: 'Acme Corp',
target: 'San Francisco',
type: 'headquartered_in',
},
],
});
Creating a procedural memory
const memory = await agent.memories.create({
type: 'procedural',
content: 'Deploy to production workflow',
steps: [
{ order: 1, instruction: 'Run the test suite', command: 'npm test' },
{ order: 2, instruction: 'Build the application', command: 'npm run build' },
{ order: 3, instruction: 'Deploy to staging', command: 'npm run deploy:staging' },
{ order: 4, instruction: 'Run smoke tests', command: 'npm run test:smoke' },
{ order: 5, instruction: 'Promote to production', command: 'npm run deploy:prod' },
],
});

Searching Memories

Search locally or across the cloud. Local searches are instant; cloud searches use vector embeddings for semantic similarity.

Semantic search
const results = await agent.memories.search({
query: 'What do we know about the customer onboarding process?',
type: 'procedural', // Optional: filter by type
context: 'agent', // Optional: filter by context
limit: 10,
threshold: 0.7, // Minimum similarity score
includeMetadata: true,
});
for (const result of results) {
console.log(result.memory.content);
console.log(result.score); // Similarity score 0.0 - 1.0
console.log(result.source); // 'local' | 'cloud'
}
Filtered search
// Search with metadata filters
const results = await agent.memories.search({
query: 'revenue',
filters: {
tags: { contains: 'finance' },
createdAfter: '2024-06-01T00:00:00Z',
importance: { gte: 0.5 },
},
sort: 'relevance', // 'relevance' | 'recency' | 'importance'
});

Entity Management

Manage entities and their relationships in the knowledge graph. Entities are automatically extracted from semantic memories, but you can also create and manage them directly.

// Create an entity
const entity = await agent.entities.create({
name: 'Acme Corp',
type: 'organization',
attributes: {
industry: 'Technology',
founded: 2010,
website: 'https://acme.example.com',
},
});
// Create a relationship between entities
await agent.entities.relate({
sourceId: entity.id,
targetId: 'ent_person_456',
type: 'employs',
attributes: { role: 'CEO', since: '2018-01-01' },
});
// Query the knowledge graph
const graph = await agent.entities.graph({
rootId: entity.id,
depth: 2, // Traverse 2 levels
types: ['employs', 'headquartered_in', 'acquired'],
});
// List entities by type
const orgs = await agent.entities.list({
type: 'organization',
limit: 50,
});

Agent Lifecycle

The SDK provides full agent lifecycle management: spawn child agents, send messages between agents, and terminate agents cleanly.

Spawning and messaging agents
// Spawn a child agent
const child = await agent.spawn({
agentId: 'research-assistant',
config: {
model: 'claude-3-opus',
systemPrompt: 'You are a research assistant...',
},
memory: {
context: 'agent', // Isolated memory context
inheritParent: true, // Copy parent memories
},
});
// Send a message to a child agent
const response = await agent.send(child.agentId, {
type: 'task',
payload: {
query: 'Research the latest AI safety papers from 2024',
},
});
// Terminate a child agent
await agent.terminate(child.agentId, {
preserveMemories: true, // Keep memories after termination
});
Agent status and listing
// Get agent status
const status = await agent.status();
// { agentId: 'my-assistant', state: 'running', uptime: 3600, ... }
// List all child agents
const children = await agent.listChildren();
for (const child of children) {
console.log(child.agentId, child.state);
}

Offline Mode

The SDK works fully offline. All memory operations read from and write to the local SQLite database. When connectivity is restored, changes are automatically synced.

// Force offline mode (useful for testing)
const agent = new RekallAgent({
apiKey: process.env.REKALL_API_KEY!,
agentId: 'my-assistant',
storage: { path: './rekall.db' },
sync: { enabled: false }, // Start in offline mode
});
await agent.initialize();
// All operations work locally
const memory = await agent.memories.create({
type: 'episodic',
content: 'Created while offline',
});
// Later, enable sync to push changes
agent.enableSync();
await agent.sync(); // Push all pending changes

Conflict resolution

When the same memory is modified locally and remotely, the configured sync strategy determines the outcome. The default last-write-wins strategy uses timestamps. You can also use manual to handle conflicts yourself via the onConflict event listener.

Sync Strategies

Choose a conflict resolution strategy that fits your use case.

StrategyBehaviorUse Case
last-write-winsMost recent write by timestamp is keptSingle-agent systems, simple setups
cloud-winsCloud version always takes precedenceShared memory across multiple clients
local-winsLocal version always takes precedenceAuthoritative local agent
manualFires an onConflict event for custom resolutionComplex multi-agent setups
Manual conflict resolution
const agent = new RekallAgent({
apiKey: process.env.REKALL_API_KEY!,
agentId: 'my-assistant',
storage: { path: './rekall.db' },
sync: {
strategy: 'manual',
},
});
agent.on('conflict', async (event) => {
const { localVersion, cloudVersion, memoryId } = event;
// Custom merge logic
if (localVersion.updatedAt > cloudVersion.updatedAt) {
await event.resolveWith(localVersion);
} else {
await event.resolveWith(cloudVersion);
}
});

Event Listeners

Subscribe to lifecycle and sync events to react to changes in real time.

// Sync events
agent.on('sync:start', () => console.log('Sync started'));
agent.on('sync:complete', (result) => {
console.log(`Synced ${result.pushed} up, ${result.pulled} down`);
});
agent.on('sync:error', (error) => console.error('Sync failed:', error));
// Memory events
agent.on('memory:created', (memory) => {
console.log('New memory:', memory.id);
});
agent.on('memory:updated', (memory) => {
console.log('Updated:', memory.id);
});
agent.on('memory:deleted', (memoryId) => {
console.log('Deleted:', memoryId);
});
// Conflict events (when strategy is 'manual')
agent.on('conflict', async (event) => {
console.log('Conflict on:', event.memoryId);
});
// Agent lifecycle events
agent.on('agent:spawned', (child) => console.log('Spawned:', child.agentId));
agent.on('agent:terminated', (agentId) => console.log('Terminated:', agentId));
agent.on('agent:message', (msg) => console.log('Message from:', msg.from));
// Remove a listener
const handler = (m: Memory) => console.log(m);
agent.on('memory:created', handler);
agent.off('memory:created', handler);

TypeScript Types

The SDK exports all types for full IntelliSense support. Key types are listed below.

Key exported types
import type {
// Core types
Memory,
MemoryType,
MemoryContext,
MemoryCreateInput,
MemorySearchInput,
MemorySearchResult,
// Entity types
Entity,
EntityCreateInput,
Relationship,
GraphQueryResult,
// Agent types
AgentConfig,
AgentStatus,
SpawnOptions,
AgentMessage,
// Sync types
SyncStatus,
SyncStrategy,
ConflictEvent,
// Storage types
StorageConfig,
// Event types
RekallEventMap,
} from '@rekall/agent-sdk';
Memory type definition
interface Memory {
id: string;
type: MemoryType;
content: string;
context: MemoryContext;
importance: number;
metadata: Record<string, unknown>;
entities: Entity[];
relationships: Relationship[];
createdAt: string;
updatedAt: string;
accessedAt: string;
accessCount: number;
decayFactor: number;
syncStatus: 'pending' | 'synced' | 'conflict';
}
type MemoryType =
| 'episodic'
| 'semantic'
| 'procedural'
| 'long_term'
| 'short_term'
| 'execution'
| 'preferences';
type MemoryContext = 'agent' | 'user' | 'hive';

Progressive Disclosure (Infinite Context)

The agent SDK provides three methods for token-efficient memory retrieval. Instead of loading full content for every search result, use the two-step pattern to preview memories first, then load only what you need.

recallIndex()

Returns a lightweight index with snippets, scores, and token estimates.

const index = await agent.recallIndex('project requirements', {
tokenBudget: 2000,
types: ['ltm', 'episodic', 'procedural'],
limit: 50,
});
// index.index: MemoryIndexEntry[] — lightweight entries
// index.indexTokens: number — total tokens used by index
// index.totalFullTokens: number — tokens if all were loaded fully
// index.hasMore: boolean — more results available
for (const entry of index.index) {
console.log(`[${entry.type}] ${entry.snippet} (score: ${entry.score})`);
}

recallFull()

Loads full content for specific memory IDs returned by recallIndex.

// Select the most relevant memories
const topIds = index.index
.filter(e => e.score > 0.7)
.map(e => e.id);
// Load full content only for selected items
const memories = await agent.recallFull(topIds);
for (const mem of memories) {
console.log(mem.content); // Full memory content
}

smartRecall()

Combines both steps automatically — fetches the index, selects memories within your token budget, and returns full content.

// Automatic progressive disclosure within budget
const memories = await agent.smartRecall('user preferences', {
tokenBudget: 4000,
progressive: true,
});
// Returns full content for as many memories as fit in 4000 tokens

observe()

Captures tool outputs, file reads, and API responses. These become searchable via recallIndex in future sessions.

// Capture a file read for future retrieval
await agent.observe(fileContent, {
toolName: 'file_read',
tags: ['config', 'deployment'],
type: 'file_read',
});
// Later, find it via progressive disclosure
const index = await agent.recallIndex('deployment config');
Rekall
rekall