wontopos 2.2.33 → 2.2.35

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
@@ -4,10 +4,13 @@
4
4
  npm install wontopos
5
5
  ```
6
6
 
7
+ Get an API key in the [console](https://wontopos.com). Keys look like `wos-live-...`;
8
+ the client also reads `WONTOPOS_API_KEY` from the environment.
9
+
7
10
  ```ts
8
11
  import { Client } from "wontopos";
9
12
 
10
- const mem = new Client({ apiKey: "wos-..." });
13
+ const mem = new Client({ apiKey: "wos-live-..." });
11
14
 
12
15
  // Each end-user / agent / topic gets its own store — create it once.
13
16
  // (A `default` store already exists, so you can skip this and omit the id.)
@@ -20,15 +23,23 @@ const ctx = await mem.recall("what does alice drink?", "alice");
20
23
 
21
24
  ## Why
22
25
 
23
- - **Pure semantic retrieval** — no keyword/BM25. Identical recall in every language (한국어 · 日本語 · 中文 · English).
24
- - **No LLM in the loop** — `store` / `search` / `recall` never call a language model. You pay embeddings, not generation.
26
+ - **The same in every language** — identical recall whichever language a memory was written in (Korean · Japanese · Chinese · English).
27
+ - **No LLM in the loop** — `store` / `search` / `recall` never call a language model. You pay retrieval, not generation.
25
28
  - **Bounded retrieval** — `recall()` returns a small, fixed-size slice (~1,200 tokens) regardless of how much you've stored.
26
29
 
27
30
  ## Methods
28
31
 
29
- `add` · `addTurn` · `addBulk` · `update` · `search` · `recall` · `history` · `stats` · `get` · `listMemories` · `delete` · `deleteAll`
32
+ `add` · `addTurn` · `addBulk` · `update` · `search` · `searchFull` · `recall` · `history` · `stats` · `get` · `listMemories` · `delete` · `deleteAll`
30
33
 
31
- All methods take `{ userId }` — it names the **store**: one isolated memory space per end-user, agent, or topic, then per account. WHO said each memory inside a store is the `speaker` tag below — storing the assistant's own words never needs a separate id.
34
+ Where the store id goes depends on whether the method takes a payload. A method that
35
+ writes or searches puts the payload first and the store second — `mem.add("...", "alice")`,
36
+ `mem.search("...", "alice")`. A method that only addresses a store takes it first —
37
+ `mem.listMemories("alice")`, `mem.stats("alice")`, `mem.delete("alice", memoryId)`.
38
+ Most of them let you leave it out for the client's default store. Four take it
39
+ positionally and cannot — `get`, `delete`, `lineage` and `forgetImage` want an explicit
40
+ `undefined` there, because a second required argument follows it. Python omits it in
41
+ all four; this is the one place the three clients differ in shape rather than meaning.
42
+ Let your editor confirm the order: every signature is typed. A store is one isolated memory space per end-user, agent, or topic. WHO said each memory inside a store is the `speaker` tag below — storing the assistant's own words never needs a separate id.
32
43
 
33
44
  ## Who said it (speakers)
34
45
 
