wontopos 2.2.19 → 2.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,10 @@ npm install wontopos
8
8
  import { Client } from "wontopos";
9
9
 
10
10
  const mem = new Client({ apiKey: "wos-..." });
11
+
12
+ // Each end-user / agent / topic gets its own store — create it once.
13
+ // (A `default` store already exists, so you can skip this and omit the id.)
14
+ await mem.createStore("alice");
11
15
  await mem.add("she prefers tea over coffee", "alice");
12
16
 
13
17
  // one call → short-term + long-term + context, ready for your LLM prompt
package/dist/wontopos.js CHANGED
@@ -37,7 +37,7 @@
37
37
  * runtime's (never disabled by this SDK; Node 18+ floors TLS at 1.2). Prefer
38
38
  * `Client.fromEnv()` over keys in source code.
39
39
  */
40
- const VERSION = "2.2.19";
40
+ const VERSION = "2.2.21";
41
41
  /** Runtime info helps support debug a report ("node 18 on Windows...") —
42
42
  * platform only, never anything identifying. Browsers have no `process` (and
43
43
  * silently drop the UA header anyway). */
@@ -129,6 +129,13 @@ function parseRateLimit(h) {
129
129
  // that isn't actually an array/object to the empty value.
130
130
  const asList = (x) => (Array.isArray(x) ? x : []);
131
131
  const asObj = (x) => (x != null && typeof x === "object" && !Array.isArray(x) ? x : {});
132
+ // A list of API records (memories, turns, models...) — every element is an object
133
+ // by contract. `asList` only fixes the CONTAINER: a hostile/broken server sending
134
+ // `{"memories": [null, 1, "x", {...}]}` still handed the caller those non-objects,
135
+ // typed as Memory[], and the first `m.content` threw a raw TypeError from inside
136
+ // user code. Drop elements that aren't objects and keep the valid records, so one
137
+ // bad element never poisons the batch (and never destroys it).
138
+ const asRecords = (x) => asList(x).filter((m) => m != null && typeof m === "object" && !Array.isArray(m));
132
139
  // Attach a delivery-form override (memoir/archive) + tz to a body, the way search
133
140
  // passes them; on Scroll 1.2+ this renders each returned memory's time in that form.
134
141
  function withForm(body, form, tz) {
@@ -343,7 +350,7 @@ export class Client {
343
350
  // Reserved fields win over ...opts: an app that forwards untrusted input as
344
351
  // opts must not be able to override the store (user_id), query, or limit.
345
352
  const r = await this.post("/api/v1/memory/search", { ...opts, user_id: this.uid(userId), query, max_results: limit });
346
- return asList(r.memories);
353
+ return asRecords(r.memories);
347
354
  }
348
355
  /** Search a self-memory model (Scroll 1.2+): both lanes from ONE call. Returns
349
356
  * `{ memories, self_memories }` — `memories` is the main lane (what others said /
@@ -352,7 +359,7 @@ export class Client {
352
359
  * without a self lane, `self_memories` is `[]`. `userId` optional (default store). */
353
360
  async searchSelf(query, userId, limit = 10, opts = {}) {
354
361
  const r = await this.post("/api/v1/memory/search", { ...opts, user_id: this.uid(userId), query, max_results: limit });
355
- return { memories: asList(r.memories), self_memories: asList(r.self_memories) };
362
+ return { memories: asRecords(r.memories), self_memories: asRecords(r.self_memories) };
356
363
  }
357
364
  /** One-call LLM context: short-term turns + long-term matches + surrounding context.
358
365
  * `form` ("memoir"/"archive", Scroll 1.2+) renders each long-term memory's time in
@@ -368,7 +375,7 @@ export class Client {
368
375
  /** Recent conversation turns (short-term memory). */
369
376
  async history(userId) {
370
377
  const r = await this.post("/api/v1/memory/history", { user_id: this.uid(userId) });
371
- return asList(r.turns);
378
+ return asRecords(r.turns);
372
379
  }
373
380
  /** Memory counts for a store: { total_memories, short_term_turns }. */
374
381
  stats(userId) {
@@ -416,7 +423,7 @@ export class Client {
416
423
  // server that mints a FRESH cursor every page forever — bound the walk.
417
424
  for (let page = 0; page < MAX_PAGES; page++) {
418
425
  const p = await this.listMemories(userId, { limit: opts.pageSize ?? 100, cursor });
419
- for (const m of asList(p.memories))
426
+ for (const m of asRecords(p.memories))
420
427
  yield m;
421
428
  const next = p.next_cursor ?? undefined;
422
429
  if (!next || seen.has(next))
@@ -443,7 +450,7 @@ export class Client {
443
450
  /** Available models: `[{ id, name, available, memory }, ...]`. Needs no API key. */
444
451
  async listModels() {
445
452
  const data = await this.request("GET", "/api/v1/models");
446
- return asList(data.models);
453
+ return asRecords(data.models);
447
454
  }
448
455
  // ----- stores -----
449
456
  /** Create a store — the `userId` you read and write under. Stores are explicit:
@@ -456,7 +463,7 @@ export class Client {
456
463
  /** List your stores: `[{ user_id, created_at }, ...]` (`default` first). */
457
464
  async listStores() {
458
465
  const r = await this.request("GET", "/api/v1/memory/collections");
459
- return asList(r.collections);
466
+ return asRecords(r.collections);
460
467
  }
461
468
  /** Delete a store and ALL its memories. Returns `{ user_id, status }`. */
462
469
  deleteStore(userId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.19",
3
+ "version": "2.2.21",
4
4
  "description": "Wontopos — long-term memory for AI agents. Pure semantic retrieval, identical recall in every language, ~100× lower LLM bill.",
5
5
  "keywords": [
6
6
  "memory",