wontopos 2.2.18 → 2.2.20

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
@@ -299,10 +299,19 @@ export declare class Client {
299
299
  * speaker "me"), kept separate so the reader never confuses who said what. On a model
300
300
  * without a self lane, `self_memories` is `[]`. `userId` optional (default store). */
301
301
  searchSelf(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<SelfSearchResult>;
302
- /** One-call LLM context: short-term turns + long-term matches + surrounding context. */
303
- recall(query: string, userId?: string): Promise<RecallResult>;
304
- /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result. */
305
- engram(name: string, query: string, userId?: string): Promise<EngramResult>;
302
+ /** One-call LLM context: short-term turns + long-term matches + surrounding context.
303
+ * `form` ("memoir"/"archive", Scroll 1.2+) renders each long-term memory's time in
304
+ * that form; `tz` is your UTC-offset hours for that rendering. */
305
+ recall(query: string, userId?: string, opts?: {
306
+ form?: string;
307
+ tz?: number;
308
+ }): Promise<RecallResult>;
309
+ /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result.
310
+ * `form`/`tz` render memory times (memoir/archive) on Scroll 1.2+, same as search/recall. */
311
+ engram(name: string, query: string, userId?: string, opts?: {
312
+ form?: string;
313
+ tz?: number;
314
+ }): Promise<EngramResult>;
306
315
  /** Recent conversation turns (short-term memory). */
307
316
  history(userId?: string): Promise<HistoryTurn[]>;
308
317
  /** Memory counts for a store: { total_memories, short_term_turns }. */
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.17";
40
+ const VERSION = "2.2.20";
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). */
@@ -123,6 +123,21 @@ function parseRateLimit(h) {
123
123
  return null;
124
124
  return { limit, remaining, reset };
125
125
  }
126
+ // A list/object field the API promises. `x ?? []` only replaces null/undefined — a
127
+ // broken or hostile server sending a truthy wrong type (`"memories": "oops"`) would
128
+ // slip through as a string and blow up the caller's `for (…of…)`. Coerce anything
129
+ // that isn't actually an array/object to the empty value.
130
+ const asList = (x) => (Array.isArray(x) ? x : []);
131
+ const asObj = (x) => (x != null && typeof x === "object" && !Array.isArray(x) ? x : {});
132
+ // Attach a delivery-form override (memoir/archive) + tz to a body, the way search
133
+ // passes them; on Scroll 1.2+ this renders each returned memory's time in that form.
134
+ function withForm(body, form, tz) {
135
+ if (form !== undefined)
136
+ body.form = form;
137
+ if (tz !== undefined)
138
+ body.tz = tz;
139
+ return body;
140
+ }
126
141
  export class WosError extends Error {
127
142
  status;
128
143
  /** The server's id for the request when it sent one — include it when contacting support. */
@@ -328,7 +343,7 @@ export class Client {
328
343
  // Reserved fields win over ...opts: an app that forwards untrusted input as
329
344
  // opts must not be able to override the store (user_id), query, or limit.
330
345
  const r = await this.post("/api/v1/memory/search", { ...opts, user_id: this.uid(userId), query, max_results: limit });
331
- return (r.memories ?? []);
346
+ return asList(r.memories);
332
347
  }
333
348
  /** Search a self-memory model (Scroll 1.2+): both lanes from ONE call. Returns
334
349
  * `{ memories, self_memories }` — `memories` is the main lane (what others said /
@@ -337,20 +352,23 @@ export class Client {
337
352
  * without a self lane, `self_memories` is `[]`. `userId` optional (default store). */
338
353
  async searchSelf(query, userId, limit = 10, opts = {}) {
339
354
  const r = await this.post("/api/v1/memory/search", { ...opts, user_id: this.uid(userId), query, max_results: limit });
340
- return { memories: (r.memories ?? []), self_memories: (r.self_memories ?? []) };
355
+ return { memories: asList(r.memories), self_memories: asList(r.self_memories) };
341
356
  }
342
- /** One-call LLM context: short-term turns + long-term matches + surrounding context. */
343
- recall(query, userId) {
344
- return this.post("/api/v1/memory/recall", { user_id: this.uid(userId), query });
357
+ /** One-call LLM context: short-term turns + long-term matches + surrounding context.
358
+ * `form` ("memoir"/"archive", Scroll 1.2+) renders each long-term memory's time in
359
+ * that form; `tz` is your UTC-offset hours for that rendering. */
360
+ recall(query, userId, opts = {}) {
361
+ return this.post("/api/v1/memory/recall", withForm({ user_id: this.uid(userId), query }, opts.form, opts.tz));
345
362
  }
346
- /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result. */
347
- engram(name, query, userId) {
348
- return this.post("/api/v1/engram/run", { name, user_id: this.uid(userId), query });
363
+ /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result.
364
+ * `form`/`tz` render memory times (memoir/archive) on Scroll 1.2+, same as search/recall. */
365
+ engram(name, query, userId, opts = {}) {
366
+ return this.post("/api/v1/engram/run", withForm({ name, user_id: this.uid(userId), query }, opts.form, opts.tz));
349
367
  }
350
368
  /** Recent conversation turns (short-term memory). */
351
369
  async history(userId) {
352
370
  const r = await this.post("/api/v1/memory/history", { user_id: this.uid(userId) });
353
- return (r.turns ?? []);
371
+ return asList(r.turns);
354
372
  }
355
373
  /** Memory counts for a store: { total_memories, short_term_turns }. */
356
374
  stats(userId) {
@@ -366,7 +384,7 @@ export class Client {
366
384
  if (!memoryId || typeof memoryId !== "string") {
367
385
  throw new Error("memoryId is required — the id that add/store or listMemories returned.");
368
386
  }
369
- return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId }).then((r) => (r.memory ?? {}));
387
+ return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId }).then((r) => asObj(r.memory));
370
388
  }
371
389
  /** List a store's stored memories — the original text plus metadata, never the
372
390
  * vector. Paginated: pass the returned `next_cursor` back as `cursor` for the next
@@ -398,7 +416,7 @@ export class Client {
398
416
  // server that mints a FRESH cursor every page forever — bound the walk.
399
417
  for (let page = 0; page < MAX_PAGES; page++) {
400
418
  const p = await this.listMemories(userId, { limit: opts.pageSize ?? 100, cursor });
401
- for (const m of p.memories ?? [])
419
+ for (const m of asList(p.memories))
402
420
  yield m;
403
421
  const next = p.next_cursor ?? undefined;
404
422
  if (!next || seen.has(next))
@@ -425,7 +443,7 @@ export class Client {
425
443
  /** Available models: `[{ id, name, available, memory }, ...]`. Needs no API key. */
426
444
  async listModels() {
427
445
  const data = await this.request("GET", "/api/v1/models");
428
- return (data.models ?? []);
446
+ return asList(data.models);
429
447
  }
430
448
  // ----- stores -----
431
449
  /** Create a store — the `userId` you read and write under. Stores are explicit:
@@ -438,7 +456,7 @@ export class Client {
438
456
  /** List your stores: `[{ user_id, created_at }, ...]` (`default` first). */
439
457
  async listStores() {
440
458
  const r = await this.request("GET", "/api/v1/memory/collections");
441
- return (r.collections ?? []);
459
+ return asList(r.collections);
442
460
  }
443
461
  /** Delete a store and ALL its memories. Returns `{ user_id, status }`. */
444
462
  deleteStore(userId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.18",
3
+ "version": "2.2.20",
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",