Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0c91af5
feat(eve): Redis-backed memory integration for eve's native memory slots
upstash-tag[bot] Sep 2, 2026
0a80800
fix(eve/memory): don't trust a single "absent" read for a document we…
upstash-tag[bot] Sep 2, 2026
765d9f7
test(eve/memory): poll the exists-after-del assertion too
upstash-tag[bot] Sep 2, 2026
ac5bf6a
test(eve/memory): cover redisMemory() recall invocation and Redis per…
upstash-tag[bot] Sep 2, 2026
0977414
feat(sdk): let AgentMemory records carry an optional conversationId
CahidArda Sep 2, 2026
58cf38d
refactor(eve/memory): split eve-memory.ts into memory-documents.ts an…
CahidArda Sep 2, 2026
9dd5eb9
feat(eve/memory)!: default autoCapture off, rename the config knobs, …
CahidArda Sep 2, 2026
9c9f818
test(eve-demo): drive the recall slot through its save tool now that …
CahidArda Sep 2, 2026
604af53
fix(eve-demo): make `vercel build` work from the pnpm workspace root
CahidArda Sep 2, 2026
2b3acf9
refactor(eve): move the memory-slot modules into src/memory/
CahidArda Sep 2, 2026
f3e9102
refactor(eve/memory): type the message helpers against eve's real Mod…
CahidArda Sep 2, 2026
b10dcf7
feat(eve/memory)!: ship only redisDocuments; drop the redisMemory pro…
CahidArda Sep 2, 2026
2cd61f4
feat(eve/memory): restore redisMemory, drop memoryTools and the extra…
CahidArda Sep 2, 2026
a30bedc
docs(eve/memory): document the four lifecycle hooks and what a recall…
CahidArda Sep 2, 2026
fb613be
fix(eve-demo): scope both memory slots with byPrincipal
CahidArda Sep 2, 2026
2008ee2
feat(sdk,eve)!: give AgentMemory typed metadata; label each recalled …
CahidArda Sep 2, 2026
47aba71
docs(eve/memory): fix the recalled-block example — every source carri…
CahidArda Sep 2, 2026
ea6f906
feat(eve/memory)!: add search_memory; rename the two core knobs; defa…
CahidArda Sep 2, 2026
160992e
docs(eve): fold the memory-slot lifecycle and recall details into col…
CahidArda Sep 2, 2026
671f5a1
feat(sdk)!: let AgentMemory carry extra indexed fields; drop the no-m…
CahidArda Sep 3, 2026
477d151
feat(eve/memory)!: one store for the slot, indexed by session and source
CahidArda Sep 3, 2026
5fdf2fa
fix(eve/memory)!: default rememberMessages to "fromUser"; drop forget…
CahidArda Sep 3, 2026
dd54011
test(sdk): cover metadataSchema, and document it in the README
CahidArda Sep 3, 2026
7c18a55
test(sdk): stabilize the memory suite against fresh-index visibility lag
CahidArda Sep 3, 2026
e848716
test(ai-sdk,eve): provision search indexes before seeding, and poll t…
CahidArda Sep 3, 2026
fa0ca25
feat(sdk)!: derive memory metadata and filters from `metadataSchema`
CahidArda Sep 4, 2026
e28aed7
refactor(sdk): type the private memory query filter too
CahidArda Sep 4, 2026
3cbfba0
fix(eve)!: bump @upstash/redis to 1.38.4 and drop the read-your-write…
CahidArda Sep 4, 2026
dd633f5
docs(eve/memory): record why the recall replay cache stays
CahidArda Sep 4, 2026
6704b91
fix(eve/memory): look forget_memory up by key, and correct the stale …
CahidArda Sep 4, 2026
c9fc65c
docs(changeset): cut the changesets down to changelog entries
CahidArda Sep 4, 2026
1f290a8
docs(eve/memory): correct the claim that Upstash REST has no MULTI
CahidArda Sep 4, 2026
ac9e0fd
docs(eve): drop the stale WATCH/MULTI claim from the README
CahidArda Sep 4, 2026
554d9f7
fix(eve/memory): dedupe captured turns per source, not per text
CahidArda Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 7 additions & 20 deletions .changeset/eve-extension-0490-rebuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,12 @@

