TypeScript-SDK

TypeScript - jede Methode, drei Gruppen.

Schreiben, Lesen, Löschen. Jedes Beispiel unten wurde am 2026-08-01 gegen die Live-API ausgeführt; die Antworten sind wortwörtlich wiedergegeben.

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

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

Modell wählen

Der API-Schlüssel bestimmt, welches Gedächtnis (Ihr Konto); das Modell bestimmt, welche Engine es liest. Alle Modelle teilen sich ein Gedächtnis, Sie können also mit einem speichern und mit einem anderen abrufen. Setzen Sie einen Standard im Konstruktor; überschreiben Sie einen einzelnen Aufruf mit 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

Der Katalog - die IDs, die Sie an model übergeben können, und ob sie jeweils live sind. Modelle mit memory: "shared" lesen denselben Store; "isolated" führt einen eigenen. Benötigt keinen API-Schlüssel.

await mem.listModels();
Tatsächliche Antwort
[{"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

Verbindung und Gültigkeit des API-Schlüssels in einer Zeile prüfen.

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

Der Katalog oben spiegelt stets die aktuell verfügbaren Modelle wider - übergeben Sie eine andere ID, erhalten Sie einen klaren Fehler. Neue Modelle erscheinen dort automatisch, sobald sie ausgeliefert werden.

Schreiben

add

Eine Erinnerung speichern. Kein LLM-Aufruf beim Eingang - Sie zahlen nur den Schreibtarif.

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
Tatsächliche Antwort
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}

addTurn

Einen Gesprächszug (Nutzer + Assistent) zugleich ins Kurzzeit- und Langzeitgedächtnis speichern.

await mem.addTurn("hi", "hello!", "alice");
Tatsächliche Antwort
{"status": "ok"}

speaker

Jede Erinnerung kann tragen, wer sie gesagt hat. Registriere eine Person einmal und übergib danach ihren Namen als speaker; "me" (die eigenen Worte des Assistenten) braucht nie eine Registrierung. Auch die Suche akzeptiert speaker, um nur die Worte einer Person zu holen.

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" });
Sprecher sind explizit, wie Stores. Registriere die Person zuerst, speichere dann unter ihrem Namen: ein Tippfehler wird nie still zu einer neuen Person. Ein Store registriert zunächst bis zu 50 Personen (wir planen mehr), und "me" braucht nie Registrierung und zählt nie.

addBulk

Einen großen Textblock nachladen. Serverseitig aufgeteilt und indexiert - ideal für den Import bestehender Historie.

await mem.addBulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", "alice");
Tatsächliche Antwort
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}

update

Ein Fakt hat sich geändert. Die alte Erinnerung wird als ersetzt markiert (für die Historie behalten); die neue nimmt ihren Platz im Recall ein.

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

Lesen

search

Semantische Suche, das Relevanteste zuerst. Jede Sprache findet jede Erinnerung, in welcher Sprache sie auch geschrieben wurde. Das SDK gibt das memories-Array direkt zurück; der rohe HTTP-Body steht unten. Manche Modelle antworten mit mehr als einem Ergebnissatz, und das SDK gibt sie zusammengeführt zurück - das Array kann also MEHR als max_results enthalten. Bemessen Sie Ihr Prompt-Fenster an dem, was Sie erhalten, nicht an der angefragten Zahl.

const r = await mem.search("what does she drink?", "alice", 1);
Tatsächliche Antwort (HTTP-Body)
[{
   "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"
 }]
FeldBedeutung
similarityWie nah diese Erinnerung an Ihrer Abfrage liegt (0–1).
is_supersededTrue, wenn dieser Fakt durch update() ersetzt wurde.
search_msServerseitige Abrufzeit.

recall

Ein Roundtrip liefert alles, was Ihr LLM braucht - fügen Sie das Ergebnis direkt in Ihren Prompt ein: ein begrenzter Kontext fester Größe, egal wie viel Sie gespeichert haben.

