wontopos 2.2.17 → 2.2.19

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.
@@ -171,6 +171,13 @@ export interface MemoryPage {
171
171
  next_cursor: string | null;
172
172
  [key: string]: unknown;
173
173
  }
174
+ /** `searchSelf` result: the main lane plus the assistant's own words, split. */
175
+ export interface SelfSearchResult {
176
+ /** Main lane — what others said / general memories. */
177
+ memories: Memory[];
178
+ /** The assistant's own words (stored with speaker "me"); `[]` on non-self models. */
179
+ self_memories: Memory[];
180
+ }
174
181
  export interface ModelInfo {
175
182
  id: string;
176
183
  name: string;
@@ -286,10 +293,25 @@ export declare class Client {
286
293
  update(oldMemoryId: string, newContent: string, userId?: string): Promise<UpdateResult>;
287
294
  /** Semantic search over a store's memories. Returns memories, most relevant first. */
288
295
  search(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<Memory[]>;
289
- /** One-call LLM context: short-term turns + long-term matches + surrounding context. */
290
- recall(query: string, userId?: string): Promise<RecallResult>;
291
- /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result. */
292
- engram(name: string, query: string, userId?: string): Promise<EngramResult>;
296
+ /** Search a self-memory model (Scroll 1.2+): both lanes from ONE call. Returns
297
+ * `{ memories, self_memories }` — `memories` is the main lane (what others said /
298
+ * general memories), `self_memories` is the assistant's OWN words (stored with
299
+ * speaker "me"), kept separate so the reader never confuses who said what. On a model
300
+ * without a self lane, `self_memories` is `[]`. `userId` optional (default store). */
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
+ * `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>;
293
315
  /** Recent conversation turns (short-term memory). */
294
316
  history(userId?: string): Promise<HistoryTurn[]>;
295
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.19";
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,20 +343,32 @@ 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);
347
+ }
348
+ /** Search a self-memory model (Scroll 1.2+): both lanes from ONE call. Returns
349
+ * `{ memories, self_memories }` — `memories` is the main lane (what others said /
350
+ * general memories), `self_memories` is the assistant's OWN words (stored with
351
+ * speaker "me"), kept separate so the reader never confuses who said what. On a model
352
+ * without a self lane, `self_memories` is `[]`. `userId` optional (default store). */
353
+ async searchSelf(query, userId, limit = 10, opts = {}) {
354
+ 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) };
332
356
  }
333
- /** One-call LLM context: short-term turns + long-term matches + surrounding context. */
334
- recall(query, userId) {
335
- 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));
336
362
  }
337
- /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result. */
338
- engram(name, query, userId) {
339
- 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));
340
367
  }
341
368
  /** Recent conversation turns (short-term memory). */
342
369
  async history(userId) {
343
370
  const r = await this.post("/api/v1/memory/history", { user_id: this.uid(userId) });
344
- return (r.turns ?? []);
371
+ return asList(r.turns);
345
372
  }
346
373
  /** Memory counts for a store: { total_memories, short_term_turns }. */
347
374
  stats(userId) {
@@ -357,7 +384,7 @@ export class Client {
357
384
  if (!memoryId || typeof memoryId !== "string") {
358
385
  throw new Error("memoryId is required — the id that add/store or listMemories returned.");
359
386
  }
360
- 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));
361
388
  }
362
389
  /** List a store's stored memories — the original text plus metadata, never the
363
390
  * vector. Paginated: pass the returned `next_cursor` back as `cursor` for the next
@@ -389,7 +416,7 @@ export class Client {
389
416
  // server that mints a FRESH cursor every page forever — bound the walk.
390
417
  for (let page = 0; page < MAX_PAGES; page++) {
391
418
  const p = await this.listMemories(userId, { limit: opts.pageSize ?? 100, cursor });
392
- for (const m of p.memories ?? [])
419
+ for (const m of asList(p.memories))
393
420
  yield m;
394
421
  const next = p.next_cursor ?? undefined;
395
422
  if (!next || seen.has(next))
@@ -416,7 +443,7 @@ export class Client {
416
443
  /** Available models: `[{ id, name, available, memory }, ...]`. Needs no API key. */
417
444
  async listModels() {
418
445
  const data = await this.request("GET", "/api/v1/models");
419
- return (data.models ?? []);
446
+ return asList(data.models);
420
447
  }
421
448
  // ----- stores -----
422
449
  /** Create a store — the `userId` you read and write under. Stores are explicit:
@@ -429,7 +456,7 @@ export class Client {
429
456
  /** List your stores: `[{ user_id, created_at }, ...]` (`default` first). */
430
457
  async listStores() {
431
458
  const r = await this.request("GET", "/api/v1/memory/collections");
432
- return (r.collections ?? []);
459
+ return asList(r.collections);
433
460
  }
434
461
  /** Delete a store and ALL its memories. Returns `{ user_id, status }`. */
435
462
  deleteStore(userId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.17",
3
+ "version": "2.2.19",
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",