wontopos 2.2.25 → 2.2.26

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
@@ -52,7 +52,13 @@ registration and never counts against it.
52
52
  ## Recall caching
53
53
 
54
54
  Opt in per search and repeated or extended queries reuse the previous result
55
- at 10% of the normal rate (Tablet and Scroll models):
55
+ at 10% of the normal rate (Tablet and Scroll models).
56
+
57
+ It is not free to turn on: the FIRST call writes the cache and bills the query
58
+ tokens at 2x for a `5m` TTL, 3x for `1h`. Only hits inside the TTL bill at 0.1x.
59
+ So it pays for a query you repeat or extend, and costs more for one you issue
60
+ once — do not switch it on globally. Any write to the store invalidates its cache
61
+ at once, so a hit can never predate a new memory.
56
62
 
57
63
  ```ts
58
64
  const hits = await mem.search("...the conversation so far...", "alice", 10,
@@ -74,6 +80,27 @@ Built in, no configuration needed:
74
80
  meaning "delete everything"; wiping a store is only ever the explicit
75
81
  `deleteAll(userId)` / `deleteStore(userId)`.
76
82
 
83
+ ### Behind a corporate proxy
84
+
85
+ Node's global `fetch` (undici) ignores `HTTP_PROXY` / `HTTPS_PROXY`, so behind an
86
+ egress proxy this SDK could not connect at all — while the Python and Rust SDKs went
87
+ through, because `requests` and `reqwest` both read those variables. Pass a
88
+ proxy-aware `fetch` and the three behave the same:
89
+
90
+ ```ts
91
+ import { ProxyAgent } from "undici";
92
+
93
+ const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
94
+ const mem = new Client({
95
+ apiKey: process.env.WONTOPOS_API_KEY!,
96
+ fetch: (url, init) => fetch(url, { ...init, dispatcher: agent } as any),
97
+ });
98
+ ```
99
+
100
+ The same option is how you add instrumentation or drive the client without a
101
+ network in tests. It replaces the transport, not the client's rules — retries,
102
+ timeouts, redirect refusal and the response cap all still apply.
103
+
77
104
  ## Security
78
105
 
79
106
  Built in, none of it configurable off:
@@ -286,7 +286,9 @@ export interface SearchFilters {
286
286
  export declare function _resetFilterWarnings(): void;
287
287
  /** Known `search` options; extra fields pass through to the API untouched. */
288
288
  export interface SearchOptions {
289
- /** Recall caching: repeated/extended queries within the TTL bill at 0.1×. */
289
+ /** Recall caching. A hit inside the TTL bills at 0.1× — but the FIRST call
290
+ * writes the cache and bills the query tokens at 2× (`5m`) or 3× (`1h`), so
291
+ * this only pays for a query you repeat or extend. Do not enable it globally. */
290
292
  cache_control?: {
291
293
  ttl: "5m" | "1h";
292
294
  };
@@ -336,6 +338,26 @@ export interface ClientOptions {
336
338
  * key on an options object is not an error in TS at runtime). Both names work;
337
339
  * `maxRetries` wins if somehow both are given. */
338
340
  retries?: number;
341
+ /** The `fetch` used for every request (default: the global `fetch`).
342
+ *
343
+ * This is the seam for anything the runtime cannot express through options.
344
+ * The one that matters in practice is a corporate egress proxy: Node's global
345
+ * fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy
346
+ * this SDK could not connect at all — while the Python and Rust SDKs went
347
+ * through, because `requests` and `reqwest` both read those variables. Pass a
348
+ * proxy-aware fetch and the three behave the same again:
349
+ *
350
+ * import { ProxyAgent } from "undici";
351
+ * const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
352
+ * const mem = new Client({
353
+ * apiKey,
354
+ * fetch: (url, init) => fetch(url, { ...init, dispatcher: agent } as any),
355
+ * });
356
+ *
357
+ * It is also how you add instrumentation or drive the client in a test without
358
+ * a network. Retries, timeouts, redirect refusal and the size cap all still
359
+ * apply — this replaces the transport, not the client's rules. */
360
+ fetch?: typeof fetch;
339
361
  }
340
362
  /** Wontopos memory client. Store and recall memories, isolated per `userId`. */
341
363
  export declare class Client {
@@ -345,6 +367,9 @@ export declare class Client {
345
367
  private readonly apiKey;
346
368
  private readonly model;
347
369
  private readonly maxRetries;
370
+ /** Transport. Held as a field (not read off `globalThis` per call) so a caller
371
+ * who passes one gets it for every request, including from cloned clients. */
372
+ private readonly fetchImpl;
348
373
  /** The store every call uses unless one passes `userId`. */
349
374
  private readonly defaultUser;
350
375
  private _rateLimit;
package/dist/wontopos.js CHANGED
@@ -37,7 +37,7 @@
37
37
  * runtime's (never disabled by this SDK; Node 18+ floors TLS at 1.2). Prefer
38
38
  * `Client.fromEnv()` over keys in source code.
39
39
  */
40
- const VERSION = "2.2.25";
40
+ const VERSION = "2.2.26";
41
41
  /** Runtime info helps support debug a report ("node 18 on Windows...") —
42
42
  * platform only, never anything identifying. Browsers have no `process` (and
43
43
  * silently drop the UA header anyway). */
@@ -310,6 +310,9 @@ export class Client {
310
310
  apiKey;
311
311
  model;
312
312
  maxRetries;
313
+ /** Transport. Held as a field (not read off `globalThis` per call) so a caller
314
+ * who passes one gets it for every request, including from cloned clients. */
315
+ fetchImpl;
313
316
  /** The store every call uses unless one passes `userId`. */
314
317
  defaultUser;
315
318
  _rateLimit = null;
@@ -344,6 +347,17 @@ export class Client {
344
347
  // "retries exhausted"; Infinity would retry forever.
345
348
  const mr = opts.maxRetries ?? opts.retries;
346
349
  this.maxRetries = typeof mr === "number" && Number.isFinite(mr) ? Math.max(0, Math.floor(mr)) : 2;
350
+ // Bind the global so `fetch` is not called as a method of `globalThis`, which
351
+ // throws "Illegal invocation" on some runtimes. A caller-supplied fetch is
352
+ // taken as given — it is already whatever they meant to hand us.
353
+ const f = opts.fetch;
354
+ if (f !== undefined && typeof f !== "function") {
355
+ throw new Error("fetch must be a function (a fetch-compatible transport)");
356
+ }
357
+ this.fetchImpl = f ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : undefined);
358
+ if (typeof this.fetchImpl !== "function") {
359
+ throw new Error("no fetch available in this runtime — pass one as `fetch` (Node 18+ has it built in; older Node needs undici)");
360
+ }
347
361
  this.headers = {
348
362
  "X-API-Key": this.apiKey,
349
363
  "Content-Type": "application/json",
@@ -395,6 +409,10 @@ export class Client {
395
409
  model: this.model,
396
410
  userId: this.defaultUser,
397
411
  maxRetries: this.maxRetries,
412
+ // Carry the transport across clones. Without this, `withModel(...)` and
413
+ // friends would silently fall back to the global fetch and a proxy-bound
414
+ // client would stop connecting the moment it was cloned.
415
+ fetch: this.fetchImpl,
398
416
  ...overrides,
399
417
  });
400
418
  }
@@ -710,7 +728,7 @@ export class Client {
710
728
  const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
711
729
  let res;
712
730
  try {
713
- res = await fetch(`${this.base}${path}`, {
731
+ res = await this.fetchImpl(`${this.base}${path}`, {
714
732
  method,
715
733
  headers: extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers,
716
734
  body: body === undefined ? undefined : JSON.stringify(body),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos",
3
- "version": "2.2.25",
3
+ "version": "2.2.26",
4
4
  "description": "Wontopos — long-term memory for AI agents. Pure semantic retrieval, identical recall in every language, ~100× lower LLM bill.",
5
5
  "keywords": [
6
6
  "memory",