SDK TypeScript

TypeScript - chaque méthode, en trois groupes.

Écrire, lire, supprimer. Chaque exemple ci-dessous a été exécuté contre l'API en production le 2026-08-01 ; les réponses sont verbatim.

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

const mem = new Client({ apiKey: "wos-live-..." });

Choisir un modèle

La clé API détermine quelle mémoire (votre compte) ; le modèle détermine quel moteur la lit. Tous les modèles partagent une même mémoire : vous pouvez donc stocker avec l'un et rappeler avec un autre. Définissez un défaut dans le constructeur ; surchargez un appel isolé avec withModel().

const mem = new Client({ apiKey: "wos-live-...", model: "tablet-1" });  // default
mem.recall("...", "alice");                          // tablet-1
mem.withModel("scroll-1").recall("...", "alice");  // or pick a model per call

listModels

Le catalogue - les ids que vous pouvez passer à model et la disponibilité de chacun. Les modèles memory: "shared" lisent le même store ; "isolated" garde le sien. Aucune clé API requise.

await mem.listModels();
Réponse réelle
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"},
 {"id": "tablet-2", "name": "Tablet 2", "available": true, "memory": "shared"},
 {"id": "scroll-1", "name": "Scroll 1", "available": true, "memory": "shared"},
 {"id": "scroll-1.2", "name": "Scroll 1.2", "available": true, "memory": "shared"}]

ping

Vérifiez la connexion et que votre clé API fonctionne : un contrôle en une ligne.

await mem.ping();   // true, or throws AuthenticationError / PaymentRequiredError

Le catalogue ci-dessus reflète toujours les modèles disponibles à l'instant même - passez tout autre id et vous obtenez une erreur claire. Les nouveaux modèles y apparaissent automatiquement dès leur sortie.

Écrire

add

Stocke un souvenir. Aucun appel LLM à l’entrée : vous ne payez que le tarif d’écriture.

await mem.add("she prefers tea over coffee", "alice");
await mem.add("I promised the summary by Friday", "alice", { speaker: "me" });  // its own words - no registration needed
Réponse réelle
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}

addTurn

Stocke un tour de conversation (utilisateur + assistant) à la fois en mémoire court terme et long terme.

await mem.addTurn("hi", "hello!", "alice");
Réponse réelle
{"status": "ok"}

speaker

Chaque souvenir peut porter son locuteur. Enregistrez une personne une fois, puis passez son nom comme speaker ; "me" (les mots de l'assistant) n'a jamais besoin d'enregistrement. La recherche accepte aussi speaker, pour ne rappeler que les mots d'une personne.

await mem.addSpeaker("Bob", "alice");  // once per person; "me" needs no registration
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" });
Les locuteurs sont explicites, comme les magasins. Enregistrez d'abord la personne, puis stockez sous son nom : une faute de frappe ne devient jamais silencieusement une nouvelle personne. Un magasin enregistre jusqu'à 50 personnes pour commencer (nous comptons relever ce plafond), et "me" n'a jamais besoin d'enregistrement ni ne compte.

addBulk

Importez un gros bloc de texte. Découpé et indexé côté serveur, idéal pour reprendre un historique existant.

await mem.addBulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", "alice");
Réponse réelle
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}

update

Un fait a changé. L'ancien souvenir est marqué superseded (conservé pour l'historique) ; le nouveau prend sa place dans le recall.

await mem.update("576700aa-...", "she switched to coffee this year", "alice");
Réponse réelle
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
 "old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}

Lire

search

Recherche sémantique, du plus pertinent au moins pertinent. N’importe quelle langue trouve n’importe quel souvenir, quelle que soit la langue dans laquelle il a été écrit. Le SDK renvoie directement le tableau memories ; le corps HTTP brut est affiché ci-dessous. Certains modèles répondent avec plusieurs ensembles de résultats et le SDK les renvoie fusionnés : le tableau peut donc contenir PLUS que max_results. Dimensionnez votre fenêtre de prompt sur ce que vous recevez, pas sur le nombre demandé.

const r = await mem.search("what does she drink?", "alice", 1);
Réponse réelle (corps HTTP)
[{
   "id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
   "content": "she prefers tea over coffee",
   "category": "general",
   "time_bucket": "2026-06",
   "importance": 0.3,
   "similarity": 0.6316057443618774,
   "is_superseded": false,
   "superseded_by": null,
   "created_at": "2026-07-10T04:20:39.688276876Z"
 }]
ChampSignification
similarityÀ quel point ce souvenir est proche de votre requête (0–1).
is_supersededVrai si ce fait a été remplacé par update().
search_msTemps de récupération côté serveur.

recall

Un seul aller-retour renvoie tout ce dont votre LLM a besoin - collez le résultat directement dans votre prompt : un contexte borné, de taille fixe, quel que soit le volume stocké.

