## 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:
1. **`sdk-dedup/tools-ts`** (this PR) — TypeScript SDK core + integrations
2. `sdk-dedup/python` — Python SDKs
3. `sdk-dedup/playground` — playground debug view reflects the SDK-owned block
## What?
Move profile deduplication into the SDK middleware for the TypeScript tools package.
- Facts are normalized (strip leading `[YYYY-MM-DD]`, trim, collapse whitespace, casefold) and deduplicated in **`static > dynamic > search`** priority within a single request.
- The result is injected as one **owned `<supermemory>` block** that *replaces* the previous block instead of accumulating a new one each turn.
- Dedup is **mode-aware**: in query mode, search results are not dropped against a profile that isn't being injected.
- Deduplication is **request-local** — no global/browser `Set`. Safe for multiple users, concurrent requests, and Cloudflare Worker isolates.
Covers AI SDK, OpenAI (Chat + Responses), Mastra, and VoltAgent. New `shared/memory-context.ts` owns 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 test` in `packages/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](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!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/tools` so 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. `deduplicateMemoriesForMode` keeps search hits in **query** mode when the profile is not injected.
>
> **Owned `<supermemory>` block:** New `shared/memory-context.ts` wraps 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 `containerTag` isolation.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 2fa2e0d85c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Refresh canonical tool descriptions in `tools-shared.ts`
- Align OpenAI and AI SDK tool bindings with 7-tool surface
- Export `TOOL_DESCRIPTIONS` / `PARAMETER_DESCRIPTIONS` from package index
Stacked on #1431
## Test plan
- [ ] `bun run test:unit` in `packages/tools`
Made with [Cursor](https://cursor.com)
Three places where ClaudeMemoryTool diverges from the documented
memory_20250818 wire format:
- rename sends old_path/new_path, not path. handleCommand validated
command.path, so every rename coming from a real model died with
"Cannot read properties of undefined (reading 'startsWith')".
path is still accepted as the source for existing callers.
- insert_line means "insert after this line" (0 = top of file), but we
spliced at insertLine - 1 and rejected 0, so every insert landed one
line above where Claude asked and inserting at the top was impossible.
- str_replace with new_str omitted is a deletion per the spec; we
rejected it.
The new tests mock the supermemory client so they run without an API
key. Also fixed the rename example in the docs, which showed the same
path shape the code expected.
Cherry-picks four contributor PRs for `@supermemory/tools` onto one branch, and bumps the package to 2.2.0.
- #1244 (@rajarshidattapy): `withSupermemory` accepts `options.apiKey` instead of only reading `SUPERMEMORY_API_KEY`, matching the Vercel, Mastra and Voltagent integrations. Unblocks secrets managers, edge runtimes and per-request keys.
- #1574 (@Agnik47): re-exports `PromptTemplate`, `MemoryPromptData` and `WithSupermemoryOptions` from `ai-sdk`. `./vercel` is not a published subpath, so the documented custom-template example did not compile.
- #1488 (@abhinav7x94): malformed tool-call JSON returns an error result instead of throwing out of the request.
- #1507 (@abhinav7x94): VoltAgent `onEnd` awaits the conversation save, which was fire-and-forget and could be dropped when a serverless runtime tore down.
Dropped the `middleware.test.ts` added by #1244. Note that editing `packages/tools/package.json` triggers the npm publish workflow on merge.
Co-Authored-By: rajarshidattapy <138959719+rajarshidattapy@users.noreply.github.com>
Co-Authored-By: Agnik47 <140933190+Agnik47@users.noreply.github.com>
Co-Authored-By: abhinav7x94 <204053250+abhinav7x94@users.noreply.github.com>
- Published the v2.0.0 docs and a 1.4 → 2.0 migration guide so existing users have a clear upgrade path.
- Updated the four integration pages (AI SDK, OpenAI, Mastra, VoltAgent) to reflect v2 defaults and link to the migration guide.
- Added a short explainer on the two required fields (containerTag, customId) so new users aren't blocked at first integration.
**`withSupermemory`** **(AI SDK)**
- **`skipMemoryOnError`** **defaults to** **`true`**. memory errors/timeouts log and the model runs on the **original** prompt unless you set `skipMemoryOnError: false`.
- **Pre-LLM** **`/v4/profile`** **is aborted after 5s** via `AbortSigna`
**Docs**
- `packages/tools/README.md`, **`apps/docs/integrations/ai-sdk.md`**
adds withSupermemory wrapper and input/output processors for
mastra agents:
- input processor fetches and injects memories into system prompt
before llm calls
- output processor saves conversations to supermemory after
responses
- supports profile, query, and full memory search modes
- includes custom prompt templates and requestcontext support
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), instructions:
"..." },
"user-123",
{ mode: "full", addMemory: "always", threadId: "conv-456" }
))
includes docs as well
this pr also reworks how the tools package works into shared modules
## Add customizable prompt templates for memory injection
**Changes:**
- Add `promptTemplate` option to `withSupermemory()` for full control over injected memory format (XML, custom branding, etc.)
- New `MemoryPromptData` interface with `userMemories` and `generalSearchMemories` fields
- Exclude `system` messages from persistence to avoid storing injected prompts
- Add JSDoc comments to all public interfaces for better DevEx
**Usage:**
```typescript
const customPrompt = (data: MemoryPromptData) => `
<user_memories>
${data.userMemories}
${data.generalSearchMemories}
</user_memories>
`.trim()
const model = withSupermemory(openai("gpt-4"), "user-123", {
promptTemplate: customPrompt,
})
```