wontopos 2.2.44 → 2.2.45

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
@@ -5,7 +5,7 @@ npm install wontopos
5
5
  ```
6
6
 
7
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.
8
+ `Client.fromEnv()` reads `WONTOPOS_API_KEY` from the environment instead.
9
9
 
10
10
  ```ts
11
11
  import { Client } from "wontopos";
@@ -35,10 +35,10 @@ Where the store id goes depends on whether the method takes a payload. A method
35
35
  writes or searches puts the payload first and the store second — `mem.add("...", "alice")`,
36
36
  `mem.search("...", "alice")`. A method that only addresses a store takes it first —
37
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.
38
+ Most of them let you leave it out for the client's default store. Five take it
39
+ positionally and cannot — `get`, `getImage`, `delete`, `lineage` and `forgetImage` want
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.
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)
@@ -112,6 +112,7 @@ This one needs `npm i undici` — Node's global `fetch` is built on undici, but
112
112
  package itself is not installed for you.
113
113
 
114
114
  ```ts
115
+ import { Client } from "wontopos";
115
116
  import { ProxyAgent } from "undici";
116
117
 
117
118
  const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
@@ -147,7 +148,7 @@ Every method returns a documented shape — `StoreResult`, `UpdateResult`,
147
148
  `RecallResult`, `EngramResult`, `StatsResult`, `SpeakersList`, `Memory[]`, ... —
148
149
  each with an index signature, so new server fields flow through without an SDK
149
150
  update. `search` options are typed too (`SearchOptions`: `cache_control`,
150
- `speaker`, plus pass-through).
151
+ `speaker`, plus `extra` for pass-through).
151
152
 
152
153
  ```ts
153
154
  import { type Memory } from "wontopos";
@@ -206,14 +207,14 @@ Found something wrong, or something that looks unsafe? Tell us — every report
206
207
  - Security: <https://wontopos.com/contact?topic=security> (also published at