const ctx = await mem.recall("what does she drink?", "alice");
Réponse réelle (forme - listes raccourcies)
{"short_term":  {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
 "long_term":   {"count": 4, "memories": [{"content": "she prefers tea over coffee",
                                           "similarity": 0.63, ...}]},
 "context":     {"count": 4, "around_top_memory": [
                  "[match] she prefers tea over coffee",
                  "[after] Alice moved to Brooklyn in March. ..."]},
 "instruction": "Use short_term for recent context, long_term for relevant
                 past memories, context for surrounding conversation of the
                 most relevant memory."}

history

Tours de conversation récents (mémoire court terme), du plus ancien au plus récent.

const turns = await mem.history("alice");
Réponse réelle (corps HTTP)
{"count": 2, "turns": [
   {"role": "user",      "content": "hi",     "timestamp": "2026-07-10T04:20:40.989011337Z"},
   {"role": "assistant", "content": "hello!", "timestamp": "2026-07-10T04:20:40.989013416Z"}
 ], "user_id": "alice"}

stats

Compteurs de souvenirs pour un utilisateur.

await mem.stats("alice");
Réponse réelle
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}

get

Récupère une mémoire par id - l'id renvoyé par add ou list_memories. Uniquement le texte original stocké et ses métadonnées. Un id d'un autre store, ou une mémoire supprimée ou invalidée, renvoie 404.

const m = await mem.get("alice", "576700aa-...");
response
{"id": "576700aa-...", "content": "she prefers tea over coffee",
 "category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
 "is_superseded": false, "superseded_by": null}

listMemories

Liste les mémoires d'un espace : uniquement le texte original que vous avez stocké et ses métadonnées. Paginé par curseur : renvoyez le next_cursor reçu pour la page suivante.

const page = await mem.listMemories("alice", { limit: 100 });
response
{"count": 2, "next_cursor": null, "memories": [
   {"id": "576700aa-...", "content": "she prefers tea over coffee",
    "category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
 "is_superseded": false, "superseded_by": null}
 ]}

iterMemories · exportMemories

Parcourez toutes les mémoires sans gérer le curseur, ou récupérez tout l'espace d'un coup.

for await (const m of mem.iterMemories("alice")) console.log(m.id, m.content);
const everything = await mem.exportMemories("alice");

Supprimer

delete

Supprime un souvenir unique par id.

await mem.delete("alice", "576700aa-...");
Réponse réelle
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}

deleteAll

Efface tout pour un utilisateur - un seul appel, conforme RGPD.

await mem.deleteAll("alice");
Réponse réelle
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}

Erreurs et fiabilité

Chaque échec est une erreur typée : attrapez le cas précis (limite de débit, authentification, paiement) ou tous avec le WosError de base.

import { NotFoundError, PaymentRequiredError } from "wontopos";
try {
  await mem.add("...", "alice");
} catch (e) {
  if (e instanceof NotFoundError) await mem.createStore("alice");
  else if (e instanceof PaymentRequiredError) topUp();   // out of credit
  else throw e;
}

rateLimit

Lisez le quota restant après chaque appel et ralentissez avant d'atteindre la limite.

await mem.search("...", "alice");
const rl = mem.rateLimit;   // { limit: 150, remaining: 3, reset: ... }

searchSelf

Les deux voies en un seul appel sur un modèle à mémoire de soi (Scroll 1.2+) : ce que les autres ont dit, et les propres mots de l'agent - séparés, pour que le lecteur ne confonde jamais qui a parlé.

const { memories, self_memories } = await mem.searchSelf("what did I promise?", "alice");
// memories = what others said · self_memories = the agent's OWN words

listEngrams

Demandez au service quels engrammes et formes de rendu le modèle sélectionné peut exécuter, au lieu de coder en dur des noms qui deviennent obsolètes dès qu'un nouveau sort.

const { engrams, forms } = await mem.listEngrams();  // ask, never hard-code

filters

Restreint la recherche à une partie du magasin. Appliqué avant le classement : vous obtenez les meilleures correspondances à l'intérieur du filtre, pas un top-N filtré après coup.

await mem.search("what did we decide", "alice", 10, {
  filters: { categories: ["work"], event_from: "2026-01-01" },  // when it HAPPENED
});
Clés : categories · event_from / event_to (quand le contenu S'EST PRODUIT - metadata.event_date) · time_from / time_to (quand il a été écrit) · min_importance. Les clés non listées sont ignorées, pas rejetées : une faute de frappe élargit donc la recherche en silence.

idempotencyKey

Rend sûr le fait de répéter une écriture. À utiliser quand la reprise vient de vous - un job interrompu puis relancé, une file qui redélivre.

await mem.add("she prefers tea", "alice", {}, { idempotencyKey: `import:${row.id}` });
Dérivez la clé de ce qui est stocké (import:row-42), jamais une constante : une clé réutilisée pour deux écritures différentes rejoue la première, et la seconde est perdue en silence. Format : 1 à 128 caractères parmi [A-Za-z0-9._:-].

withTimeout / withRetries / withDeadline

Ajustez un seul point d'appel sans toucher au client déjà construit : un clone avec un délai plus long pour une grosse reprise, ou sans reprises à l'intérieur de votre propre boucle de retry.

timeout borne une tentative, donc un appel qui réessaie peut lui survivre : avec les valeurs par défaut, un seul appel peut retenir une connexion 30 s, attendre, réessayer, puis réessayer encore. deadline borne l'appel entier : chaque tentative est plafonnée à ce qu'il reste, et aucune attente ne dort au-delà du budget. Réglez-le quand l'appelant a une vraie limite, comme un gestionnaire de requête disposant de cinq secondes.

await mem.withTimeout(120_000).addBulk(bigBlob, "alice");  // this slow call only
await mem.withRetries(0).add("...", "alice");            // you retry, not the SDK
await mem.withDeadline(5_000).recall("...", "alice");          // 5s for the whole call
await mem.withSignal(ctrl.signal).recall("...", "alice");      // caller can cancel