@@ -69,7 +80,12 @@ const hits = await mem.search("...the conversation so far...", "alice", 10,
69
80
 
70
81
  Built in, no configuration needed:
71
82
 
72
- - **Automatic retries** — 429 / 502 / 503 and connection errors retry twice with
83
+ - **Automatic retries** — 429 always, and 502 / 503 or a connection error only when a
84
+ retry cannot double-process a write. The writes and the searches are POSTs, and a
85
+ 502 on one of those may have been returned *after* the service already stored and
86
+ billed it, so those get 429 and connect-level failures only. The reads and the
87
+ deletes that address a whole store — `listStores`, `listSpeakers`, `deleteStore`,
88
+ `removeSpeaker`, `forgetImage` — are GET or DELETE and do retry a 502. Twice, with
73
89
  exponential backoff + jitter, honoring the server's `Retry-After`. Tune with
74
90
  `new Client({ maxRetries })`; `0` disables.
75
91
  - **Redirects refused** — the API key never follows a 3xx to another host.
@@ -87,6 +103,9 @@ egress proxy this SDK could not connect at all — while the Python and Rust SDK
87
103
  through, because `requests` and `reqwest` both read those variables. Pass a
88
104
  proxy-aware `fetch` and the three behave the same:
89
105
 
106
+ This one needs `npm i undici` — Node's global `fetch` is built on undici, but the
107
+ package itself is not installed for you.
108
+
90
109
  ```ts
91
110
  import { ProxyAgent } from "undici";
92
111
 
@@ -126,10 +145,17 @@ update. `search` options are typed too (`SearchOptions`: `cache_control`,
126
145
  `speaker`, plus pass-through).
127
146
 
128
147
  ```ts
148
+ import { type Memory } from "wontopos";
149
+
129
150
  const hits: Memory[] = await mem.search("what did Bob say?", "alice", 10, { speaker: "Bob" });
130
151
  console.log(hits[0].speaker, hits[0].time_bucket); // typed: string | undefined
131
152
  ```
132
153
 
154
+ `hits` arrives best-first — take it in the order given. `similarity` is a raw
155
+ closeness score, not the ranking key: what produces the order is internal and is
156
+ not returned, so sorting by `similarity` overrides the ranking and makes results
157
+ worse. There is no `score` field.
158
+
133
159
  ## Errors
134
160
 
135
161
  Any non-2xx response throws `WosError` with `status`, `message`, and — when the
@@ -162,8 +188,7 @@ const mem = new Client({ apiKey: "...", baseUrl: "https://wos.your-host.com" });
162
188
 
163
189
  ## Reporting a bug
164
190
 
165
- Found something wrong, or something that looks unsafe? Tell us — one person reads
166
- every report.
191
+ Found something wrong, or something that looks unsafe? Tell us — every report gets read.
167
192
 
168
193
  - Bugs: <https://wontopos.com/contact?topic=bug>
169
194
  - Security: <https://wontopos.com/contact?topic=security> (also published at
@@ -177,7 +202,60 @@ data to fix it.
177
202
 
178
203
  The three clients release in lockstep — same version, same surface, same day.
179
204
  Patch releases are additive: nothing is removed or reordered within a minor line.
180
-
205
+ Two have bent that, deliberately and named at the top of their entry — 2.2.34 moved the
206
+ default engine, and 2.2.35 gives `search` the count range `recall` has always had. A
207
+ rule you can bend without saying so is not a rule, so both are stated rather than left
208
+ for a reader to hit.
209
+
210
+ - **2.2.35** — fixes, and **two behaviour changes**, both listed first because a patch
211
+ release is not the place to find one by surprise.
212
+
213
+ ⚠️ **`search`'s count is now 5–20, and out of range is refused rather than
214
+ adjusted.** `recall` has carried that range from the start; search had no contract
215
+ anywhere, so the clients sent whatever they were given and the service capped at 50
216
+ with no floor. `search(q, limit=50)` and `search(q, limit=3)` both worked on 2.2.34
217
+ and now raise. The default is still 10, so a call that passes no count is unaffected.
218
+ Asking for 20 and silently getting 10 reads as "that is all there is", which is why
219
+ this refuses instead of clamping.
220
+
221
+ ⚠️ **A Rust `limit` of 0 is no longer rewritten to 10.** It was, and the caller whose
222
+ prompt budget computed zero was handed ten memories and the bill for them. It is now
223
+ refused under the range above rather than quietly changed to something nobody asked
224
+ for.
225
+
226
+ The fixes: a `null` idempotency key no longer becomes the literal key `"null"`, which
227
+ had made every write on that path share one key so the second onward stored nothing
228
+ (TypeScript). The async client no longer reads an explicit `retries=2` as "the caller
229
+ said nothing" (Python). The image read is actually streamed, so the 64MB cap bounds a
230
+ compressed body instead of measuring bytes already in memory (Python async), and that
231
+ route's ERROR body is capped too (Rust). `iter_images` stops on a repeated cursor the
232
+ way `iter_memories` always did (Python). `iterImages` no longer throws on the ordinary
233
+ end of a walk (TypeScript). `get_image` retries a 429 and a connect-level failure like
234
+ every other call — it had none at all — in the **sync Python client and TypeScript**;
235
+ the async Python client and the Rust crate still make a single attempt there, and say
236
+ so at the method. An empty or control-character API key is refused at the call site
237
+ rather than at the network in **Python and Rust**; TypeScript still refuses only empty
238
+ and whitespace. And a page walk that reaches its ceiling now raises instead of
239
+ returning a truncated list that looks complete.
240
+ - **2.2.34** — the default engine is `tablet-2`: same token price as `tablet-1`, and
241
+ the one that serves images and re-ask. Pin the old one with
242
+ `model="tablet-1"` for the previous behaviour exactly. (The engine's own default
243
+ count differs between the two, but these SDKs always send one, so a call through
244
+ them is unaffected.) New `search_full` /
245
+ `searchFull` keeps every field the answer came with — `search` was merging away
246
+ the photos and `verify_used`, both of which you were billed for. Also: results arrive
247
+ best-first — take the order as given. `similarity` is a raw closeness score, and
248
+ sorting by it makes results worse.
249
+ - **2.2.33** — `get_image` can return a different FORMAT than you uploaded: the
250
+ service re-encodes on downscale, so a large PNG comes back as WebP. Take the file
251
+ extension from the response Content-Type, not from what you sent.
252
+ - **2.2.32** — `get_image` returns the picture the SERVICE holds, not your original.
253
+ An image whose long edge is over 1568px is downscaled on the way in, and that
254
+ smaller picture is what is stored and handed back. Keep your own copy if you need
255
+ the full file.
256
+ - **2.2.31** — images, by-speaker, lineage and revisions are SDK methods in all three
257
+ clients; `search` and `recall` take `verify`, `max_images` and recall's `limit` /
258
+ `context_limit`.
181
259
  - **2.2.30** — documentation only: this changelog, which had not been updated
182
260
  in the shipped package since 2.2.10.
183
261
  - **2.2.29** — a store id that was PASSED but unusable no longer becomes the
@@ -15,12 +15,15 @@
15
15
  * so with no `userId` anywhere the zero-setup path just works.
16
16
  *
17
17
  * The API key picks *which memory* (your account); `model` picks *which engine*
18
- * reads it. All models share one memory, so you can store
19
- * with one and recall with another. Set a default in the constructor, or override
20
- * a single call with `mem.withModel("tablet-1").recall(...)`.
18
+ * reads it. Models on the shared pool read the same memory, so you can store with
19
+ * one and recall with another; `listModels()` reports each model's `memory` as
20
+ * `"shared"` or `"isolated"`, and an isolated one starts empty. Set a default in
21
+ * the constructor, or override a single call with
22
+ * `mem.withModel("tablet-1").recall(...)`.
21
23
  *
22
- * Pure semantic retrieval — no keyword/BM25 matching, so it works the SAME in every
23
- * language. Storing and searching call no LLM; you only pay for embeddings.
24
+ * Recall quality does not depend on which language a memory was written in: a
25
+ * memory stored in one language is found by a question asked in another. Storing
26
+ * and searching call no LLM.
24
27
  *
25
28
  * Reliability: every call retries transient failures with exponential backoff +
26
29
  * jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors only
@@ -39,6 +42,8 @@
39
42
  */
40
43
  /** Test hook — the warn-once set is process-global by design. */
41
44
  export declare function _resetStoreIdWarnings(): void;
45
+ export declare const SEARCH_LIMIT_MIN = 5;
46
+ export declare const SEARCH_LIMIT_MAX = 20;
42
47
  /** Quota from a response's `X-RateLimit-*` headers, or `null` if absent. */
43
48
  export interface RateLimit {
44
49
  limit: number | null;
@@ -84,8 +89,8 @@ export declare class RateLimitError extends WosError {
84
89
  * `501` is NOT transient. It means the engine behind the model you selected does
85
90
  * not implement that endpoint at all (the error carries `model` and `endpoint`).
86
91
  * Retrying can never succeed — pick a model that supports it (`listModels`) or
87
- * drop the call. This class used to say "safe to retry" for the whole 5xx range,
88
- * which sent callers into a loop that could not end.
92
+ * drop the call. Treating the whole 5xx range as retryable sends a caller into a
93
+ * loop that cannot end.
89
94
  */
90
95
  export declare class ServerError extends WosError {
91
96
  }
@@ -99,9 +104,12 @@ export interface Memory {
99
104
  content?: string;
100
105
  /** Cognitive category, e.g. "general". */
101
106
  category?: string;
102
- /** Semantic relevance to the query — higher is closer. This IS the relevance score; there is no `score` field. */
107
+ /** Raw closeness to the query — higher is closer. NOT the ranking key: results already
108
+ * arrive best-first, and what produces that order is internal and not returned, so
109
+ * re-sorting by `similarity` overrides the ranking and makes results worse. Take the
110
+ * list in the order given. There is no `score` field. */
103
111
  similarity?: number;
104
- /** Learned importance weight of the memory. */
112
+ /** Importance weight the service assigns to the memory. */
105
113
  importance?: number;
106
114
  /** Month bucket the memory belongs to (e.g. "2026-07"). Omitted when temporal fields are stripped. */
107
115
  time_bucket?: string;
@@ -125,11 +133,11 @@ export interface StoreResult {
125
133
  /**
126
134
  * Present when `status` is `"duplicate"`: the id of the memory this write collided with.
127
135
  *
128
- * The match is by MEANING, not by exact text — a genuinely new fact that only varies a
129
- * detail of one already stored ("allergic to shellfish" next to "allergic to peanuts")
130
- * lands here too. So a duplicate is not always a harmless no-op: read this id, and
131
- * either merge the two into one sentence or `update()` the existing memory. Without it
132
- * a dropped write gave the caller nothing to act on.
136
+ * A genuinely new fact that only varies a detail of one already stored ("no meetings
137
+ * before 10am" next to "no meetings on Fridays") can land here too, so a duplicate is
138
+ * not always a harmless no-op: read this id, and either store one sentence that states
139
+ * both or `update()` the existing memory with the combined statement. Without it a
140
+ * dropped write gave the caller nothing to act on.
133
141
  */
134
142
  duplicate_of?: string;
135
143
  /** Present when the store was a duplicate and something (e.g. a speaker tag) was dropped. */
@@ -150,9 +158,9 @@ export interface StatusResult {
150
158
  *
151
159
  * What the service keeps is NOT your original. An image whose long edge is over
152
160
  * 1568px is downscaled to 1568 before anything else happens, and that smaller
153
- * picture is what gets embedded, stored, and handed back by `getImage`. Nothing in
154
- * the retrieval path ever looks above 1568 — the embedder and the reader both
155
- * shrink to it — so the extra pixels would be bytes nobody reads. This is a memory
161
+ * picture is what gets stored, and handed back by `getImage`. Nothing on our side
162
+ * ever uses more than 1568, so the extra pixels would be bytes nobody reads. This
163
+ * is a memory
156
164
  * service, not a photo host: the picture it holds has the resolution of a memory.
157
165
  *
158
166
  * It can also come back in a different FORMAT. Downscaling means re-encoding, and
@@ -231,7 +239,7 @@ export interface RevisionsResult {
231
239
  revised?: number;
232
240
  /** Memories nothing has touched since they were written. Always `total - revised`. */
233
241
  unrevised?: number;
234
- /** Every memory in the store. Atoms — the fragments search uses internally — are not counted. */
242
+ /** Every memory in the store. Internal records nobody stored directly are not counted. */
235
243
  total?: number;
236
244
  /** What `revised` counts, spelled out by the service. */
237
245
  counts?: string;
@@ -280,7 +288,7 @@ export interface LineageResult {
280
288
  export interface SpeakerPage {
281
289
  speaker?: string;
282
290
  memories: Memory[];
283
- /** Sentence-level fragments behind these memories — what a delete would actually remove. */
291
+ /** How many underlying records a delete would actually remove, beyond what was returned. */
284
292
  chunks?: number;
285
293
  points_to_delete?: number;
286
294
  returned?: number;
@@ -296,6 +304,26 @@ export interface UpdateResult {
296
304
  status?: string;
297
305
  [key: string]: unknown;
298
306
  }
307
+ /** `searchFull` → everything one search answered with, not just the merged memories.
308
+ *
309
+ * `search` returns the memories and nothing else, which is the right answer for
310
+ * almost every call. Two options make it the wrong one: `max_images` asks for
311
+ * photos, which arrive in their own field, and `verify` is reported on by
312
+ * `verify_used`. Merging away both means paying for a
313
+ * search you shaped and never seeing what came of it. */
314
+ export interface SearchResult {
315
+ /** What others said, and general memories. */
316
+ memories: Memory[];
317
+ /** The assistant's own words (speaker "me"); `[]` on a model that does not keep
318
+ * them apart. */
319
+ self_memories: Memory[];
320
+ /** Image memories, when `max_images` asked for any; `[]` otherwise. */
321
+ images: Memory[];
322
+ /** Re-ask passes actually performed. Present only when `verify` was sent; lower
323
+ * than you asked for means the store had nothing further to add. */
324
+ verify_used?: number;
325
+ [key: string]: unknown;
326
+ }
299
327
  export interface RecallResult {
300
328
  short_term: {
301
329
  turns: unknown[];
@@ -333,7 +361,7 @@ export interface StatsResult {
333
361
  short_term_turns?: number;
334
362
  [key: string]: unknown;
335
363
  }
336
- /** `listMemories` → one page of a store's raw memories (original text + metadata, no vector). */
364
+ /** `listMemories` → one page of a store's raw memories (the text you stored, and its metadata). */
337
365
  export interface MemoryPage {
338
366
  memories: Memory[];
339
367
  count: number;
@@ -341,9 +369,9 @@ export interface MemoryPage {
341
369
  next_cursor: string | null;
342
370
  [key: string]: unknown;
343
371
  }
344
- /** `searchSelf` result: the main lane plus the assistant's own words, split. */
372
+ /** `searchSelf` result: general memories and the assistant's own, kept apart. */
345
373
  export interface SelfSearchResult {
346
- /** Main lane — what others said / general memories. */
374
+ /** What others said, and general memories. */
347
375
  memories: Memory[];
348
376
  /** The assistant's own words (stored with speaker "me"); `[]` on non-self models. */
349
377
  self_memories: Memory[];
@@ -406,10 +434,11 @@ export interface SpeakersList {
406
434
  [key: string]: unknown;
407
435
  }
408
436
  /**
409
- * Narrow a search to part of a store — applied BEFORE ranking, so the results are
410
- * the best matches *within* the filter, not a filtered top-N.
437
+ * Narrow a search to part of a store. The filter chooses what is searched, not what
438
+ * is kept afterwards — so a narrow filter still returns your full `limit` when that
439
+ * many matches sit inside it.
411
440
  *
412
- * Retrieval itself stays purely semantic (no keyword matching), so these behave
441
+ * Retrieval behaves the same in every language, so these behave
413
442
  * identically in every language. Unlisted keys are dropped by the API rather than
414
443
  * rejected, so a typo silently widens the search — spell them exactly.
415
444
  */
@@ -443,7 +472,7 @@ export interface SearchOptions {
443
472
  /**
444
473
  * Re-ask passes, 0–3. After the first retrieval the service asks the engine again up
445
474
  * to this many times, each time telling it what was already returned so it skips
446
- * those and reaches further back. Retrieval stays purely semantic — no LLM at any
475
+ * those and reaches further back. No LLM runs at any
447
476
  * value. Stops early when a pass finds nothing new; `verify_used` in the response
448
477
  * says how many actually ran.
449
478
  *
@@ -452,7 +481,7 @@ export interface SearchOptions {
452
481
  * that need several distinct memories from far apart in the history; it does little
453
482
  * on a single-fact lookup.
454
483
  *
455
- * ⚠️Requires a re-ask-capable model. Older ones REFUSE the call (403) rather than
484
+ * Requires a re-ask-capable model. Older ones REFUSE the call (403) rather than
456
485
  * quietly charging you for passes that never happened.
457
486
  */
458
487
  verify?: number;
@@ -461,7 +490,7 @@ export interface SearchOptions {
461
490
  * Out of range is refused, not clamped — silently cutting 5 to 1 would leave you
462
491
  * believing you got five.
463
492
  *
464
- * ⚠️Requires an image-capable model, and is refused (403) on one without it rather
493
+ * Requires an image-capable model, and is refused (403) on one without it rather
465
494
  * than answering with no images.
466
495
  */
467
496
  max_images?: number;
@@ -473,8 +502,11 @@ export interface SearchOptions {
473
502
  * `idempotencyKey` makes repeating THIS EXACT write safe: the API replays the first
474
503
  * response instead of storing again, for 10 minutes, and answers 422 if the same key
475
504
  * arrives with a different body. Use it when a retry is your own (a job that died and
476
- * was re-run, a queue that redelivers) — the SDK never retries writes by itself,
477
- * because without a key it cannot know whether the first attempt landed.
505
+ * was re-run, a queue that redelivers). The SDK retries a write on exactly one
506
+ * status: 429, which the service answers before it processes anything, so nothing
507
+ * was stored. It never retries a write on 502 / 503 or a dropped body, where the
508
+ * request may already have been stored and billed — without a key it cannot know
509
+ * whether that first attempt landed.
478
510
  *
479
511
  * The key must be UNIQUE PER LOGICAL WRITE — derive it from the thing being stored
480
512
  * (`` `import:${row.id}` ``), never a constant, or the second write replays the first
@@ -578,8 +610,7 @@ export declare class Client {
578
610
  withRetries(maxRetries: number): Client;
579
611
  /** Store one memory. `metadata` optional — e.g. `{ event_date: "2026-03-01" }` for when the
580
612
  * content actually happened. A plain date works (read as UTC midnight); a full RFC3339
581
- * timestamp also works. The API used to demand the full form and answered a bare date with
582
- * an opaque 400, which is why older examples here show the long one.
613
+ * timestamp also works; older examples here show the long form.
583
614
  * `metadata.speaker`: "me" = the assistant's own words, or a person's name (up to 50 per store).
584
615
  *
585
616
  * `opts.idempotencyKey`: see {@link WriteOptions} — pass one to make YOUR retry
@@ -589,27 +620,42 @@ export declare class Client {
589
620
  store(content: string, userId?: string, metadata?: Record<string, unknown>, opts?: WriteOptions): Promise<StoreResult>;
590
621
  /** Store a conversation turn (user + assistant). Payload first, userId last — same shape as add/search. */
591
622
  addTurn(userMsg: string, assistantMsg: string, userId?: string, opts?: WriteOptions): Promise<StatusResult>;
592
- /** Bulk-ingest a large blob of text (chunked + embedded server-side). For backfilling.
623
+ /** Bulk-ingest a large blob of text in one call. For backfilling.
593
624
  * The call most worth an `opts.idempotencyKey`: a backfill that dies halfway and is
594
- * re-run would otherwise re-chunk and re-bill the whole blob. */
625
+ * re-run would otherwise re-ingest and re-bill the whole blob. */
595
626
  addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
596
627
  /** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
597
628
  update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
598
- /** Semantic search over a store's memories. Returns memories, most relevant first.
599
- *
600
- * `limit` bounds the MAIN lane, not the returned array. On a self-lane model
601
- * (Scroll 1.2+) the service answers in two disjoint lanes — the assistant's own
602
- * words come back separately — and both are returned here, so the array can hold
603
- * more than `limit`. They were retrieved and billed either way; dropping them would
604
- * only hide what you already paid for. Size a prompt window on the array you get
605
- * back, not on `limit`. `searchSelf()` hands the lanes back apart. */
629
+ /** Search a store's memories. Returns them most relevant first.
630
+ *
631
+ * `limit` bounds `memories`, not the returned array. On a model that keeps the
632
+ * assistant's own words separate (Scroll 1.2+) those come back as well, so the
633
+ * array can hold more than `limit`. They were retrieved and billed either way;
634
+ * dropping them would only hide what you already paid for. Size a prompt window
635
+ * on the array you get back, not on `limit`. `searchSelf()` hands the two back
636
+ * apart. */
606
637
  search(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<Memory[]>;
607
- /** Search a self-memory model (Scroll 1.2+): both lanes from ONE call. Returns
608
- * `{ memories, self_memories }` — `memories` is the main lane (what others said /
609
- * general memories), `self_memories` is the assistant's OWN words (stored with
610
- * speaker "me"), kept separate so the reader never confuses who said what. On a model
611
- * without a self lane, `self_memories` is `[]`. `userId` optional (default store). */
638
+ /** Search a self-memory model (Scroll 1.2+): both fields from ONE call. Returns
639
+ * `{ memories, self_memories }` — `memories` is what others said and general
640
+ * memories, `self_memories` is the assistant's OWN words (stored with speaker
641
+ * "me"), kept apart so whoever reads them never confuses who said what. On a model
642
+ * that does not keep them apart, `self_memories` is `[]`. `userId` may be omitted. */
612
643
  searchSelf(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<SelfSearchResult>;
644
+ /**
645
+ * Search, and keep every field the answer came with.
646
+ *
647
+ * Same request as {@link Client.search} — reach for this one when the options make
648
+ * the merged array an incomplete answer:
649
+ *
650
+ * const r = await mem.searchFull("the day we moved", "alice", 10,
651
+ * { max_images: 3, verify: 2 });
652
+ * r.images.length; // the photos, which `search` drops
653
+ * r.verify_used; // how many re-ask passes actually ran (you are billed per pass)
654
+ *
655
+ * Both options need Tablet 2 or newer and are refused (403) on an older engine
656
+ * rather than accepted and ignored.
657
+ */
658
+ searchFull(query: string, userId?: string, limit?: number, opts?: SearchOptions): Promise<SearchResult>;
613
659
  /** One-call LLM context: short-term turns + long-term matches + surrounding context.
614
660
  * `form` ("memoir"/"archive", Scroll 1.2+) renders each long-term memory's time in
615
661
  * that form; `tz` is your UTC-offset hours for that rendering. */
@@ -620,16 +666,18 @@ export declare class Client {
620
666
  * Long-term memories to recall, 5–20. Default 10. Out of range is refused, not
621
667
  * clamped — asking for 20 and silently getting 10 reads as "that is all there is".
622
668
  *
623
- * ⚠️Needs a limit-aware model. An older one recalls a fixed ten no matter what you
669
+ * Needs a limit-aware model. An older one recalls a fixed ten no matter what you
624
670
  * send, so the API refuses the call (403) rather than answering with a number you
625
671
  * did not ask for.
626
672
  */
627
673
  limit?: number;
628
- /** Chunks of surrounding context attached around the best match, 0–20. Default 10;
674
+ /** How much surrounding context is attached around the best match, 0–20. Default 10;
629
675
  * 0 attaches none. Same model floor as `limit`. */
630
676
  context_limit?: number;
631
677
  }): Promise<RecallResult>;
632
- /** Run a built-in engram ("deep_recall" | "timeline" | "gather"). Returns the merged result.
678
+ /** Run a built-in engram ("deep_recall" | "timeline" | "gather" | "equilibrium" |
679
+ * "tone_stabilizer"; the service is the authority — an unknown name comes back with
680
+ * the list it accepts). Returns the merged result.
633
681
  * `form`/`tz` render memory times (memoir/archive) on Scroll 1.2+, same as search/recall. */
634
682
  engram(name: string, query: string, userId?: string, opts?: {
635
683
  form?: string;
@@ -639,13 +687,14 @@ export declare class Client {
639
687
  history(userId?: string): Promise<HistoryTurn[]>;
640
688
  /** Memory counts for a store: { total_memories, short_term_turns }. */
641
689
  stats(userId?: string): Promise<StatsResult>;
642
- /** Fetch ONE memory by id — the original text + metadata, never the vector.
690
+ /** Fetch ONE memory by id — the text you stored, and its metadata.
643
691
  * The id is what `add`/`store` or `listMemories` returned. Same visibility as
644
- * `listMemories`: an id from another store, a decomposed atom sentence, or an
645
- * invalidated memory rejects with `NotFoundError`. `userId` optional (default store). */
692
+ * `listMemories`: an id from another store, an internal record id, or an
693
+ * invalidated memory rejects with `NotFoundError`. Pass `undefined` as `userId` for
694
+ * the default store — it is positional here, not omittable as it is in Python. */
646
695
  get(userId: string | undefined, memoryId: string): Promise<Memory>;
647
- /** List a store's stored memories — the original text plus metadata, never the
648
- * vector. Paginated: pass the returned `next_cursor` back as `cursor` for the next
696
+ /** List a store's stored memories — the text you stored, plus its metadata.
697
+ * Paginated: pass the returned `next_cursor` back as `cursor` for the next
649
698
  * page; a `null` cursor means the last page. Use it to browse or export a store.
650
699
  *
651
700
  * let cursor: string | null = null;
@@ -661,14 +710,14 @@ export declare class Client {
661
710
  cursor?: string;
662
711
  }): Promise<MemoryPage>;
663
712
  /** Async-iterate every stored memory in a store, paging under the hood — no cursor
664
- * bookkeeping. Original text + metadata only, never vectors.
713
+ * bookkeeping. The text you stored and its metadata only.
665
714
  *
666
715
  * for await (const m of mem.iterMemories()) console.log(m.id, m.content);
667
716
  */
668
717
  iterMemories(userId?: string, opts?: {
669
718
  pageSize?: number;
670
719
  }): AsyncGenerator<Memory>;
671
- /** Collect ALL of a store's memories into an array (original text + metadata, no vectors). */
720
+ /** Collect ALL of a store's memories into an array (the text you stored, and its metadata). */
672
721
  exportMemories(userId?: string): Promise<Memory[]>;
673
722
  /**
674
723
  * Fetch the bytes of an image memory — the picture the SERVICE holds.
@@ -676,8 +725,8 @@ export declare class Client {
676
725
  * Not necessarily your upload, in size OR in format: an image whose long edge was
677
726
  * over 1568px was downscaled to 1568 on the way in and re-encoded — lossless
678
727
  * formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG — and that
679
- * smaller picture is what was embedded, stored and comes back here. Nothing in the
680
- * retrieval path looks above 1568, so the extra pixels would be bytes nobody reads.
728
+ * smaller picture is what is stored and comes back here. Nothing on our side ever
729
+ * uses more than 1568, so the extra pixels would be bytes nobody reads.
681
730
  * Keep your own copy for the full-resolution file.
682
731
  *
683
732
  * The type is sniffed from the bytes themselves rather than from whatever the file
@@ -735,10 +784,10 @@ export declare class Client {
735
784
  *
736
785
  * Counts memories a transform touched (supersede, update, retract, image removed).
737
786
  * Deletions are NOT counted: a deleted memory leaves nothing to count. Neither are
738
- * atoms, the sentence fragments search uses internally — nobody stored one, so they
739
- * do not belong in a ratio that answers "how much of MY memory changed".
787
+ * the internal records derived from what you stored — nobody stored those directly,
788
+ * so they do not belong in a ratio that answers "how much of MY memory changed".
740
789
  *
741
- * ★By default this returns COUNTS ONLY, and the answer is the same size for a store
790
+ * By default this returns COUNTS ONLY, and the answer is the same size for a store
742
791
  * of a hundred memories and a store of a hundred million. That is deliberate: a model
743
792
  * asks this mid-conversation, and an answer that grew with the store would be
744
793
  * unusable for exactly the customers who most need to ask.
@@ -759,10 +808,10 @@ export declare class Client {
759
808
  * }
760
809
  * ```
761
810
  *
762
- * ⚠️Pages are ordered by when each memory was STORED, not by when it was edited.
811
+ * Pages are ordered by when each memory was STORED, not by when it was edited.
763
812
  * The response says so in `ordered_by`.
764
813
  *
765
- * ★Served from `/api/v1/won/*`, not `/api/v1/memory/*`. Won is the surface for calls
814
+ * Served from `/api/v1/won/*`, not `/api/v1/memory/*`. Won is the surface for calls
766
815
  * a model makes ABOUT its memory rather than calls an application makes WITH it, and
767
816
  * the address says so. The old path still answers, for clients published before
768
817
  * 2026-08-18, and both share one rate-limit budget.
@@ -795,9 +844,9 @@ export declare class Client {
795
844
  * assistant's own words, otherwise a person's name. Same cursor paging as
796
845
  * `listImages`.
797
846
  *
798
- * `chunks` / `points_to_delete` report the sentence-level fragments behind these
799
- * memories, which is what a delete would actually remove — usually larger than
800
- * `returned`, and worth showing before anyone confirms one.
847
+ * `chunks` / `points_to_delete` report how many internal records a delete would
848
+ * actually remove — usually more than `returned`, and worth showing to whoever is
849
+ * about to confirm one.
801
850
  */
802
851
  bySpeaker(speaker: string, userId?: string, opts?: {
803
852
  limit?: number;
@@ -813,10 +862,9 @@ export declare class Client {
813
862
  /**
814
863
  * The engrams (and delivery forms) the selected model can actually run.
815
864
  *
816
- * `engram()` could always RUN one, but there was no way to ASK what exists — so a
817
- * caller had to hard-code names from the docs, and every engram we shipped after
818
- * their code was written stayed invisible to them. The MCP server hit exactly this
819
- * and stopped hard-coding for the same reason. The service is the authority: what
865
+ * Ask rather than hard-code: a name copied from the docs freezes a caller to the
866
+ * catalogue as it was that day, and anything added later stays invisible. The
867
+ * service is the authority. What
820
868
  * comes back depends on the model (delivery forms need Scroll 1.2+), so pass
821
869
  * `withModel()` if you want another model's catalogue.
822
870
  *
@@ -840,7 +888,8 @@ export declare class Client {
840
888
  listSpeakers(userId?: string): Promise<SpeakersList>;
841
889
  /** Unregister a person. Their memories stay; the name tag goes. */
842
890
  removeSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
843
- /** Delete a single memory by id. `userId` optional (uses the default store). */
891
+ /** Delete a single memory by id. Pass `undefined` as `userId` for the default
892
+ * store — it is positional here, not omittable as it is in Python. */
844
893
  delete(userId: string | undefined, memoryId: string): Promise<StatusResult>;
845
894
  /** Delete ALL memories for a store (GDPR erase). `userId` is required on purpose -
846
895
  * this is destructive, so it never falls back to the default store. */
@@ -870,10 +919,9 @@ export declare class Client {
870
919
  private readCapped;
871
920
  /** The same cap, for a body that is not text.
872
921
  *
873
- * `getImage` used to call `arrayBuffer()` here, which buffers whatever arrives. The
874
- * cap exists for a hostile or broken `baseUrl`, and a photo endpoint is exactly where
875
- * that shows up — the JSON paths were guarded while the one path that returns
876
- * megabytes was not. One implementation, two callers, so they cannot drift. */
922
+ * `arrayBuffer()` would buffer whatever arrives. The cap exists for a hostile or
923
+ * broken `baseUrl`, and an endpoint that returns megabytes is where that costs the
924
+ * most. One implementation, two callers, so they cannot drift. */
877
925
  private readCappedBytes;
878
926
  private request;
879
927
  }