const ctx = await mem.recall("what does she drink?", "alice");
Tatsächliche Antwort (Struktur - Listen gekürzt)
{"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

Jüngste Gesprächszüge (Kurzzeitgedächtnis), älteste zuerst.

const turns = await mem.history("alice");
Tatsächliche Antwort (HTTP-Body)
{"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

Anzahl der Erinnerungen für einen Nutzer.

await mem.stats("alice");
Tatsächliche Antwort
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}

get

Ruft eine einzelne Erinnerung per id ab - die id, die add oder list_memories zurückgegeben hat. Nur der gespeicherte Originaltext plus Metadaten. Eine id aus einem anderen Store oder eine gelöschte/invalidierte Erinnerung ergibt 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

Listet die Erinnerungen eines Stores auf - nur der gespeicherte Originaltext und seine Metadaten. Seitenweise per Cursor: den zurückgegebenen next_cursor für die nächste Seite mitgeben.

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

Alle Erinnerungen ohne Cursor-Verwaltung durchlaufen oder den ganzen Store auf einmal holen.

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

Löschen

delete

Eine einzelne Erinnerung per ID löschen.

await mem.delete("alice", "576700aa-...");
Tatsächliche Antwort
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}

deleteAll

Alles für einen Nutzer löschen - ein Aufruf, DSGVO-bereit.

await mem.deleteAll("alice");
Tatsächliche Antwort
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}

Fehler und Zuverlässigkeit

Jeder Fehler ist typisiert - fange den konkreten (Rate-Limit, Auth, Zahlung) oder alle mit dem Basis-WosError.

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

Lies das verbleibende Kontingent nach jedem Aufruf und drossle, bevor du das Limit erreichst.

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

searchSelf

Beide Spuren aus einem Aufruf bei einem Selbstgedächtnis-Modell (Scroll 1.2+): was andere sagten und die EIGENEN Worte des Agenten - getrennt gehalten, damit der Leser nie verwechselt, wer was gesagt hat.

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

listEngrams

Frag den Dienst, welche Engramme und Delivery-Formen das gewählte Modell ausführen kann, statt Namen fest zu verdrahten, die veralten, sobald ein neues erscheint.

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

filters

Grenzt die Suche auf einen Teil des Stores ein. Wird vor dem Ranking angewendet, du bekommst also die besten Treffer innerhalb des Filters - kein nachträglich gefiltertes Top-N.

await mem.search("what did we decide", "alice", 10, {
  filters: { categories: ["work"], event_from: "2026-01-01" },  // when it HAPPENED
});
Schlüssel: categories · event_from / event_to (wann der Inhalt GESCHAH - metadata.event_date) · time_from / time_to (wann er geschrieben wurde) · min_importance. Nicht aufgeführte Schlüssel werden verworfen, nicht abgelehnt - ein Tippfehler erweitert die Suche also stillschweigend.

idempotencyKey

Macht das Wiederholen eines Schreibvorgangs sicher. Nutze ihn, wenn der Retry von dir kommt - ein Job, der abgebrochen und neu gestartet wurde, eine Queue, die erneut zustellt.

await mem.add("she prefers tea", "alice", {}, { idempotencyKey: `import:${row.id}` });
Leite den Schlüssel aus dem ab, was gespeichert wird (import:row-42), nie eine Konstante: ein Schlüssel, der für zwei verschiedene Schreibvorgänge wiederverwendet wird, spielt den ersten erneut ab - der zweite geht stillschweigend verloren. Format: 1-128 Zeichen aus [A-Za-z0-9._:-].

withTimeout / withRetries / withDeadline

Stelle eine einzelne Aufrufstelle ein, ohne den bereits gebauten Client anzufassen: ein Klon mit längerem Timeout für einen großen Backfill oder mit abgeschalteten Retries innerhalb deiner eigenen Retry-Schleife.

timeout begrenzt einen Versuch, deshalb kann ein Aufruf mit Wiederholungen ihn überdauern: mit den Standardwerten hält ein einzelner Aufruf eine Verbindung 30 s, wartet, versucht es erneut und noch einmal. deadline begrenzt stattdessen den gesamten Aufruf — jeder Versuch wird auf die verbleibende Zeit gedeckelt, und keine Wartezeit wird über das Budget hinaus verschlafen. Setzen Sie es, wenn der Aufrufer eine echte Grenze hat, etwa ein Request-Handler mit fünf Sekunden.

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