wontopos 2.2.15 → 2.2.17
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/dist/wontopos.d.ts +1 -1
- package/dist/wontopos.js +28 -9
- package/package.json +1 -1
package/dist/wontopos.d.ts
CHANGED
|
@@ -273,7 +273,7 @@ export declare class Client {
|
|
|
273
273
|
withTimeout(timeoutMs: number): Client;
|
|
274
274
|
/** A client with a different retry budget (everything else kept). 0 disables retries. */
|
|
275
275
|
withRetries(maxRetries: number): Client;
|
|
276
|
-
/** Store one memory. `metadata` optional (e.g. { event_date: "2026-03-
|
|
276
|
+
/** Store one memory. `metadata` optional (e.g. { event_date: "2026-03-01T00:00:00Z" } — RFC3339).
|
|
277
277
|
* `metadata.speaker`: "me" = the assistant's own words, or a person's name (up to 50 per store). */
|
|
278
278
|
add(content: string, userId?: string, metadata?: Record<string, unknown>): Promise<StoreResult>;
|
|
279
279
|
/** Alias of `add` — store one memory. Same surface as the Python SDK's `store`. */
|
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.
|
|
40
|
+
const VERSION = "2.2.17";
|
|
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). */
|
|
@@ -69,6 +69,10 @@ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
|
|
|
69
69
|
const MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
70
70
|
/** Model names travel in a header — only header-safe characters. */
|
|
71
71
|
const MODEL_RE = /^[A-Za-z0-9._-]+$/;
|
|
72
|
+
/** Cap a server-controlled error message so a hostile body can't blow up a log. */
|
|
73
|
+
const MAX_ERR_MSG = 4096;
|
|
74
|
+
/** Paging backstop: bound the walk so a fresh-cursor-forever server can't loop us. */
|
|
75
|
+
const MAX_PAGES = 1_000_000;
|
|
72
76
|
/** Error codes that only occur while ESTABLISHING a connection — the request
|
|
73
77
|
* never reached the server, so a retry can't double-process a write. Mid-stream
|
|
74
78
|
* codes (ECONNRESET, EPIPE, UND_ERR_SOCKET, ETIMEDOUT) are ambiguous: the server
|
|
@@ -294,7 +298,7 @@ export class Client {
|
|
|
294
298
|
// ----- write -----
|
|
295
299
|
// `userId` is optional in every call below - omit it to use the client's default
|
|
296
300
|
// store (set via `new Client({ apiKey, userId })` or `withUser`).
|
|
297
|
-
/** Store one memory. `metadata` optional (e.g. { event_date: "2026-03-
|
|
301
|
+
/** Store one memory. `metadata` optional (e.g. { event_date: "2026-03-01T00:00:00Z" } — RFC3339).
|
|
298
302
|
* `metadata.speaker`: "me" = the assistant's own words, or a person's name (up to 50 per store). */
|
|
299
303
|
add(content, userId, metadata = {}) {
|
|
300
304
|
return this.post("/api/v1/memory/store", { user_id: this.uid(userId), content, metadata });
|
|
@@ -381,12 +385,13 @@ export class Client {
|
|
|
381
385
|
async *iterMemories(userId, opts = {}) {
|
|
382
386
|
let cursor;
|
|
383
387
|
const seen = new Set();
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
388
|
+
// Backstop: the cursor-repeat guard catches a repeated cursor, but not a
|
|
389
|
+
// server that mints a FRESH cursor every page forever — bound the walk.
|
|
390
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
391
|
+
const p = await this.listMemories(userId, { limit: opts.pageSize ?? 100, cursor });
|
|
392
|
+
for (const m of p.memories ?? [])
|
|
387
393
|
yield m;
|
|
388
|
-
const next =
|
|
389
|
-
// Stop on last page OR a server that repeats a cursor (would loop forever).
|
|
394
|
+
const next = p.next_cursor ?? undefined;
|
|
390
395
|
if (!next || seen.has(next))
|
|
391
396
|
break;
|
|
392
397
|
seen.add(next);
|
|
@@ -564,6 +569,7 @@ export class Client {
|
|
|
564
569
|
}
|
|
565
570
|
if ((res.status >= 300 && res.status < 400) || res.type === "opaqueredirect") {
|
|
566
571
|
clearTimeout(timer);
|
|
572
|
+
void res.body?.cancel(); // release the stream — otherwise the connection leaks
|
|
567
573
|
throw new WosError(res.status, "unexpected redirect — refused (the API key never follows a redirect). Check baseUrl: exact host, https://.");
|
|
568
574
|
}
|
|
569
575
|
this._rateLimit = parseRateLimit(res.headers) ?? this._rateLimit;
|
|
@@ -612,18 +618,31 @@ export class Client {
|
|
|
612
618
|
catch {
|
|
613
619
|
/* keep raw text */
|
|
614
620
|
}
|
|
621
|
+
// Cap the FINAL message (a string `error` field can be as huge as the body),
|
|
622
|
+
// so a hostile server can't turn our exception/log into a giant string.
|
|
623
|
+
if (typeof msg === "string" && msg.length > MAX_ERR_MSG)
|
|
624
|
+
msg = msg.slice(0, MAX_ERR_MSG) + "…(truncated)";
|
|
615
625
|
throw errorFor(res.status, msg, requestId);
|
|
616
626
|
}
|
|
617
627
|
if (!text)
|
|
618
|
-
return
|
|
628
|
+
return {};
|
|
629
|
+
let data;
|
|
619
630
|
try {
|
|
620
|
-
|
|
631
|
+
data = JSON.parse(text);
|
|
621
632
|
}
|
|
622
633
|
catch (e) {
|
|
623
634
|
// A 2xx with a corrupt body is a real failure — surface it, don't leak
|
|
624
635
|
// a raw SyntaxError (parity with the Rust SDK).
|
|
625
636
|
throw new WosError(res.status, `invalid JSON in response: ${e?.message ?? e}`);
|
|
626
637
|
}
|
|
638
|
+
// Enforce a JSON OBJECT: every method reads the result with `.field`, so a
|
|
639
|
+
// body that parses to null / a number / a string / an array (a broken or
|
|
640
|
+
// hostile server) must be a clean WosError, not a `null.memories` TypeError.
|
|
641
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
642
|
+
const got = data === null ? "null" : Array.isArray(data) ? "array" : typeof data;
|
|
643
|
+
throw new WosError(res.status, `expected a JSON object in the response, got ${got}`);
|
|
644
|
+
}
|
|
645
|
+
return data;
|
|
627
646
|
}
|
|
628
647
|
throw new WosError(0, "retries exhausted"); // unreachable; keeps the compiler happy
|
|
629
648
|
}
|
package/package.json
CHANGED