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

wontopos

v2.2.17

Published

Wontopos — long-term memory for AI agents. Pure semantic retrieval, identical recall in every language, ~100× lower LLM bill.

Readme

Wontopos — long-term memory for AI agents

npm install wontopos
import { Client } from "wontopos";

const mem = new Client({ apiKey: "wos-..." });
await mem.add("she prefers tea over coffee", "alice");

// one call → short-term + long-term + context, ready for your LLM prompt
const ctx = await mem.recall("what does alice drink?", "alice");

Why

  • Pure semantic retrieval — no keyword/BM25. Identical recall in every language (한국어 · 日本語 · 中文 · English).
  • No LLM in the loopstore / search / recall never call a language model. You pay embeddings, not generation.
  • Bounded retrievalrecall() returns a small, fixed-size slice (~1,200 tokens) regardless of how much you've stored.

Methods

add · addTurn · addBulk · update · search · recall · history · stats · get · listMemories · delete · deleteAll

All methods take { userId } — it names the store: one isolated memory space per end-user, agent, or topic, then per account. WHO said each memory inside a store is the speaker tag below — storing the assistant's own words never needs a separate id.

Who said it (speakers)

Every memory can carry a speaker: "me" for the assistant's own words, or a person's name. Speakers are explicit, like stores: register a person once, then store under their name — a typo can never silently become a new person. Search accepts a speaker too, to recall one person's words only.

await mem.addSpeaker("Bob", "alice");        // once per person
await mem.add("I promised to send the report on Friday", "alice", { speaker: "me" });
await mem.add("Bob said the deadline moved to Tuesday", "alice", { speaker: "Bob" });
await mem.search("what did Bob say about deadlines?", "alice", 10, { speaker: "Bob" });

listSpeakers() shows the registered people with per-person memory counts; removeSpeaker() unregisters (memories stay, the tag goes). A store registers up to 50 people to start (a limit we plan to raise); "me" never needs registration and never counts against it.

Recall caching

Opt in per search and repeated or extended queries reuse the previous result at 10% of the normal rate (Tablet and Scroll models):

const hits = await mem.search("...the conversation so far...", "alice", 10,
                              { cache_control: { ttl: "5m" } });  // or "1h"

Reliability

Built in, no configuration needed:

  • Automatic retries — 429 / 502 / 503 and connection errors retry twice with exponential backoff + jitter, honoring the server's Retry-After. Tune with new Client({ maxRetries }); 0 disables.
  • Redirects refused — the API key never follows a 3xx to another host.
  • Timeouts — 30s per attempt by default (timeoutMs).
  • Key never in logsJSON.stringify(client) and Node's console.log(client) print a masked key.
  • Wipe guarddelete() without a memoryId throws instead of silently meaning "delete everything"; wiping a store is only ever the explicit deleteAll(userId) / deleteStore(userId).

Security

Built in, none of it configurable off:

  • Redirects refused — a 3xx is an error, so the key never follows one to another host.
  • Response size cap — anything over 64MB is refused instead of buffered.
  • Key hygiene — keys are trimmed (a stray newline from an env var otherwise becomes a mystery 401) and inner whitespace is rejected; model names are validated before they reach a header.
  • Client.fromEnv() reads WONTOPOS_API_KEY (or WOS_API_KEY) — keep keys out of source code.
  • TLS certificate verification is never touched (runtime defaults; Node 18+ floors TLS at 1.2). Plain-HTTP base URLs on non-local hosts warn. Zero runtime dependencies.

Typed responses

Every method returns a documented shape — StoreResult, UpdateResult, RecallResult, EngramResult, StatsResult, SpeakersList, Memory[], ... — each with an index signature, so new server fields flow through without an SDK update. search options are typed too (SearchOptions: cache_control, speaker, plus pass-through).

const hits: Memory[] = await mem.search("what did Bob say?", "alice", 10, { speaker: "Bob" });
console.log(hits[0].speaker, hits[0].time_bucket);   // typed: string | undefined

Errors

Any non-2xx response throws WosError with status, message, and — when the server sent one — requestId (include it when contacting support).

import { Client, WosError } from "wontopos";

try {
  await mem.search("...", "alice");
} catch (e) {
  if (e instanceof WosError) {
    if (e.status === 401) console.error("API key invalid or revoked");
    else if (e.status === 429) console.error("Rate limited");   // already retried twice by then
    else console.error(e.status, e.message, e.requestId);
  }
}

Self-host

const mem = new Client({ apiKey: "...", baseUrl: "https://wos.your-host.com" });

Links

Changelog

  • 2.2.10Memory fix: the relevance field is similarity (not score, which was never in the response); added typed importance, category, is_superseded, superseded_by, created_at, event_date.
  • 2.2.9 — typed responses for every method (index-signature friendly) + typed SearchOptions; security round 2: 64MB response cap, key/model-name hygiene, Client.fromEnv().
  • 2.2.8 — reliability + hardening: automatic retries (429/502/503 + connection errors, backoff + jitter, honors Retry-After), redirects refused, key masked in toJSON/inspect, WosError.requestId, guard against delete() without memoryId, plain-HTTP warning, User-Agent.
  • 2.2.7 — speakers: addSpeaker / listSpeakers / removeSpeaker.
  • 2.2.6 — docs: userId = store, clarified everywhere.
  • 2.2.5 — docs: speakers + recall caching sections.
  • 2.2.4 — Python/TypeScript/Rust converge on one version; all three now release in lockstep (same version, same surface). Patch releases are additive.

License: MIT.