wontopos 2.2.45 → 2.2.46

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
@@ -24,8 +24,8 @@ const ctx = await mem.recall("what does alice drink?", "alice");
24
24
  ## Why
25
25
 
26
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.
28
- - **Bounded retrieval** — `recall()` returns a small, fixed-size slice regardless of how much you've stored (~1,000 tokens on `tablet-2`, the default engine).
27
+ - **No LLM reads your memories** — no language model reads your stored memories, on any model. You pay retrieval, not generation.
28
+ - **Bounded retrieval** — `recall()` returns the recent turns, at most `limit` long-term memories (default 10, up to 20) and at most `context_limit` rows of the surrounding conversation (default 10, up to 20), however much you've stored.
29
29
 
30
30
  ## Methods
31
31
 
@@ -38,7 +38,7 @@ writes or searches puts the payload first and the store second — `mem.add("...
38
38
  Most of them let you leave it out for the client's default store. Five take it
39
39
  positionally and cannot — `get`, `getImage`, `delete`, `lineage` and `forgetImage` want
40
40
  an explicit `undefined` there, because a second required argument follows it. Python
41
- omits it in all five; this is the one place the three clients differ in shape rather than meaning.
41
+ omits it in all five, and Rust takes it positionally (`None` for the default store, where there is one).
42
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.
43
43
 
44
44
  ## Who said it (speakers)
@@ -56,19 +56,22 @@ await mem.search("what did Bob say about deadlines?", "alice", 10, { speaker: "B
56
56
  ```
57
57
 
58
58
  `listSpeakers()` shows the registered people with per-person memory counts;
59
- `removeSpeaker()` unregisters (memories stay, the tag goes). A store registers
59
+ `removeSpeaker()` unregisters (memories stay; the tag goes, except on `tablet-2`, which keeps it on memories already stored). A store registers
60
60
  up to 50 people to start (a limit we plan to raise); `"me"` never needs
61
61
  registration and never counts against it.
62
62
 
63
63
  ## Recall caching
64
64
 
65
- Opt in per search and repeated or extended queries reuse the previous result
66
- at 10% of the normal rate (Tablet and Scroll models).
65
+ Opt in per search and a repeated query reuses the previous result at 10% of the
66
+ normal rate; an extended one reuses part of it (Tablet and Scroll models).
67
67
 
68
68
  It is not free to turn on: the FIRST call writes the cache and bills the query
69
- tokens at 2x for a `5m` TTL, 3x for `1h`. Only hits inside the TTL bill at 0.1x.
70
- So it pays for a query you repeat or extend, and costs more for one you issue
71
- once — do not switch it on globally. Any write to the store invalidates its cache
69
+ tokens at 2x for a `5m` TTL, 3x for `1h`. The same query again inside the TTL bills
70
+ its query and its results at 0.1x. A query that extends the cached one (its text plus
71
+ more) bills the cached part at 0.1x, the new part at the write rate and its results
72
+ at the normal rate. A store keeps one cached query per model and set of options, so a
73
+ different query replaces it. So it pays for a query you repeat or extend, and costs
74
+ more for one you issue once — do not switch it on globally. Any write to the store invalidates its cache
72
75
  at once, so a hit can never predate a new memory.
73
76
 
74
77
  ```ts
@@ -82,11 +85,11 @@ Built in, no configuration needed:
82
85
 
83
86
  - **Automatic retries** — 429 and a 409 for a write already in flight always, and 408 /
84
87
  502 / 503 / 504 or a connection error only when a retry cannot double-process a write.
85
- The writes and the searches are POSTs, and any of those four may have been returned
86
- *after* the write already landed, so those get 429, that 409 and connect-level
87
- failures only. The GET and DELETE calls — `ping`, `listStores`, `listSpeakers`,
88
- `listModels`, `listEngrams`, `usage`, `deleteStore`, `removeSpeaker`, `forgetImage` —
89
- retry all four. Twice, with exponential backoff + jitter, honoring the server's
88
+ A write may already have landed when any of those four comes back, so a POST write gets
89
+ 429, that 409 and connect-level failures only. The calls that are safe to send again
90
+ retry all four: the reads sent as POST (`search`, `recall`, `get`, `listMemories`,
91
+ `stats`, ...) and the GET and DELETE calls (`ping`, `listStores`, `listSpeakers`,
92
+ `listModels`, `listEngrams`, `usage`, `deleteStore`, `removeSpeaker`, `forgetImage`). Twice, with exponential backoff + jitter, honoring the server's
90
93
  `Retry-After` up to 30s. Tune with `new Client({ maxRetries })`; `0` disables.
91
94
  - **Redirects refused** — the API key never follows a 3xx to another host.
92
95
  - **Timeouts** — 30s per attempt by default (`timeoutMs`), and a total budget for
@@ -147,7 +150,8 @@ Built in, none of it configurable off:
147
150
  Every method returns a documented shape — `StoreResult`, `UpdateResult`,
148
151
  `RecallResult`, `EngramResult`, `StatsResult`, `SpeakersList`, `Memory[]`, ... —
149
152
  each with an index signature, so new server fields flow through without an SDK
150
- update. `search` options are typed too (`SearchOptions`: `cache_control`,
153
+ update. `searchSelf`'s result and `getImage`'s bytes are built by the client and
154
+ carry only their own fields. `search` options are typed too (`SearchOptions`: `cache_control`,
151
155
  `speaker`, plus `extra` for pass-through).
152
156
 
153
157
  ```ts
