A one-route Dawn app (notes) with a note-taking agent that has durable,
cross-session memory. It ships with a zero-setup SQLite backend and switches
to Postgres + pgvector with a single environment variable — the same app
code, a different store.
src/app/notes/index.ts— the agent (gpt-5-mini) with aremember/recallsystem prompt.src/app/notes/memory.ts— asemanticmemory schema (subject/predicate/value), route-scoped.
The remember and recall tools are generated from memory.ts.
The backend is chosen at load time from the environment (see dawn.config.ts):
| Env | Store | Recall |
|---|---|---|
| (none) | SQLite (default) | keyword-only |
OPENAI_API_KEY |
SQLite | hybrid keyword + vector |
DATABASE_URL |
Postgres/pgvector | keyword-only |
DATABASE_URL + OPENAI_API_KEY |
Postgres/pgvector | hybrid keyword + vector |
The two toggles are independent. DATABASE_URL swaps the store; OPENAI_API_KEY
lights up vector/semantic recall (text-embedding-3-small, 1536 dims). Both the
store and the embedder connect lazily, so nothing touches the network until the
first remember/recall.
pnpm --filter @dawn-example/memory devMemory persists to .dawn/memory.sqlite. That's it — no key, no database.
Start a pgvector-enabled Postgres:
docker run --rm -e POSTGRES_PASSWORD=postgres -p 5432:5432 pgvector/pgvector:pg16Point the app at it (and, optionally, add a key for vector recall):
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
export OPENAI_API_KEY="sk-..." # optional — enables hybrid keyword+vector recall
pnpm --filter @dawn-example/memory devThe app creates its tables + HNSW index on first write.
Once the app has accumulated some history, the two distillation passes compact it — consolidation summarizes old episodes per (namespace, ISO week); reflection derives durable insights:
# See the plan without spending a token (no model call, no key needed):
pnpm --filter @dawn-example/memory exec dawn memory consolidate --dry-run
pnpm --filter @dawn-example/memory exec dawn memory reflect --dry-run
# Run them for real (needs OPENAI_API_KEY — the default model is gpt-5-mini):
pnpm --filter @dawn-example/memory exec dawn memory consolidate
pnpm --filter @dawn-example/memory exec dawn memory reflectBoth are threshold-aware no-ops, so running them before there is anything to
distill just prints nothing to consolidate / nothing to reflect on and exits
0 without constructing a model. Reflection writes its insights as candidates
by default — review them with dawn memory list and promote with
dawn memory approve <id>. See the distillation docs for every flag
and the memory.distill config block.
packages/testing/test/memory-example-dogfood.test.ts drives this real
example app through a scripted remember → recall flow:
- Always (CI-safe, no key, no Docker): the default SQLite backend, proving the memory route works end-to-end.
- Gated (
DAWN_TEST_PGVECTOR=1, Docker): the same flow against a Testcontainers Postgres, proving recall works through pgvector.
# CI-safe (SQLite) — gated block auto-skips:
pnpm --filter @dawn-ai/testing exec vitest run test/memory-example-dogfood.test.ts
# Local hands-on pgvector dogfood (needs Docker):
DAWN_TEST_PGVECTOR=1 pnpm --filter @dawn-ai/testing exec vitest run test/memory-example-dogfood.test.ts