npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@contextq/ai-sdk-memory

v0.1.0

Published

Vercel AI SDK memory tools (searchMemories, addMemory) backed by ContextQ -- a tool-provider adapter for the AI SDK memory page

Readme

@contextq/ai-sdk-memory

Vercel AI SDK memory tools -- searchMemories and addMemory -- backed by ContextQ. This is a tool provider, the same integration shape the AI SDK's own memory page documents for Supermemory (addMemory/searchMemories), not a language-model middleware or provider wrapper -- there is no documented AI SDK extension point for "read memory before generation, write after," so this package hands the model two ordinary tools instead. Background: .contextq/tasks/tasks/T726.md ("Decisions" section) is the design record this package implements.

Install

npm install @contextq/ai-sdk-memory ai @contextq/sdk

ai and @contextq/sdk are peer dependencies -- bring your own versions (tested against ai@^7.0.90, the version current when this package was built; @contextq/sdk@^0.2.0).

Quick start

import { generateText } from 'ai';
import { contextQMemoryTools } from '@contextq/ai-sdk-memory';

const memory = contextQMemoryTools({
  apiKey: process.env.CONTEXT_API_KEY!, // sk_live_<40 hex>
  subjectId: 'user-42',                 // the end-user this conversation is with
});

// Turn 1: the model decides to remember something.
await generateText({
  model: yourModel,
  tools: memory,
  prompt: 'Remember that I prefer dark mode, then confirm it.',
});

// Turn 2 (later, same subject): the model decides to recall it.
const { text } = await generateText({
  model: yourModel,
  tools: memory,
  prompt: 'What UI theme do I prefer?',
});
console.log(text);

contextQMemoryTools(options) returns a plain ToolSet ({ searchMemories, addMemory }) -- pass it straight to tools on generateText/streamText, or spread it into a larger tool set alongside your own tools.

Options

| Option | Default | Notes | |---|---|---| | subjectId | -- | Required. ContextQ's end-user scoping id (the same field POST /api/retrieve/POST /api/memory already accept). Fixed at factory time, not a tool input -- the model can never read or write another subject's memories. | | apiKey | process.env.CONTEXT_API_KEY | ContextQ API key, sk_live_<40 hex>. Required (one of this, client, or the env var). | | baseUrl | process.env.CONTEXT_API_URL || http://localhost:38200 | ContextQ API base URL. Ignored when client is given. | | client | -- | A pre-configured @contextq/sdk ContextQ instance. Use this instead of apiKey/baseUrl to control retries, timeouts, extra headers (e.g. a gateway auth header), or a custom fetch. | | workspace | tenant default (global when none set) | ContextQ workspace slug both tools are scoped to. | | defaultLimit | 8 | Default top_k for searchMemories when the model omits limit. |

The two tools

searchMemories

Input: { query: string, limit?: number }.

Calls client.retrieve.query() (POST /api/retrieve) and flattens all three response arrays -- passages (workspace/tenant context chunks), memories (this subject's own extracted facts), and groupMemories (T414 shared account/group facts the subject belongs to) -- into one list sorted by score, plus a single citedText block:

{
  citedText: "[1] (memory, score 0.812) The user's preferred identifier is ...\n\n[2] ...",
  hits: [{ ref: 1, source: 'memory', text: '...', score: 0.812, contextId: 42, title: '...' }],
  empty: false,
}

addMemory

Input: { content: string } -- a plain-language statement to remember.

Calls client.memory.add([{ role: 'user', content }], { subjectId, workspace }) (POST /api/memory), ContextQ's real LLM-extraction pipeline (Mem0-parity): it is not a raw append log -- ContextQ extracts durable atomic facts from the text and dedupes/updates/archives against what it already knows. A short, test-artifact-shaped string (e.g. a bare UUID with no first-person framing) can legitimately extract zero facts; write real statements ("My preferred X is Y") for this to do anything.

Two possible outputs, depending on the server's sync/async threshold:

{ status: 'written', created: [...], updated: [...], extractedClaims: 1 }
// or, for a long conversation the server queues:
{ status: 'queued', jobId: 99, statusUrl: '/api/memory/jobs/99' }

Why subjectId is a factory option, not a tool argument

The AI SDK memory page's Mem0 row configures the equivalent (user_id) the same way -- once, at setup, not per tool call. Exposing it as a tool argument instead would let a model (or a prompt-injected instruction) read or write an arbitrary subject's memories by just naming a different id in its tool call. searchMemories/addMemory's execute closures capture subjectId from the factory call and never read it from the model's input, so the wire request always carries the id the calling application chose.

Development

npm install
npm test          # unit tests against @contextq/sdk with a fake `fetch` -- no real ContextQ needed
npm run typecheck # typecheck (src + scripts)
npm run build     # emit dist/ (src only, tests excluded)

@contextq/sdk is not published to npm yet (see "What 'publishable' means here" below), so local development resolves it via a file:../../sdk devDependency instead of the registry. That only works when this repository's own sdk/ directory sits two levels up, which is always true inside a clone of this repo but NOT inside a container that mounts only this package's own directory -- sdk/ must be built (cd ../../sdk && npm ci && npm run build) and reachable on disk before npm ci/npm install here can resolve it.

Live e2e

scripts/e2e-live.ts proves the full loop against a real, running local ContextQ instance -- no fakes, no mocks:

  1. addMemory writes a real fact through the server's real LLM-extraction pipeline, verified independently via GET /api/contexts (not just "the tool didn't throw").
  2. A second, separate searchMemories call retrieves that same fact via the server's real embedding-backed retrieval.

Unlike the sibling adapters/anthropic-memory-tool/scripts/e2e-live.ts, this script never calls a real LLM API itself -- this package is a tool provider, so calling tool.execute() directly is exactly what the AI SDK's own tool-calling runtime does once a model decides to invoke a tool. The ContextQ round trip on the other end (the part this package actually wraps) is real.

Requires:

  • The local ContextQ dev stack already running and reachable (default http://localhost:38200), with both an LLM and an embedding provider configured -- check curl $CONTEXT_API_URL/health/deep shows llm.status and embedding.status both "ok". Without an LLM configured, addMemory still returns 200 but extracts zero facts (server-side policy, not an adapter bug) and the script fails loudly with a message pointing at this check rather than a confusing downstream assertion.
  • Docker access to the shared-context-postgres container (used to create a throwaway tenant + owner user and mint an API key for them by direct INSERT -- see the script's own doc comments; same sanctioned local-dev pattern the sibling adapter's e2e-live.ts uses).
CONTEXT_API_URL=http://localhost:38200 npm run e2e:live

Every write is confined to a throwaway tenant + workspace created at the start; cleanup runs in the script's finally block regardless of pass/fail (the throwaway tenant row itself is left in place only if it already owns a chained activity_logs row -- the same accepted trade-off src/test-helpers/tenant-cleanup.ts documents, to avoid forking the global audit hash chain).

What "publishable" means here

This package is built and tested to be published, but publishing itself (npm publish) is deliberately out of scope -- see the repo's task tracker (T679) for the user-owned release flow this hands off to. Note that @contextq/sdk itself is not published to npm yet either, so a real npm install of this package by an external user is blocked until that release lands; the peer dependency range is set for that future state.