Eve
Add long-term agent memory to Vercel Eve agents with the Zep SDK
A complete working example is available on GitHub: examples/typescript/eve
Eve is Vercel’s framework for building production agents. This guide shows how to wire Zep into an Eve agent with the Zep TypeScript SDK — no separate integration package. The pattern uses Eve hooks for persistence, channel onMessage plus turn-scoped dynamic instructions for automatic recall, and authored tools for on-demand search.
Why this pattern
Eve hooks are observe-only: they can persist side effects, but they cannot inject prompt context. Eve also resolves dynamic instructions on turn.started before message.received, and that resolver does not receive the inbound user text. Channel onMessage runs earlier (after the HTTP body is parsed), so the working pattern is:
Do not return channel context for memory — those strings enter durable session history and accumulate. Turn-scoped dynamic instructions replace the previous turn’s memory block, so only the latest recall sits in the system prompt.
Identity mapping stays outside the model:
- Eve
session.id→ ZepthreadId(for exampleeve-<sessionId>) - Eve session auth principal (or your app’s user id) → Zep
userId
Never accept userId or threadId from the model.
Architecture
Use graph.search with the current utterance for auto-recall. thread.getUserContext only queries from messages already on the Zep thread — and at turn.started the inbound message has not been persisted yet.
Setup
Requires Node.js 24+ (Eve), @getzep/zep-cloud, and a Zep Cloud API key from app.getzep.com.
Provision users and threads with create-then-catch-conflict helpers so repeats are safe (the working example implements ensureZepUserAndThread). After ensure, warm the user cache as fire-and-forget — do not await it on the path before turn.started recall (stash TTL / first-turn latency):
Automatic context injection
1. Stash the utterance in channel onMessage
Record the inbound text for the upcoming turn.started resolver. Create-session requests often have no sessionId yet — queue by userId and rebind onto the session in session.started:
2. Search and inject on turn.started
Peek the stash, run turn-relevant graph.search, then clear the stash only after search settles:
Automatic message capture
Persist each turn with a hook. Skip interim narration before tool calls (finishReason === "tool-calls"); persist other completions (stop, length, and similar):
Wrap Zep I/O in try/catch so a Zep outage never fails the Eve turn (see the full example for the guarded handlers).
Zep indexes knowledge asynchronously. Facts from a turn are not reliably searchable until processing finishes — often tens of seconds. Confirm facts in the Zep app before expecting preference recall in a new session.
On-demand search tools
Expose graph.search as authored tools when the turn’s memory section is incomplete. Pin userId / graphId from session auth or config — never from the model:
For shared org knowledge, create and seed a standalone graph with the Zep SDK (no Eve runtime), wait for episodes to process (ingestion status), then search it from a tool. The working example seeds eve-demo-company via npm run seed:company.
Production notes
- Replace demo identity — resolve
userIdfrom real auth in multi-tenant production. - Idempotent ensure — catch “already exists” on user/thread create (or use helpers like the example).
- Message size — Zep rejects thread messages over 4,096 characters; truncate to ~4,000 before
thread.addMessages. - Search query size — Zep rejects
graph.searchqueries over 400 characters; truncate before calling. - Async indexing — do not expect read-after-write within the same turn.
- Utterance stash is in-process —
onMessageandturn.startedmust run in the same Node process. Create-session queues byuserId(FIFO); avoid concurrent new sessions that share one demo user id, and set a stable user id (ZEP_DEMO_USER_IDor real auth) so create-session turns can stash. - Prompt caching — fresh memory in the system prompt breaks the cache prefix each turn; turn-scoped instructions are still preferable to accumulating channel
context.