wontopos 2.2.26 → 2.2.27

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.
@@ -418,7 +418,14 @@ export declare class Client {
418
418
  addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
419
419
  /** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
420
420
  update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
421
- /** Semantic search over a store's memories. Returns memories, most relevant first. */
421
+ /** Semantic search over a store's memories. Returns memories, most relevant first.
422
+ *
423
+ * `limit` bounds the MAIN lane, not the returned array. On a self-lane model
424
+ * (Scroll 1.2+) the service answers in two disjoint lanes — the assistant's own
425
+ * words come back separately — and both are returned here, so the array can hold
426
+ * more than `limit`. They were retrieved and billed either way; dropping them would
427
+ * only hide what you already paid for. Size a prompt window on the array you get
428
+ * back, not on `limit`. `searchSelf()` hands the lanes back apart. */
422
429
  search(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<Memory[]>;
423
430
  /** Search a self-memory model (Scroll 1.2+): both lanes from ONE call. Returns
424
431
  * `{ memories, self_memories }` — `memories` is the main lane (what others said /
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.26";
40
+ const VERSION = "2.2.27";
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). */
@@ -91,6 +91,17 @@ const IDEMPOTENCY_KEY_RE = /^[A-Za-z0-9._:\-]{1,128}$/;
91
91
  function normalizeStoreId(id) {
92
92
  return id.toLowerCase().replace(/[^a-z0-9_]/g, "_");
93
93
  }
94
+ /** Ids already warned about, so each is reported once.
95
+ *
96
+ * Bounded on purpose. The warning fires on ids that fold, and the shape it exists
97
+ * to catch is an email — which this SDK's own documented pattern ("one store per
98
+ * end user") turns into one entry PER USER, held for the life of the process and
99
+ * never released, even when the client is. A server passing 50,000 user ids grew
100
+ * this to 50,000 entries, a leak in exactly the case being warned about. A `Set`
101
+ * keeps insertion order, so the oldest entry is the one evicted; dropping the
102
+ * oldest rather than refusing new ones means a collision that first shows up late
103
+ * still gets its one warning. */
104
+ const WARNED_STORE_IDS_MAX = 1024;
94
105
  const warnedStoreIds = new Set();
95
106
  function warnIfStoreIdCollapses(id) {
96
107
  if (!id || warnedStoreIds.has(id))
@@ -99,6 +110,12 @@ function warnIfStoreIdCollapses(id) {
99
110
  if (normalized === id)
100
111
  return;
101
112
  warnedStoreIds.add(id);
113
+ while (warnedStoreIds.size > WARNED_STORE_IDS_MAX) {
114
+ const oldest = warnedStoreIds.values().next().value;
115
+ if (oldest === undefined)
116
+ break;
117
+ warnedStoreIds.delete(oldest);
118
+ }
102
119
  console.warn(`wontopos: store id ${JSON.stringify(id)} is stored as ${JSON.stringify(normalized)} ` +
103
120
  `(lowercased, and anything outside [a-z0-9_] becomes "_"). Ids that differ only by case or ` +
104
121
  `punctuation share ONE store and therefore one set of memories — if these ids come from your ` +
@@ -473,7 +490,14 @@ export class Client {
473
490
  return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts.idempotencyKey);
474
491
  }
475
492
  // ----- read -----
476
- /** Semantic search over a store's memories. Returns memories, most relevant first. */
493
+ /** Semantic search over a store's memories. Returns memories, most relevant first.
494
+ *
495
+ * `limit` bounds the MAIN lane, not the returned array. On a self-lane model
496
+ * (Scroll 1.2+) the service answers in two disjoint lanes — the assistant's own
497
+ * words come back separately — and both are returned here, so the array can hold
498
+ * more than `limit`. They were retrieved and billed either way; dropping them would
499
+ * only hide what you already paid for. Size a prompt window on the array you get
500
+ * back, not on `limit`. `searchSelf()` hands the lanes back apart. */
477
501
  async search(query, userId, limit = 10, opts = {}) {
478
502
  // Reserved fields win over ...opts: an app that forwards untrusted input as
479
503
  // opts must not be able to override the store (user_id), query, or limit.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.26",
3
+ "version": "2.2.27",
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",