Lattice keeps a SQLite database and a set of context files in sync — so every agent session starts with accurate state, and agent output becomes permanent data.
Your agents never read stale data. Lattice renders database rows into text files and re-renders whenever data changes. One call to db.watch() keeps everything current.
Each agent loads only the files relevant to its entity — not a giant dump of everything. One directory per entity, one file per relationship, with automatic cleanup when entities are deleted.
Agents don't just read context — they write back. Lattice watches output files, parses structured entries, and persists them to the database with deduplication and offset tracking.
Declare your schema in YAML, run lattice generate, and get TypeScript types and SQL migrations. No manual table creation, no boilerplate.
LLM context windows are ephemeral. Your application state lives in a database. Every agent session starts cold unless something bridges them. Lattice is that bridge. If you've ever had an agent repeat work, contradict itself, or forget what happened in a previous session — that's the problem Lattice solves.
Your DB (SQLite)
│ Lattice reads rows → render → text
▼
Context files (Markdown, JSON, etc.)
│ LLM agents read these at session start
▼
Agent output files
│ Lattice writeback parses these
▼
Your DB (rows inserted/updated)Lattice reads your database for rendering, and provides a full CRUD API plus a writeback pipeline for persisting agent output back to the DB.
import { Lattice } from 'latticesql';
const db = new Lattice('./state.db');
db.define('agents', {
columns: {
id: 'TEXT PRIMARY KEY',
name: 'TEXT NOT NULL',
persona: 'TEXT',
active: 'INTEGER DEFAULT 1',
},
render(rows) {
return rows
.filter((r) => r.active)
.map((r) => `## ${r.name}\n\n${r.persona ?? ''}`)
.join('\n\n---\n\n');
},
outputFile: 'AGENTS.md',
});
await db.init();
await db.insert('agents', { name: 'Alpha', persona: 'Research assistant.' });
// Render DB → context files
await db.render('./context');
// Writes: context/AGENTS.md
// Watch for changes, re-render every 5 seconds
const stop = await db.watch('./context', { interval: 5000 });One directory per entity. One file per relationship. Agents load exactly what they need — no giant context dumps.
db.defineEntityContext('agents', {
slug: (row) => row.slug as string,
index: {
outputFile: 'agents/AGENTS.md',
render: (rows) => rows.map((r) => `- ${r.name}`).join('\n'),
},
files: {
'AGENT.md': {
source: { type: 'self' },
render: ([r]) => `# ${r.name}\n\n${r.bio ?? ''}`,
},
'TASKS.md': {
source: { type: 'hasMany', table: 'tasks', foreignKey: 'agent_id' },
render: (rows) => rows.map((r) => `- ${r.title}`).join('\n'),
omitIfEmpty: true,
},
},
combined: { outputFile: 'CONTEXT.md', exclude: [] },
protectedFiles: ['SESSION.md'],
});
// Produces per-agent directories:
// context/agents/alpha/AGENT.md
// context/agents/alpha/TASKS.md
// context/agents/alpha/CONTEXT.mdcontext/
├── agents/
│ └── AGENTS.md ← global index
├── agents/alpha/
│ ├── AGENT.md
│ ├── TASKS.md ← omitted when empty
│ └── CONTEXT.md ← all files combined
└── agents/beta/
├── AGENT.md
└── CONTEXT.mdAlso included
reconcile() removes orphaned directories when entities are deletedlattice generateGenerate TypeScript types and SQL migration from YAML config
lattice renderOne-shot render — write all context files from the database
lattice reconcileRender + orphan cleanup — remove stale entity directories
lattice statusDry-run reconcile — preview what would change
lattice watchPoll the DB and re-render on every interval
Lattice has no opinions about how your data is structured or how your context files look. Define your tables, write your render functions, and Lattice handles sync, cleanup, and persistence.