SDK de TypeScript

TypeScript - todos los métodos, tres grupos.

Escribir, leer, eliminar. Cada ejemplo de abajo se ejecutó contra la API en vivo el 2026-08-01; las respuestas son textuales.

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

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

Elige un modelo

La clave de API elige qué memoria (tu cuenta); el modelo elige qué motor la lee. Todos los modelos comparten una misma memoria, así que puedes almacenar con uno y recuperar con otro. Define un valor por defecto en el constructor; anula una llamada puntual con 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

El catálogo - los ids que puedes pasar a model y si cada uno está disponible. Los modelos con memory: "shared" leen el mismo store; "isolated" mantiene el suyo propio. No requiere clave de API.

await mem.listModels();
Respuesta real
[{"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

Confirma la conexión y que tu clave de API funciona: una comprobación de una línea.

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

El catálogo de arriba siempre refleja los modelos disponibles en este momento - pasa cualquier otro id y obtendrás un error claro. Los modelos nuevos aparecen ahí automáticamente cuando se lanzan.

Escribir

add

Almacena una memoria. Sin llamada a LLM al entrar: pagas solo la tarifa de escritura.

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

addTurn

Almacena un turno de conversación (usuario + asistente) en la memoria de corto y largo plazo a la vez.

await mem.addTurn("hi", "hello!", "alice");
Respuesta real
{"status": "ok"}

speaker

Cada recuerdo puede llevar quién lo dijo. Registra a una persona una vez y luego pasa su nombre como speaker; "me" (las palabras del propio asistente) nunca necesita registro. La búsqueda también acepta speaker, para recordar solo las palabras de una persona.

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" });
Los hablantes son explícitos, como los almacenes. Registra primero a la persona y luego guarda bajo su nombre: una errata nunca se convierte en silencio en una persona nueva. Un almacén registra hasta 50 personas para empezar (pensamos subirlo), y "me" nunca necesita registro ni cuenta.

addBulk

Carga un bloque grande de texto. Se divide e indexa en el servidor, ideal para importar historial existente.

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

update

Un hecho cambió. La memoria antigua se marca como reemplazada (se conserva como historial); la nueva ocupa su lugar en el recall.

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

Leer

search

Búsqueda semántica, lo más relevante primero. Cualquier idioma encuentra cualquier memoria, sin importar en qué idioma se escribió. El SDK devuelve directamente el array memories; el cuerpo HTTP en bruto se muestra abajo. Algunos modelos responden con más de un conjunto de resultados y el SDK los devuelve fusionados, así que el array puede contener MÁS de max_results. Dimensiona tu ventana de prompt según lo que recibes, no según el número que pediste.

const r = await mem.search("what does she drink?", "alice", 1);
Respuesta real (cuerpo 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"
 }]
CampoSignificado
similarityQué tan cerca está esta memoria de tu consulta (0–1).
is_supersededTrue si este hecho fue reemplazado por update().
search_msTiempo de recuperación del lado del servidor.

recall

Un solo viaje de ida y vuelta devuelve todo lo que tu LLM necesita - pega el resultado directamente en tu prompt: un contexto acotado y de tamaño fijo sin importar cuánto hayas almacenado.

const ctx = await mem.recall("what does she drink?", "alice");
Respuesta real (forma - listas acortadas)
{"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

Turnos de conversación recientes (memoria de corto plazo), los más antiguos primero.

const turns = await mem.history("alice");
Respuesta real (cuerpo 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

Conteos de memoria de un usuario.

await mem.stats("alice");
Respuesta real
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}

get

Recupera una memoria por id - el id que devolvió add o list_memories. Solo el texto original almacenado y sus metadatos. Un id de otro store, o una memoria borrada o invalidada, devuelve 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

Lista las memorias de un almacén: solo el texto original que guardaste y sus metadatos. Paginado por cursor: reenvía el next_cursor devuelto para la página siguiente.

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

Recorre todas las memorias sin gestionar el cursor, o trae el almacén entero de una vez.

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

Eliminar

delete

Elimina una sola memoria por id.

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

deleteAll

Borra todo lo de un usuario - una llamada, lista para GDPR.

await mem.deleteAll("alice");
Respuesta real
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}

Errores y fiabilidad

Cada fallo es un error tipado: captura el concreto (límite de tasa, autenticación, pago) o todos con el WosError 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

Lee la cuota restante tras cualquier llamada y reduce el ritmo antes de llegar al límite.

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

searchSelf

Ambos carriles en una sola llamada en un modelo de memoria propia (Scroll 1.2+): lo que dijeron otros y las palabras PROPIAS del agente, separadas para que quien lee nunca confunda quién habló.

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

listEngrams

Pregunta al servicio qué engramas y formas de entrega puede ejecutar el modelo seleccionado, en vez de fijar nombres que quedan obsoletos en cuanto sale uno nuevo.

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

filters

Acota la búsqueda a una parte del almacén. Se aplica antes del ranking, así que obtienes las mejores coincidencias dentro del filtro, no un top-N filtrado después.

await mem.search("what did we decide", "alice", 10, {
  filters: { categories: ["work"], event_from: "2026-01-01" },  // when it HAPPENED
});
Claves: categories · event_from / event_to (cuándo OCURRIÓ el contenido - metadata.event_date) · time_from / time_to (cuándo se escribió) · min_importance. Las claves no listadas se descartan, no se rechazan, así que una errata amplía la búsqueda en silencio.

idempotencyKey

Hace segura la repetición de una escritura. Úsala cuando el reintento es tuyo - un trabajo que murió y se relanzó, una cola que reentrega.

await mem.add("she prefers tea", "alice", {}, { idempotencyKey: `import:${row.id}` });
Deriva la clave de aquello que se almacena (import:row-42), nunca una constante: una clave reutilizada en dos escrituras distintas reproduce la primera y la segunda se pierde en silencio. Formato: 1-128 caracteres de [A-Za-z0-9._:-].

withTimeout / withRetries / withDeadline

Ajusta un único punto de llamada sin tocar el cliente que ya construiste: un clon con más tiempo de espera para un backfill grande, o sin reintentos dentro de tu propio bucle de reintento.

timeout acota un intento, así que una llamada que reintenta puede sobrevivirlo: con los valores por defecto una sola llamada puede retener una conexión 30 s, esperar, reintentar y reintentar otra vez. deadline acota la llamada entera: cada intento se limita a lo que queda, y ninguna espera se duerme más allá del presupuesto. Ponlo cuando quien llama tiene un límite real, como un manejador de peticiones con cinco segundos.

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