SQLite-backed graph database for agent memory. Works with Cloudflare Durable Objects and Node.js (via better-sqlite3).
Inspired by Boris Tane's DO graph DB pattern and Mem0's memory classification.
- Graph database — Nodes + edges with typed relationships, BFS traversal, neighbor queries
- Mem0 memory types — Semantic (facts), Procedural (skills), Episodic (events), Working (context)
- FTS5 search with graceful LIKE fallback when FTS5 isn't available
- Context aggregation — Build structured memory context for LLM agent reasoning
- Skill registry — Register, search, track, and relate agent skills (separate subpath export)
- Two adapters — Cloudflare Durable Object SQL or better-sqlite3 (Node.js)
- Zero runtime dependencies —
@cloudflare/workers-typesandbetter-sqlite3are optional peer deps - Schema migrations with rollback support
npm install @divinci-ai/do-graph-memory
# or
pnpm add @divinci-ai/do-graph-memoryFor Node.js usage, also install better-sqlite3:
npm install better-sqlite3import { GraphMemory, createBetterSqlite3Adapter } from "@divinci-ai/do-graph-memory";
import Database from "better-sqlite3";
const db = new Database("agent-memory.db");
const adapter = createBetterSqlite3Adapter(db);
const memory = new GraphMemory({ adapter });
memory.initialize();
// Add facts
memory.addFact("user:theme", "User theme preference", "dark");
memory.addFact("config:model", "Default model", "gpt-4");
// Add a skill
memory.addSkill("skill:summarize", "Summarize Text", "Summarizes long text into key points", {
capabilities: ["text-read"],
});
// Connect nodes
memory.addEdge({
source: "user:theme",
target: "config:model",
relationship: "configured_with",
});
// Traverse the graph
const result = memory.traverse("user:theme", { maxDepth: 3 });
console.log(`Found ${result.nodes.length} connected nodes`);
// Build context for LLM reasoning
const context = memory.buildContext({ anchor: "user:theme", depth: 2 });
// context.facts, context.skills, context.history, context.workingimport { GraphMemory, createDurableObjectAdapter } from "@divinci-ai/do-graph-memory";
export class AgentMemory extends DurableObject {
private memory: GraphMemory;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
const adapter = createDurableObjectAdapter(ctx.storage);
this.memory = new GraphMemory({ adapter });
ctx.blockConcurrencyWhile(async () => this.memory.initialize());
}
async addFact(id: string, label: string, value: unknown) {
return this.memory.addFact(id, label, value);
}
async buildContext(anchor: string) {
return this.memory.buildContext({ anchor, depth: 3 });
}
}new GraphMemory(options: {
adapter: SqlAdapter; // Required: DO or better-sqlite3 adapter
logger?: Logger; // Optional: defaults to silent
autoId?: boolean; // Optional: auto-generate UUIDs for nodes
})Node Operations:
addNode(node)/addNodes(nodes)— Create one or many nodesgetNode(id)— Get by IDupdateNode(id, updates)— Partial updatedeleteNode(id)— Delete (cascades to edges)queryNodes(query)— Filter by type, subtype, label, data, datessearchNodes(term, limit?)— Full-text search (FTS5 or LIKE fallback)
Edge Operations:
addEdge(edge)/addEdges(edges)— Create relationshipsgetEdges(nodeId, direction?)— Get edges for a nodedeleteEdge(source, target, relationship)— Remove a relationshipfindNeighbors(nodeId, options?)— Find connected nodes
Traversal & Context:
traverse(startId, options?)— BFS graph traversalbuildContext(options)— Aggregate memory context for agent reasoning
Convenience (Mem0-style):
addFact(id, label, value, options?)— Semantic nodeaddSkill(id, label, description, options?)— Procedural nodeaddEpisode(id, label, description, options?)— Episodic nodesetWorkingContext(id, label, state, ttlMs?)— Working memory with TTLgetWorkingContext()— Get current working context
import { SkillRegistry } from "@divinci-ai/do-graph-memory/skill-registry";
const registry = new SkillRegistry(memory, memory.adapter);
registry.initialize(); // Runs skill_stats migration
registry.registerSkill({
id: "skill:analyze",
name: "Analyze Data",
description: "Analyzes datasets",
category: "data",
capabilities: ["data-read"],
});
// Track executions
registry.recordExecution({
skillId: "skill:analyze",
outcome: "success",
duration: 1200,
});
// Query stats
const stats = registry.getSkillStats("skill:analyze");
const topSkills = registry.getMostSuccessfulSkills(5);// Node.js
import { createBetterSqlite3Adapter } from "@divinci-ai/do-graph-memory/adapters/better-sqlite3";
import Database from "better-sqlite3";
const adapter = createBetterSqlite3Adapter(new Database("file.db"));
// Cloudflare Durable Objects
import { createDurableObjectAdapter } from "@divinci-ai/do-graph-memory/adapters/durable-object";
const adapter = createDurableObjectAdapter(ctx.storage);Implement this to bring your own SQLite driver:
interface SqlAdapter {
exec(query: string, ...bindings: unknown[]): SqlRow[];
run(query: string, ...bindings: unknown[]): { changes: number };
transaction<T>(fn: () => T): T;
}Query nodes by JSON data properties using json_extract:
memory.queryNodes({
type: "semantic",
dataFilters: { category: "network", priority: 1 },
});| Import path | What you get |
|---|---|
@divinci-ai/do-graph-memory |
GraphMemory, types, adapters, schema utils |
@divinci-ai/do-graph-memory/skill-registry |
SkillRegistry, skill types |
@divinci-ai/do-graph-memory/adapters/better-sqlite3 |
createBetterSqlite3Adapter, hasFts5 |
@divinci-ai/do-graph-memory/adapters/durable-object |
createDurableObjectAdapter |
MIT