feat(tools): SDK-level cross-source memory deduplication - #1531
Conversation
|
Claude finished @ved015's task in 1m 52s —— View job Code Review Complete
SummaryReviewed the SDK-level memory deduplication implementation across all TypeScript integrations. The implementation is solid:
Score: 10/10 — No issues found, ready to ship. |
There was a problem hiding this comment.
Overview: Moves memory deduplication from the playground UI into the SDK middleware, implementing request-scoped context block replacement across all TypeScript SDK integrations (AI SDK, OpenAI, Mastra, VoltAgent).
Issues found: None — this looks good to ship.
Details:
The implementation is solid:
-
Request isolation is correct — The
seenMemoriesSet indeduplicateMemories()is created fresh per call, and theinjectedflag in the middlewaremap()callbacks is closure-scoped per request. No global state that could cause cross-user leakage. -
Context block regex is safe — The pattern
/<supermemory context="user-memories" readonly>[\s\S]*?<\/supermemory>/uses non-greedy matching with fixed anchors, avoiding ReDoS concerns. The specific attribute match (context="user-memories" readonly) ensures user-authored supermemory tags with different attributes won't be stripped. -
Normalization logic is sound —
normalizeMemoryFact()strips date prefixes, collapses whitespace, and lowercases for comparison while preserving the original memory string for output. The priority order (static > dynamic > search) is maintained correctly. -
Edge cases handled — Empty memories return early without injecting content. Multiple system messages get the context injected into only the first one, with stale blocks stripped from subsequent ones.
-
Good test coverage — Tests verify context replacement (not accumulation), cross-user isolation, and normalized deduplication.
Score: 10/10
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
supermemory-mcp | be97748 | Aug 24 2026, 04:26 PM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
supermemory-app | be97748 | Commit Preview URL Branch Preview URL |
Aug 24 2026, 04:27 PM |
93f92cd to
4e04a0d
Compare
Move profile deduplication into the SDK middleware. Facts are normalized (strip leading date, trim, collapse whitespace, casefold) and deduplicated in static > dynamic > search priority within each request, then injected as one owned <supermemory> block that replaces the previous block instead of accumulating. Dedup is mode-aware so query-mode search results are not dropped against an omitted profile. No global/browser Set: request-local only, safe for concurrent requests and Cloudflare Workers. Covers AI SDK, OpenAI Chat/Responses, Mastra, and VoltAgent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # packages/tools/src/openai/middleware.ts # packages/tools/src/voltagent/middleware.ts
2fa2e0d to
be97748
Compare
There was a problem hiding this comment.
Overview: Moves memory deduplication from the playground UI into SDK middleware, implementing request-scoped context block replacement across all TypeScript integrations (AI SDK, OpenAI Chat/Responses, Mastra, VoltAgent).
Issues found: None — this looks good to ship.
Details:
The implementation is solid:
-
Request isolation is correct — The
seenMemoriesSet indeduplicateMemories()is created fresh per call, and thememoryCachein each middleware context is per-request/per-user. No global state that could cause cross-user leakage in Cloudflare Worker isolates or concurrent requests. -
Context block regex is safe — The pattern
/<supermemory context="user-memories" readonly>[\s\S]*?<\/supermemory>/guses non-greedy matching with fixed anchors, avoiding ReDoS. The specific attribute match ensures user-authored supermemory tags with different attributes won't be stripped. -
Tag injection prevented —
escapeMemoryContextDelimiters()escapes<and>in memory content, preventing injection attacks where memory text containing</supermemory>could break out of the SDK-owned block. -
Mode-aware deduplication handles edge cases — In "query" mode, the profile isn't injected, so search results correctly skip deduplication against the profile. A memory appearing in both profile and search results survives in search results, preventing legitimate context from disappearing.
-
Normalization logic is sound —
normalizeMemoryFact()strips[YYYY-MM-DD]date prefixes,[recent]tags, collapses whitespace, and lowercases for comparison while preserving the original memory string for output. -
Good test coverage — Tests verify block replacement (stale → fresh), no accumulation (single
<supermemory>tag), cross-user isolation, normalized deduplication variants, and query mode search result preservation.
Score: 10/10

Stack Context
This stack moves memory deduplication out of the playground UI and into the SDKs themselves, so every integration injects a single, deduplicated, self-replacing memory block. Three PRs:
sdk-dedup/tools-ts(this PR) — TypeScript SDK core + integrationssdk-dedup/python— Python SDKssdk-dedup/playground— playground debug view reflects the SDK-owned blockWhat?
Move profile deduplication into the SDK middleware for the TypeScript tools package.
[YYYY-MM-DD], trim, collapse whitespace, casefold) and deduplicated instatic > dynamic > searchpriority within a single request.<supermemory>block that replaces the previous block instead of accumulating a new one each turn.Set. Safe for multiple users, concurrent requests, and Cloudflare Worker isolates.Covers AI SDK, OpenAI (Chat + Responses), Mastra, and VoltAgent. New
shared/memory-context.tsowns the block-replacement logic.Why?
The earlier "conversation-scoped deduplication" was only a playground browser
Set— a UI debug affordance that did not change what the SDK sent to the model, and would have been unsafe as server-side global state. Real cross-source dedup belongs in the SDK, applied fresh per stateless model request.Testing
bun run testinpackages/tools: 145 passed (the one failing suite,claude-memory.test.ts, is a pre-existing broken import unrelated to this change).🤖 Generated with Claude Code
Note
Medium Risk
Changes how system prompts and instructions are built across all TypeScript integrations; behavior is well-covered by unit tests but incorrect strip/replace logic could drop or duplicate context in production prompts.
Overview
Moves cross-source memory deduplication and owned prompt injection into
@supermemory/toolsso every integration sends one deduplicated memory block per request instead of growing context each turn.Deduplication: Facts are normalized via
normalizeMemoryFact(strip[YYYY-MM-DD], trim, collapse whitespace, lowercase) and deduplicated with static → dynamic → search priority.deduplicateMemoriesForModekeeps search hits in query mode when the profile is not injected.Owned
<supermemory>block: Newshared/memory-context.tswraps memories in<supermemory context="user-memories" readonly>, strips stale blocks, and replaces prior SDK context while preserving caller system instructions. Applied in AI SDK (injectMemoriesIntoParams), OpenAI Chat/Responses middleware, Mastra input processor (wrapMemoryContext), and VoltAgent hooks.Tests: Unit coverage for block replacement (with-supermemory, OpenAI, VoltAgent), Mastra wrapper tag assertion, normalized dedup variants, and concurrent
containerTagisolation.Reviewed by Cursor Bugbot for commit 2fa2e0d. Bugbot is set up for automated code reviews on this repo. Configure here.