$smart fuzzy operator.
Two packages bring AgentKit to Eve, the Vercel agent framework. They work together in one agent:
eve and an AI-SDK provider
for you:
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
agent/extensions/ mounts everything. Every config field is optional, and the smallest
mount gives the model long-term memory:
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; thememory field
only tunes recall:
Tools, options, and the userId tenant boundary
Tools, options, and the userId tenant boundary
agentkit__recall_memory— searches the user’s memories; called with noqueryit 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: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_historyruns 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_historyreads one of those chats back bysessionId, newest messages last.
userId from the session, so the model cannot widen a lookup past the current user’s
own transcripts.
Options, storage, and reading history from your own code
Options, storage, and reading history from your own code
true to tune storage:chatHistory.prefix— base key prefix (defaultagentkit:chat).chatHistory.indexName— Redis Search index name (defaults to the identifier-safeprefix).chatHistory.ttlSeconds— per-chat TTL. Omit for no expiry.
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 getssearch, 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:
Options and how the tools are described to the model
Options and how the tools are described to the model
search.schema(required) — built withsfrom@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 forsearch(10).
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
Dropping or overriding a contribution
Dropping or overriding a contribution
@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.
Options and the required identifier
Options and the required identifier
limiter(required) — e.g.Ratelimit.slidingWindow(20, "1 m")orfixedWindow(...).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, orx-forwarded-forfor per-IP).prefix— base key prefix; keys are<prefix>:<identifier>(defaultagentkit:rateLimit).message— 403 body when over the limit.redis— defaults toRedis.fromEnv().
null to fall through to the next AuthFn; over it throws
a 403.Why only POST requests are counted
Why only POST requests are counted
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’svercel() backend, powered by
Upstash Box. Swap the import and keep the rest of your
sandbox file the same.
Config: Box's BoxConfig
Config: Box's BoxConfig
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.Security: network egress is deny-all by default
Security: network egress is deny-all by default
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.Brokering credentials (injecting headers)
Brokering credentials (injecting headers)
transform
header injection, forwardURL) have no Box equivalent, so passing them in use({ networkPolicy })
throws rather than silently sending the request unauthenticated:attachHeaders instead (set at backend creation; a proxy on the box
injects them), and open the domain with a plain allow-list:Lifecycle: one box per conversation
Lifecycle: one box per conversation
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’sdefineTool, but the execute result is memoized in Redis.
Options
Options
description/inputSchema/execute— the usualdefineToolfields;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 toRedis.fromEnv().
agentkit:toolCache:<userId>:<toolName>:<hash>.Memory and RAG as individual tool files
The same two features the extension mounts, written as standaloneagent/tools/ files. Use these when
you want to configure each tool on its own.
Memory takes one file per tool:
defineSearchTools, the counterpart to the
AI SDK adapter’s createSearchTools:
Options and the one-file-per-tool rule
Options and the one-file-per-tool rule
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.
redisfalls back toRedis.fromEnv()in every helper, so you almost never pass it. - Repeat config (schema, names) in each file instead of sharing a module.
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 completeeve apps live in the AgentKit repo:
examples/eve-extension-demois a minimal agent whose whole configuration is one extension mount, with memory, chat history, and book search turned on.examples/eve-demouses 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.