> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Vercel eve

> Memory tools and a per-turn short-term-context resolver for Vercel eve agents (TypeScript).

Add persistent, cross-session memory to a [Vercel eve](https://vercel.com/eve) agent. eve is a filesystem-first framework for durable backend agents — you author an agent as files under `agent/`. This integration adds two of those files: memory **tools** the model can call, and an **instructions resolver** that injects Synap's short-term context into every turn.

<Note>
  eve's own durability (Vercel Workflows) persists a single session's turn state so it survives crashes and redeploys — that is short-term *session* state, not cross-session memory. Synap is the durable, cross-session layer on top. Tested against `eve@0.25.1` (AI SDK v7); eve is in beta, so pin your version.
</Note>

<Warning>
  `@maximem/synap-eve` uses the JavaScript SDK, which wraps the Python SDK as a subprocess. **It requires a Python 3.11+ runtime on the host.** eve deploys to Vercel Functions — ensure your deployment runtime provides Python, or point the SDK at a remote Synap endpoint. Local `eve dev` on a machine with Python works out of the box. See [Installation → JavaScript / TypeScript SDK](/setup/installation#javascript-typescript-sdk).
</Warning>

## Overview

This guide shows how to add Synap to an eve agent so it can:

* Recall long-term memory and store facts on demand, via tools the model chooses to call
* Automatically inject Synap's compacted short-term context into the system prompt every turn
* Scope memory per user without the model ever seeing the identity

The integration ships three factories, each meant to be the default export of a file under `agent/`:

| Export                    | eve file                      | Purpose                                                                       |
| ------------------------- | ----------------------------- | ----------------------------------------------------------------------------- |
| `createSynapSearchTool`   | `agent/tools/synap_search.ts` | Model-driven memory search (`defineTool`)                                     |
| `createSynapStoreTool`    | `agent/tools/synap_store.ts`  | Model-driven memory store (`defineTool`)                                      |
| `createSynapInstructions` | `agent/instructions/synap.ts` | Always-on per-turn short-term recall into the system prompt (`defineDynamic`) |

The filename under `agent/tools/` becomes the model-facing tool name. The instructions resolver **augments** `instructions.md` — it adds a system message, it never replaces your prompt.

## Setup

Install the package alongside eve:

```bash theme={null}
npm install @maximem/synap-eve @maximem/synap-js-sdk eve zod
```

<Note>
  Import the integration from its package name directly: `import { createSynapSearchTool } from "@maximem/synap-eve"`. The core SDK (`createClient`) comes from the separate `@maximem/synap-js-sdk` package.
</Note>

Configure your API key. Generate one from the [Synap Dashboard](https://synap.maximem.ai).

```bash .env theme={null}
SYNAP_API_KEY=synap_your_key_here
```

Initialize the SDK once and export it so your `agent/` files can share it:

```typescript agent/lib/synap.ts theme={null}
import { createClient } from "@maximem/synap-js-sdk";

export const sdk = createClient();
await sdk.init();
```

See [SDK Initialization](/sdk/initialization) for the full lifecycle and configuration options.

## Basic integration

The smallest useful integration is a single search tool. Drop this file in and the model can pull long-term context whenever it decides it needs it:

```typescript agent/tools/synap_search.ts theme={null}
import { createSynapSearchTool } from "@maximem/synap-eve";
import { sdk } from "../lib/synap.js";

// The filename is the tool name the model sees: `synap_search`.
export default createSynapSearchTool({ sdk });
```

Identity is resolved automatically from the eve session: the Synap `user_id` comes from `ctx.session.auth.current.principalId` and the `conversation_id` from `ctx.session.id`. On an authenticated channel that is all you need — the model never sees the user identity, and **reads degrade gracefully** (empty result on failure, so a recall miss never aborts the turn).

To also inject short-term context on every turn without the model asking, add the instructions resolver below.

***

## Core concepts

### Memory tools

Both tool factories return branded eve `ToolDefinition`s. Search reads; store writes:

```typescript agent/tools/synap_store.ts theme={null}
import { createSynapStoreTool } from "@maximem/synap-eve";
import { sdk } from "../lib/synap.js";

export default createSynapStoreTool({ sdk });
```

`synap_search` input: `{ query: string, maxResults?: number }`. `synap_store` input: `{ content: string, metadata?: Record<string, unknown> }`.

**Error policy** follows the Synap-wide contract:

* **`synap_search` degrades gracefully**: returns `{ available: false }` and logs on failure.
* **`synap_store` surfaces failures**: raises `SynapIntegrationError`, so the model sees that an ingestion outage happened rather than assuming the write succeeded.

### Short-term context resolver

`createSynapInstructions` returns a `defineDynamic` resolver that runs at the start of every turn, fetches Synap's compacted short-term summary for the conversation, and lowers it to one extra system message:

```typescript agent/instructions/synap.ts theme={null}
import { createSynapInstructions } from "@maximem/synap-eve";
import { sdk } from "../lib/synap.js";

export default createSynapInstructions({
  sdk,
  style: "narrative",   // "narrative" | "structured" | "bullet_points"
});
```

It **augments** your `agent/instructions.md`; it never replaces it. When there is no context — or on an SDK failure with the default `onError: "fallback"` — the resolver returns `null`, which eve treats as "contribute nothing," and the turn proceeds unchanged.

### Identity and scoping

Every factory accepts the standard scoping options, which override the session-derived defaults:

| Option           | Default                                | Notes                                                                                |
| ---------------- | -------------------------------------- | ------------------------------------------------------------------------------------ |
| `userId`         | `ctx.session.auth.current.principalId` | **Required explicitly on unauthenticated channels** (where `auth.current` is `null`) |
| `customerId`     | none                                   | Required on B2B Synap instances                                                      |
| `conversationId` | `ctx.session.id`                       | Scopes to a single session                                                           |
| `mode` (search)  | `"accurate"`                           | `"fast"` for the hot path                                                            |

The integration deliberately does **not** fall back to the session *initiator*: on a delegated or system-initiated session that is whoever started it (an admin or service), not the end user the turn acts for — using it would scope memory to the wrong principal.

***

## Complete example: memory-augmented agent

A complete agent that has always-on short-term recall AND lets the model search/store explicitly is just a few files:

```
agent/
├── agent.ts
├── instructions.md
├── lib/
│   └── synap.ts               # createClient() + sdk.init()
├── instructions/
│   └── synap.ts               # createSynapInstructions({ sdk })
└── tools/
    ├── synap_search.ts        # createSynapSearchTool({ sdk })
    └── synap_store.ts         # createSynapStoreTool({ sdk })
```

```typescript agent/agent.ts theme={null}
import { defineAgent } from "eve";

export default defineAgent({
  model: "openai/gpt-5.4-mini",
});
```

```markdown agent/instructions.md theme={null}
You are a personal assistant with long-term memory.
Use `synap_search` when you need older context that is not already in the prompt.
Use `synap_store` when the user shares a new fact, preference, or decision.
```

```typescript agent/instructions/synap.ts theme={null}
import { createSynapInstructions } from "@maximem/synap-eve";
import { sdk } from "../lib/synap.js";

export default createSynapInstructions({ sdk });
```

Run it locally and start a session:

```bash theme={null}
npx eve dev
curl -X POST http://127.0.0.1:3000/eve/v1/session \
  -H 'content-type: application/json' \
  -d '{"message":"What plan am I on?"}'
```

Three things to notice:

1. **The resolver and the tools are complementary.** `createSynapInstructions` sets the always-on baseline; the tools handle the explicit "I should look this up / bookmark this" path.
2. **Scope is derived per turn** from the eve session — no per-request agent construction needed on authenticated channels.
3. **The instructions are the policy.** Telling the model when to call `synap_search` / `synap_store` is what produces the explicit-memory behavior.

***

## Advanced patterns

### Unauthenticated channels

If a channel does not authenticate the caller (`ctx.session.auth.current` is `null`), the session-derived `user_id` is unavailable. Pass an explicit `userId` (and `customerId` for B2B) into the factories:

```typescript agent/tools/synap_search.ts theme={null}
export default createSynapSearchTool({ sdk, userId: "alice", customerId: "acme" });
```

### Latency vs. comprehensiveness

`synap_search` accepts `mode`:

|          | `fast`         | `accurate`                                              |
| -------- | -------------- | ------------------------------------------------------- |
| Search   | Vector + graph | Vector + graph + LLM subquery decomposition + reranking |
| Best for | Real-time chat | Multi-entity queries                                    |

```typescript theme={null}
export default createSynapSearchTool({ sdk, mode: "fast" });
```

### Failure semantics

* **Reads (`synap_search`, the instructions resolver) degrade gracefully**: empty result / `null`, logged.
* **Writes (`synap_store`) surface failures**: raise `SynapIntegrationError`.

Read failures shouldn't break a user-facing turn, but a silent write failure would let memory drift away from reality.

***

## Going further

* [Patterns overview](/patterns/overview): reusable memory patterns across frameworks.
* [Cookbook overview](/cookbook/overview): end-to-end worked examples.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Vercel AI SDK" icon="https://github.com/vercel.png" href="/integrations/vercel-ai-sdk">
    Model middleware for the Vercel AI SDK — the layer below eve.
  </Card>

  <Card title="Mastra" icon="https://github.com/mastra-ai.png" href="/integrations/mastra">
    `SynapMemory` class and tools for Mastra.
  </Card>

  <Card title="Context Fetch" icon="magnifying-glass" href="/sdk/context-fetch">
    The retrieval API behind `synap_search` and the instructions resolver.
  </Card>

  <Card title="Memory Scopes" icon="layer-group" href="/concepts/memory-scopes">
    How `userId`, `customerId`, and `conversationId` interact across reads.
  </Card>
</CardGroup>
