$smart fuzzy operator.
@upstash/agentkit-eve brings AgentKit to Eve, the Vercel agent framework. You drop these into your
agent/ tree:
| Import | Feature |
|---|---|
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. |
eve and an AI-SDK provider for you):
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN from the environment by default.
How to add memory tools to Eve
Long-term memory the model reads and writes itself:recall_memory and save_memory, one file each.
Options and the userId tenant boundary
Options and the userId tenant boundary
userId(required) — a string, or(input, ctx) => string.topK— max memoriesrecallreturns.minScore— BM25 relevance floor.redis— defaults toRedis.fromEnv().
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:<userId>:<id>.How to add RAG to Eve
search / aggregate / count Eve tools over an Upstash Redis Search index. It is the counterpart to the
AI SDK adapter’s
createSearchTools. Descriptions are generated from your schema.
Options and the one-file-per-tool rule
Options and the one-file-per-tool rule
schema(required) — built withsfrom@upstash/redis.indexName— defaults to"agentkit:search"; ties all three tools to one index.prefix— key prefix for indexed JSON docs (defaults to"<indexName>:").defaultLimit— default page size forsearch(10).redis— defaults toRedis.fromEnv().
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.How to add rate limiting to Eve
A readyAuthFn 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
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 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 — 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.Security: network egress is deny-all by default
Security: network egress is deny-all by default
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
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)
Box network policies are plain domain/CIDR allow-lists. Eve’s per-domain firewall rules (Broker credentials with Box’s
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
Reuse — Eve re-opens a session several times per turn; 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 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>.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/:
- Import only from packages, never from other
agent/files. - Lean on the defaults —
redisdefaults toRedis.fromEnv()in every helper, so you almost never pass it. - Repeat config (schema, names) per file rather than sharing a module.
lib/, imported by the app — not by
agent/ files.
How to run the Eve example app
A completeeve 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.
AgentKit on GitHub
Source, packages, and the full example apps.
Eve
The Vercel agent framework this adapter targets.