# Record
**Author:** @cameron.stream (`did:plc:gfrmhdmjvxn2sjedzboeudef`)

## `3mv4exmr6tk2e`
**Collection:** `site.standard.document`
**AT URI:** `at://did:plc:gfrmhdmjvxn2sjedzboeudef/site.standard.document/3mv4exmr6tk2e`

**Title:** Persistent memory in Vercel eve
**Published:** Wed, 09 Sep 2026 20:29:19 GMT
**Updated:** Wed, 09 Sep 2026 20:26:27 GMT
**Description:** How eve carries saved preferences between conversations through scoped providers, recall messages, and memory tools.
**Publication:** `at://did:plc:gfrmhdmjvxn2sjedzboeudef/site.standard.publication/3mr4py6clps2f`
**Path:** /eve-persistent-memory
**Tags:** knowledge, lesson, ai, agents, memory

**Content:**
````json
{
  "text": "Persistent memory in [Vercel eve](https://eve.dev/) lets an agent save information outside a conversation and load it into later model requests. eve decides when memory runs and whose memory is eligible. A provider decides what to store, retrieve, and expose through tools. The built-in file provider loads a small saved document; other providers can search larger stores or extract facts automatically.\n\nThis is one implementation of [persistent agent memory](https://cameron.stream/knowledge/persistent-agent-memory). It changes the information supplied to a model, without changing the model's trained weights. The account below follows the September 9, 2026 [announcement](https://vercel.com/changelog/persistent-memory-for-eve-agents), current official documentation, and the pinned public implementation linked in Sources.\n\n## Two conversations, two separate users\n\nConsider a synthetic support agent with a memory document for each authenticated caller. User A says in conversation A1, “Remember that I prefer short answers.” If the model calls the save tool successfully, the provider stores that preference under A's memory key.\n\nUser A later opens conversation A2 and asks a new question. Before the model answers, eve recalls A's document and includes the preference in the request. A2 does not need to contain A1's transcript. The model can use the recalled preference, although saving and loading it do not guarantee compliance.\n\nUser B opens a separate conversation with the same agent. With correctly authenticated, distinct principals and the default namespace, B resolves to a different memory key. B's recall therefore reads B's document rather than A's. An empty document produces no remembered preference; it does not fall back to another user's memory.\n\nThe example depends on a successful save, stable identity, the same memory namespace, and a provider that partitions every operation correctly. Merely saying “remember” is not proof that a write happened.\n\n## Slots, providers, scopes, and namespaces\n\nA **slot** is a named memory configuration. For example, `agent/memory/profile.ts` declares a slot named `profile`. A single `agent/memory.ts` instead declares `memory`; the single-file and directory forms are mutually exclusive.\n\nA **provider** implements memory behavior. Its required recall handler returns text for model context. Optional capture handlers write after lifecycle events, and optional tools let the model request operations such as saving or deletion. A **backend** is narrower: it supplies storage for a provider whose behavior remains unchanged.\n\nA **scope** identifies who or what shares one slot's memory. It can be a string, a tuple of strings, or a function returning those values. Returning `null` disables the slot and its tools for that operation. A **namespace** separates application domains before scope is applied. eve derives an opaque `memory.scope.key` from the resolved namespace and scope; providers must use that key for every read and write.\n\nThe default namespace incorporates the slot, agent graph node, and deployment identity. Production redeployments keep their namespace; Preview branches are separated. Local development uses an application-root digest. An explicit namespace replaces the default completely, so deliberately sharing one also requires deliberate scope design.\n\n## A minimal file-memory configuration\n\nThe [official file-memory guide](https://eve.dev/docs/memory/file) documents `eve add memory/file` and its setup flow. In an existing eve project, the following definition belongs in `agent/memory/profile.ts`:\n\n```typescript\nimport { defineMemory } from \"eve/memory\";\nimport { byPrincipal } from \"eve/memory/scope\";\nimport { fileMemory } from \"eve/memory/file\";\n\nexport default defineMemory({\n  description: \"Remember stable facts and preferences about the caller.\",\n  provider: fileMemory(),\n  scope: byPrincipal,\n});\n```\n\nThis configuration is checked against the documentation and public source, not executed here as a deployed agent. The filename makes its model-facing tools `profile__save_memory` and `profile__remove_memory`. The slot description is prepended to their descriptions; it guides tool selection but does not enforce a content policy.\n\n`byPrincipal` reads `session.auth.current`, the authenticated caller for the active turn. The [implementation](https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/scope.ts) includes principal type, authenticator, issuer, and principal ID. Missing, anonymous, and runtime principals disable memory. A local-development principal gets the shared `local-dev` scope, so default local testing does not demonstrate isolation between real users.\n\nFor tenant-specific memory, the [multi-tenant guide](https://eve.dev/docs/patterns/multi-tenant-memory) combines a verified tenant ID with `byPrincipal(ctx)`. It rejects missing or incorrectly typed identity data. Tenant and user IDs should come from trusted authentication, never a model-supplied tool argument. The application must still authenticate requests and enforce conversation access at its channel boundary.\n\n## The complete read and write path\n\nThe [provider contract](https://eve.dev/docs/memory/custom-provider) and [lifecycle implementation](https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/context/memory-lifecycle.ts) define the following sequence:\n\n1. **Resolve identity.** At turn start, eve resolves each slot's scope and namespace from trusted context. It fixes those values for the operation before recall begins.\n2. **Recall stored information.** Each active provider receives its locked key, the pre-recall projected history, and the new input. All slots see the same pre-recall history. eve validates their results before committing the recall batches together.\n3. **Assemble model context.** Recalled text enters as user-role messages attributed to its slot, alongside conversation history and the current input. It never becomes a system instruction.\n4. **Expose scoped tools.** The provider's tools close over the operation's key. The model supplies an entry's text or removal index, not a different user's storage key. Ordinary eve tool approval and output handling still apply.\n5. **Generate and optionally write.** The model answers and can call memory tools during the turn. File memory writes only through its tools; it has no automatic post-turn capture handler.\n6. **Capture the completed turn, if supported.** Providers with `capture[\"turn.completed\"]` receive settled history after a successful turn. Their extraction and storage rules determine what they retain.\n\nThe file provider recalls the whole document as one message with the stable ID `file-memory-document`. Later recall of that ID supersedes the earlier copy for the same slot, namespace, and scope. Saving changes the external document; the recall handler loads its updated contents at the next recall boundary, including after compaction.\n\nCustom providers can return several keyed messages. Reusing an ID replaces its prior value; omitting an ID from a later result does not retract the earlier message. Unkeyed messages accumulate. During [context compaction](https://cameron.stream/knowledge/context-compaction), eve excludes recalled records from the summarizer and preserves their attribution separately. Providers can capture before compaction and recall afterward.\n\nFailure timing matters. A failed or invalid turn-start recall fails the turn before the model call and commits none of the slots' recall results. A failed tools handler is logged and omitted. A failed post-turn capture is logged after the response; the completed answer therefore does not prove that memory was saved. Stable operation IDs support replay, but providers must implement idempotent writes themselves.\n\n## What “file” means on Vercel and locally\n\nFile memory stores a versioned document through a backend, rather than editing a file in the agent's sandbox. On Vercel, the default backend requires Blob configuration and stores private objects under a prefix and the opaque scope key. Provisioning creates or reuses a private store and connects it through `EVE_MEMORY_`-prefixed variables. Persisting in that store lets memory survive process restarts and deployments that retain the same storage and namespace.\n\nLocal defaults are different. In `eve dev`, file memory uses shared **process-local in-memory storage**, not a disk file. It can bridge conversations in that process but loses its contents when the process is replaced. Other environments require an explicit backend; missing Blob configuration on Vercel produces an error rather than silently using temporary storage.\n\nA documentation/source discrepancy affects credential precedence. The file-memory guide lists a token before a store ID within each variable family. The pinned [backend selector](https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/file/backends/default.ts) checks the store ID first, then the token, while preferring the `EVE_MEMORY_BLOB` family over generic `BLOB` variables. Avoid relying on the documented ordering when both are configured; inspect the installed version or configure the backend explicitly.\n\n## Limits, deletion, and concurrent writes\n\nThe [file provider source](https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/file/provider.ts) enforces several bounds:\n\n| Boundary | Behavior |\n| --- | --- |\n| Recall-size budget | Saves must fit `maxCharacters`, default 4,000 characters, including the recalled heading and guidance. |\n| Individual entry | At most 2,048 UTF-8 bytes after whitespace normalization. |\n| Stored document | At most 65,536 bytes. |\n| Duplicate save | Identical normalized text is a successful no-op. |\n| Conflicting write | Re-read and retry, with at most eight conflict retries before propagating the error. |\n\nOversized saves fail rather than evicting old entries. The character budget is checked on save, not used to truncate recall. Lowering that setting does not trim an already stored document.\n\nEach saved entry receives a permanent numeric index. `remove_memory` removes an entry by index and writes the remaining document. Removing the final entry leaves an empty versioned document whose next recall explicitly says no memories are saved, superseding the old recalled copy. It does not erase facts already quoted in assistant responses, logs, or other stores.\n\nSession `clear()` removes session history, recalled records, and memory locks, but leaves provider storage intact. A later turn can recall those memories again. Retention, full-store deletion, and any downstream backups belong to the provider and storage operator; the generic memory lifecycle does not establish complete erasure.\n\nThe operation's “lock” fixes its scope; it should not be read as a global mutex across conversations. File memory uses optimistic concurrency: writes state the version they read and retry if another writer changed it. The [Blob backend](https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/file/backends/vercel-blob.ts) uses ETags and conditional writes. A custom backend must honor the same compare-and-set contract to avoid lost updates.\n\n## Isolation and security boundaries\n\nScope controls which storage partition a provider should access. It cannot compensate for forged authentication, a provider that ignores the key, or a shared session containing another person's conversation. Semantic providers must constrain retrieval by scope in the query itself, rather than search globally and filter afterward.\n\nThe default `visibility: \"scope\"` hides earlier recalled messages when a session changes scope. `visibility: \"session\"` retains earlier scopes' recalled messages within the current namespace and is intended for one trusted audience. Neither option removes information already copied into an assistant response. Applications requiring hard separation between participants need separate sessions as well as correct scoped storage.\n\nRecalled memories remain untrusted user-controlled data. User-role insertion and warnings in the recalled text preserve an authority distinction, but do not prove resistance to prompt injection. Instructions should prohibit saving secrets and define what belongs in memory; applications should add appropriate validation, permissions, and deletion controls. A private Blob object is a storage access property, not proof that all agent behavior is private or safe.\n\n## Provider portability and evidence limits\n\nThe slot interface separates application scope from provider behavior. The official overview lists Supermemory for semantic recall with automatic capture and tools, and Upstash AgentKit for Redis-backed retrieval and capture or tool-driven operation. The announcement also lists Kybernesis Arcana. Those integration listings establish availability, not comparative retrieval quality.\n\nChanging `provider` can preserve the slot configuration, but does not migrate its stored data or preserve its ranking, extraction rules, retention, or tools. A custom file-memory backend changes storage while retaining the document format and behavior. A custom provider changes those behaviors too. Existing data still needs an explicit migration plan, including stable keys and deletion handling.\n\nConversation history records a session's exchanges; persistent memory selects external information for later sessions; model weights hold learned parameters from training. eve's feature operates in the first two layers by writing external state and inserting recalled messages. Its documentation and source establish that mechanism. They do not establish how often a model saves the right preference, retrieves the right fact, or obeys it. Those questions need behavioral tests with separate authenticated users, new conversations, restarts, conflicting updates, and deletion checks.\n\nThe broader [agent-memory map](https://cameron.stream/knowledge/agent-memory) places this implementation alongside storage, retrieval, compaction, and observability as separate engineering concerns.\n\n## Sources\n\n- [Vercel announcement](<https://vercel.com/changelog/persistent-memory-for-eve-agents>)\n- [eve memory overview](<https://eve.dev/docs/memory>)\n- [File memory](<https://eve.dev/docs/memory/file>)\n- [Memory provider contract](<https://eve.dev/docs/memory/custom-provider>)\n- [Multi\\-tenant memory](<https://eve.dev/docs/patterns/multi-tenant-memory>)\n- [Pinned file provider source](<https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/file/provider.ts>)\n- [Pinned principal scope source](<https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/scope.ts>)\n- [Pinned default backend source](<https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/file/backends/default.ts>)\n- [Pinned Blob backend source](<https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/public/memory/file/backends/vercel-blob.ts>)\n- [Pinned lifecycle source](<https://github.com/vercel/eve/blob/a24b68c5774bffeb0e4e762c3aab3ec7ab64a179/packages/eve/src/context/memory-lifecycle.ts>)",
  "$type": "site.standard.content.markdown",
  "version": "1.0"
}
````

---
*Fetched from https://enoki.us-east.host.bsky.network via `com.atproto.repo.getRecord`*