Proposal: Add SDK idempotency keys for safe memory write retries
Proposal: Add SDK idempotency keys for safe memory write retries
Problem
packages/tools/src/ai-sdk.ts:addMemoryTool and documentAddTool (and the same in packages/tools/src/openai/tools.ts, claude-memory.ts) call client.add({ content, containerTags }) with no idempotency signal. If the network retries (the supermemory client retries 2× on 5XX/network), or the caller retries, the backend creates:
- duplicate memories,
- duplicate embeddings,
- duplicate writes.
There is no Idempotency-Key header and no client helper to keep the same key across retries. Offline sync and batching also need a stable per-content key.
Impact
- Duplicate memories on retry (common on mobile/flaky networks)
- No safe retry primitive for SDK consumers
- Blocks offline queue (no stable key to dedupe)
Proposal — Phase A (SDK-only, mergeable now)
Generate the key in the SDK and attach it as Idempotency-Key header. No backend change required in this phase — the header is optional and ignored until the backend honors it. The SDK is already correct for safe retries.
Idempotency-Key = SHA256(
content +
'|' + containerTags.sort().join(',') +
'|' + minuteBucket // Math.floor(Date.now()/60000)
)minuteBucketmakes the key stable within the same minute (retries share it) but rotates soon after, preventing indefinite dedupe.SHA-256is available via Web Crypto (and Node 20+crypto.subtle) — already a repo dependency for other hashing.
SDK flow:
const key = await generateIdempotencyKey(content, containerTags)
await client.add({ content, containerTags }, { headers: { "Idempotency-Key": key } })Phase A files:
- New
packages/tools/src/shared/idempotency.ts—generateIdempotencyKey(content, containerTags, now?),buildIdempotencyHeaders(...) - Wire
addMemoryTool+documentAddToolinpackages/tools/src/ai-sdk.ts(andopenai/tools.tsfor parity) to attach the header via the secondRequestOptionsarg - Unit tests in
packages/tools(vitest already exists)
Out of scope for this PR:
- Backend honoring the key (Phase B — server-side dedupe store)
- Custom
userIdin the hash (Phase B — if backend needs it, it can hashapiKeyserver-side) - Batch idempotency
Questions for maintainers
Is the backend already capable of accepting Idempotency-Key, or would this require backend support? This PR keeps the header optional so it is useful even before the backend honors it (stable key for caller-side retry dedupe, foundation for Phase B).
Verification
Vitest in packages/tools — 10 scenarios: same content+tags+minute → same key, different content → different key, different tags → different key, minute rollover → different key, custom now injection, header builder, retry helper reuses same key, empty content edge, containerTags order independence, concurrent callers share key.
Non-goals (Phase A)
- No backend change
- No persistent storage of keys
- No cross-process coordination
- No batch/bulk idempotency
Source: supermemoryai/supermemory