207
208
  [`/.well-known/security.txt`](https://wontopos.com/.well-known/security.txt))
208
209
 
209
- Include the SDK version (`the version in package.json`) and the language. If it involves a store id or a
210
+ Include the SDK version (the one in package.json) and the language. If it involves a store id or a
210
211
  memory, describe the shape rather than pasting the contents — we do not need your
211
212
  data to fix it.
212
213
 
213
214
  ## Changelog
214
215
 
215
216
  The three clients release in lockstep — same version, same surface, same day. Patch
216
- releases are additive. Eight inside 2.2 were not, deliberately and each with its
217
+ releases are additive. Nine inside 2.2 were not, deliberately and each with its
217
218
  reason; the changelog lists them.
218
219
 
219
220
  See [CHANGELOG.md](https://github.com/wontopos/wontopos-node/blob/main/CHANGELOG.md).
@@ -652,9 +652,10 @@ export interface SearchOptions {
652
652
  export interface WriteOptions {
653
653
  idempotencyKey?: string;
654
654
  /**
655
- * An image to store alongside the text. Needs a model that lists `images` in its
656
- * `listModels()` capabilities; any other refuses the write rather than store the
657
- * caption and quietly drop the image.
655
+ * An image to store alongside the text, read by `add` (and `store`) only: `addTurn`,
656
+ * `addBulk` and `update` warn about it and do not send it. Needs a model that lists
657
+ * `images` in its `listModels()` capabilities; any other refuses the write rather than
658
+ * store the caption and quietly drop the image.
658
659
  *
659
660
  * A caption is required: the service refuses empty `content` (400) even with an
660
661
  * image attached.
@@ -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.44";
6
+ const VERSION = "2.2.45";
7
7
  const RUNTIME = (() => {
8
8
  const p = globalThis?.process;
9
9
  return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
@@ -508,7 +508,7 @@ const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
508
508
  function checkSearchOpts(opts) {
509
509
  checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
510
510
  const o = opts;
511
- optRange(o.max_images !== undefined ? o.max_images : o.extra?.max_images, "max_images", 0, MAX_IMAGES_MAX);
511
+ optRange(o.max_images != null ? o.max_images : o.extra?.max_images, "max_images", 0, MAX_IMAGES_MAX);
512
512
  }
513
513
  function checkOpts(opts, known, what) {
514
514
  for (const k of Object.keys(opts)) {
@@ -558,6 +558,17 @@ function warnOnceUnknownKeys(obj, known, warned, message) {
558
558
  console.warn(message(k));
559
559
  }
560
560
  }
561
+ function givenOpts(known) {
562
+ return Object.fromEntries(Object.entries(known).filter(([, v]) => v !== null));
563
+ }
564
+ const ADD_KEYS = new Set(["idempotencyKey", "image"]);
565
+ const WRITE_KEYS = new Set(["idempotencyKey"]);
566
+ const warnedWriteKeys = new Set();
567
+ function writeKey(opts, takesImage = false) {
568
+ warnOnceUnknownKeys(opts, takesImage ? ADD_KEYS : WRITE_KEYS, warnedWriteKeys, (k) => `wontopos: write option ${JSON.stringify(k)} is not sent. ` +
569
+ (takesImage ? "add takes idempotencyKey and image." : "This call takes idempotencyKey only."));
570
+ return opts.idempotencyKey;
571
+ }
561
572
  function warnOnUnknownFilters(filters) {
562
573
  warnOnceUnknownKeys(filters, KNOWN_FILTER_KEYS, warnedFilterKeys, (k) => `wontopos: unknown search filter ${JSON.stringify(k)} — the API drops keys it does not know, ` +
563
574
  `so this filter has NO effect and the search is wider than you think. ` +
@@ -718,22 +729,22 @@ class Client {
718
729
  if (opts.image !== undefined)
719
730
  body.image = normalizeImage(opts.image);
720
731
  warnOnUnknownMetadata(metadata);
721
- return this.post("/api/v1/memory/store", body, opts.idempotencyKey);
732
+ return this.post("/api/v1/memory/store", body, writeKey(opts, true));
722
733
  }
723
734
  async store(content, userId, metadata = {}, opts = {}) {
724
735
  return this.add(content, userId, metadata, opts);
725
736
  }
726
737
  async addTurn(userMsg, assistantMsg, userId, opts = {}) {
727
- return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts.idempotencyKey);
738
+ return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, writeKey(opts));
728
739
  }
729
740
  async addBulk(content, userId, category = "general", timestamp, opts = {}) {
730
- const body = { user_id: this.uid(userId), content, category };
741
+ const body = { user_id: this.uid(userId), content, category: category ?? "general" };
731
742
  if (timestamp)
732
743
  body.timestamp = timestamp;
733
- return this.post("/api/v1/memory/bulk-store", body, opts.idempotencyKey);
744
+ return this.post("/api/v1/memory/bulk-store", body, writeKey(opts));
734
745
  }
735
746
  async update(oldMemoryId, newContent, userId, opts = {}) {
736
- return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts.idempotencyKey);
747
+ return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, writeKey(opts));
737
748
  }
738
749
  async search(query, userId, limit = 10, opts = {}) {
739
750
  checkSearchOpts(opts);
@@ -741,7 +752,7 @@ class Client {
741
752
  limit ??= 10;
742
753
  checkCount(limit, "limit");
743
754
  const { extra, ...known } = opts;
744
- const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
755
+ const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
745
756
  return mergeResults(r);
746
757
  }
747
758
  async searchSelf(query, userId, limit = 10, opts = {}) {
@@ -750,7 +761,7 @@ class Client {
750
761
  limit ??= 10;
751
762
  checkCount(limit, "limit");
752
763
  const { extra, ...known } = opts;
753
- const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
764
+ const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
754
765
  return { memories: asRecords(r.memories), self_memories: asRecords(r.self_memories) };
755
766
  }
756
767
  async searchFull(query, userId, limit = 10, opts = {}) {
@@ -759,7 +770,7 @@ class Client {
759
770
  limit ??= 10;
760
771
  checkCount(limit, "limit");
761
772
  const { extra, ...known } = opts;
762
- const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
773
+ const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
763
774
  return {
764
775
  ...r,
765
776
  memories: asRecords(r.memories),
@@ -894,9 +905,13 @@ class Client {
894
905
  throw truncatedWalk(`stopped after ${MAX_PAGES} pages — the store did not end.`);
895
906
  }
896
907
  async revisions(userId, opts = {}) {
908
+ const inc = opts.include;
909
+ if (inc !== undefined && inc !== null && inc !== "revised" && inc !== "unrevised") {
910
+ throw new Error(`include must be "revised" or "unrevised", got ${JSON.stringify(inc)}.`);
911
+ }
897
912
  const limit = optRange(opts.limit, "limit", PAGE_LIMIT_MIN, PAGE_LIMIT_MAX);
898
913
  const body = {};
899
- if (opts.include !== undefined)
914
+ if (opts.include != null)
900
915
  body.include = opts.include;
901
916
  if (limit !== undefined)
902
917
  body.limit = limit;
@@ -652,9 +652,10 @@ export interface SearchOptions {
652
652
  export interface WriteOptions {
653
653
  idempotencyKey?: string;
654
654
  /**
655
- * An image to store alongside the text. Needs a model that lists `images` in its
656
- * `listModels()` capabilities; any other refuses the write rather than store the
657
- * caption and quietly drop the image.
655
+ * An image to store alongside the text, read by `add` (and `store`) only: `addTurn`,
656
+ * `addBulk` and `update` warn about it and do not send it. Needs a model that lists
657
+ * `images` in its `listModels()` capabilities; any other refuses the write rather than
658
+ * store the caption and quietly drop the image.
658
659
  *
659
660
  * A caption is required: the service refuses empty `content` (400) even with an
660
661
  * image attached.
package/dist/wontopos.js CHANGED
@@ -1,4 +1,4 @@
1
- const VERSION = "2.2.44";
1
+ const VERSION = "2.2.45";
2
2
  const RUNTIME = (() => {
3
3
  const p = globalThis?.process;
4
4
  return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
@@ -492,7 +492,7 @@ const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
492
492
  function checkSearchOpts(opts) {
493
493
  checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
494
494
  const o = opts;
495
- optRange(o.max_images !== undefined ? o.max_images : o.extra?.max_images, "max_images", 0, MAX_IMAGES_MAX);
495
+ optRange(o.max_images != null ? o.max_images : o.extra?.max_images, "max_images", 0, MAX_IMAGES_MAX);
496
496
  }
497
497
  function checkOpts(opts, known, what) {
498
498
  for (const k of Object.keys(opts)) {
@@ -542,6 +542,17 @@ function warnOnceUnknownKeys(obj, known, warned, message) {
542
542
  console.warn(message(k));
543
543
  }
544
544
  }
545
+ function givenOpts(known) {
546
+ return Object.fromEntries(Object.entries(known).filter(([, v]) => v !== null));
547
+ }
548
+ const ADD_KEYS = new Set(["idempotencyKey", "image"]);
549
+ const WRITE_KEYS = new Set(["idempotencyKey"]);
550
+ const warnedWriteKeys = new Set();
551
+ function writeKey(opts, takesImage = false) {
552
+ warnOnceUnknownKeys(opts, takesImage ? ADD_KEYS : WRITE_KEYS, warnedWriteKeys, (k) => `wontopos: write option ${JSON.stringify(k)} is not sent. ` +
553
+ (takesImage ? "add takes idempotencyKey and image." : "This call takes idempotencyKey only."));
554
+ return opts.idempotencyKey;
555
+ }
545
556
  function warnOnUnknownFilters(filters) {
546
557
  warnOnceUnknownKeys(filters, KNOWN_FILTER_KEYS, warnedFilterKeys, (k) => `wontopos: unknown search filter ${JSON.stringify(k)} — the API drops keys it does not know, ` +
547
558
  `so this filter has NO effect and the search is wider than you think. ` +
@@ -702,22 +713,22 @@ export class Client {
702
713
  if (opts.image !== undefined)
703
714
  body.image = normalizeImage(opts.image);
704
715
  warnOnUnknownMetadata(metadata);
705
- return this.post("/api/v1/memory/store", body, opts.idempotencyKey);
716
+ return this.post("/api/v1/memory/store", body, writeKey(opts, true));
706
717
  }
707
718
  async store(content, userId, metadata = {}, opts = {}) {
708
719
  return this.add(content, userId, metadata, opts);
709
720
  }
710
721
  async addTurn(userMsg, assistantMsg, userId, opts = {}) {
711
- return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts.idempotencyKey);
722
+ return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, writeKey(opts));
712
723
  }
713
724
  async addBulk(content, userId, category = "general", timestamp, opts = {}) {
714
- const body = { user_id: this.uid(userId), content, category };
725
+ const body = { user_id: this.uid(userId), content, category: category ?? "general" };
715
726
  if (timestamp)
716
727
  body.timestamp = timestamp;
717
- return this.post("/api/v1/memory/bulk-store", body, opts.idempotencyKey);
728
+ return this.post("/api/v1/memory/bulk-store", body, writeKey(opts));
718
729
  }
719
730
  async update(oldMemoryId, newContent, userId, opts = {}) {
720
- return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts.idempotencyKey);
731
+ return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, writeKey(opts));
721
732
  }
722
733
  async search(query, userId, limit = 10, opts = {}) {
723
734
  checkSearchOpts(opts);
@@ -725,7 +736,7 @@ export class Client {
725
736
  limit ??= 10;
726
737
  checkCount(limit, "limit");
727
738
  const { extra, ...known } = opts;
728
- const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
739
+ const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
729
740
  return mergeResults(r);
730
741
  }
731
742
  async searchSelf(query, userId, limit = 10, opts = {}) {
@@ -734,7 +745,7 @@ export class Client {
734
745
  limit ??= 10;
735
746
  checkCount(limit, "limit");
736
747
  const { extra, ...known } = opts;
737
- const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
748
+ const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
738
749
  return { memories: asRecords(r.memories), self_memories: asRecords(r.self_memories) };
739
750
  }
740
751
  async searchFull(query, userId, limit = 10, opts = {}) {
@@ -743,7 +754,7 @@ export class Client {
743
754
  limit ??= 10;
744
755
  checkCount(limit, "limit");
745
756
  const { extra, ...known } = opts;
746
- const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
757
+ const r = await this.post("/api/v1/memory/search", { ...extra, ...givenOpts(known), user_id: this.uid(userId), query, max_results: limit });
747
758
  return {
748
759
  ...r,
749
760
  memories: asRecords(r.memories),
@@ -878,9 +889,13 @@ export class Client {
878
889
  throw truncatedWalk(`stopped after ${MAX_PAGES} pages — the store did not end.`);
879
890
  }
880
891
  async revisions(userId, opts = {}) {
892
+ const inc = opts.include;
893
+ if (inc !== undefined && inc !== null && inc !== "revised" && inc !== "unrevised") {
894
+ throw new Error(`include must be "revised" or "unrevised", got ${JSON.stringify(inc)}.`);
895
+ }
881
896
  const limit = optRange(opts.limit, "limit", PAGE_LIMIT_MIN, PAGE_LIMIT_MAX);
882
897
  const body = {};
883
- if (opts.include !== undefined)
898
+ if (opts.include != null)
884
899
  body.include = opts.include;
885
900
  if (limit !== undefined)
886
901
  body.limit = limit;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.44",
3
+ "version": "2.2.45",
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",