Eve Memory
The first installable memory package for Eve, Vercel’s filesystem-first framework for durable backend AI agents. Three one-line files give any Eve agent persistent, per-user memory — idempotent fact extraction from the user’s messages, budgeted turn-scoped recall, and an auth-derived identity ladder.
How it works
- Install
eve-memorysyncfrom npm. - Eve agents are defined by files on disk — you mount memory by dropping three one-line files into
agent/. - The channel line records each inbound utterance; the hooks file sends the user’s messages for fact extraction; the instructions file injects the user’s relevant memories before the model runs.
- Memories persist across restarts, deploys, and sessions — and are shared with every other MemorySync surface.
Before you start
- A MemorySync API key. Create one in the dashboard under Settings → API Keys, inside a project.
- Node.js 24+ (Eve’s own minimum) and an Eve agent project (
eve0.40+).
Install
npm install eve-memorysync
Mount the three files
Step 1 — capture user turns
Create agent/hooks/memorysync.ts — on message.received it sends the user’s message to MemorySync, which extracts the durable facts in it and stores only those; the message text itself is not stored. message.completed sends nothing: assistant replies are not stored as memories.
import { createMemoryHooks } from 'eve-memorysync'export default createMemoryHooks()
Step 2 — recall per turn
Create agent/instructions/memorysync.ts — resolves the user’s relevant memories into the prompt on every turn:
import { createMemoryInstructions } from 'eve-memorysync'export default createMemoryInstructions()
Step 3 — stash the utterance
Eve resolves dynamic instructions on turn.started before the resolver can see the inbound user text — so the channel records it first. One added line in onMessage:
import { eveChannel, defaultEveAuth } from 'eve/channels/eve'import { localDev, vercelOidc } from 'eve/channels/auth'import { stashUtterance } from 'eve-memorysync'export default eveChannel({auth: [vercelOidc(), localDev()],async onMessage(ctx, message) {stashUtterance(ctx, message) // ← the added linereturn { auth: defaultEveAuth(ctx) }},})
Step 4 — run it
Set MEMORYSYNC_API_KEY in the agent’s environment and start:
MEMORYSYNC_API_KEY=ms_... eve dev
Tell the agent a fact, restart it, ask again — it remembers. The recall block is turn-scoped: Eve replaces the previous turn’s block, so only the latest recall is ever in the prompt.
Why a package matters here
Engineering guarantees
- A memory failure can never fail a turn. A thrown Eve hook fails the whole turn, so every handler is fully guarded: API errors, timeouts, quota limits, and dead networks degrade to a
[memorysync]log line. - Facts, not turns. Each user message is sent with
role: "user"to fact extraction; only the durable facts it yields are stored, and they keep this package’s metadata (surface: "eve",session_id,role,agent,channel). Assistant replies are not sent at all. - Retries are recognised. Deterministic
user@eve::<session>#h<hash>seeds absorb Eve’s at-least-once redelivery: a redelivered message is not extracted twice. - Recall is budgeted. The
turn.startedresolver fails open after 1.2s (configurable) — slow memory cannot stall the agent. - Stash misses degrade, not die. On multi-isolate hosts where
onMessageandturn.startedmay run in different processes, a stash miss falls back to a profile recall instead of silently skipping memory. SetfallbackRecall: 'skip'to opt out. - Identity comes from auth, never the model. Ladder: your
resolveUserId(ctx)→ session auth principal (current, then initiator) →defaultUserId→MEMORYSYNC_DEFAULT_USER_ID→default. Tool arguments cannot select another user’s memory. - Loud startup, silent runtime. Factories throw at agent build time when no API key is configured — a memory product silently running with memory off is worse than a visible failure.
- Long turns are truncated at 16,000 characters before they are sent; recall prompts are clipped at 2,000.
Optional: an on-demand search tool
If you want the model to search memory explicitly (search-only — no deletes), mount a fourth file:
import { createMemoryTool } from 'eve-memorysync'export default createMemoryTool()
Troubleshooting
- Throws at startup about the API key — deliberate (loud startup). Set
MEMORYSYNC_API_KEYin the agent’s environment. - Recall block missing on a turn — the 1.2s budget failed open, or the stash missed on a multi-isolate host (it falls back to a profile recall unless you set
fallbackRecall: 'skip'). Check for[memorysync]log lines. - Same fact stored once despite retries — expected: deterministic seeds absorb Eve’s at-least-once redelivery.
- Nothing stored for the agent’s replies — by design:
message.completedsends nothing, because assistant replies are not stored as memories. - Wrong user’s memories — identity comes from the auth ladder, never model arguments; check what
resolveUserId(ctx)/ the session auth principal resolves to in your channel.
Supported versions
| Surface | Requires | Verified on |
|---|---|---|
eve-memorysync 1.1.0 | Node.js 24+ (Eve’s own minimum); eve 0.40+ as a peer dependency | 34 CI checks against the REAL eve package (defineHook/defineDynamic/defineTool brand their definitions, so acceptance proves real-agent wiring): only the user turn sent (message.completed a no-op), honest extraction outcomes with no memory id, stash TTL/bounds and the create-session handoff, the fail-open recall budget, the identity ladder, 16k truncation, quota modes, and the search-only tool |
How it compares
| Zep | Mem0 | Supermemory / Letta | MemorySync | |
|---|---|---|---|---|
| Eve integration | ⚠ copy-paste example (no package) | ✗ none (generic Vercel env-var installer) | ✗ nothing at all | ✓ first installable package |
| Retry safety | ✗ at-least-once hooks duplicate turns | — | — | ✓ idempotent seeds — a redelivered message is extracted once |
| Utterance stash | ✗ unbounded in-process Maps; multi-isolate = silent recall loss | — | — | ✓ TTL + bounds + create-session queue, profile-recall fallback on misses |
| Recall budget | ✗ none — a slow search stalls the turn | — | — | ✓ 1.2s fail-open |
| Message limits | ✗ silently rejects >4,096-char messages | — | — | ✓ 16k with explicit truncation |
| Identity | ✗ shared demo-user fallback footgun | — | — | ✓ auth-principal ladder, never model-provided |