Skip to main content
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 and its $smart fuzzy operator. Two packages bring AgentKit to Eve, the Vercel agent framework. They work together in one agent: 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:
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

One file under agent/extensions/ mounts everything. Every config field is optional, and the smallest mount gives the model long-term memory:
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 to Vercel Eve

Long-term memory the model reads and writes itself. A bare mount already has it; the memory field only tunes recall:
  • 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:
Memories are stored at agentkit:memory:<userId>:<id>.

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:
  • 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.indexNameRedis Search 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:<userId>:<sessionId>, 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

Point the extension at an Upstash Redis Search 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:
  • 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 "<indexName>:").
  • search.defaultLimit — default page size for search (10).
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

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:
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.
@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

createRateLimitAuth is a ready AuthFn that throttles inbound requests. Drop it into your channel’s auth walk ahead of your real authenticators.
  • limiter (required) — e.g. Ratelimit.slidingWindow(20, "1 m") or fixedWindow(...).
  • identifier (required) — a string, or (request) => string. There’s no implicit "global": one shared bucket lets a single abusive caller exhaust the window for everyone, so derive it per request (an auth user id, an API key, or x-forwarded-for for per-IP).
  • prefix — base key prefix; keys are <prefix>:<identifier> (default agentkit:rateLimit).
  • message — 403 body when over the limit.
  • redis — defaults to Redis.fromEnv().
It’s a gate: under the limit it returns null to fall through to the next AuthFn; over it throws a 403.
Eve runs each turn as two authenticated requests: the message POST (which invokes the model) and a follow-up GET …/stream that opens the reply stream. The auth walk runs on both, so counting both would charge every turn twice. createRateLimitAuth counts only the POSTs, so one turn costs one token: a Ratelimit.slidingWindow(20, "1 m") allows 20 turns per minute, not 10. The session-read GETs pass through unthrottled.

How to add a sandbox to Vercel Eve

A drop-in replacement for Eve’s vercel() backend, powered by Upstash Box. Swap the import and keep the rest of your sandbox file the same.
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, 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(...), 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.
Box network policies are plain domain/CIDR allow-lists. Eve’s per-domain firewall rules (transform header injection, forwardURL) have no Box equivalent, so passing them in use({ networkPolicy }) throws rather than silently sending the request unauthenticated:
Broker credentials with Box’s attachHeaders instead (set at backend creation; a proxy on the box injects them), and open the domain with a plain allow-list:
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: true only for an always-running box you manage yourself.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

Like Eve’s defineTool, but the execute result is memoized in Redis.
  • description / inputSchema / execute — the usual defineTool fields; execute’s result is memoized.
  • toolName (required) — the tool segment of the cache key.
  • userId (required) — a string, or (input, ctx) => string; scopes the cache per user.
  • ttlSeconds — per-result TTL (default: no expiry).
  • redis — defaults to Redis.fromEnv().
Keys are agentkit:toolCache:<userId>:<toolName>:<hash>.

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:
RAG is defineSearchTools, the counterpart to the AI SDK adapter’s createSearchTools:
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 such as an agent/lib/redis.ts. So inside agent/:
  • Import only from packages, never from other agent/ files.
  • 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.

How to run the Vercel Eve example apps

Two complete eve apps live in the AgentKit repo:
  • 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 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.

AgentKit on GitHub

Source, packages, and the full example apps.

Eve

The Vercel agent framework this adapter targets.