fix!: rebuild against eve 0.49.0 and raise the `eve` peer floor to `>=0.48.0`

The extension is now built with **eve 0.49.0**, whose
`dist/extension/_manifest.json` stamps formatVersion 2 and contracts
extension 1 / **tool 24** / dynamicTool 21 / hook 16 / instructions 2 / config 1 —
up from **tool 21** in the published `0.8.0` build (eve 0.47.3). Only the tool
contract moved; every other contribution contract is unchanged. This supersedes the
unreleased intermediate rebuild on eve 0.47.6 (tool 22, floor `>=0.47.5`), which
never shipped.
No source changed. The extension's `dist` is rebuilt with eve 0.49.0, which re-stamps the manifest's
tool contract 21 → 24 (every other contribution contract is unchanged). eve 0.48.0 is the first
release accepting tool 24, so the `eve` peer moves `">=0.47.0"` → `">=0.48.0"`.

eve 0.48.0 is the first release whose `EXTENSION_CAPABILITY_CONTRACTS` accept tool
24, so the `eve` peer moves from `">=0.47.0"` to `">=0.48.0"`. **The tool contract
moved twice inside three eve patch/minor releases** — 22 → 23 in **0.47.7** and
23 → 24 in **0.48.0** — so 0.47.7 is *not* sufficient either, and neither is any
0.47.x: 0.47.0–0.47.3 top out at tool 21, 0.47.5/0.47.6 at tool 22, 0.47.7 at tool
23 (0.47.4 was never published). Verified by packing the rebuilt extension into a
real eve app: on eve 0.47.5, 0.47.6 and 0.47.7 it installs cleanly and then fails
`eve build` with `Selected module binding "extensions/agentkit.ts" has no compile or
runtime usage.`, while eve 0.48.0 and 0.49.0 build and mount every contribution.
Contribution contracts move in *patch* releases, so always re-derive the floor from
the freshly built manifest rather than from the minor version.
No 0.47.x works, including 0.47.7 — the tool contract moved twice in three releases (22 → 23 in
0.47.7, 23 → 24 in 0.48.0). On an unsupported eve the mount contributes nothing and `eve build`
fails with `Selected module binding "extensions/agentkit.ts" has no compile or runtime usage.`

No extension source changed; all packages build, typecheck, lint and pass their
tests against eve 0.49.0.
This supersedes the unreleased 0.47.6 rebuild (tool 22, floor `>=0.47.5`), which never shipped.
45 changes: 45 additions & 0 deletions .changeset/eve-redis-memory-slots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
"@upstash/agentkit-eve": minor
---

feat(eve): add `@upstash/agentkit-eve/memory` — Upstash Redis behind eve's memory slots

A new subpath with two integrations for eve's [memory](https://eve.dev/docs/memory) feature
(`agent/memory/<slot>.ts`), because eve exposes two different seams:

- **`redisDocuments()`** — a `MemoryDocumentBackend` for eve's built-in `fileMemory()`, replacing its
Vercel Blob storage: `fileMemory({ backend: redisDocuments() })`. Without a `backend`,
`fileMemory()` only resolves storage under `eve dev` and on Vercel with a Blob store attached.
- **`redisMemory()`** — a full `MemoryProvider` over the SDK's `AgentMemory`: ranked BM25 recall at
`turn.started` / `compaction.completed`, capture at `turn.completed`, and the tools
`<slot>__save_memory`, `<slot>__search_memory`, `<slot>__read_session` and `<slot>__forget_memory`,
bound to the slot's locked scope. Where `fileMemory()` replays one curated document, this recalls
the top-K memories relevant to the current turn and needs no tool call to remember anything.

Both are additive: `defineMemoryRecallTool` / `defineMemorySaveTool` are unchanged and remain the
right choice for model-driven memory with no slot.

**Requirements.** The subpath imports `eve/memory` and `eve/memory/file` (added in eve 0.45.1 and
0.45.2), so it needs **eve ≥ 0.45.2** — the package's `eve` peer stays `>=0.32.0` because the root
and `./sandbox` entry points still work further back. The `@upstash/redis` peer floor moves to
**`>=1.38.4`**, whose read-your-writes fix `redisDocuments()` relies on.

