diff --git a/llms-full.txt b/llms-full.txt
index eb50e7d9..ad646146 100644
--- a/llms-full.txt
+++ b/llms-full.txt
@@ -21759,25 +21759,25 @@ lives in [`examples/ai-sdk-demo`](https://github.com/upstash/agentkit/tree/main/
-# Memory, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework
+# Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework
Source: https://upstash.com/docs/redis/sdks/agentkit/eve
[Upstash AgentKit](https://github.com/upstash/agentkit) builds AI agents on Upstash Redis: memory,
conversation history, caching, and RAG, with no separate vector database. The semantic features run on
[Upstash Redis Search](/docs/redis/search/introduction) and its `$smart` fuzzy operator.
-`@upstash/agentkit-eve` brings AgentKit to **Eve, the Vercel agent framework**. You drop these into your
-`agent/` tree:
+Two packages bring AgentKit to **Eve, the Vercel agent framework**. They work together in one agent:
-| Import | Feature |
+| Package | What it is |
| --- | --- |
-| `defineMemoryRecallTool` / `defineMemorySaveTool` | Long-term memory tools the model reads and writes. |
-| `defineSearchTools` | `search` / `aggregate` / `count` tools over a Redis Search index (this is how you do RAG). |
-| `createRateLimitAuth` | A rate-limit gate for your channel's `auth` walk. |
-| `upstash` (`@upstash/agentkit-eve/sandbox`) | Upstash Box sandbox backend for `defineSandbox`. |
-| `defineCachedTool` | A `defineTool` whose result is memoized in Redis. |
+| `@upstash/agentkit-eve-extension` | An [Eve extension](https://eve.dev/docs/extensions): one mount file adds memory, searchable chat history, and RAG. |
+| `@upstash/agentkit-eve` | Per-file building blocks, plus the two things an extension cannot contribute: rate limiting and a sandbox backend. |
+
+Mount the extension for the bundled setup. Add the package when you need a rate-limit gate, an Upstash
+Box sandbox, or control over an individual tool file.
-Start from an eve project. Scaffold one (it installs `eve` and an AI-SDK provider for you):
+Start from an eve project (0.25.2 or later). Scaffold one, which installs `eve` and an AI-SDK provider
+for you:
```bash
npx eve@latest init my-agent
@@ -21785,84 +21785,219 @@ npx eve@latest init my-agent
npx eve@latest init my-agent --channel-web-nextjs
```
-Then add the AgentKit packages:
+
+ AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default.
+ Pass your own `@upstash/redis` client as `redis` to any helper to override.
+
+
+## The Eve extension: memory, chat history, and RAG
+
+Everything in this part comes from `@upstash/agentkit-eve-extension`, configured in one mount file.
+
+### How to mount the Upstash extension in Vercel Eve
```bash
-npm install @upstash/agentkit-eve @upstash/redis
-# only if you use the sandbox backend:
-npm install @upstash/box
+npm install @upstash/agentkit-eve-extension
+```
+
+One file under `agent/extensions/` mounts everything. Every config field is optional, and the smallest
+mount gives the model long-term memory:
+
+```ts
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
+
+export default agentkit();
```
-AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default.
+The filename supplies the namespace, so contributions compose as `agentkit__recall_memory`,
+`agentkit__save_memory`, and so on. The extension also merges a short instructions fragment into your
+system prompt telling the model when to save and recall.
+
+The three feature groups below are independent of each other. Memory holds facts the model decided to
+keep. Chat history is the transcript of what was said. Search is retrieval over documents you write
+into your own index. Each has its own Redis keyspace and its own search index.
-## How to add memory tools to Vercel Eve
+### How to add memory to Vercel Eve
-Long-term memory the model reads and writes itself: `recall_memory` and `save_memory`, one file each.
+Long-term memory the model reads and writes itself. A bare mount already has it; the `memory` field
+only tunes recall:
```ts
-// agent/tools/recall_memory.ts
-import { defineMemoryRecallTool } from "@upstash/agentkit-eve";
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineMemoryRecallTool({
- userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+export default agentkit({
+ memory: { topK: 5, minScore: 1 },
});
```
+
+ * `agentkit__recall_memory` — searches the user's memories; called with no `query` it returns all of them.
+ * `agentkit__save_memory` — stores one durable fact about the user.
+ * `memory.topK` — max memories a recall returns.
+ * `memory.minScore` — relevance floor. Scores are unbounded BM25 values, not `[0,1]`.
+
+ `userId` is the only tenant boundary. It defaults to Eve's verified session auth
+ (`auth.current?.principalId`, then `auth.initiator?.principalId`, then the session id), so
+ configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) if you want the
+ principal to be trustworthy. You can also set it to a string, which puts every caller in one shared
+ scope, or derive it per call:
+
+ ```ts
+ export default agentkit({
+ userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+ });
+ ```
+
+ Memories are stored at `agentkit:memory::`.
+
+
+### How to add searchable chat history to Vercel Eve
+
+`chatHistory: true` persists every user and assistant message to Redis as the session streams, and
+gives the model two tools over that store. A user can then ask about something settled in a previous
+conversation:
+
```ts
-// agent/tools/save_memory.ts
-import { defineMemorySaveTool } from "@upstash/agentkit-eve";
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineMemorySaveTool({
- userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
-});
+export default agentkit({ chatHistory: true });
```
-
- * **`userId`** _(required)_ — a string, or `(input, ctx) => string`.
- * `topK` — max memories `recall` returns.
- * `minScore` — BM25 relevance floor.
- * `redis` — defaults to `Redis.fromEnv()`.
+* `agentkit__search_chat_history` runs a `$smart` (typo-tolerant) search over what was said and returns
+ the matching chats as summaries: `sessionId`, `title`, `updatedAt`, `messageCount`, `score`. The
+ current conversation is excluded, since it's already in context.
+* `agentkit__read_chat_history` reads one of those chats back by `sessionId`, newest messages last.
+
+Both tools take `userId` from the session, so the model cannot widen a lookup past the current user's
+own transcripts.
+
+
+ Pass an object in place of `true` to tune storage:
+
+ * `chatHistory.prefix` — base key prefix (default `agentkit:chat`).
+ * `chatHistory.indexName` — [Redis Search](/docs/redis/search/introduction) index name (defaults to the
+ identifier-safe `prefix`).
+ * `chatHistory.ttlSeconds` — per-chat TTL. Omit for no expiry.
- `userId` is the only tenant boundary (required, non-empty, no `:`). Derive it from Eve's **verified
- session auth** — `ctx.session.auth.current?.principalId` — not from anything the client supplies.
- Configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) so `principalId`
- is trustworthy; the `?? ctx.session.id` fallback only applies to unauthenticated requests. Memories
- are stored at `agentkit:memory::`.
+ Each session is one JSON document at `agentkit:chat::`, holding the raw transcript
+ plus `$smart`-indexed user and model text. A search returns summaries, and a read is capped at 50
+ messages per call with a `truncated` flag, so neither can flood the context window.
+
+ Your own code can read the same store with `ChatHistory` from `@upstash/agentkit-sdk`
+ (`listChats` / `searchChats` / `getChat`), which is how you'd build a history sidebar or run evals
+ over past sessions. Redis is the durable record here, since Eve's own workflow store is pruned after
+ a run completes.
+
+ These tools look history up on demand. They don't resume a session: Eve does that through its own
+ session cursor.
-## How to add RAG to Vercel Eve
+### How to add RAG to Vercel Eve
-`search` / `aggregate` / `count` Eve tools over an Upstash Redis Search index. It is the counterpart to the
-[AI SDK adapter's](/docs/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk)
-`createSearchTools`. Descriptions are generated from your schema.
+Point the extension at an [Upstash Redis Search](/docs/redis/search/introduction) index and the model gets
+`search`, `search_aggregate`, and `search_count` tools over it. You build the schema with `s` from
+`@upstash/redis`, so your mount file imports it. Add the package to your app:
+
+```bash
+npm install @upstash/redis
+```
```ts
-// agent/tools/search_books.ts
+// agent/extensions/agentkit.ts
import { s } from "@upstash/redis";
-import { defineSearchTools } from "@upstash/agentkit-eve";
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineSearchTools({
- schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
- indexName: "books",
-}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count
+export default agentkit({
+ search: {
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+ },
+});
```
-
- * **`schema`** _(required)_ — built with `s` from `@upstash/redis`.
- * `indexName` — defaults to `"agentkit:search"`; ties all three tools to one index.
- * `prefix` — key prefix for indexed JSON docs (defaults to `":"`).
- * `defaultLimit` — default page size for `search` (10).
- * `redis` — defaults to `Redis.fromEnv()`.
+
+ * **`search.schema`** _(required)_ — built with `s` from `@upstash/redis`.
+ * `search.indexName` — defaults to `"agentkit:search"`; ties all three tools to one index.
+ * `search.prefix` — key prefix for indexed JSON docs (defaults to `":"`).
+ * `search.defaultLimit` — default page size for `search` (10).
- Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member
- you want — repeat the same `schema` + `indexName` across `search_books.ts` / `aggregate_books.ts` /
- `count_books.ts`. The index is created reactively on first use, and each returned tool is already
- `defineTool`-branded.
+ Tool descriptions are generated from your schema (field names, types, and the filter operators that
+ apply to each), so the model learns the index without any prompt text from you. You write the
+ documents yourself with `redis.json.set` under the prefix, and the index is created on first read.
+
+ Omit `search` and these three tools don't exist at all. Like the chat-history tools, they resolve at
+ session start, which is why they don't appear in a static tool listing.
+
+
+### Extension configuration reference
+
+```ts
+// agent/extensions/agentkit.ts
+import { s } from "@upstash/redis";
+import agentkit from "@upstash/agentkit-eve-extension";
+
+export default agentkit({
+ // optional: string, or (ctx) => string. Defaults to the verified principal, then the session id.
+ userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+ // optional: an explicit client; defaults to Redis.fromEnv()
+ // redis: new Redis({ url, token }),
+ // optional: tune memory recall
+ memory: { topK: 5, minScore: 1 },
+ // optional: omit and the search tools don't exist
+ search: {
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+ prefix: "books:", // optional
+ defaultLimit: 10, // optional
+ },
+ // optional: off by default. `true`, or an object to tune storage
+ chatHistory: { ttlSeconds: 60 * 60 * 24 * 30 },
+});
+```
+
+
+ Mount as a directory and override a slot by filename. This is how you drop a tool you don't want,
+ for example to capture chat history without letting the model read it back:
+
+ ```
+ agent/extensions/agentkit/
+ extension.ts # the mount: export default agentkit({ ... })
+ tools/read_chat_history.ts # your override for agentkit__read_chat_history
+ ```
+
+ ```ts
+ // agent/extensions/agentkit/tools/read_chat_history.ts
+ import { disableTool } from "eve/tools";
+
+ export default disableTool();
+ ```
+
+ You can also re-define the memory tools, say to gate saves behind approval, by importing them from
+ `@upstash/agentkit-eve-extension/tools` and spreading them into your own `defineTool`.
-## How to add rate limiting to Vercel Eve
+
+ `@upstash/agentkit-eve` below offers memory and RAG as standalone tool files too. Use those when you
+ want to configure one tool at a time, and use the package alongside the extension for rate limiting
+ and sandboxes.
+
+
+## The Eve package: rate limiting, sandboxes, and tool files
-A ready `AuthFn` that throttles inbound requests. Drop it into your channel's
+Everything in this part comes from `@upstash/agentkit-eve`, written as individual `agent/` files.
+Rate limiting and the sandbox backend live here because an extension cannot contribute a channel or
+a sandbox.
+
+### How to add rate limiting to Vercel Eve
+
+```bash
+npm install @upstash/agentkit-eve @upstash/redis
+```
+
+`createRateLimitAuth` is a ready `AuthFn` that throttles inbound requests. Drop it into your channel's
[auth walk](https://eve.dev/docs/guides/auth-and-route-protection) ahead of your real authenticators.
```ts
@@ -21905,12 +22040,16 @@ export default eveChannel({
-## How to add a sandbox to Vercel Eve
+### How to add a sandbox to Vercel Eve
A drop-in replacement for Eve's `vercel()` backend, powered by
[Upstash Box](https://github.com/upstash/box). Swap the import and keep the rest of your
[sandbox file](https://eve.dev/docs/sandbox) the same.
+```bash
+npm install @upstash/box
+```
+
```ts
// agent/sandbox.ts
import { defineSandbox } from "eve/sandbox";
@@ -21931,16 +22070,17 @@ export default defineSandbox({
- `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim — whatever you'd pass to
+ `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim, meaning whatever you'd pass to
`Box.create({...})`: `runtime`, `size`, `apiKey` (defaults to `UPSTASH_BOX_API_KEY`), `keepAlive`,
- `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, … — plus an optional `redis` (defaults to
- `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below). `@upstash/box` is an
- optional peer dependency — only needed when you import `@upstash/agentkit-eve/sandbox`.
+ `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, and so on. It also takes an optional
+ `redis` (defaults to `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below).
+ `@upstash/box` is an optional peer dependency, needed only when you import
+ `@upstash/agentkit-eve/sandbox`.
The sandbox runs untrusted, model-generated code, so open egress would mean SSRF / data
- exfiltration / reaching your own infrastructure from inside the box. Open it per-session — in
- `bootstrap`'s `use(...)` or the session `use(...)` — never as a config knob. Note that `env` passed
+ exfiltration / reaching your own infrastructure from inside the box. Open it per-session, in
+ `bootstrap`'s `use(...)` or the session `use(...)`, and never as a config knob. Note that `env` passed
to `upstash({ env })` is readable by code running in the box; don't pass secrets you wouldn't want
it to see.
@@ -21980,19 +22120,19 @@ export default defineSandbox({
```
- **Reuse** — Eve re-opens a session several times per turn; the backend reattaches to the same Box
+ **Reuse.** Eve re-opens a session several times per turn, and the backend reattaches to the same Box
instead of creating a new one each time. Boxes default to Box's pause-based idle lifecycle
- (`keepAlive: false`) — auto-paused when idle, resumed on reattach, reaped by Box. Pass
+ (`keepAlive: false`): auto-paused when idle, resumed on reattach, reaped by Box. Pass
`keepAlive: true` only for an always-running box you manage yourself.
- **Template registry** — Eve builds your template (seed files + `bootstrap`) at build/startup, but
+ **Template registry.** Eve builds your template (seed files + `bootstrap`) at build/startup, but
session creation runs per request in a different process, so the snapshot id is stored in a durable
Redis registry (`redis`, defaulting to `Redis.fromEnv()`). Eve roots its tools at `/workspace` while
a Box session lives at `/workspace/home`; the backend bridges the two automatically.
-## How to cache tools in Vercel Eve
+### How to cache tools in Vercel Eve
Like Eve's `defineTool`, but the `execute` result is memoized in Redis.
@@ -22020,23 +22160,82 @@ export default defineCachedTool({
Keys are `agentkit:toolCache:::`.
+### Memory and RAG as individual tool files
+
+The same two features the extension mounts, written as standalone `agent/tools/` files. Use these when
+you want to configure each tool on its own.
+
+Memory takes one file per tool:
+
+```ts
+// agent/tools/recall_memory.ts
+import { defineMemoryRecallTool } from "@upstash/agentkit-eve";
+
+export default defineMemoryRecallTool({
+ userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+});
+```
+
+```ts
+// agent/tools/save_memory.ts
+import { defineMemorySaveTool } from "@upstash/agentkit-eve";
+
+export default defineMemorySaveTool({
+ userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+});
+```
+
+RAG is `defineSearchTools`, the counterpart to the
+[AI SDK adapter's](/docs/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk) `createSearchTools`:
+
+```ts
+// agent/tools/search_books.ts
+import { s } from "@upstash/redis";
+import { defineSearchTools } from "@upstash/agentkit-eve";
+
+export default defineSearchTools({
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count
+```
+
+
+ `defineMemoryRecallTool` / `defineMemorySaveTool` take a required `userId` (string or
+ `(input, ctx) => string`), plus `topK`, `minScore`, and `redis`. `defineSearchTools` takes a required
+ `schema`, plus `indexName`, `prefix`, `defaultLimit`, and `redis`.
+
+ Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member
+ you want, repeating the same `schema` and `indexName` across `search_books.ts`, `aggregate_books.ts`,
+ and `count_books.ts`. The index is created on first use, and every returned tool is already
+ `defineTool`-branded.
+
+ This package has no chat history. It comes from the extension, or from `ChatHistory` in
+ `@upstash/agentkit-sdk` if you're writing the code yourself.
+
+
## Working with Eve's `agent/` files
-Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it — it
-does **not** include shared `agent/`-source modules (e.g. a `agent/lib/redis.ts`). So inside `agent/`:
+Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it. It
+does **not** include shared `agent/`-source modules such as an `agent/lib/redis.ts`. So inside `agent/`:
* Import only from packages, never from other `agent/` files.
-* Lean on the defaults — **`redis` defaults to `Redis.fromEnv()`** in every helper, so you almost never pass it.
-* Repeat config (schema, names) per file rather than sharing a module.
+* Lean on the defaults. `redis` falls back to `Redis.fromEnv()` in every helper, so you almost never pass it.
+* Repeat config (schema, names) in each file instead of sharing a module.
+
+Shared app code, like a seeder a page calls, belongs in your project `lib/` and is imported by the app,
+not by `agent/` files. Extensions are exempt from all of this. The extension package ships as one
+compiled unit, which is why its whole configuration fits in a single mount file.
-Shared app code (e.g. a seeder a page calls) lives in your project `lib/`, imported by the app — not by
-`agent/` files.
+## How to run the Vercel Eve example apps
-## How to run the Vercel Eve example app
+Two complete `eve` apps live in the AgentKit repo:
-A complete `eve` agent app (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox,
-with a chat UI that renders tool calls inline) lives in
-[`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo).
+* [`examples/eve-extension-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-extension-demo)
+ is a minimal agent whose whole configuration is one extension mount, with memory, chat history, and
+ book search turned on.
+* [`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo) uses the
+ file-by-file package (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox),
+ with a chat UI that renders tool calls inline.
@@ -22047,25 +22246,25 @@ with a chat UI that renders tool calls inline) lives in
-# Memory, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework
+# Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework
Source: https://upstash.com/docs/redis/sdks/agentkit/eve
[Upstash AgentKit](https://github.com/upstash/agentkit) builds AI agents on Upstash Redis: memory,
conversation history, caching, and RAG, with no separate vector database. The semantic features run on
[Upstash Redis Search](/docs/redis/search/introduction) and its `$smart` fuzzy operator.
-`@upstash/agentkit-eve` brings AgentKit to **Eve, the Vercel agent framework**. You drop these into your
-`agent/` tree:
+Two packages bring AgentKit to **Eve, the Vercel agent framework**. They work together in one agent:
-| Import | Feature |
+| Package | What it is |
| --- | --- |
-| `defineMemoryRecallTool` / `defineMemorySaveTool` | Long-term memory tools the model reads and writes. |
-| `defineSearchTools` | `search` / `aggregate` / `count` tools over a Redis Search index (this is how you do RAG). |
-| `createRateLimitAuth` | A rate-limit gate for your channel's `auth` walk. |
-| `upstash` (`@upstash/agentkit-eve/sandbox`) | Upstash Box sandbox backend for `defineSandbox`. |
-| `defineCachedTool` | A `defineTool` whose result is memoized in Redis. |
+| `@upstash/agentkit-eve-extension` | An [Eve extension](https://eve.dev/docs/extensions): one mount file adds memory, searchable chat history, and RAG. |
+| `@upstash/agentkit-eve` | Per-file building blocks, plus the two things an extension cannot contribute: rate limiting and a sandbox backend. |
+
+Mount the extension for the bundled setup. Add the package when you need a rate-limit gate, an Upstash
+Box sandbox, or control over an individual tool file.
-Start from an eve project. Scaffold one (it installs `eve` and an AI-SDK provider for you):
+Start from an eve project (0.25.2 or later). Scaffold one, which installs `eve` and an AI-SDK provider
+for you:
```bash
npx eve@latest init my-agent
@@ -22073,84 +22272,219 @@ npx eve@latest init my-agent
npx eve@latest init my-agent --channel-web-nextjs
```
-Then add the AgentKit packages:
+
+ AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default.
+ Pass your own `@upstash/redis` client as `redis` to any helper to override.
+
+
+## The Eve extension: memory, chat history, and RAG
+
+Everything in this part comes from `@upstash/agentkit-eve-extension`, configured in one mount file.
+
+### How to mount the Upstash extension in Vercel Eve
```bash
-npm install @upstash/agentkit-eve @upstash/redis
-# only if you use the sandbox backend:
-npm install @upstash/box
+npm install @upstash/agentkit-eve-extension
+```
+
+One file under `agent/extensions/` mounts everything. Every config field is optional, and the smallest
+mount gives the model long-term memory:
+
+```ts
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
+
+export default agentkit();
```
-AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default.
+The filename supplies the namespace, so contributions compose as `agentkit__recall_memory`,
+`agentkit__save_memory`, and so on. The extension also merges a short instructions fragment into your
+system prompt telling the model when to save and recall.
+
+The three feature groups below are independent of each other. Memory holds facts the model decided to
+keep. Chat history is the transcript of what was said. Search is retrieval over documents you write
+into your own index. Each has its own Redis keyspace and its own search index.
-## How to add memory tools to Vercel Eve
+### How to add memory to Vercel Eve
-Long-term memory the model reads and writes itself: `recall_memory` and `save_memory`, one file each.
+Long-term memory the model reads and writes itself. A bare mount already has it; the `memory` field
+only tunes recall:
```ts
-// agent/tools/recall_memory.ts
-import { defineMemoryRecallTool } from "@upstash/agentkit-eve";
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineMemoryRecallTool({
- userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+export default agentkit({
+ memory: { topK: 5, minScore: 1 },
});
```
+
+ * `agentkit__recall_memory` — searches the user's memories; called with no `query` it returns all of them.
+ * `agentkit__save_memory` — stores one durable fact about the user.
+ * `memory.topK` — max memories a recall returns.
+ * `memory.minScore` — relevance floor. Scores are unbounded BM25 values, not `[0,1]`.
+
+ `userId` is the only tenant boundary. It defaults to Eve's verified session auth
+ (`auth.current?.principalId`, then `auth.initiator?.principalId`, then the session id), so
+ configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) if you want the
+ principal to be trustworthy. You can also set it to a string, which puts every caller in one shared
+ scope, or derive it per call:
+
+ ```ts
+ export default agentkit({
+ userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+ });
+ ```
+
+ Memories are stored at `agentkit:memory::`.
+
+
+### How to add searchable chat history to Vercel Eve
+
+`chatHistory: true` persists every user and assistant message to Redis as the session streams, and
+gives the model two tools over that store. A user can then ask about something settled in a previous
+conversation:
+
```ts
-// agent/tools/save_memory.ts
-import { defineMemorySaveTool } from "@upstash/agentkit-eve";
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineMemorySaveTool({
- userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
-});
+export default agentkit({ chatHistory: true });
```
-
- * **`userId`** _(required)_ — a string, or `(input, ctx) => string`.
- * `topK` — max memories `recall` returns.
- * `minScore` — BM25 relevance floor.
- * `redis` — defaults to `Redis.fromEnv()`.
+* `agentkit__search_chat_history` runs a `$smart` (typo-tolerant) search over what was said and returns
+ the matching chats as summaries: `sessionId`, `title`, `updatedAt`, `messageCount`, `score`. The
+ current conversation is excluded, since it's already in context.
+* `agentkit__read_chat_history` reads one of those chats back by `sessionId`, newest messages last.
+
+Both tools take `userId` from the session, so the model cannot widen a lookup past the current user's
+own transcripts.
+
+
+ Pass an object in place of `true` to tune storage:
+
+ * `chatHistory.prefix` — base key prefix (default `agentkit:chat`).
+ * `chatHistory.indexName` — [Redis Search](/docs/redis/search/introduction) index name (defaults to the
+ identifier-safe `prefix`).
+ * `chatHistory.ttlSeconds` — per-chat TTL. Omit for no expiry.
- `userId` is the only tenant boundary (required, non-empty, no `:`). Derive it from Eve's **verified
- session auth** — `ctx.session.auth.current?.principalId` — not from anything the client supplies.
- Configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) so `principalId`
- is trustworthy; the `?? ctx.session.id` fallback only applies to unauthenticated requests. Memories
- are stored at `agentkit:memory::`.
+ Each session is one JSON document at `agentkit:chat::`, holding the raw transcript
+ plus `$smart`-indexed user and model text. A search returns summaries, and a read is capped at 50
+ messages per call with a `truncated` flag, so neither can flood the context window.
+
+ Your own code can read the same store with `ChatHistory` from `@upstash/agentkit-sdk`
+ (`listChats` / `searchChats` / `getChat`), which is how you'd build a history sidebar or run evals
+ over past sessions. Redis is the durable record here, since Eve's own workflow store is pruned after
+ a run completes.
+
+ These tools look history up on demand. They don't resume a session: Eve does that through its own
+ session cursor.
-## How to add RAG to Vercel Eve
+### How to add RAG to Vercel Eve
-`search` / `aggregate` / `count` Eve tools over an Upstash Redis Search index. It is the counterpart to the
-[AI SDK adapter's](/docs/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk)
-`createSearchTools`. Descriptions are generated from your schema.
+Point the extension at an [Upstash Redis Search](/docs/redis/search/introduction) index and the model gets
+`search`, `search_aggregate`, and `search_count` tools over it. You build the schema with `s` from
+`@upstash/redis`, so your mount file imports it. Add the package to your app:
+
+```bash
+npm install @upstash/redis
+```
```ts
-// agent/tools/search_books.ts
+// agent/extensions/agentkit.ts
import { s } from "@upstash/redis";
-import { defineSearchTools } from "@upstash/agentkit-eve";
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineSearchTools({
- schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
- indexName: "books",
-}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count
+export default agentkit({
+ search: {
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+ },
+});
```
-
- * **`schema`** _(required)_ — built with `s` from `@upstash/redis`.
- * `indexName` — defaults to `"agentkit:search"`; ties all three tools to one index.
- * `prefix` — key prefix for indexed JSON docs (defaults to `":"`).
- * `defaultLimit` — default page size for `search` (10).
- * `redis` — defaults to `Redis.fromEnv()`.
+
+ * **`search.schema`** _(required)_ — built with `s` from `@upstash/redis`.
+ * `search.indexName` — defaults to `"agentkit:search"`; ties all three tools to one index.
+ * `search.prefix` — key prefix for indexed JSON docs (defaults to `":"`).
+ * `search.defaultLimit` — default page size for `search` (10).
- Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member
- you want — repeat the same `schema` + `indexName` across `search_books.ts` / `aggregate_books.ts` /
- `count_books.ts`. The index is created reactively on first use, and each returned tool is already
- `defineTool`-branded.
+ Tool descriptions are generated from your schema (field names, types, and the filter operators that
+ apply to each), so the model learns the index without any prompt text from you. You write the
+ documents yourself with `redis.json.set` under the prefix, and the index is created on first read.
+
+ Omit `search` and these three tools don't exist at all. Like the chat-history tools, they resolve at
+ session start, which is why they don't appear in a static tool listing.
+
+
+### Extension configuration reference
+
+```ts
+// agent/extensions/agentkit.ts
+import { s } from "@upstash/redis";
+import agentkit from "@upstash/agentkit-eve-extension";
+
+export default agentkit({
+ // optional: string, or (ctx) => string. Defaults to the verified principal, then the session id.
+ userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+ // optional: an explicit client; defaults to Redis.fromEnv()
+ // redis: new Redis({ url, token }),
+ // optional: tune memory recall
+ memory: { topK: 5, minScore: 1 },
+ // optional: omit and the search tools don't exist
+ search: {
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+ prefix: "books:", // optional
+ defaultLimit: 10, // optional
+ },
+ // optional: off by default. `true`, or an object to tune storage
+ chatHistory: { ttlSeconds: 60 * 60 * 24 * 30 },
+});
+```
+
+
+ Mount as a directory and override a slot by filename. This is how you drop a tool you don't want,
+ for example to capture chat history without letting the model read it back:
+
+ ```
+ agent/extensions/agentkit/
+ extension.ts # the mount: export default agentkit({ ... })
+ tools/read_chat_history.ts # your override for agentkit__read_chat_history
+ ```
+
+ ```ts
+ // agent/extensions/agentkit/tools/read_chat_history.ts
+ import { disableTool } from "eve/tools";
+
+ export default disableTool();
+ ```
+
+ You can also re-define the memory tools, say to gate saves behind approval, by importing them from
+ `@upstash/agentkit-eve-extension/tools` and spreading them into your own `defineTool`.
-## How to add rate limiting to Vercel Eve
+
+ `@upstash/agentkit-eve` below offers memory and RAG as standalone tool files too. Use those when you
+ want to configure one tool at a time, and use the package alongside the extension for rate limiting
+ and sandboxes.
+
+
+## The Eve package: rate limiting, sandboxes, and tool files
-A ready `AuthFn` that throttles inbound requests. Drop it into your channel's
+Everything in this part comes from `@upstash/agentkit-eve`, written as individual `agent/` files.
+Rate limiting and the sandbox backend live here because an extension cannot contribute a channel or
+a sandbox.
+
+### How to add rate limiting to Vercel Eve
+
+```bash
+npm install @upstash/agentkit-eve @upstash/redis
+```
+
+`createRateLimitAuth` is a ready `AuthFn` that throttles inbound requests. Drop it into your channel's
[auth walk](https://eve.dev/docs/guides/auth-and-route-protection) ahead of your real authenticators.
```ts
@@ -22193,12 +22527,16 @@ export default eveChannel({
-## How to add a sandbox to Vercel Eve
+### How to add a sandbox to Vercel Eve
A drop-in replacement for Eve's `vercel()` backend, powered by
[Upstash Box](https://github.com/upstash/box). Swap the import and keep the rest of your
[sandbox file](https://eve.dev/docs/sandbox) the same.
+```bash
+npm install @upstash/box
+```
+
```ts
// agent/sandbox.ts
import { defineSandbox } from "eve/sandbox";
@@ -22219,16 +22557,17 @@ export default defineSandbox({
- `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim — whatever you'd pass to
+ `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim, meaning whatever you'd pass to
`Box.create({...})`: `runtime`, `size`, `apiKey` (defaults to `UPSTASH_BOX_API_KEY`), `keepAlive`,
- `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, … — plus an optional `redis` (defaults to
- `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below). `@upstash/box` is an
- optional peer dependency — only needed when you import `@upstash/agentkit-eve/sandbox`.
+ `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, and so on. It also takes an optional
+ `redis` (defaults to `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below).
+ `@upstash/box` is an optional peer dependency, needed only when you import
+ `@upstash/agentkit-eve/sandbox`.
The sandbox runs untrusted, model-generated code, so open egress would mean SSRF / data
- exfiltration / reaching your own infrastructure from inside the box. Open it per-session — in
- `bootstrap`'s `use(...)` or the session `use(...)` — never as a config knob. Note that `env` passed
+ exfiltration / reaching your own infrastructure from inside the box. Open it per-session, in
+ `bootstrap`'s `use(...)` or the session `use(...)`, and never as a config knob. Note that `env` passed
to `upstash({ env })` is readable by code running in the box; don't pass secrets you wouldn't want
it to see.
@@ -22268,19 +22607,19 @@ export default defineSandbox({
```
- **Reuse** — Eve re-opens a session several times per turn; the backend reattaches to the same Box
+ **Reuse.** Eve re-opens a session several times per turn, and the backend reattaches to the same Box
instead of creating a new one each time. Boxes default to Box's pause-based idle lifecycle
- (`keepAlive: false`) — auto-paused when idle, resumed on reattach, reaped by Box. Pass
+ (`keepAlive: false`): auto-paused when idle, resumed on reattach, reaped by Box. Pass
`keepAlive: true` only for an always-running box you manage yourself.
- **Template registry** — Eve builds your template (seed files + `bootstrap`) at build/startup, but
+ **Template registry.** Eve builds your template (seed files + `bootstrap`) at build/startup, but
session creation runs per request in a different process, so the snapshot id is stored in a durable
Redis registry (`redis`, defaulting to `Redis.fromEnv()`). Eve roots its tools at `/workspace` while
a Box session lives at `/workspace/home`; the backend bridges the two automatically.
-## How to cache tools in Vercel Eve
+### How to cache tools in Vercel Eve
Like Eve's `defineTool`, but the `execute` result is memoized in Redis.
@@ -22308,23 +22647,82 @@ export default defineCachedTool({
Keys are `agentkit:toolCache:::`.
+### Memory and RAG as individual tool files
+
+The same two features the extension mounts, written as standalone `agent/tools/` files. Use these when
+you want to configure each tool on its own.
+
+Memory takes one file per tool:
+
+```ts
+// agent/tools/recall_memory.ts
+import { defineMemoryRecallTool } from "@upstash/agentkit-eve";
+
+export default defineMemoryRecallTool({
+ userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+});
+```
+
+```ts
+// agent/tools/save_memory.ts
+import { defineMemorySaveTool } from "@upstash/agentkit-eve";
+
+export default defineMemorySaveTool({
+ userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+});
+```
+
+RAG is `defineSearchTools`, the counterpart to the
+[AI SDK adapter's](/docs/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk) `createSearchTools`:
+
+```ts
+// agent/tools/search_books.ts
+import { s } from "@upstash/redis";
+import { defineSearchTools } from "@upstash/agentkit-eve";
+
+export default defineSearchTools({
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count
+```
+
+
+ `defineMemoryRecallTool` / `defineMemorySaveTool` take a required `userId` (string or
+ `(input, ctx) => string`), plus `topK`, `minScore`, and `redis`. `defineSearchTools` takes a required
+ `schema`, plus `indexName`, `prefix`, `defaultLimit`, and `redis`.
+
+ Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member
+ you want, repeating the same `schema` and `indexName` across `search_books.ts`, `aggregate_books.ts`,
+ and `count_books.ts`. The index is created on first use, and every returned tool is already
+ `defineTool`-branded.
+
+ This package has no chat history. It comes from the extension, or from `ChatHistory` in
+ `@upstash/agentkit-sdk` if you're writing the code yourself.
+
+
## Working with Eve's `agent/` files
-Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it — it
-does **not** include shared `agent/`-source modules (e.g. a `agent/lib/redis.ts`). So inside `agent/`:
+Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it. It
+does **not** include shared `agent/`-source modules such as an `agent/lib/redis.ts`. So inside `agent/`:
* Import only from packages, never from other `agent/` files.
-* Lean on the defaults — **`redis` defaults to `Redis.fromEnv()`** in every helper, so you almost never pass it.
-* Repeat config (schema, names) per file rather than sharing a module.
+* Lean on the defaults. `redis` falls back to `Redis.fromEnv()` in every helper, so you almost never pass it.
+* Repeat config (schema, names) in each file instead of sharing a module.
+
+Shared app code, like a seeder a page calls, belongs in your project `lib/` and is imported by the app,
+not by `agent/` files. Extensions are exempt from all of this. The extension package ships as one
+compiled unit, which is why its whole configuration fits in a single mount file.
-Shared app code (e.g. a seeder a page calls) lives in your project `lib/`, imported by the app — not by
-`agent/` files.
+## How to run the Vercel Eve example apps
-## How to run the Vercel Eve example app
+Two complete `eve` apps live in the AgentKit repo:
-A complete `eve` agent app (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox,
-with a chat UI that renders tool calls inline) lives in
-[`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo).
+* [`examples/eve-extension-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-extension-demo)
+ is a minimal agent whose whole configuration is one extension mount, with memory, chat history, and
+ book search turned on.
+* [`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo) uses the
+ file-by-file package (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox),
+ with a chat UI that renders tool calls inline.
diff --git a/llms.txt b/llms.txt
index a312214b..9d996ff1 100644
--- a/llms.txt
+++ b/llms.txt
@@ -357,8 +357,8 @@
- [Agent Analytics](https://upstash.com/docs/redis/sdks/agent-analytics.md)
- [Vercel AI SDK Memory, RAG & Chat History with Redis](https://upstash.com/docs/redis/sdks/agentkit/ai-sdk.md): Add long-term memory, RAG, and chat history to the Vercel AI SDK with Upstash Redis — drop-in tools for generateText and streamText, no separate vector database.
- [Vercel AI SDK Memory, RAG & Chat History with Redis](https://upstash.com/docs/redis/sdks/agentkit/ai-sdk.md): Add long-term memory, RAG, and chat history to the Vercel AI SDK with Upstash Redis — drop-in tools for generateText and streamText, no separate vector database.
-- [Memory, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework](https://upstash.com/docs/redis/sdks/agentkit/eve.md): Add long-term memory, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database.
-- [Memory, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework](https://upstash.com/docs/redis/sdks/agentkit/eve.md): Add long-term memory, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database.
+- [Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework](https://upstash.com/docs/redis/sdks/agentkit/eve.md): Add long-term memory, searchable chat history, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database.
+- [Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework](https://upstash.com/docs/redis/sdks/agentkit/eve.md): Add long-term memory, searchable chat history, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database.
- [Upstash Redis MCP](https://upstash.com/docs/redis/sdks/mcp.md)
- [ECHO](https://upstash.com/docs/redis/sdks/py/commands/auth/echo.md)
- [PING](https://upstash.com/docs/redis/sdks/py/commands/auth/ping.md): Send a ping to the server and get a response if the server is alive.
diff --git a/redis/sdks/agentkit/eve.mdx b/redis/sdks/agentkit/eve.mdx
index e94d1d45..312338d6 100644
--- a/redis/sdks/agentkit/eve.mdx
+++ b/redis/sdks/agentkit/eve.mdx
@@ -1,25 +1,25 @@
---
-title: "Memory, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework"
+title: "Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework"
sidebarTitle: "Vercel Eve"
-description: "Add long-term memory, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database."
+description: "Add long-term memory, searchable chat history, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database."
---
[Upstash AgentKit](https://github.com/upstash/agentkit) builds AI agents on Upstash Redis: memory,
conversation history, caching, and RAG, with no separate vector database. The semantic features run on
[Upstash Redis Search](/redis/search/introduction) and its `$smart` fuzzy operator.
-`@upstash/agentkit-eve` brings AgentKit to **Eve, the Vercel agent framework**. You drop these into your
-`agent/` tree:
+Two packages bring AgentKit to **Eve, the Vercel agent framework**. They work together in one agent:
-| Import | Feature |
+| Package | What it is |
| --- | --- |
-| `defineMemoryRecallTool` / `defineMemorySaveTool` | Long-term memory tools the model reads and writes. |
-| `defineSearchTools` | `search` / `aggregate` / `count` tools over a Redis Search index (this is how you do RAG). |
-| `createRateLimitAuth` | A rate-limit gate for your channel's `auth` walk. |
-| `upstash` (`@upstash/agentkit-eve/sandbox`) | Upstash Box sandbox backend for `defineSandbox`. |
-| `defineCachedTool` | A `defineTool` whose result is memoized in Redis. |
+| `@upstash/agentkit-eve-extension` | An [Eve extension](https://eve.dev/docs/extensions): one mount file adds memory, searchable chat history, and RAG. |
+| `@upstash/agentkit-eve` | Per-file building blocks, plus the two things an extension cannot contribute: rate limiting and a sandbox backend. |
-Start from an eve project. Scaffold one (it installs `eve` and an AI-SDK provider for you):
+Mount the extension for the bundled setup. Add the package when you need a rate-limit gate, an Upstash
+Box sandbox, or control over an individual tool file.
+
+Start from an eve project (0.25.2 or later). Scaffold one, which installs `eve` and an AI-SDK provider
+for you:
```bash
npx eve@latest init my-agent
@@ -27,84 +27,219 @@ npx eve@latest init my-agent
npx eve@latest init my-agent --channel-web-nextjs
```
-Then add the AgentKit packages:
+
+ AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default.
+ Pass your own `@upstash/redis` client as `redis` to any helper to override.
+
+
+## The Eve extension: memory, chat history, and RAG
+
+Everything in this part comes from `@upstash/agentkit-eve-extension`, configured in one mount file.
+
+### How to mount the Upstash extension in Vercel Eve
```bash
-npm install @upstash/agentkit-eve @upstash/redis
-# only if you use the sandbox backend:
-npm install @upstash/box
+npm install @upstash/agentkit-eve-extension
+```
+
+One file under `agent/extensions/` mounts everything. Every config field is optional, and the smallest
+mount gives the model long-term memory:
+
+```ts
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
+
+export default agentkit();
```
-AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default.
+The filename supplies the namespace, so contributions compose as `agentkit__recall_memory`,
+`agentkit__save_memory`, and so on. The extension also merges a short instructions fragment into your
+system prompt telling the model when to save and recall.
-## How to add memory tools to Vercel Eve
+The three feature groups below are independent of each other. Memory holds facts the model decided to
+keep. Chat history is the transcript of what was said. Search is retrieval over documents you write
+into your own index. Each has its own Redis keyspace and its own search index.
-Long-term memory the model reads and writes itself: `recall_memory` and `save_memory`, one file each.
+### How to add memory to Vercel Eve
+
+Long-term memory the model reads and writes itself. A bare mount already has it; the `memory` field
+only tunes recall:
```ts
-// agent/tools/recall_memory.ts
-import { defineMemoryRecallTool } from "@upstash/agentkit-eve";
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineMemoryRecallTool({
- userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+export default agentkit({
+ memory: { topK: 5, minScore: 1 },
});
```
+
+ - `agentkit__recall_memory` — searches the user's memories; called with no `query` it returns all of them.
+ - `agentkit__save_memory` — stores one durable fact about the user.
+ - `memory.topK` — max memories a recall returns.
+ - `memory.minScore` — relevance floor. Scores are unbounded BM25 values, not `[0,1]`.
+
+ `userId` is the only tenant boundary. It defaults to Eve's verified session auth
+ (`auth.current?.principalId`, then `auth.initiator?.principalId`, then the session id), so
+ configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) if you want the
+ principal to be trustworthy. You can also set it to a string, which puts every caller in one shared
+ scope, or derive it per call:
+
+ ```ts
+ export default agentkit({
+ userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+ });
+ ```
+
+ Memories are stored at `agentkit:memory::`.
+
+
+### How to add searchable chat history to Vercel Eve
+
+`chatHistory: true` persists every user and assistant message to Redis as the session streams, and
+gives the model two tools over that store. A user can then ask about something settled in a previous
+conversation:
+
```ts
-// agent/tools/save_memory.ts
-import { defineMemorySaveTool } from "@upstash/agentkit-eve";
+// agent/extensions/agentkit.ts
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineMemorySaveTool({
- userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
-});
+export default agentkit({ chatHistory: true });
```
-
- - **`userId`** _(required)_ — a string, or `(input, ctx) => string`.
- - `topK` — max memories `recall` returns.
- - `minScore` — BM25 relevance floor.
- - `redis` — defaults to `Redis.fromEnv()`.
+- `agentkit__search_chat_history` runs a `$smart` (typo-tolerant) search over what was said and returns
+ the matching chats as summaries: `sessionId`, `title`, `updatedAt`, `messageCount`, `score`. The
+ current conversation is excluded, since it's already in context.
+- `agentkit__read_chat_history` reads one of those chats back by `sessionId`, newest messages last.
+
+Both tools take `userId` from the session, so the model cannot widen a lookup past the current user's
+own transcripts.
+
+
+ Pass an object in place of `true` to tune storage:
- `userId` is the only tenant boundary (required, non-empty, no `:`). Derive it from Eve's **verified
- session auth** — `ctx.session.auth.current?.principalId` — not from anything the client supplies.
- Configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) so `principalId`
- is trustworthy; the `?? ctx.session.id` fallback only applies to unauthenticated requests. Memories
- are stored at `agentkit:memory::`.
+ - `chatHistory.prefix` — base key prefix (default `agentkit:chat`).
+ - `chatHistory.indexName` — [Redis Search](/redis/search/introduction) index name (defaults to the
+ identifier-safe `prefix`).
+ - `chatHistory.ttlSeconds` — per-chat TTL. Omit for no expiry.
+
+ Each session is one JSON document at `agentkit:chat::`, holding the raw transcript
+ plus `$smart`-indexed user and model text. A search returns summaries, and a read is capped at 50
+ messages per call with a `truncated` flag, so neither can flood the context window.
+
+ Your own code can read the same store with `ChatHistory` from `@upstash/agentkit-sdk`
+ (`listChats` / `searchChats` / `getChat`), which is how you'd build a history sidebar or run evals
+ over past sessions. Redis is the durable record here, since Eve's own workflow store is pruned after
+ a run completes.
+
+ These tools look history up on demand. They don't resume a session: Eve does that through its own
+ session cursor.
-## How to add RAG to Vercel Eve
+### How to add RAG to Vercel Eve
-`search` / `aggregate` / `count` Eve tools over an Upstash Redis Search index. It is the counterpart to the
-[AI SDK adapter's](/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk)
-`createSearchTools`. Descriptions are generated from your schema.
+Point the extension at an [Upstash Redis Search](/redis/search/introduction) index and the model gets
+`search`, `search_aggregate`, and `search_count` tools over it. You build the schema with `s` from
+`@upstash/redis`, so your mount file imports it. Add the package to your app:
+
+```bash
+npm install @upstash/redis
+```
```ts
-// agent/tools/search_books.ts
+// agent/extensions/agentkit.ts
import { s } from "@upstash/redis";
-import { defineSearchTools } from "@upstash/agentkit-eve";
+import agentkit from "@upstash/agentkit-eve-extension";
-export default defineSearchTools({
- schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
- indexName: "books",
-}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count
+export default agentkit({
+ search: {
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+ },
+});
```
-
- - **`schema`** _(required)_ — built with `s` from `@upstash/redis`.
- - `indexName` — defaults to `"agentkit:search"`; ties all three tools to one index.
- - `prefix` — key prefix for indexed JSON docs (defaults to `":"`).
- - `defaultLimit` — default page size for `search` (10).
- - `redis` — defaults to `Redis.fromEnv()`.
+
+ - **`search.schema`** _(required)_ — built with `s` from `@upstash/redis`.
+ - `search.indexName` — defaults to `"agentkit:search"`; ties all three tools to one index.
+ - `search.prefix` — key prefix for indexed JSON docs (defaults to `":"`).
+ - `search.defaultLimit` — default page size for `search` (10).
- Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member
- you want — repeat the same `schema` + `indexName` across `search_books.ts` / `aggregate_books.ts` /
- `count_books.ts`. The index is created reactively on first use, and each returned tool is already
- `defineTool`-branded.
+ Tool descriptions are generated from your schema (field names, types, and the filter operators that
+ apply to each), so the model learns the index without any prompt text from you. You write the
+ documents yourself with `redis.json.set` under the prefix, and the index is created on first read.
+
+ Omit `search` and these three tools don't exist at all. Like the chat-history tools, they resolve at
+ session start, which is why they don't appear in a static tool listing.
+
+
+### Extension configuration reference
+
+```ts
+// agent/extensions/agentkit.ts
+import { s } from "@upstash/redis";
+import agentkit from "@upstash/agentkit-eve-extension";
+
+export default agentkit({
+ // optional: string, or (ctx) => string. Defaults to the verified principal, then the session id.
+ userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+ // optional: an explicit client; defaults to Redis.fromEnv()
+ // redis: new Redis({ url, token }),
+ // optional: tune memory recall
+ memory: { topK: 5, minScore: 1 },
+ // optional: omit and the search tools don't exist
+ search: {
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+ prefix: "books:", // optional
+ defaultLimit: 10, // optional
+ },
+ // optional: off by default. `true`, or an object to tune storage
+ chatHistory: { ttlSeconds: 60 * 60 * 24 * 30 },
+});
+```
+
+
+ Mount as a directory and override a slot by filename. This is how you drop a tool you don't want,
+ for example to capture chat history without letting the model read it back:
+
+ ```
+ agent/extensions/agentkit/
+ extension.ts # the mount: export default agentkit({ ... })
+ tools/read_chat_history.ts # your override for agentkit__read_chat_history
+ ```
+
+ ```ts
+ // agent/extensions/agentkit/tools/read_chat_history.ts
+ import { disableTool } from "eve/tools";
+
+ export default disableTool();
+ ```
+
+ You can also re-define the memory tools, say to gate saves behind approval, by importing them from
+ `@upstash/agentkit-eve-extension/tools` and spreading them into your own `defineTool`.
-## How to add rate limiting to Vercel Eve
+
+ `@upstash/agentkit-eve` below offers memory and RAG as standalone tool files too. Use those when you
+ want to configure one tool at a time, and use the package alongside the extension for rate limiting
+ and sandboxes.
+
+
+## The Eve package: rate limiting, sandboxes, and tool files
+
+Everything in this part comes from `@upstash/agentkit-eve`, written as individual `agent/` files.
+Rate limiting and the sandbox backend live here because an extension cannot contribute a channel or
+a sandbox.
+
+### How to add rate limiting to Vercel Eve
-A ready `AuthFn` that throttles inbound requests. Drop it into your channel's
+```bash
+npm install @upstash/agentkit-eve @upstash/redis
+```
+
+`createRateLimitAuth` is a ready `AuthFn` that throttles inbound requests. Drop it into your channel's
[auth walk](https://eve.dev/docs/guides/auth-and-route-protection) ahead of your real authenticators.
```ts
@@ -147,12 +282,16 @@ export default eveChannel({
-## How to add a sandbox to Vercel Eve
+### How to add a sandbox to Vercel Eve
A drop-in replacement for Eve's `vercel()` backend, powered by
[Upstash Box](https://github.com/upstash/box). Swap the import and keep the rest of your
[sandbox file](https://eve.dev/docs/sandbox) the same.
+```bash
+npm install @upstash/box
+```
+
```ts
// agent/sandbox.ts
import { defineSandbox } from "eve/sandbox";
@@ -173,16 +312,17 @@ export default defineSandbox({
- `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim — whatever you'd pass to
+ `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim, meaning whatever you'd pass to
`Box.create({...})`: `runtime`, `size`, `apiKey` (defaults to `UPSTASH_BOX_API_KEY`), `keepAlive`,
- `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, … — plus an optional `redis` (defaults to
- `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below). `@upstash/box` is an
- optional peer dependency — only needed when you import `@upstash/agentkit-eve/sandbox`.
+ `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, and so on. It also takes an optional
+ `redis` (defaults to `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below).
+ `@upstash/box` is an optional peer dependency, needed only when you import
+ `@upstash/agentkit-eve/sandbox`.
The sandbox runs untrusted, model-generated code, so open egress would mean SSRF / data
- exfiltration / reaching your own infrastructure from inside the box. Open it per-session — in
- `bootstrap`'s `use(...)` or the session `use(...)` — never as a config knob. Note that `env` passed
+ exfiltration / reaching your own infrastructure from inside the box. Open it per-session, in
+ `bootstrap`'s `use(...)` or the session `use(...)`, and never as a config knob. Note that `env` passed
to `upstash({ env })` is readable by code running in the box; don't pass secrets you wouldn't want
it to see.
@@ -222,19 +362,19 @@ export default defineSandbox({
```
- **Reuse** — Eve re-opens a session several times per turn; the backend reattaches to the same Box
+ **Reuse.** Eve re-opens a session several times per turn, and the backend reattaches to the same Box
instead of creating a new one each time. Boxes default to Box's pause-based idle lifecycle
- (`keepAlive: false`) — auto-paused when idle, resumed on reattach, reaped by Box. Pass
+ (`keepAlive: false`): auto-paused when idle, resumed on reattach, reaped by Box. Pass
`keepAlive: true` only for an always-running box you manage yourself.
- **Template registry** — Eve builds your template (seed files + `bootstrap`) at build/startup, but
+ **Template registry.** Eve builds your template (seed files + `bootstrap`) at build/startup, but
session creation runs per request in a different process, so the snapshot id is stored in a durable
Redis registry (`redis`, defaulting to `Redis.fromEnv()`). Eve roots its tools at `/workspace` while
a Box session lives at `/workspace/home`; the backend bridges the two automatically.
-## How to cache tools in Vercel Eve
+### How to cache tools in Vercel Eve
Like Eve's `defineTool`, but the `execute` result is memoized in Redis.
@@ -262,23 +402,82 @@ export default defineCachedTool({
Keys are `agentkit:toolCache:::`.
+### Memory and RAG as individual tool files
+
+The same two features the extension mounts, written as standalone `agent/tools/` files. Use these when
+you want to configure each tool on its own.
+
+Memory takes one file per tool:
+
+```ts
+// agent/tools/recall_memory.ts
+import { defineMemoryRecallTool } from "@upstash/agentkit-eve";
+
+export default defineMemoryRecallTool({
+ userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+});
+```
+
+```ts
+// agent/tools/save_memory.ts
+import { defineMemorySaveTool } from "@upstash/agentkit-eve";
+
+export default defineMemorySaveTool({
+ userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id,
+});
+```
+
+RAG is `defineSearchTools`, the counterpart to the
+[AI SDK adapter's](/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk) `createSearchTools`:
+
+```ts
+// agent/tools/search_books.ts
+import { s } from "@upstash/redis";
+import { defineSearchTools } from "@upstash/agentkit-eve";
+
+export default defineSearchTools({
+ schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }),
+ indexName: "books",
+}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count
+```
+
+
+ `defineMemoryRecallTool` / `defineMemorySaveTool` take a required `userId` (string or
+ `(input, ctx) => string`), plus `topK`, `minScore`, and `redis`. `defineSearchTools` takes a required
+ `schema`, plus `indexName`, `prefix`, `defaultLimit`, and `redis`.
+
+ Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member
+ you want, repeating the same `schema` and `indexName` across `search_books.ts`, `aggregate_books.ts`,
+ and `count_books.ts`. The index is created on first use, and every returned tool is already
+ `defineTool`-branded.
+
+ This package has no chat history. It comes from the extension, or from `ChatHistory` in
+ `@upstash/agentkit-sdk` if you're writing the code yourself.
+
+
## Working with Eve's `agent/` files
-Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it — it
-does **not** include shared `agent/`-source modules (e.g. a `agent/lib/redis.ts`). So inside `agent/`:
+Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it. It
+does **not** include shared `agent/`-source modules such as an `agent/lib/redis.ts`. So inside `agent/`:
- Import only from packages, never from other `agent/` files.
-- Lean on the defaults — **`redis` defaults to `Redis.fromEnv()`** in every helper, so you almost never pass it.
-- Repeat config (schema, names) per file rather than sharing a module.
+- Lean on the defaults. `redis` falls back to `Redis.fromEnv()` in every helper, so you almost never pass it.
+- Repeat config (schema, names) in each file instead of sharing a module.
+
+Shared app code, like a seeder a page calls, belongs in your project `lib/` and is imported by the app,
+not by `agent/` files. Extensions are exempt from all of this. The extension package ships as one
+compiled unit, which is why its whole configuration fits in a single mount file.
-Shared app code (e.g. a seeder a page calls) lives in your project `lib/`, imported by the app — not by
-`agent/` files.
+## How to run the Vercel Eve example apps
-## How to run the Vercel Eve example app
+Two complete `eve` apps live in the AgentKit repo:
-A complete `eve` agent app (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox,
-with a chat UI that renders tool calls inline) lives in
-[`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo).
+- [`examples/eve-extension-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-extension-demo)
+ is a minimal agent whose whole configuration is one extension mount, with memory, chat history, and
+ book search turned on.
+- [`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo) uses the
+ file-by-file package (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox),
+ with a chat UI that renders tool calls inline.