Convex Supermemory
Give your Convex app long-term, searchable memory with Supermemory. Reactive local state, backed by direct REST calls.
git clone https://github.com/sholajegede/convex-supermemory.git
cd convex-supermemoryThis upstream package has not been adapted or qualified for Bijection. Read the original documentation before adopting it.
Description
Give your Convex app long-term, searchable memory with Supermemory. Reactive local state, backed by direct REST calls.
Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.
Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.
Getting started
- Get the source at the revision above and read its package documentation.
- Check the required Bijection SDK version, dependencies, and external service credentials.
- Follow the package setup instructions and the guide to using components.
- Exercise the component in your deployment according to your project’s verification rules.
Open source
Maintained source: sholajegede/convex-supermemory. License: See repository. Consult the repository for license terms, notices, and contributor attribution.
From the upstream publisher
Give your Convex app long-term, semantically searchable memory with Supermemory. Reactive local memory and document state, direct REST integration.
Explore the demo ↗Upstream overview
From the upstream package documentation.
convex-supermemory integrates Supermemory's memory API into Convex applications, providing semantic search over stored facts and documents without running a separate vector database. It mirrors everything written to Supermemory into local Convex tables, so memory state is reactively queryable via standard Convex queries. The component handles both short discrete facts via addMemory() and larger content via addDocument(), which goes through Supermemory's async chunking and embedding pipeline.
Use cases
From the upstream package documentation.
- Adding per-user long-term memory to an AI agent so it recalls preferences, history, or facts across separate sessions
- Scoping memory to individual users or workspaces using
containerTagand running semantic search against only that container - Ingesting large documents (transcripts, notes, documentation) and querying them later with hybrid RAG search without managing embeddings
- Building a reactive UI that displays a user's stored memories and documents using Convex queries without extra API calls
- Implementing a forgetting mechanism to remove specific memories or documents from both Supermemory and local Convex state in sync
How it works
From the upstream package documentation.
Setup requires three steps: adding convexSupermemory to convex.config.ts via app.use(), setting SUPERMEMORY_API_KEY as a Convex environment variable, and instantiating new Supermemory(components.convexSupermemory, { apiKey }) in a Convex module. The instance is then imported into any Convex action that needs to read or write memory.
The component exposes two write paths. addMemory() stores a single fact immediately with no processing delay and accepts options like isStatic, forgetAfter, and arbitrary metadata. addDocument() sends larger content through Supermemory's extraction and chunking pipeline asynchronously; status progresses through queued, extracting, chunking, embedding, indexing, and done. Callers must poll status manually using refreshDocument(), for example from a Convex scheduled function or cron.
Every memory and document written is mirrored into Convex tables (memories and documents), making them available through reactive query methods: listMemories(), listDocuments(), getMemory(), getDocument(), and getStats(). Semantic search via search() calls Supermemory live and is not cached locally, since results are a ranked view over the underlying data rather than durable records.
Deletion is handled by forgetMemory() and deleteDocument(), both of which update local Convex state to match what Supermemory removes. deleteDocument() throws on a 409 if the document is still processing, so callers should wait for a terminal status before attempting deletion.
Upstream package FAQ
What is the difference between addMemory and addDocument in convex-supermemory?
addMemory() is for short, discrete facts that are immediately available for search with no processing delay. addDocument() is for longer content that needs chunking and embedding; processing is asynchronous and moves through stages (queued, extracting, chunking, embedding, indexing, done). Use addMemory() for conversational facts and addDocument() for transcripts, documentation pages, or other multi-paragraph content.
Does convex-supermemory cache search results in Convex tables?
No. The search() method in convex-supermemory always calls the Supermemory API live and does not mirror results into Convex tables. Memories and documents are mirrored locally, but search results are a ranked view over that data and are intentionally not persisted.
How does containerTag work in convex-supermemory?
Every write, search, and profile operation in convex-supermemory is scoped to a containerTag string you provide. Container tags are exact-match scopes, not fuzzy. A common convention is one containerTag per user ID or one per agent workspace. Search only looks within the containerTag you pass, so tags must be consistent between writes and reads.
What happens when you call forgetMemory or deleteDocument in convex-supermemory?
forgetMemory() asks Supermemory to remove the memory scoped by both containerTag and memoryId, then updates the local Convex record's forgotten field to match the response. deleteDocument() removes the document from Supermemory and from the local Convex table, but will throw if Supermemory returns a 409 because the document is still processing. You should wait until refreshDocument() reports a terminal status before deleting.
What Convex version and Node.js version does convex-supermemory require?
convex-supermemory requires Convex v1.33.1 or later and Node.js 18 or later. You also need a Supermemory API key starting with sm_, which you set as the SUPERMEMORY_API_KEY environment variable in your Convex deployment.
Use with agents and CLI
Give your coding agent the component’s source identity and setup guidance.
Help me integrate Convex Supermemory with Bijection.
Read: https://bijection.com/components/convex-supermemory/SKILL.md
Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.Upstream README.md
View repository ↗Documentation from sholajegede/convex-supermemory, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.
convex-supermemory
Give your Convex app long-term, semantically searchable memory with Supermemory. Reactive local memory and document state, direct REST integration.
const supermemory = new Supermemory(components.convexSupermemory, {
apiKey: process.env.SUPERMEMORY_API_KEY!,
});
// Store a fact about a user
const { memoryId } = await supermemory.addMemory(ctx, {
containerTag: userId,
content: "Prefers dark mode and terse responses",
});
// Recall it later with semantic search
const { results } = await supermemory.search({
containerTag: userId,
query: "how does this user like responses formatted?",
});
What this does
An AI agent is only as good as what it remembers between calls. Supermemory is a memory API purpose-built for this: you write facts and documents into a containerTag (typically one per user or agent), and it handles embedding, chunking, and semantic search so you can recall the right context later — without you running your own vector database.
This component wraps Supermemory's REST API directly (no SDK dependency) and mirrors everything you write into Convex tables, so you get:
- Fast-path memory —
addMemory()stores a single fact immediately, no processing pipeline - Document ingestion —
addDocument()sends larger content through Supermemory's extraction/chunking pipeline, withrefreshDocument()to poll status - Semantic search —
search()proxies Supermemory's hybrid RAG + memory search live - Reactive local state — every memory and document is queryable from Convex without an extra API call
- Forgetting —
forgetMemory()/deleteDocument()keep local state in sync with what Supermemory actually removes
Table of Contents
- Install
- Quick Start
- Usage
- Memories vs. Documents
- API Reference
- Type Reference
- Database Schema
- Container Tags
- Testing
- Example App
- Limitations
- Troubleshooting
- Contributing
- Changelog
Install
npm install convex-supermemory
Requirements: Convex v1.33.1 or later, Node.js 18+, a Supermemory API key
Quick Start
Three steps to add memory to your Convex app.
1. Add the component
In convex/convex.config.ts:
import { defineApp } from "convex/server";
import convexSupermemory from "convex-supermemory/convex.config";
const app = defineApp();
app.use(convexSupermemory);
export default app;
2. Set environment variables
npx convex env set SUPERMEMORY_API_KEY sm_xxxxxxxxxxxx
3. Initialize the client
In convex/memory.ts:
import { components } from "./_generated/api";
import { Supermemory } from "convex-supermemory";
export const supermemory = new Supermemory(components.convexSupermemory, {
apiKey: process.env.SUPERMEMORY_API_KEY!,
});
Import supermemory from this file in any Convex action that needs to read or write memory.
Usage
Remember a fact
export const remember = action({
args: { userId: v.string(), fact: v.string() },
handler: async (ctx, args) => {
return await supermemory.addMemory(ctx, {
containerTag: args.userId,
content: args.fact,
isStatic: true, // long-term fact, not a fading conversational detail
});
},
});
// Returns: { memoryId }
Recall with semantic search
export const recall = action({
args: { userId: v.string(), query: v.string() },
handler: async (ctx, args) => {
return await supermemory.search({
containerTag: args.userId,
query: args.query,
limit: 5,
});
},
});
// Returns: { results: [{ documentId, score, summary, chunks, ... }], total }
Forget a memory
containerTag is required here too — Supermemory scopes the delete by it, not just by memoryId.
export const forget = action({
args: { userId: v.string(), memoryId: v.string() },
handler: async (ctx, args) => {
return await supermemory.forgetMemory(ctx, {
containerTag: args.userId,
memoryId: args.memoryId,
});
},
});
// Returns: { forgotten: boolean }
List a user's memories reactively
export const getMemories = query({
args: { userId: v.string() },
handler: async (ctx, args) => {
return await supermemory.listMemories(ctx, { containerTag: args.userId });
},
});
Memories vs. Documents
Supermemory exposes two ways to write content, and this component exposes both:
addMemory()— for short, discrete facts ("the user is on the Pro plan", "prefers metric units"). No processing delay; usable in search immediately.addDocument()— for longer content (a support transcript, a page of documentation, a whole conversation) that needs to be chunked and embedded. Processing is asynchronous — callrefreshDocument()to check onstatus(queued→extracting→chunking→embedding→indexing→done).
API Reference
| Method | Kind | Description |
|---|---|---|
addMemory(ctx, args) | action | Stores a single fact immediately |
forgetMemory(ctx, args) | action | Requests Supermemory forget a memory (scoped by containerTag) |
addDocument(ctx, args) | action | Ingests larger content through the chunking pipeline |
refreshDocument(ctx, args) | action | Pulls the latest status/title/summary for a document |
deleteDocument(ctx, args) | action | Deletes a document from Supermemory and locally |
search(args) | plain async | Live semantic search — not cached locally |
getMemory(ctx, args) | query | Fetch one memory by id |
listMemories(ctx, args) | query | List a container's memories, newest first |
getDocument(ctx, args) | query | Fetch one document by id |
listDocuments(ctx, args) | query | List a container's documents, newest first |
getStats(ctx) | query | Aggregate memory/document counts for a small dashboard |
listRecentMemories(ctx, args?) | query | Every memory, newest first, regardless of containerTag |
listRecentDocuments(ctx, args?) | query | Every document, newest first, regardless of containerTag |
Type Reference
type AddMemoryArgs = {
containerTag: string;
content: string;
isStatic?: boolean;
metadata?: Record<string, unknown>;
forgetAfter?: number; // ms epoch
forgetReason?: string;
};
type AddDocumentArgs = {
containerTag: string;
content: string;
customId?: string;
metadata?: Record<string, unknown>;
};
type SearchArgs = {
containerTag: string;
query: string;
limit?: number;
rerank?: boolean;
includeFullDocs?: boolean;
};
type SearchResponse = {
results: Array<{
documentId: string;
title: string | null;
score: number;
summary: string | null;
content: string | null;
chunks: Array<{ content: string; score: number; position: number; isRelevant: boolean }>;
}>;
total: number;
timing?: number; // ms, when Supermemory reports it
};
Database Schema
memories: {
memoryId, containerTag, content, isStatic, metadata?,
forgetAfter?, forgetReason?, forgotten,
createdAt, updatedAt,
}
documents: {
documentId, containerTag, content?, customId?, metadata?, status,
title?, summary?,
createdAt, updatedAt,
}
getStats(), listRecentMemories(), and listRecentDocuments() read across these tables directly with a full scan — fine for a dashboard or history view, not intended as a high-volume production query path.
Container Tags
Supermemory scopes everything — writes, search, and profiles — by containerTag. Use one per user for personal memory, or one per agent/workspace for shared memory. Search only ever looks within the container tag you pass, so pick a convention early (this component doesn't enforce one).
Testing
npm run test
Component logic is tested with convex-test in src/component/lib.test.ts. Import convex-supermemory/test in your own app to register this component's schema against your test instance.
Example App
example/ is a full Vite + React demo that exercises the entire component end to end against your own Supermemory account:
- Memories — add a fact with
addMemory()(with suggestion chips to try), forget it withforgetMemory(), all reactive. - Documents — ingest longer content with
addDocument(), poll status withrefreshDocument(), remove it withdeleteDocument(). - Search — run
search()against whichever containerTag is selected, showing result score, matching chunks, and Supermemory's own reportedtotal/timing. - containerTag switcher — a row of chips lets you flip between a few demo users live, so you can see the exact same actions land in completely separate memory spaces.
- History — every memory and document ever written in the deployment, newest first, across every containerTag. Expand a row for full detail, or hit Recreate live to replay it as a brand-new memory/document.
- Activity console — a side-docked live log of every call this app makes into the Supermemory client, including real API errors (e.g. Supermemory returns
409if you try to delete a document that's still processing).
Run it from the repo root:
npm install --legacy-peer-deps
npx convex env set SUPERMEMORY_API_KEY sm_xxxxxxxxxxxx
npm run dev
npm run dev starts the Convex backend and the Vite frontend together — there's no need to cd example or run either one separately.
Limitations
search()always calls Supermemory live — results are not cached or mirrored into Convex tables, since they're a ranked view over the underlying memories/documents rather than durable records themselves.- Document processing is asynchronous; this component does not poll for you. Call
refreshDocument()on a schedule (e.g. a Convex cron or scheduled function) if you need status without a user-triggered refresh. forgetMemory()reflects whateverforgottenvalue Supermemory returns — if Supermemory forgets asynchronously, the local record may briefly still showforgotten: false.deleteDocument()throws if Supermemory reports the document is still processing (409) — retry after the document reaches a terminal status (done/failed).
Troubleshooting
401/403 from Supermemory — confirm SUPERMEMORY_API_KEY is set and starts with sm_.
Search returns nothing — confirm you're searching the same containerTag you wrote memories/documents into; container tags are exact-match scopes, not fuzzy. If the document was ingested moments ago, give it a few seconds to finish indexing even after refreshDocument() reports done.
Document stuck in queued/extracting — large documents can take longer to process; call refreshDocument() again after a short delay.
Deleting a document fails with a "still processing" error — Supermemory returns 409 for a document that hasn't reached a terminal status yet; wait for refreshDocument() to report done or failed, then delete.
Contributing
See CONTRIBUTING.md.
Changelog
See CHANGELOG.md.