wontopos 2.2.37 → 2.2.39
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 +11 -108
- package/dist/cjs/wontopos.d.cts +1054 -0
- package/dist/cjs/wontopos.js +129 -43
- package/dist/wontopos.d.ts +39 -17
- package/dist/wontopos.js +129 -43
- package/package.json +17 -7
package/dist/cjs/wontopos.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.WME = exports.Client = exports.ServerError = exports.RateLimitError = 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.
|
|
6
|
+
const VERSION = "2.2.39";
|
|
7
7
|
const RUNTIME = (() => {
|
|
8
8
|
const p = globalThis?.process;
|
|
9
9
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -18,7 +18,7 @@ function logDebug(msg) {
|
|
|
18
18
|
const DEFAULT_BASE_URL = "https://api.wontopos.com";
|
|
19
19
|
const DEFAULT_MODEL = "tablet-2";
|
|
20
20
|
const RETRY_ALWAYS = new Set([429]);
|
|
21
|
-
const RETRY_IF_IDEMPOTENT = new Set([502, 503]);
|
|
21
|
+
const RETRY_IF_IDEMPOTENT = new Set([408, 502, 503, 504]);
|
|
22
22
|
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
23
23
|
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
|
|
24
24
|
const MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
@@ -29,6 +29,12 @@ function normalizeStoreId(id) {
|
|
|
29
29
|
return id.toLowerCase().replace(/[^a-z0-9_]/g, "_");
|
|
30
30
|
}
|
|
31
31
|
const WARNED_STORE_IDS_MAX = 1024;
|
|
32
|
+
function assertUsableStoreId(userId) {
|
|
33
|
+
if (userId !== undefined && (typeof userId !== "string" || !userId.trim())) {
|
|
34
|
+
throw new Error(`userId must be a non-blank string; got ${JSON.stringify(userId)}. Omit it to use the client's ` +
|
|
35
|
+
"default store, or pass a real store id — anything else would silently write into the default store.");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
32
38
|
const WARNED_FILTER_KEYS_MAX = 1024;
|
|
33
39
|
const warnedStoreIds = new Set();
|
|
34
40
|
function warnIfStoreIdCollapses(id) {
|
|
@@ -212,6 +218,47 @@ const KNOWN_FILTER_KEYS = new Set([
|
|
|
212
218
|
"min_importance",
|
|
213
219
|
]);
|
|
214
220
|
const warnedFilterKeys = new Set();
|
|
221
|
+
const KNOWN_SEARCH_KEYS = new Set([
|
|
222
|
+
"cache_control", "speaker", "filters", "verify", "max_images", "extra",
|
|
223
|
+
]);
|
|
224
|
+
const RESERVED_SEARCH_KEYS = new Set(["user_id", "query", "max_results"]);
|
|
225
|
+
const KNOWN_RECALL_KEYS = new Set(["form", "tz", "limit", "context_limit"]);
|
|
226
|
+
const KNOWN_ENGRAM_KEYS = new Set(["form", "tz"]);
|
|
227
|
+
function checkSearchOpts(opts) {
|
|
228
|
+
checkOpts(opts, KNOWN_SEARCH_KEYS, "search");
|
|
229
|
+
}
|
|
230
|
+
function checkOpts(opts, known, what) {
|
|
231
|
+
for (const k of Object.keys(opts)) {
|
|
232
|
+
if (RESERVED_SEARCH_KEYS.has(k))
|
|
233
|
+
throw new Error(`${JSON.stringify(k)} is set by the call, not by options — pass it as an argument. ` +
|
|
234
|
+
`An app forwarding untrusted input as options cannot steer the store, the query ` +
|
|
235
|
+
`or the count, and this says so rather than dropping it silently.`);
|
|
236
|
+
if (known.has(k))
|
|
237
|
+
continue;
|
|
238
|
+
const near = [...known].find((n) => n !== "extra" && editWithin(k, n, 2));
|
|
239
|
+
throw new Error(`unknown ${what} option ${JSON.stringify(k)}` +
|
|
240
|
+
(near ? ` — did you mean ${JSON.stringify(near)}?` : "") +
|
|
241
|
+
`. It would have had no effect, and the call would have looked like it worked.` +
|
|
242
|
+
(known.has("extra")
|
|
243
|
+
? ` Pass it under \`extra\` if the service accepts it and this client does not know it yet.`
|
|
244
|
+
: ""));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function editWithin(a, b, max) {
|
|
248
|
+
if (Math.abs(a.length - b.length) > max)
|
|
249
|
+
return false;
|
|
250
|
+
const prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
251
|
+
for (let i = 1; i <= a.length; i++) {
|
|
252
|
+
let diag = prev[0];
|
|
253
|
+
prev[0] = i;
|
|
254
|
+
for (let j = 1; j <= b.length; j++) {
|
|
255
|
+
const tmp = prev[j];
|
|
256
|
+
prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
257
|
+
diag = tmp;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return prev[b.length] <= max;
|
|
261
|
+
}
|
|
215
262
|
function warnOnUnknownFilters(filters) {
|
|
216
263
|
if (!filters || typeof filters !== "object" || Array.isArray(filters))
|
|
217
264
|
return;
|
|
@@ -257,6 +304,11 @@ class Client {
|
|
|
257
304
|
throw new Error("apiKey contains whitespace - check for a stray newline or paste error");
|
|
258
305
|
if (/[\x00-\x1f\x7f]/.test(key))
|
|
259
306
|
throw new Error("apiKey contains a control character - check for a stray byte or paste error");
|
|
307
|
+
if (/[^\x00-\x7f]/.test(key)) {
|
|
308
|
+
const bad = key.match(/[^\x00-\x7f]/)?.[0];
|
|
309
|
+
throw new Error(`apiKey contains a non-ASCII character (${JSON.stringify(bad)}) - rich text turns '-' into an ` +
|
|
310
|
+
"en dash; copy the key from a plain-text field");
|
|
311
|
+
}
|
|
260
312
|
this.apiKey = key;
|
|
261
313
|
this.base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
262
314
|
const tm = opts.timeoutMs;
|
|
@@ -265,7 +317,8 @@ class Client {
|
|
|
265
317
|
if (this.model && !MODEL_RE.test(this.model)) {
|
|
266
318
|
throw new Error(`invalid model name: ${JSON.stringify(this.model)} (letters, digits, '.', '_', '-' only)`);
|
|
267
319
|
}
|
|
268
|
-
|
|
320
|
+
assertUsableStoreId(opts.userId);
|
|
321
|
+
this.defaultUser = opts.userId ?? DEFAULT_USER;
|
|
269
322
|
const mr = opts.maxRetries ?? opts.retries;
|
|
270
323
|
this.maxRetries = typeof mr === "number" && Number.isFinite(mr) ? Math.max(0, Math.floor(mr)) : 2;
|
|
271
324
|
const dl = opts.deadlineMs;
|
|
@@ -313,11 +366,8 @@ class Client {
|
|
|
313
366
|
return `Client(${this.base}, model=${this.model}, userId=${this.defaultUser}, apiKey=${maskKey(this.apiKey)})`;
|
|
314
367
|
}
|
|
315
368
|
uid(userId) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
"default store, or pass a real store id — anything else would silently write into the default store.");
|
|
319
|
-
}
|
|
320
|
-
const id = userId || this.defaultUser;
|
|
369
|
+
assertUsableStoreId(userId);
|
|
370
|
+
const id = userId ?? this.defaultUser;
|
|
321
371
|
warnIfStoreIdCollapses(id);
|
|
322
372
|
return id;
|
|
323
373
|
}
|
|
@@ -339,6 +389,10 @@ class Client {
|
|
|
339
389
|
return this.clone({ model });
|
|
340
390
|
}
|
|
341
391
|
withUser(userId) {
|
|
392
|
+
if (userId === undefined) {
|
|
393
|
+
throw new Error("withUser() needs a store id. It was called with undefined — usually a lookup that " +
|
|
394
|
+
"found nothing. Use the client's own default instead of calling withUser at all.");
|
|
395
|
+
}
|
|
342
396
|
return this.clone({ userId });
|
|
343
397
|
}
|
|
344
398
|
withSignal(signal) {
|
|
@@ -353,43 +407,49 @@ class Client {
|
|
|
353
407
|
withRetries(maxRetries) {
|
|
354
408
|
return this.clone({ maxRetries });
|
|
355
409
|
}
|
|
356
|
-
add(content, userId, metadata = {}, opts = {}) {
|
|
410
|
+
async add(content, userId, metadata = {}, opts = {}) {
|
|
357
411
|
const body = { user_id: this.uid(userId), content, metadata };
|
|
358
412
|
if (opts.image !== undefined)
|
|
359
413
|
body.image = normalizeImage(opts.image);
|
|
360
414
|
return this.post("/api/v1/memory/store", body, opts.idempotencyKey);
|
|
361
415
|
}
|
|
362
|
-
store(content, userId, metadata = {}, opts = {}) {
|
|
416
|
+
async store(content, userId, metadata = {}, opts = {}) {
|
|
363
417
|
return this.add(content, userId, metadata, opts);
|
|
364
418
|
}
|
|
365
|
-
addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
419
|
+
async addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
366
420
|
return this.post("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, opts.idempotencyKey);
|
|
367
421
|
}
|
|
368
|
-
addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
422
|
+
async addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
369
423
|
const body = { user_id: this.uid(userId), content, category };
|
|
370
424
|
if (timestamp)
|
|
371
425
|
body.timestamp = timestamp;
|
|
372
426
|
return this.post("/api/v1/memory/bulk-store", body, opts.idempotencyKey);
|
|
373
427
|
}
|
|
374
|
-
update(oldMemoryId, newContent, userId, opts = {}) {
|
|
428
|
+
async update(oldMemoryId, newContent, userId, opts = {}) {
|
|
375
429
|
return this.post("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, opts.idempotencyKey);
|
|
376
430
|
}
|
|
377
431
|
async search(query, userId, limit = 10, opts = {}) {
|
|
432
|
+
checkSearchOpts(opts);
|
|
378
433
|
warnOnUnknownFilters(opts.filters);
|
|
379
434
|
checkCount(limit, "limit");
|
|
380
|
-
const
|
|
435
|
+
const { extra, ...known } = opts;
|
|
436
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
381
437
|
return mergeResults(r);
|
|
382
438
|
}
|
|
383
439
|
async searchSelf(query, userId, limit = 10, opts = {}) {
|
|
440
|
+
checkSearchOpts(opts);
|
|
384
441
|
warnOnUnknownFilters(opts.filters);
|
|
385
442
|
checkCount(limit, "limit");
|
|
386
|
-
const
|
|
443
|
+
const { extra, ...known } = opts;
|
|
444
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
387
445
|
return { memories: asRecords(r.memories), self_memories: asRecords(r.self_memories) };
|
|
388
446
|
}
|
|
389
447
|
async searchFull(query, userId, limit = 10, opts = {}) {
|
|
448
|
+
checkSearchOpts(opts);
|
|
390
449
|
warnOnUnknownFilters(opts.filters);
|
|
391
450
|
checkCount(limit, "limit");
|
|
392
|
-
const
|
|
451
|
+
const { extra, ...known } = opts;
|
|
452
|
+
const r = await this.post("/api/v1/memory/search", { ...extra, ...known, user_id: this.uid(userId), query, max_results: limit });
|
|
393
453
|
return {
|
|
394
454
|
...r,
|
|
395
455
|
memories: asRecords(r.memories),
|
|
@@ -398,6 +458,7 @@ class Client {
|
|
|
398
458
|
};
|
|
399
459
|
}
|
|
400
460
|
async recall(query, userId, opts = {}) {
|
|
461
|
+
checkOpts(opts, KNOWN_RECALL_KEYS, "recall");
|
|
401
462
|
const body = { user_id: this.uid(userId), query };
|
|
402
463
|
if (opts.limit !== undefined) {
|
|
403
464
|
checkCount(opts.limit, "limit");
|
|
@@ -409,14 +470,15 @@ class Client {
|
|
|
409
470
|
}
|
|
410
471
|
return this.post("/api/v1/memory/recall", withForm(body, opts.form, opts.tz));
|
|
411
472
|
}
|
|
412
|
-
engram(name, query, userId, opts = {}) {
|
|
473
|
+
async engram(name, query, userId, opts = {}) {
|
|
474
|
+
checkOpts(opts, KNOWN_ENGRAM_KEYS, "engram");
|
|
413
475
|
return this.post("/api/v1/engram/run", withForm({ name, user_id: this.uid(userId), query }, opts.form, opts.tz));
|
|
414
476
|
}
|
|
415
477
|
async history(userId) {
|
|
416
478
|
const r = await this.post("/api/v1/memory/history", { user_id: this.uid(userId) });
|
|
417
479
|
return asRecords(r.turns);
|
|
418
480
|
}
|
|
419
|
-
stats(userId) {
|
|
481
|
+
async stats(userId) {
|
|
420
482
|
return this.post("/api/v1/memory/stats", { user_id: this.uid(userId) });
|
|
421
483
|
}
|
|
422
484
|
async usage(days = 7) {
|
|
@@ -425,13 +487,13 @@ class Client {
|
|
|
425
487
|
}
|
|
426
488
|
return this.request("GET", `/api/v1/won/usage?days=${days}`);
|
|
427
489
|
}
|
|
428
|
-
get(userId, memoryId) {
|
|
429
|
-
if (
|
|
490
|
+
async get(userId, memoryId) {
|
|
491
|
+
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
430
492
|
throw new Error("memoryId is required — the id that add/store or listMemories returned.");
|
|
431
493
|
}
|
|
432
|
-
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId }).then((r) => asObj(r.memory));
|
|
494
|
+
return this.post("/api/v1/memory/get", { user_id: this.uid(userId), memory_id: memoryId.trim() }).then((r) => asObj(r.memory));
|
|
433
495
|
}
|
|
434
|
-
listMemories(userId, opts = {}) {
|
|
496
|
+
async listMemories(userId, opts = {}) {
|
|
435
497
|
const body = { user_id: this.uid(userId), limit: opts.limit ?? 100 };
|
|
436
498
|
if (opts.cursor)
|
|
437
499
|
body.cursor = opts.cursor;
|
|
@@ -459,22 +521,28 @@ class Client {
|
|
|
459
521
|
out.push(m);
|
|
460
522
|
return out;
|
|
461
523
|
}
|
|
462
|
-
|
|
463
|
-
|
|
524
|
+
async exportImages(userId, opts = {}) {
|
|
525
|
+
const out = [];
|
|
526
|
+
for await (const m of this.iterImages(userId, opts))
|
|
527
|
+
out.push(m);
|
|
528
|
+
return out;
|
|
529
|
+
}
|
|
530
|
+
async getImage(userId, memoryId) {
|
|
531
|
+
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
464
532
|
throw new Error("memoryId is required — the id that add/store or listImages returned.");
|
|
465
533
|
}
|
|
466
|
-
return this.requestBytes("/api/v1/memory/image", { user_id: this.uid(userId), memory_id: memoryId });
|
|
534
|
+
return this.requestBytes("/api/v1/memory/image", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
467
535
|
}
|
|
468
|
-
forgetImage(userId, memoryId, opts = {}) {
|
|
469
|
-
if (
|
|
536
|
+
async forgetImage(userId, memoryId, opts = {}) {
|
|
537
|
+
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
470
538
|
throw new Error("memoryId is required — the id of the memory whose image you want removed.");
|
|
471
539
|
}
|
|
472
|
-
const body = { user_id: this.uid(userId), memory_id: memoryId };
|
|
540
|
+
const body = { user_id: this.uid(userId), memory_id: memoryId.trim() };
|
|
473
541
|
if (opts.preview)
|
|
474
542
|
body.preview = true;
|
|
475
543
|
return this.request("DELETE", "/api/v1/memory/image", body);
|
|
476
544
|
}
|
|
477
|
-
listImages(userId, opts = {}) {
|
|
545
|
+
async listImages(userId, opts = {}) {
|
|
478
546
|
const body = { user_id: this.uid(userId) };
|
|
479
547
|
if (opts.limit !== undefined)
|
|
480
548
|
body.limit = opts.limit;
|
|
@@ -504,7 +572,7 @@ class Client {
|
|
|
504
572
|
throw new Error(`stopped after ${MAX_PAGES} pages — the store did not end. This is a truncated ` +
|
|
505
573
|
`answer, not the whole store.`);
|
|
506
574
|
}
|
|
507
|
-
revisions(userId, opts = {}) {
|
|
575
|
+
async revisions(userId, opts = {}) {
|
|
508
576
|
const body = {};
|
|
509
577
|
if (opts.include !== undefined)
|
|
510
578
|
body.include = opts.include;
|
|
@@ -517,13 +585,13 @@ class Client {
|
|
|
517
585
|
body.user_id = this.uid(userId);
|
|
518
586
|
return this.post("/api/v1/won/revisions", body);
|
|
519
587
|
}
|
|
520
|
-
lineage(userId, memoryId) {
|
|
521
|
-
if (
|
|
588
|
+
async lineage(userId, memoryId) {
|
|
589
|
+
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
522
590
|
throw new Error("memoryId is required — the memory whose history you want.");
|
|
523
591
|
}
|
|
524
|
-
return this.post("/api/v1/memory/lineage", { user_id: this.uid(userId), memory_id: memoryId });
|
|
592
|
+
return this.post("/api/v1/memory/lineage", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
525
593
|
}
|
|
526
|
-
bySpeaker(speaker, userId, opts = {}) {
|
|
594
|
+
async bySpeaker(speaker, userId, opts = {}) {
|
|
527
595
|
if (!speaker || typeof speaker !== "string" || speaker.trim() === "") {
|
|
528
596
|
throw new Error('speaker is required — "me" for the assistant, or a person\'s name.');
|
|
529
597
|
}
|
|
@@ -547,41 +615,42 @@ class Client {
|
|
|
547
615
|
async listEngrams() {
|
|
548
616
|
const data = await this.request("GET", "/api/v1/engram");
|
|
549
617
|
return {
|
|
618
|
+
...data,
|
|
550
619
|
engrams: asRecords(data.engrams),
|
|
551
620
|
forms: asRecords(data.forms),
|
|
552
621
|
note: typeof data.note === "string" ? data.note : undefined,
|
|
553
622
|
};
|
|
554
623
|
}
|
|
555
|
-
createStore(userId) {
|
|
624
|
+
async createStore(userId) {
|
|
556
625
|
return this.post("/api/v1/memory/collection", { user_id: this.uid(userId) });
|
|
557
626
|
}
|
|
558
627
|
async listStores() {
|
|
559
628
|
const r = await this.request("GET", "/api/v1/memory/collections");
|
|
560
629
|
return asRecords(r.collections);
|
|
561
630
|
}
|
|
562
|
-
deleteStore(userId) {
|
|
631
|
+
async deleteStore(userId) {
|
|
563
632
|
if (typeof userId !== "string" || !userId.trim())
|
|
564
633
|
throw new Error("userId is required (a non-blank string) — deleteStore never falls back to the default store.");
|
|
565
634
|
warnIfStoreIdCollapses(userId);
|
|
566
635
|
return this.request("DELETE", "/api/v1/memory/collection", { user_id: userId });
|
|
567
636
|
}
|
|
568
|
-
addSpeaker(speaker, userId) {
|
|
637
|
+
async addSpeaker(speaker, userId) {
|
|
569
638
|
return this.post("/api/v1/memory/speakers", { user_id: this.uid(userId), speaker });
|
|
570
639
|
}
|
|
571
640
|
async listSpeakers(userId) {
|
|
572
641
|
const r = await this.request("GET", `/api/v1/memory/speakers?user_id=${encodeURIComponent(this.uid(userId))}`);
|
|
573
642
|
return { ...r, speakers: asRecords(r?.speakers) };
|
|
574
643
|
}
|
|
575
|
-
removeSpeaker(speaker, userId) {
|
|
644
|
+
async removeSpeaker(speaker, userId) {
|
|
576
645
|
return this.request("DELETE", "/api/v1/memory/speakers", { user_id: this.uid(userId), speaker });
|
|
577
646
|
}
|
|
578
|
-
delete(userId, memoryId) {
|
|
647
|
+
async delete(userId, memoryId) {
|
|
579
648
|
if (typeof memoryId !== "string" || !memoryId.trim()) {
|
|
580
649
|
throw new Error("memoryId is required (non-blank). To delete every memory in a store, call deleteAll(userId) explicitly.");
|
|
581
650
|
}
|
|
582
651
|
return this.post("/api/v1/memory/forget", { user_id: this.uid(userId), memory_id: memoryId.trim() });
|
|
583
652
|
}
|
|
584
|
-
deleteAll(userId) {
|
|
653
|
+
async deleteAll(userId) {
|
|
585
654
|
if (typeof userId !== "string" || !userId.trim())
|
|
586
655
|
throw new Error("userId is required (a non-blank string) for deleteAll — anything else would wipe the default store.");
|
|
587
656
|
warnIfStoreIdCollapses(userId);
|
|
@@ -716,7 +785,9 @@ class Client {
|
|
|
716
785
|
if (e?.name === "AbortError" || att.signal.aborted) {
|
|
717
786
|
throw new APIConnectionError(0, this.abortMessage(att.why()));
|
|
718
787
|
}
|
|
719
|
-
|
|
788
|
+
if (e instanceof WosError)
|
|
789
|
+
throw e;
|
|
790
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
720
791
|
}
|
|
721
792
|
if (buf.byteLength === 0) {
|
|
722
793
|
throw new WosError(res.status, "empty image body — the service returned no bytes");
|
|
@@ -789,6 +860,14 @@ class Client {
|
|
|
789
860
|
const attempts = this.maxRetries + 1;
|
|
790
861
|
const logPath = path.split("?")[0];
|
|
791
862
|
const deadlineAt = this.deadlineAt();
|
|
863
|
+
let payload;
|
|
864
|
+
try {
|
|
865
|
+
payload = body === undefined ? undefined : JSON.stringify(body);
|
|
866
|
+
}
|
|
867
|
+
catch (e) {
|
|
868
|
+
throw new Error(`request body could not be serialized: ${e instanceof Error ? e.message : String(e)}. ` +
|
|
869
|
+
"Pass plain JSON values — an ORM entity, a BigInt or a circular reference cannot be sent.");
|
|
870
|
+
}
|
|
792
871
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
793
872
|
const start = Date.now();
|
|
794
873
|
const att = this.beginAttempt(deadlineAt);
|
|
@@ -797,7 +876,7 @@ class Client {
|
|
|
797
876
|
res = await this.fetchImpl(`${this.base}${path}`, {
|
|
798
877
|
method,
|
|
799
878
|
headers: extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers,
|
|
800
|
-
body:
|
|
879
|
+
body: payload,
|
|
801
880
|
signal: att.signal,
|
|
802
881
|
redirect: "manual",
|
|
803
882
|
});
|
|
@@ -841,7 +920,14 @@ class Client {
|
|
|
841
920
|
}
|
|
842
921
|
if (e instanceof WosError)
|
|
843
922
|
throw e;
|
|
844
|
-
|
|
923
|
+
if (!(IDEMPOTENT_METHODS.has(method.toUpperCase()) && attempt + 1 < attempts)) {
|
|
924
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
925
|
+
}
|
|
926
|
+
att.clear();
|
|
927
|
+
const delay = this.backoffMs(attempt);
|
|
928
|
+
logDebug(`${method} ${logPath} — body dropped mid-stream, retrying in ${delay}ms (attempt ${attempt + 1}/${attempts})`);
|
|
929
|
+
await this.backoffSleep(delay, deadlineAt);
|
|
930
|
+
continue;
|
|
845
931
|
}
|
|
846
932
|
finally {
|
|
847
933
|
att.clear();
|
package/dist/wontopos.d.ts
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* and searching call no LLM.
|
|
27
27
|
*
|
|
28
28
|
* Reliability: every call retries transient failures with exponential backoff +
|
|
29
|
-
* jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors
|
|
30
|
-
* when a retry can never double-process a write (idempotent calls, or a failure
|
|
29
|
+
* jitter, honoring `Retry-After` — 429 always; 408/502/503/504 and connection errors
|
|
30
|
+
* only when a retry can never double-process a write (idempotent calls, or a failure
|
|
31
31
|
* at connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`, or per
|
|
32
32
|
* call site via the `withTimeout()` / `withRetries()` clones.
|
|
33
33
|
*
|
|
@@ -86,8 +86,8 @@ export declare class RateLimitError extends WosError {
|
|
|
86
86
|
/**
|
|
87
87
|
* 5xx — the service failed.
|
|
88
88
|
*
|
|
89
|
-
* `502` / `503` are transient: the client already retries them for calls
|
|
90
|
-
* retry cannot double-process a write, and retrying yourself is reasonable.
|
|
89
|
+
* `502` / `503` / `504` are transient: the client already retries them for calls
|
|
90
|
+
* where a retry cannot double-process a write, and retrying yourself is reasonable.
|
|
91
91
|
*
|
|
92
92
|
* `501` is NOT transient. It means the engine behind the model you selected does
|
|
93
93
|
* not implement that endpoint at all (the error carries `model` and `endpoint`).
|
|
@@ -428,6 +428,9 @@ export interface EngramCatalog {
|
|
|
428
428
|
engrams: EngramInfo[];
|
|
429
429
|
forms: EngramInfo[];
|
|
430
430
|
note?: string;
|
|
431
|
+
/** Anything else the reply carried. Responses widen; the runtime keeps those fields
|
|
432
|
+
* and this is what lets a typed caller read them. */
|
|
433
|
+
[key: string]: unknown;
|
|
431
434
|
}
|
|
432
435
|
/** `createStore` / `deleteStore` → `{ user_id, status }`. */
|
|
433
436
|
export interface StoreOpResult {
|
|
@@ -490,7 +493,10 @@ export interface SearchFilters {
|
|
|
490
493
|
}
|
|
491
494
|
/** Test hook — the warn-once set is process-global by design. */
|
|
492
495
|
export declare function _resetFilterWarnings(): void;
|
|
493
|
-
/** Known `search` options
|
|
496
|
+
/** Known `search` options. An unknown key is refused before anything is sent — the
|
|
497
|
+
* service drops keys it does not recognise and answers normally, so a misspelling
|
|
498
|
+
* could not be told from an option that worked. Put a genuinely new one under
|
|
499
|
+
* `extra`. */
|
|
494
500
|
export interface SearchOptions {
|
|
495
501
|
/** Recall caching. A hit inside the TTL bills at 0.1× — but the FIRST call
|
|
496
502
|
* writes the cache and bills the query tokens at 2× (`5m`) or 3× (`1h`), so
|
|
@@ -519,7 +525,9 @@ export interface SearchOptions {
|
|
|
519
525
|
*/
|
|
520
526
|
verify?: number;
|
|
521
527
|
/**
|
|
522
|
-
* How many image memories the answer may carry, 0–5.
|
|
528
|
+
* How many image memories the answer may carry, 0–5. Omit it and the service uses
|
|
529
|
+
* 1; `0` asks for none. The MCP server defaults its own tool to 0 instead, so most
|
|
530
|
+
* searches through it carry no image rows.
|
|
523
531
|
* Out of range is refused, not clamped — silently cutting 5 to 1 would leave you
|
|
524
532
|
* believing you got five.
|
|
525
533
|
*
|
|
@@ -527,7 +535,13 @@ export interface SearchOptions {
|
|
|
527
535
|
* than answering with no images.
|
|
528
536
|
*/
|
|
529
537
|
max_images?: number;
|
|
530
|
-
|
|
538
|
+
/**
|
|
539
|
+
* Fields the service accepts that this version does not know about, merged into the
|
|
540
|
+
* request as written. Every other key is refused, so a typo cannot reach the wire —
|
|
541
|
+
* `verfy: 3` used to be sent, dropped by the service, and answered normally, leaving
|
|
542
|
+
* a caller billed for one pass believing they had bought three.
|
|
543
|
+
*/
|
|
544
|
+
extra?: Record<string, unknown>;
|
|
531
545
|
}
|
|
532
546
|
/**
|
|
533
547
|
* Per-call options for a write.
|
|
@@ -537,9 +551,9 @@ export interface SearchOptions {
|
|
|
537
551
|
* arrives with a different body. Use it when a retry is your own (a job that died and
|
|
538
552
|
* was re-run, a queue that redelivers). The SDK retries a write on exactly one
|
|
539
553
|
* status: 429, which the service answers before it processes anything, so nothing
|
|
540
|
-
* was stored. It never retries a write on 502 / 503 or a dropped body,
|
|
541
|
-
*
|
|
542
|
-
* whether
|
|
554
|
+
* was stored. It never retries a write on 408 / 502 / 503 / 504 or a dropped body,
|
|
555
|
+
* where the first attempt may already have landed — without a key the client cannot
|
|
556
|
+
* know whether it did.
|
|
543
557
|
*
|
|
544
558
|
* The key must be UNIQUE PER LOGICAL WRITE — derive it from the thing being stored
|
|
545
559
|
* (`` `import:${row.id}` ``), never a constant, or the second write replays the first
|
|
@@ -571,8 +585,8 @@ export interface ClientOptions {
|
|
|
571
585
|
* to the account's built-in `default` store. */
|
|
572
586
|
userId?: string;
|
|
573
587
|
/** How many times to retry transient failures before throwing — 429 always;
|
|
574
|
-
* 502/503 and connection errors only when a retry can never
|
|
575
|
-
* write. 0 disables retries (default 2). */
|
|
588
|
+
* 408/502/503/504 and connection errors only when a retry can never
|
|
589
|
+
* double-process a write. 0 disables retries (default 2). */
|
|
576
590
|
maxRetries?: number;
|
|
577
591
|
/** Alias of `maxRetries`. The Python SDK calls this option `retries`, and these
|
|
578
592
|
* SDKs are published in lockstep as "the same surface" — so code ported between
|
|
@@ -600,10 +614,8 @@ export interface ClientOptions {
|
|
|
600
614
|
*
|
|
601
615
|
* This is the seam for anything the runtime cannot express through options.
|
|
602
616
|
* The one that matters in practice is a corporate egress proxy: Node's global
|
|
603
|
-
* fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy
|
|
604
|
-
*
|
|
605
|
-
* through, because `requests` and `reqwest` both read those variables. Pass a
|
|
606
|
-
* proxy-aware fetch and the three behave the same again:
|
|
617
|
+
* fetch (undici) IGNORES `HTTP_PROXY` / `HTTPS_PROXY`, so behind such a proxy no
|
|
618
|
+
* request leaves at all. Pass a proxy-aware fetch and they go through:
|
|
607
619
|
*
|
|
608
620
|
* import { ProxyAgent } from "undici";
|
|
609
621
|
* const agent = new ProxyAgent(process.env.HTTPS_PROXY!);
|
|
@@ -685,11 +697,16 @@ export declare class Client {
|
|
|
685
697
|
addTurn(userMsg: string, assistantMsg: string, userId?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
686
698
|
/** Bulk-ingest a large blob of text in one call. For backfilling.
|
|
687
699
|
* The call most worth an `opts.idempotencyKey`: a backfill that dies halfway and is
|
|
688
|
-
* re-run would otherwise
|
|
700
|
+
* re-run would otherwise ingest the whole blob a second time. */
|
|
689
701
|
addBulk(content: string, userId?: string, category?: string, timestamp?: string, opts?: WriteOptions): Promise<StatusResult>;
|
|
690
702
|
/** Supersede an old memory with new content. Payload first, userId last — same shape as add/search. */
|
|
691
703
|
update(oldMemoryId: string, newContent: string, userId?: string, opts?: WriteOptions): Promise<UpdateResult>;
|
|
692
704
|
/** Search a store's memories. Returns them most relevant first.
|
|
705
|
+
*
|
|
706
|
+
* `limit` is 5-20, and out of range is refused rather than clamped: asking for 50
|
|
707
|
+
* and silently receiving 20 reads as "that is all there is". The default is 10, so
|
|
708
|
+
* a call that passes no count is unaffected. Before 2.2.35 the count was sent on
|
|
709
|
+
* unchecked.
|
|
693
710
|
*
|
|
694
711
|
* `limit` bounds `memories`, not the returned array. On a model that keeps the
|
|
695
712
|
* assistant's own words separate (Scroll 1.2+) those come back as well, so the
|
|
@@ -796,6 +813,11 @@ export declare class Client {
|
|
|
796
813
|
}): AsyncGenerator<Memory>;
|
|
797
814
|
/** Collect ALL of a store's memories into an array (the text you stored, and its metadata). */
|
|
798
815
|
exportMemories(userId?: string): Promise<Memory[]>;
|
|
816
|
+
/** Collect ALL of a store's image memories into an array — the image-side pair of
|
|
817
|
+
* `exportMemories`. */
|
|
818
|
+
exportImages(userId?: string, opts?: {
|
|
819
|
+
pageSize?: number;
|
|
820
|
+
}): Promise<Memory[]>;
|
|
799
821
|
/**
|
|
800
822
|
* Fetch the bytes of an image memory — the picture the SERVICE holds.
|
|
801
823
|
*
|