@@ -157,7 +161,8 @@ const hits: Memory[] = await mem.search("what did Bob say?", "alice", 10, { spea
157
161
  console.log(hits[0].speaker, hits[0].time_bucket); // typed: string | undefined
158
162
  ```
159
163
 
160
- `hits` arrives best-first — take it in the order given. `similarity` is a raw
164
+ `hits` holds memories best-first, then the assistant's own words and image memories in
165
+ their own order. Take it in the order given. `similarity` is a raw
161
166
  closeness score, not the ranking key: what produces the order is internal and is
162
167
  not returned, so sorting by `similarity` overrides the ranking and makes results
163
168
  worse. There is no `score` field.
@@ -14,7 +14,7 @@
14
14
  * (`createStore`) or calls return 404. Every account starts with a `default` store,
15
15
  * so with no `userId` anywhere the zero-setup path just works.
16
16
  *
17
- * The API key picks *which memory* (your account); `model` picks *which engine*
17
+ * The API key picks *which memory* (your workspace); `model` picks *which engine*
18
18
  * reads it. Models on the shared pool read the same memory, so you can store with
19
19
  * one and recall with another; `listModels()` reports each model's `memory` as
20
20
  * `"shared"` or `"isolated"`, and an isolated one starts empty. Set a default in
@@ -22,14 +22,14 @@
22
22
  * `mem.withModel("tablet-1").recall(...)`.
23
23
  *
24
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.
25
+ * memory stored in one language is found by a question asked in another. No language
26
+ * model reads your stored memories.
27
27
  *
28
28
  * Reliability: every call retries transient failures with exponential backoff +
29
29
  * jitter, honoring `Retry-After` up to 30s — 429 and a 409 that says another write
30
30
  * to the store was in flight, always; 408/502/503/504 and connection errors only when
31
- * a retry can never double-process a write (idempotent calls, or a failure at connect
32
- * time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
31
+ * a retry can never double-process a write (reads, the DELETE calls, or a failure at
32
+ * connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
33
33
  * the `withTimeout()` / `withRetries()` clones.
34
34
  *
35
35
  * Debugging: set `WONTOPOS_LOG=debug` to log method/path/status/timing/retries
@@ -89,7 +89,9 @@ export declare class BadRequestError extends WosError {
89
89
  /** 401 — the API key is missing, wrong, or revoked. */
90
90
  export declare class AuthenticationError extends WosError {
91
91
  }
92
- /** 402 — no card on file or the balance is depleted. Top up to continue. */
92
+ /** 402 — no card on file, the balance is depleted, or the month's spend cap for the
93
+ * account's tier is reached (`details.reason` is `tier_cap_reached`). Top up to
94
+ * continue; a top-up also raises the tier. */
93
95
  export declare class PaymentRequiredError extends WosError {
94
96
  }
95
97
  /** 403 — the key/model isn't allowed to do this. */
@@ -194,8 +196,9 @@ export interface StatusResult {
194
196
  /**
195
197
  * An image attached to a memory, passed to `add`/`store` via `opts.image`.
196
198
  *
197
- * Both edges must be 700px or more; a smaller image is refused (400). A request
198
- * body is capped at 10MB.
199
+ * Both edges must be 700px or more and the long edge at most 2500px, and the image
200
+ * file at most 7MB; anything else is refused (400) with the reason. A request body is
201
+ * capped at 10MB.
199
202
  *
200
203
  * What the service keeps is NOT your original. An image whose long edge is over
201
204
  * 1568px is downscaled to 1568 before anything else happens, and that smaller
@@ -241,8 +244,9 @@ export interface ImageBytes {
241
244
  * The image as stored. Write it to a file, or wrap it in a Blob to show it.
242
245
  *
243
246
  * This is the service's copy, not necessarily your upload: anything over 1568px
244
- * on its long edge was downscaled on the way in, and re-encoded to WebP unless it
245
- * was a JPEG (see `ImageInput`). Take the file extension from `contentType`.
247
+ * on its long edge (2500 at most) was downscaled on the way in, and re-encoded to
248
+ * WebP unless it was a JPEG (see `ImageInput`). Take the file extension from
249
+ * `contentType`.
246
250
  */
247
251
  bytes: Uint8Array;
248
252
  /** Sniffed from the BYTES, not from whatever the upload was named — e.g. `image/jpeg`. */
@@ -317,7 +321,8 @@ export interface LineageStep {
317
321
  is_current?: boolean;
318
322
  [key: string]: unknown;
319
323
  }
320
- /** `lineage` → the full chain of edits behind one memory, oldest first. */
324
+ /** `lineage` → the edits that followed one memory, from the id passed forward to the
325
+ * version in force, oldest first. */
321
326
  export interface LineageResult {
322
327
  memory_id?: string;
323
328
  chain: LineageStep[];
@@ -360,8 +365,8 @@ export interface SearchResult {
360
365
  /** Image memories the answer carried (one by default on an image-capable model,
361
366
  * up to `max_images`); `[]` when there were none. */
362
367
  images: Memory[];
363
- /** Re-ask passes actually performed. Present only when `verify` was sent; lower
364
- * than you asked for means the store had nothing further to add. */
368
+ /** Re-ask passes that added memories. Present only when `verify` above 0 was sent;
369
+ * lower than you asked for means a pass found nothing new, or could not run. */
365
370
  verify_used?: number;
366
371
  [key: string]: unknown;
367
372
  }
@@ -414,7 +419,7 @@ export interface UsageResult {
414
419
  output_tokens?: number;
415
420
  since?: string;
416
421
  };
417
- /** The workspace this key belongs to, over the window. */
422
+ /** The workspace this key belongs to, over the window: every key in it, not only this one. */
418
423
  workspace?: {
419
424
  workspace_id?: string | null;
420
425
  requests?: number;
@@ -542,9 +547,8 @@ export interface SpeakersList {
542
547
  [key: string]: unknown;
543
548
  }
544
549
  /**
545
- * Narrow a search to part of a store. The filter chooses what is searched, not what
546
- * is kept afterwards — so a narrow filter still returns your full `limit` when that
547
- * many matches sit inside it.
550
+ * Narrow a search to part of a store. A narrow filter can return fewer than `limit`
551
+ * even when the store holds more inside it.
548
552
  *
549
553
  * Retrieval behaves the same in every language, so these behave
550
554
  * identically in every language. Unlisted keys are dropped by the API rather than
@@ -565,7 +569,8 @@ export interface SearchFilters {
565
569
  time_from?: string;
566
570
  time_to?: string;
567
571
  /** WHEN THE CONTENT HAPPENED (`metadata.event_date`), not when it was written —
568
- * this is the one you usually want. */
572
+ * this is the one you usually want. A memory stored without an `event_date` is
573
+ * matched by when it was stored. */
569
574
  event_from?: string;
570
575
  event_to?: string;
571
576
  /** Drop matches the engine scored below this importance (0–1). */
@@ -578,9 +583,11 @@ export declare function _resetFilterWarnings(): void;
578
583
  * could not be told from an option that worked. Put a genuinely new one under
579
584
  * `extra`. */
580
585
  export interface SearchOptions {
581
- /** Recall caching. A hit inside the TTL bills at 0.1× — but the FIRST call
582
- * writes the cache and bills the query tokens at 2× (`5m`) or 3× (`1h`), so
583
- * this only pays for a query you repeat or extend. Do not enable it globally. */
586
+ /** Recall caching. The same query again inside the TTL bills at 0.1×; one that
587
+ * extends the cached query bills only its cached part at 0.1×, the new part at the
588
+ * write rate and its results at the normal rate. The FIRST call writes the cache
589
+ * and bills the query tokens at 2× (`5m`) or 3× (`1h`), so this only pays for a
590
+ * query you repeat or extend. Do not enable it globally. */
584
591
  cache_control?: {
585
592
  ttl: "5m" | "1h";
586
593
  };
@@ -591,9 +598,9 @@ export interface SearchOptions {
591
598
  /**
592
599
  * Re-ask passes, 0–3. After the first retrieval the service asks the engine again up
593
600
  * to this many times, each time telling it what was already returned so it skips
594
- * those and reaches further back. No LLM runs at any
601
+ * those and reaches further back. Raising it adds no language model call at any
595
602
  * value. Stops early when a pass finds nothing new; `verify_used` in the response
596
- * says how many actually ran.
603
+ * says how many passes added memories.
597
604
  *
598
605
  * Each pass is another engine call and can add up to `limit` more memories, so it
599
606
  * costs more — you are billed for what is delivered. Default 0. Worth it on questions
@@ -859,15 +866,16 @@ export declare class Client {
859
866
  addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
860
867
  /** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
861
868
  update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
862
- /** Search a store's memories. Returns them most relevant first.
869
+ /** Search a store's memories. They come most relevant first; the assistant's own
870
+ * words and image memories, when there are any, follow them in their own order.
863
871
  *
864
872
  * `limit` is 5-20, and out of range is refused rather than clamped: asking for 50
865
873
  * and silently receiving 20 reads as "that is all there is". The default is 10.
866
874
  *
867
- * `limit` bounds `memories`, not the returned array. The assistant's own words (on
868
- * a model that lists `self_memories` in its capabilities) and image memories (one
869
- * unless `max_images` says otherwise, on a model that lists `images`) come back in
870
- * it as well, so it can hold more than `limit`. They are billed either way. Size a
875
+ * `limit` bounds `memories`, not the returned array, and each `verify` pass can add
876
+ * up to `limit` more. The assistant's own words (on a model that lists
877
+ * `self_memories` in its capabilities) and image memories (one unless `max_images`
878
+ * says otherwise, on a model that lists `images`) come back after them in it as well, so it can hold more than `limit`. They are billed either way. Size a
871
879
  * prompt window on the array you get back, not on `limit`. `searchFull()` hands the
872
880
  * fields back apart.
873
881
  *
@@ -891,7 +899,7 @@ export declare class Client {
891
899
  * const r = await mem.searchFull("the day we moved", "alice", 10,
892
900
  * { max_images: 3, verify: 2 });
893
901
  * r.images.length; // the photos, apart from the text memories
894
- * r.verify_used; // how many re-ask passes actually ran (you are billed per pass)
902
+ * r.verify_used; // re-ask passes that added memories (each billed for what it added)
895
903
  *
896
904
  * Each option needs a model that lists it in its `listModels()` capabilities
897
905
  * (`images`, `re_ask`) and is refused (403) on any other rather than accepted and
@@ -928,7 +936,8 @@ export declare class Client {
928
936
  }): Promise<EngramResult>;
929
937
  /** Recent conversation turns (short-term memory). */
930
938
  history(userId?: string): Promise<HistoryTurn[]>;
931
- /** Memory counts for a store: { total_memories, short_term_turns }. */
939
+ /** Memory counts for a store: { total_memories, short_term_turns }. `total_memories`
940
+ * can be larger than the number `listMemories` lists. */
932
941
  stats(userId?: string): Promise<StatsResult>;
933
942
  /**
934
943
  * What this key has spent, and what is left — the numbers behind "can I keep going?".
@@ -936,18 +945,17 @@ export declare class Client {
936
945
  * Free: it carries no charge and skips the balance gate, because an account at zero
937
946
  * still has to be able to find out why. Rate-limited instead.
938
947
  *
939
- * Scoped to THIS key: its own lifetime spend, its workspace and stores over the
940
- * window. It never returns another key's spend. `balance_cents` is account-wide,
941
- * because that is what gates the next call whichever key makes it.
948
+ * `key` is THIS key's own lifetime spend. `workspace` and `stores` cover the window
949
+ * for the workspace this key belongs to, every key in it included. `balance_cents` is
950
+ * account-wide, because that is what gates the next call whichever key makes it.
942
951
  *
943
952
  * const u = await mem.usage(7);
944
953
  * if (u.balance_cents! < 100) stop();
945
954
  */
946
955
  usage(days?: number): Promise<UsageResult>;
947
956
  /** Fetch ONE memory by id — the text you stored, and its metadata.
948
- * The id is what `add`/`store` or `listMemories` returned. Same visibility as
949
- * `listMemories`: an id from another store, an id `listMemories` does not return,
950
- * or an invalidated memory rejects with `NotFoundError`. Pass `undefined` as
957
+ * The id is what `add`/`store` or `listMemories` returned. An unknown id or one
958
+ * from another store rejects with `NotFoundError`. Pass `undefined` as
951
959
  * `userId` for the default store — it is positional here. */
952
960
  get(userId: string | undefined, memoryId: string): Promise<Memory>;
953
961
  /** List a store's stored memories — the text you stored, plus its metadata.
@@ -993,9 +1001,10 @@ export declare class Client {
993
1001
  * Fetch the bytes of an image memory — the picture the SERVICE holds.
994
1002
  *
995
1003
  * Not necessarily your upload, in size OR in format: an image whose long edge was
996
- * over 1568px was downscaled to 1568 on the way in and re-encoded — lossless
997
- * formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG — and that
998
- * smaller picture is what is stored and comes back here. Nothing on our side ever
1004
+ * over 1568px (2500 at most) was downscaled to 1568 on the way in and re-encoded —
1005
+ * lossless formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG;
1006
+ * kept as sent when the re-encode came out bigger — and that smaller picture is what
1007
+ * is stored and comes back here. Nothing on our side ever
999
1008
  * uses more than 1568, so the extra pixels would be bytes nobody reads.
1000
1009
  * Keep your own copy for the full-resolution file.
1001
1010
  *
@@ -1100,11 +1109,13 @@ export declare class Client {
1100
1109
  skipIds?: string[];
1101
1110
  }): Promise<RevisionsResult>;
1102
1111
  /**
1103
- * The full chain of edits behind one memory, oldest first.
1112
+ * The edits that followed one memory: from the id you pass forward to the version in
1113
+ * force, oldest first.
1104
1114
  *
1105
- * `is_current` marks the version in force. Use it to answer "what did this used to
1106
- * say, and when did it change" — `revisions()` tells you HOW MUCH a store moved,
1107
- * this tells you what happened to one fact.
1115
+ * `is_current` marks the version in force. The chain runs forward only, so to answer
1116
+ * "what did this used to say, and when did it change" pass the first version's id;
1117
+ * the current version's id (what `search` returns) comes back alone. `revisions()`
1118
+ * tells you HOW MUCH a store moved, this tells you what happened to one fact.
1108
1119
  */
1109
1120
  lineage(userId: string | undefined, memoryId: string): Promise<LineageResult>;
1110
1121
  /**
@@ -1126,8 +1137,7 @@ export declare class Client {
1126
1137
  * `AuthenticationError` (bad key), `PaymentRequiredError` (valid key but no card /
1127
1138
  * depleted balance), or `APIConnectionError` (unreachable). Makes one metered request. */
1128
1139
  ping(): Promise<boolean>;
1129
- /** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`.
1130
- * Needs no API key. */
1140
+ /** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`. */
1131
1141
  listModels(): Promise<ModelInfo[]>;
1132
1142
  /**
1133
1143
  * The engrams (and delivery forms) the selected model can actually run.
@@ -1160,7 +1170,9 @@ export declare class Client {
1160
1170
  addSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
1161
1171
  /** The store's registered people, each with its memory count. */
1162
1172
  listSpeakers(userId?: string): Promise<SpeakersList>;
1163
- /** Unregister a person. Their memories stay; the name tag goes. Retried like
1173
+ /** Unregister a person. Their memories stay, and the name can no longer tag a new
1174
+ * memory or filter a search. The tag goes from stored memories too, except on
1175
+ * `tablet-2`, which keeps it on memories already stored with it. Retried like
1164
1176
  * `deleteStore`, with the same note on a 404 after an ambiguous failure. */
1165
1177
  removeSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
1166
1178
  /** Delete a single memory by id. Pass `undefined` as `userId` for the default
@@ -1210,12 +1222,12 @@ export declare class Client {
1210
1222
  *
1211
1223
  * Retried: 429, and a 409 whose error carries `retry_after_ms` (another write to the
1212
1224
  * store was in flight; nothing was stored), on every method; 408/502/503/504 and
1213
- * dropped connections on idempotent methods; connect failures on every method. A
1214
- * retry whose wait is over the cap, or does not fit the deadline, is not made: the
1215
- * call fails with the error of the response it has. So does a retry the deadline
1216
- * leaves no time to send, and the retry of an idempotent method or of a POST that
1217
- * only reads that the deadline cuts short. Any other retry cut short stays a status-0
1218
- * deadline error: a write may have been applied.
1225
+ * dropped connections on a call that only reads (an idempotent method, or a POST
1226
+ * that only reads); connect failures on every method. A retry whose wait is over the
1227
+ * cap, or does not fit the deadline, is not made: the call fails with the error of
1228
+ * the response it has. So does a retry the deadline leaves no time to send, and the
1229
+ * retry of a call that only reads that the deadline cuts short. Any other retry cut
1230
+ * short stays a status-0 deadline error: a write may have been applied.
1219
1231
  */
1220
1232
  private send;
1221
1233
  /**
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WME = exports.Client = exports.ServerError = exports.RateLimitError = exports.GoneError = exports.ConflictError = exports.NotFoundError = exports.PermissionDeniedError = exports.PaymentRequiredError = exports.AuthenticationError = exports.BadRequestError = exports.APIConnectionError = exports.WosError = exports.CONTEXT_LIMIT_MAX = exports.CONTEXT_LIMIT_MIN = exports.SEARCH_LIMIT_MAX = exports.SEARCH_LIMIT_MIN = void 0;
4
4
  exports._resetStoreIdWarnings = _resetStoreIdWarnings;
5
5
  exports._resetFilterWarnings = _resetFilterWarnings;
6
- const VERSION = "2.2.45";
6
+ const VERSION = "2.2.46";
7
7
  const RUNTIME = (() => {
8
8
  const p = globalThis?.process;
9
9
  return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
@@ -1113,8 +1113,8 @@ class Client {
1113
1113
  }
1114
1114
  const attempts = this.maxRetries + 1;
1115
1115
  const upper = method.toUpperCase();
1116
- const idempotent = IDEMPOTENT_METHODS.has(upper);
1117
1116
  const logPath = path.split("?")[0];
1117
+ const idempotent = IDEMPOTENT_METHODS.has(upper) || reads || (upper === "POST" && READ_POSTS.has(logPath));
1118
1118
  const deadlineAt = this.deadlineAt();
1119
1119
  const headers = extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers;
1120
1120
  let ambiguous = false;
@@ -1135,7 +1135,7 @@ class Client {
1135
1135
  };
1136
1136
  const cutShort = () => {
1137
1137
  const why = att.why();
1138
- if (why === "deadline" && (idempotent || reads || (upper === "POST" && READ_POSTS.has(logPath))) && previous) {
1138
+ if (why === "deadline" && idempotent && previous) {
1139
1139
  return previous;
1140
1140
  }
1141
1141
  return new APIConnectionError(0, this.abortMessage(why));
@@ -14,7 +14,7 @@
14
14
  * (`createStore`) or calls return 404. Every account starts with a `default` store,
15
15
  * so with no `userId` anywhere the zero-setup path just works.
16
16
  *
17
- * The API key picks *which memory* (your account); `model` picks *which engine*
17
+ * The API key picks *which memory* (your workspace); `model` picks *which engine*
18
18
  * reads it. Models on the shared pool read the same memory, so you can store with
19
19
  * one and recall with another; `listModels()` reports each model's `memory` as
20
20
  * `"shared"` or `"isolated"`, and an isolated one starts empty. Set a default in
@@ -22,14 +22,14 @@
22
22
  * `mem.withModel("tablet-1").recall(...)`.
23
23
  *
24
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.
25
+ * memory stored in one language is found by a question asked in another. No language
26
+ * model reads your stored memories.
27
27
  *
28
28
  * Reliability: every call retries transient failures with exponential backoff +
29
29
  * jitter, honoring `Retry-After` up to 30s — 429 and a 409 that says another write
30
30
  * to the store was in flight, always; 408/502/503/504 and connection errors only when
31
- * a retry can never double-process a write (idempotent calls, or a failure at connect
32
- * time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
31
+ * a retry can never double-process a write (reads, the DELETE calls, or a failure at
32
+ * connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per call site via
33
33
  * the `withTimeout()` / `withRetries()` clones.
34
34
  *
35
35
  * Debugging: set `WONTOPOS_LOG=debug` to log method/path/status/timing/retries
@@ -89,7 +89,9 @@ export declare class BadRequestError extends WosError {
89
89
  /** 401 — the API key is missing, wrong, or revoked. */
90
90
  export declare class AuthenticationError extends WosError {
91
91
  }
92
- /** 402 — no card on file or the balance is depleted. Top up to continue. */
92
+ /** 402 — no card on file, the balance is depleted, or the month's spend cap for the
93
+ * account's tier is reached (`details.reason` is `tier_cap_reached`). Top up to
94
+ * continue; a top-up also raises the tier. */
93
95
  export declare class PaymentRequiredError extends WosError {
94
96
  }
95
97
  /** 403 — the key/model isn't allowed to do this. */
@@ -194,8 +196,9 @@ export interface StatusResult {
194
196
  /**
195
197
  * An image attached to a memory, passed to `add`/`store` via `opts.image`.
196
198
  *
197
- * Both edges must be 700px or more; a smaller image is refused (400). A request
198
- * body is capped at 10MB.
199
+ * Both edges must be 700px or more and the long edge at most 2500px, and the image
200
+ * file at most 7MB; anything else is refused (400) with the reason. A request body is
201
+ * capped at 10MB.
199
202
  *
200
203
  * What the service keeps is NOT your original. An image whose long edge is over
201
204
  * 1568px is downscaled to 1568 before anything else happens, and that smaller
@@ -241,8 +244,9 @@ export interface ImageBytes {
241
244
  * The image as stored. Write it to a file, or wrap it in a Blob to show it.
242
245
  *
243
246
  * This is the service's copy, not necessarily your upload: anything over 1568px
244
- * on its long edge was downscaled on the way in, and re-encoded to WebP unless it
245
- * was a JPEG (see `ImageInput`). Take the file extension from `contentType`.
247
+ * on its long edge (2500 at most) was downscaled on the way in, and re-encoded to
248
+ * WebP unless it was a JPEG (see `ImageInput`). Take the file extension from
249
+ * `contentType`.
246
250
  */
247
251
  bytes: Uint8Array;
248
252
  /** Sniffed from the BYTES, not from whatever the upload was named — e.g. `image/jpeg`. */
@@ -317,7 +321,8 @@ export interface LineageStep {
317
321
  is_current?: boolean;
318
322
  [key: string]: unknown;
319
323
  }
320
- /** `lineage` → the full chain of edits behind one memory, oldest first. */
324
+ /** `lineage` → the edits that followed one memory, from the id passed forward to the
325
+ * version in force, oldest first. */
321
326
  export interface LineageResult {
322
327
  memory_id?: string;
323
328
  chain: LineageStep[];
@@ -360,8 +365,8 @@ export interface SearchResult {
360
365
  /** Image memories the answer carried (one by default on an image-capable model,
361
366
  * up to `max_images`); `[]` when there were none. */
362
367
  images: Memory[];
363
- /** Re-ask passes actually performed. Present only when `verify` was sent; lower
364
- * than you asked for means the store had nothing further to add. */
368
+ /** Re-ask passes that added memories. Present only when `verify` above 0 was sent;
369
+ * lower than you asked for means a pass found nothing new, or could not run. */
365
370
  verify_used?: number;
366
371
  [key: string]: unknown;
367
372
  }
@@ -414,7 +419,7 @@ export interface UsageResult {
414
419
  output_tokens?: number;
415
420
  since?: string;
416
421
  };
417
- /** The workspace this key belongs to, over the window. */
422
+ /** The workspace this key belongs to, over the window: every key in it, not only this one. */
418
423
  workspace?: {
419
424
  workspace_id?: string | null;
420
425
  requests?: number;
@@ -542,9 +547,8 @@ export interface SpeakersList {
542
547
  [key: string]: unknown;
543
548
  }
544
549
  /**
545
- * Narrow a search to part of a store. The filter chooses what is searched, not what
546
- * is kept afterwards — so a narrow filter still returns your full `limit` when that
547
- * many matches sit inside it.
550
+ * Narrow a search to part of a store. A narrow filter can return fewer than `limit`
551
+ * even when the store holds more inside it.
548
552
  *
549
553
  * Retrieval behaves the same in every language, so these behave
550
554
  * identically in every language. Unlisted keys are dropped by the API rather than
@@ -565,7 +569,8 @@ export interface SearchFilters {
565
569
  time_from?: string;
566
570
  time_to?: string;
567
571
  /** WHEN THE CONTENT HAPPENED (`metadata.event_date`), not when it was written —
568
- * this is the one you usually want. */
572
+ * this is the one you usually want. A memory stored without an `event_date` is
573
+ * matched by when it was stored. */
569
574
  event_from?: string;
570
575
  event_to?: string;
571
576
  /** Drop matches the engine scored below this importance (0–1). */
@@ -578,9 +583,11 @@ export declare function _resetFilterWarnings(): void;
578
583
  * could not be told from an option that worked. Put a genuinely new one under
579
584
  * `extra`. */
580
585
  export interface SearchOptions {
581
- /** Recall caching. A hit inside the TTL bills at 0.1× — but the FIRST call
582
- * writes the cache and bills the query tokens at 2× (`5m`) or 3× (`1h`), so
583
- * this only pays for a query you repeat or extend. Do not enable it globally. */
586
+ /** Recall caching. The same query again inside the TTL bills at 0.1×; one that
587
+ * extends the cached query bills only its cached part at 0.1×, the new part at the
588
+ * write rate and its results at the normal rate. The FIRST call writes the cache
589
+ * and bills the query tokens at 2× (`5m`) or 3× (`1h`), so this only pays for a
590
+ * query you repeat or extend. Do not enable it globally. */
584
591
  cache_control?: {
585
592
  ttl: "5m" | "1h";
586
593
  };
@@ -591,9 +598,9 @@ export interface SearchOptions {
591
598
  /**
592
599
  * Re-ask passes, 0–3. After the first retrieval the service asks the engine again up
593
600
  * to this many times, each time telling it what was already returned so it skips
594
- * those and reaches further back. No LLM runs at any
601
+ * those and reaches further back. Raising it adds no language model call at any
595
602
  * value. Stops early when a pass finds nothing new; `verify_used` in the response
596
- * says how many actually ran.
603
+ * says how many passes added memories.
597
604
  *
598
605
  * Each pass is another engine call and can add up to `limit` more memories, so it
599
606
  * costs more — you are billed for what is delivered. Default 0. Worth it on questions
@@ -859,15 +866,16 @@ export declare class Client {
859
866
  addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
860
867
  /** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
861
868
  update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
862
- /** Search a store's memories. Returns them most relevant first.
869
+ /** Search a store's memories. They come most relevant first; the assistant's own
870
+ * words and image memories, when there are any, follow them in their own order.
863
871
  *
864
872
  * `limit` is 5-20, and out of range is refused rather than clamped: asking for 50
865
873
  * and silently receiving 20 reads as "that is all there is". The default is 10.
866
874
  *
867
- * `limit` bounds `memories`, not the returned array. The assistant's own words (on
868
- * a model that lists `self_memories` in its capabilities) and image memories (one
869
- * unless `max_images` says otherwise, on a model that lists `images`) come back in
870
- * it as well, so it can hold more than `limit`. They are billed either way. Size a
875
+ * `limit` bounds `memories`, not the returned array, and each `verify` pass can add
876
+ * up to `limit` more. The assistant's own words (on a model that lists
877
+ * `self_memories` in its capabilities) and image memories (one unless `max_images`
878
+ * says otherwise, on a model that lists `images`) come back after them in it as well, so it can hold more than `limit`. They are billed either way. Size a
871
879
  * prompt window on the array you get back, not on `limit`. `searchFull()` hands the
872
880
  * fields back apart.
873
881
  *
@@ -891,7 +899,7 @@ export declare class Client {
891
899
  * const r = await mem.searchFull("the day we moved", "alice", 10,
892
900
  * { max_images: 3, verify: 2 });
893
901
  * r.images.length; // the photos, apart from the text memories
894
- * r.verify_used; // how many re-ask passes actually ran (you are billed per pass)
902
+ * r.verify_used; // re-ask passes that added memories (each billed for what it added)
895
903
  *
896
904
  * Each option needs a model that lists it in its `listModels()` capabilities
897
905
  * (`images`, `re_ask`) and is refused (403) on any other rather than accepted and
@@ -928,7 +936,8 @@ export declare class Client {
928
936
  }): Promise<EngramResult>;
929
937
  /** Recent conversation turns (short-term memory). */
930
938
  history(userId?: string): Promise<HistoryTurn[]>;
931
- /** Memory counts for a store: { total_memories, short_term_turns }. */
939
+ /** Memory counts for a store: { total_memories, short_term_turns }. `total_memories`
940
+ * can be larger than the number `listMemories` lists. */
932
941
  stats(userId?: string): Promise<StatsResult>;
933
942
  /**
934
943
  * What this key has spent, and what is left — the numbers behind "can I keep going?".
@@ -936,18 +945,17 @@ export declare class Client {
936
945
  * Free: it carries no charge and skips the balance gate, because an account at zero
937
946
  * still has to be able to find out why. Rate-limited instead.
938
947
  *
939
- * Scoped to THIS key: its own lifetime spend, its workspace and stores over the
940
- * window. It never returns another key's spend. `balance_cents` is account-wide,
941
- * because that is what gates the next call whichever key makes it.
948
+ * `key` is THIS key's own lifetime spend. `workspace` and `stores` cover the window
949
+ * for the workspace this key belongs to, every key in it included. `balance_cents` is
950
+ * account-wide, because that is what gates the next call whichever key makes it.
942
951
  *
943
952
  * const u = await mem.usage(7);
944
953
  * if (u.balance_cents! < 100) stop();
945
954
  */
946
955
  usage(days?: number): Promise<UsageResult>;
947
956
  /** Fetch ONE memory by id — the text you stored, and its metadata.
948
- * The id is what `add`/`store` or `listMemories` returned. Same visibility as
949
- * `listMemories`: an id from another store, an id `listMemories` does not return,
950
- * or an invalidated memory rejects with `NotFoundError`. Pass `undefined` as
957
+ * The id is what `add`/`store` or `listMemories` returned. An unknown id or one
958
+ * from another store rejects with `NotFoundError`. Pass `undefined` as
951
959
  * `userId` for the default store — it is positional here. */
952
960
  get(userId: string | undefined, memoryId: string): Promise<Memory>;
953
961
  /** List a store's stored memories — the text you stored, plus its metadata.
@@ -993,9 +1001,10 @@ export declare class Client {
993
1001
  * Fetch the bytes of an image memory — the picture the SERVICE holds.
994
1002
  *
995
1003
  * Not necessarily your upload, in size OR in format: an image whose long edge was
996
- * over 1568px was downscaled to 1568 on the way in and re-encoded — lossless
997
- * formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG — and that
998
- * smaller picture is what is stored and comes back here. Nothing on our side ever
1004
+ * over 1568px (2500 at most) was downscaled to 1568 on the way in and re-encoded —
1005
+ * lossless formats as WebP, so a PNG comes back as `image/webp`; JPEG stays JPEG;
1006
+ * kept as sent when the re-encode came out bigger — and that smaller picture is what
1007
+ * is stored and comes back here. Nothing on our side ever
999
1008
  * uses more than 1568, so the extra pixels would be bytes nobody reads.
1000
1009
  * Keep your own copy for the full-resolution file.
1001
1010
  *
@@ -1100,11 +1109,13 @@ export declare class Client {
1100
1109
  skipIds?: string[];
1101
1110
  }): Promise<RevisionsResult>;
1102
1111
  /**
1103
- * The full chain of edits behind one memory, oldest first.
1112
+ * The edits that followed one memory: from the id you pass forward to the version in
1113
+ * force, oldest first.
1104
1114
  *
1105
- * `is_current` marks the version in force. Use it to answer "what did this used to
1106
- * say, and when did it change" — `revisions()` tells you HOW MUCH a store moved,
1107
- * this tells you what happened to one fact.
1115
+ * `is_current` marks the version in force. The chain runs forward only, so to answer
1116
+ * "what did this used to say, and when did it change" pass the first version's id;
1117
+ * the current version's id (what `search` returns) comes back alone. `revisions()`
1118
+ * tells you HOW MUCH a store moved, this tells you what happened to one fact.
1108
1119
  */
1109
1120
  lineage(userId: string | undefined, memoryId: string): Promise<LineageResult>;
1110
1121
  /**
@@ -1126,8 +1137,7 @@ export declare class Client {
1126
1137
  * `AuthenticationError` (bad key), `PaymentRequiredError` (valid key but no card /
1127
1138
  * depleted balance), or `APIConnectionError` (unreachable). Makes one metered request. */
1128
1139
  ping(): Promise<boolean>;
1129
- /** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`.
1130
- * Needs no API key. */
1140
+ /** Available models: `[{ id, name, available, memory, capabilities, retires_at? }, ...]`. */
1131
1141
  listModels(): Promise<ModelInfo[]>;
1132
1142
  /**
1133
1143
  * The engrams (and delivery forms) the selected model can actually run.
@@ -1160,7 +1170,9 @@ export declare class Client {
1160
1170
  addSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
1161
1171
  /** The store's registered people, each with its memory count. */
1162
1172
  listSpeakers(userId?: string): Promise<SpeakersList>;
1163
- /** Unregister a person. Their memories stay; the name tag goes. Retried like
1173
+ /** Unregister a person. Their memories stay, and the name can no longer tag a new
1174
+ * memory or filter a search. The tag goes from stored memories too, except on
1175
+ * `tablet-2`, which keeps it on memories already stored with it. Retried like
1164
1176
  * `deleteStore`, with the same note on a 404 after an ambiguous failure. */
1165
1177
  removeSpeaker(speaker: string, userId?: string): Promise<SpeakerOpResult>;
1166
1178
  /** Delete a single memory by id. Pass `undefined` as `userId` for the default
@@ -1210,12 +1222,12 @@ export declare class Client {
1210
1222
  *
1211
1223
  * Retried: 429, and a 409 whose error carries `retry_after_ms` (another write to the
1212
1224
  * store was in flight; nothing was stored), on every method; 408/502/503/504 and
1213
- * dropped connections on idempotent methods; connect failures on every method. A
1214
- * retry whose wait is over the cap, or does not fit the deadline, is not made: the
1215
- * call fails with the error of the response it has. So does a retry the deadline
1216
- * leaves no time to send, and the retry of an idempotent method or of a POST that
1217
- * only reads that the deadline cuts short. Any other retry cut short stays a status-0
1218
- * deadline error: a write may have been applied.
1225
+ * dropped connections on a call that only reads (an idempotent method, or a POST
1226
+ * that only reads); connect failures on every method. A retry whose wait is over the
1227
+ * cap, or does not fit the deadline, is not made: the call fails with the error of
1228
+ * the response it has. So does a retry the deadline leaves no time to send, and the
1229
+ * retry of a call that only reads that the deadline cuts short. Any other retry cut
1230
+ * short stays a status-0 deadline error: a write may have been applied.
1219
1231
  */
1220
1232
  private send;
1221
1233
  /**
package/dist/wontopos.js CHANGED
@@ -1,4 +1,4 @@
1
- const VERSION = "2.2.45";
1
+ const VERSION = "2.2.46";
2
2
  const RUNTIME = (() => {
3
3
  const p = globalThis?.process;
4
4
  return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
@@ -1097,8 +1097,8 @@ export class Client {
1097
1097
  }
1098
1098
  const attempts = this.maxRetries + 1;
1099
1099
  const upper = method.toUpperCase();
1100
- const idempotent = IDEMPOTENT_METHODS.has(upper);
1101
1100
  const logPath = path.split("?")[0];
1101
+ const idempotent = IDEMPOTENT_METHODS.has(upper) || reads || (upper === "POST" && READ_POSTS.has(logPath));
1102
1102
  const deadlineAt = this.deadlineAt();
1103
1103
  const headers = extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers;
1104
1104
  let ambiguous = false;
@@ -1119,7 +1119,7 @@ export class Client {
1119
1119
  };
1120
1120
  const cutShort = () => {
1121
1121
  const why = att.why();
1122
- if (why === "deadline" && (idempotent || reads || (upper === "POST" && READ_POSTS.has(logPath))) && previous) {
1122
+ if (why === "deadline" && idempotent && previous) {
1123
1123
  return previous;
1124
1124
  }
1125
1125
  return new APIConnectionError(0, this.abortMessage(why));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.45",
3
+ "version": "2.2.46",
4
4
  "description": "Wontopos \u2014 long-term memory for AI agents. Identical recall in every language, ~100\u00d7 lower LLM bill.",
5
5
  "keywords": [
6
6
  "memory",