**`redisMemory()` options:** `rememberMessages` (default `true`, meaning `"fromUser"`; also `"all"`,
`"fromModel"`, `false`), `maxRecallCharacters` (4000), `maxMemoryCharacters` (2048), plus `topK`,
`minScore`, `prefix`, `indexName`.

Three behaviours worth knowing before you configure it:

- **Automatic recall injects saved facts only.** Captured messages share the store but not the
ranking, and are reached on demand through `search_memory` / `read_session`. Otherwise a stored
*"What do you remember?"* outranks real facts on the next identical question.
- **`forget_memory` redacts rather than deletes.** The text is erased and the entry marked deleted,
so it can never be recalled or searched again, but `read_session` renders it as `[redacted]` — a
silent gap invites re-deriving the very thing that was removed.
- **`"all"` and `"fromModel"` do not get `forget_memory`.** Those modes store the assistant's
replies, and a reply confirming a deletion quotes the text it deleted — so erasing something would
write a fresh copy of it. They contribute `save_memory`, `search_memory` and `read_session` only.

Everything the slot keeps lives in one keyspace of its own (`agentkit:memorySlot`) with `sessionId`,
`source` and `deleted` indexed, so there is no separate transcript store to fall out of sync.
Recalled memories are tagged `session=<id>`, and `read_session` replays that session in order.
38 changes: 38 additions & 0 deletions .changeset/sdk-memory-metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
"@upstash/agentkit-sdk": minor
---

feat(sdk): typed indexed metadata on `AgentMemory`, plus `get`/`list`/`count`

`AgentMemory` accepts a `metadataSchema` of Upstash Search field builders, whose values are supplied
per record as `metadata` and can then be filtered on:

```ts
const memory = new AgentMemory({
redis,
prefix: "myapp:memory", // ← its own prefix; see below
metadataSchema: { source: s.string().noTokenize(), deleted: s.boolean() },
});

await memory.add({ text: "…", userId: "u1", metadata: { source: "agent", deleted: false } });
await memory.recall({ userId: "u1", query: "…", filter: { source: { $eq: "agent" } } });
```

The schema types everything: `metadata` and each `filter` are derived from it, so a wrong operand
type or an undeclared field is a compile error rather than a query that quietly matches nothing. To
narrow a derived type, pass it as a second argument, constrained to the schema:
`new AgentMemory<typeof schema, { source: "agent" | "userMessage" }>(…)`.

Also new: `list({ userId, filter, limit })` (filter-first, unranked), `count({ userId, filter })`,
and `get({ userId, id })` — a direct-key read, for when you have an id and a bounded search page
could hide it.

**Give an extended store its own `prefix`.** A stricter schema pointed at a keyspace that already
holds records written without those fields makes them permanently unreachable: Upstash Search does
not match a missing field against `{$eq: …}` and has no `$ne`. Omitting `metadataSchema` leaves the
store exactly as it was.

**Behaviour change:** `recall()` no longer falls back to "everything for the user" when a `query`
matches nothing; it returns nothing, since the fallback made a miss indistinguishable from a hit.
Omitting the query is still how you ask for the whole set. This reaches every caller of `recall`,
including the memory tools in `@upstash/agentkit-ai-sdk`, `@upstash/agentkit-eve` and the extension.
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ jobs:
- name: Build example apps
run: pnpm -r --filter "./examples/*" build

- name: E2E eval (eve memory slots, mocked model)
# Boots the eve demo's agent with a scripted mockModel (no model provider, no
# OPENAI_API_KEY) and asserts both Upstash Redis memory integrations run at eve's real
# memory lifecycle boundaries against real Redis: redisMemory() captures a turn and recalls
# it, and fileMemory({ backend: redisDocuments() }) saves and recalls its document.
working-directory: examples/eve-demo
env:
UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
AGENTKIT_MOCK_MODEL: "1"
run: |
if [ -z "$UPSTASH_REDIS_REST_URL" ]; then
echo "No Redis secrets available — skipping the e2e eval."
exit 0
fi
npx eve eval

- name: E2E eval (eve extension, mocked model)
# Boots the extension demo's agent with a scripted mockModel (no model provider,
# no OPENAI_API_KEY) and asserts the extension's tools execute against real Redis
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ coverage
*.log
.DS_Store
.turbo
.vercel
.env
.env.*
!.env.example
Expand Down
Loading
Loading