wontopos 2.2.45 → 2.2.47
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 +134 -21
- package/dist/cjs/wontopos.d.cts +117 -76
- package/dist/cjs/wontopos.js +98 -22
- package/dist/wontopos.d.ts +117 -76
- package/dist/wontopos.js +97 -22
- package/package.json +1 -1
package/dist/cjs/wontopos.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
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
|
+
exports._resetRetirementWarnings = _resetRetirementWarnings;
|
|
5
6
|
exports._resetFilterWarnings = _resetFilterWarnings;
|
|
6
|
-
const VERSION = "2.2.
|
|
7
|
+
const VERSION = "2.2.47";
|
|
7
8
|
const RUNTIME = (() => {
|
|
8
9
|
const p = globalThis?.process;
|
|
9
10
|
return p?.version ? ` (node/${p.version}; ${p.platform}-${p.arch})` : "";
|
|
@@ -268,12 +269,7 @@ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "
|
|
|
268
269
|
const IMF_FIXDATE = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{2}) ([A-Z][a-z]{2}) (\d{4}) (\d{2}):(\d{2}):(\d{2}) GMT$/;
|
|
269
270
|
const RFC850_DATE = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d{2})-([A-Z][a-z]{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) GMT$/;
|
|
270
271
|
const ASCTIME_DATE = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) ([A-Z][a-z]{2}) ([ \d]\d) (\d{2}):(\d{2}):(\d{2}) (\d{4})$/;
|
|
271
|
-
function
|
|
272
|
-
if (v == null)
|
|
273
|
-
return undefined;
|
|
274
|
-
const s = v.trim();
|
|
275
|
-
if (/^\d+$/.test(s))
|
|
276
|
-
return Math.min(Number(s), Number.MAX_SAFE_INTEGER / 1000) * 1000;
|
|
272
|
+
function httpDateMs(s, now = Date.now()) {
|
|
277
273
|
let parts;
|
|
278
274
|
let m;
|
|
279
275
|
if ((m = IMF_FIXDATE.exec(s)))
|
|
@@ -293,7 +289,37 @@ function retryAfterMs(v, now = Date.now()) {
|
|
|
293
289
|
const d = Number(day), h = Number(hh), mi = Number(mm), se = Number(ss);
|
|
294
290
|
if (month < 0 || d < 1 || d > 31 || h > 23 || mi > 59 || se > 60)
|
|
295
291
|
return undefined;
|
|
296
|
-
return
|
|
292
|
+
return Date.UTC(year, month, d, h, mi, se);
|
|
293
|
+
}
|
|
294
|
+
function retryAfterMs(v, now = Date.now()) {
|
|
295
|
+
if (v == null)
|
|
296
|
+
return undefined;
|
|
297
|
+
const s = v.trim();
|
|
298
|
+
if (/^\d+$/.test(s))
|
|
299
|
+
return Math.min(Number(s), Number.MAX_SAFE_INTEGER / 1000) * 1000;
|
|
300
|
+
const at = httpDateMs(s, now);
|
|
301
|
+
return at === undefined ? undefined : Math.max(0, at - now);
|
|
302
|
+
}
|
|
303
|
+
const warnedRetirements = new Set();
|
|
304
|
+
const WARNED_RETIREMENTS_MAX = 64;
|
|
305
|
+
function warnIfRetiring(model, h) {
|
|
306
|
+
const v = h.get("Sunset");
|
|
307
|
+
if (!v)
|
|
308
|
+
return;
|
|
309
|
+
const at = httpDateMs(v.trim());
|
|
310
|
+
if (at === undefined)
|
|
311
|
+
return;
|
|
312
|
+
const day = new Date(at).toISOString().slice(0, 10);
|
|
313
|
+
const k = `${model}|${day}`;
|
|
314
|
+
if (warnedRetirements.has(k) || warnedRetirements.size >= WARNED_RETIREMENTS_MAX)
|
|
315
|
+
return;
|
|
316
|
+
warnedRetirements.add(k);
|
|
317
|
+
const which = model ? `model ${JSON.stringify(model)}` : "the model this client uses";
|
|
318
|
+
console.warn(`wontopos: ${which} retires on ${day} (UTC). From then on its calls answer 410, except ` +
|
|
319
|
+
"deleteStore. listModels() lists the models you can move to.");
|
|
320
|
+
}
|
|
321
|
+
function _resetRetirementWarnings() {
|
|
322
|
+
warnedRetirements.clear();
|
|
297
323
|
}
|
|
298
324
|
const BRIEF_BODY_MS = 1_000;
|
|
299
325
|
class SlowBody extends Error {
|
|
@@ -318,6 +344,23 @@ function tryUrl(s, base) {
|
|
|
318
344
|
return undefined;
|
|
319
345
|
}
|
|
320
346
|
}
|
|
347
|
+
function inBrowserPage() {
|
|
348
|
+
try {
|
|
349
|
+
const g = globalThis;
|
|
350
|
+
return typeof g.window !== "undefined" && typeof g.window.document !== "undefined" && typeof g.navigator !== "undefined";
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function isServiceHost(host) {
|
|
357
|
+
const h = host.toLowerCase();
|
|
358
|
+
return h === "wontopos.com" || h.endsWith(".wontopos.com");
|
|
359
|
+
}
|
|
360
|
+
const BROWSER_REFUSAL = "wontopos: refusing to run in a browser page. The API key would be readable by anyone who " +
|
|
361
|
+
"opens the page, and with it every memory in its workspace. Call Wontopos from your server, " +
|
|
362
|
+
"or put a proxy of your own in front of it and point baseUrl there. For a local demo with a " +
|
|
363
|
+
"key you will revoke, pass dangerouslyAllowBrowser: true.";
|
|
321
364
|
function pageHref() {
|
|
322
365
|
try {
|
|
323
366
|
const href = globalThis.location?.href;
|
|
@@ -394,6 +437,18 @@ const memoryFromGet = (r) => {
|
|
|
394
437
|
return o.memory;
|
|
395
438
|
return typeof o.id === "string" ? o : {};
|
|
396
439
|
};
|
|
440
|
+
function recallShape(r) {
|
|
441
|
+
const o = asObj(r);
|
|
442
|
+
const st = asObj(o.short_term);
|
|
443
|
+
const lt = asObj(o.long_term);
|
|
444
|
+
const cx = asObj(o.context);
|
|
445
|
+
return {
|
|
446
|
+
...o,
|
|
447
|
+
short_term: { ...st, turns: asRecords(st.turns) },
|
|
448
|
+
long_term: { ...lt, memories: asRecords(lt.memories) },
|
|
449
|
+
context: { ...cx, around_top_memory: asList(cx.around_top_memory).filter((x) => typeof x === "string") },
|
|
450
|
+
};
|
|
451
|
+
}
|
|
397
452
|
function mergeResults(r) {
|
|
398
453
|
const out = asRecords(r?.memories);
|
|
399
454
|
const seen = new Set(out.map((m) => m.id).filter((id) => typeof id === "string"));
|
|
@@ -569,6 +624,15 @@ function writeKey(opts, takesImage = false) {
|
|
|
569
624
|
(takesImage ? "add takes idempotencyKey and image." : "This call takes idempotencyKey only."));
|
|
570
625
|
return opts.idempotencyKey;
|
|
571
626
|
}
|
|
627
|
+
function newIdempotencyKey() {
|
|
628
|
+
const c = globalThis.crypto;
|
|
629
|
+
if (typeof c?.randomUUID === "function")
|
|
630
|
+
return `sdk-${String(c.randomUUID()).replace(/-/g, "")}`;
|
|
631
|
+
let hex = "";
|
|
632
|
+
for (let i = 0; i < 4; i++)
|
|
633
|
+
hex += Math.floor(Math.random() * 0x100000000).toString(16).padStart(8, "0");
|
|
634
|
+
return `sdk-${hex}`;
|
|
635
|
+
}
|
|
572
636
|
function warnOnUnknownFilters(filters) {
|
|
573
637
|
warnOnceUnknownKeys(filters, KNOWN_FILTER_KEYS, warnedFilterKeys, (k) => `wontopos: unknown search filter ${JSON.stringify(k)} — the API drops keys it does not know, ` +
|
|
574
638
|
`so this filter has NO effect and the search is wider than you think. ` +
|
|
@@ -597,6 +661,7 @@ class Client {
|
|
|
597
661
|
userinfo;
|
|
598
662
|
pageRelative;
|
|
599
663
|
customFetch;
|
|
664
|
+
allowBrowser;
|
|
600
665
|
defaultUser;
|
|
601
666
|
_rateLimit = null;
|
|
602
667
|
get rateLimit() {
|
|
@@ -630,6 +695,10 @@ class Client {
|
|
|
630
695
|
throw new Error(notAUrl(trimmed));
|
|
631
696
|
this.pageRelative = !url;
|
|
632
697
|
this.userinfo = Boolean(parsed.username || parsed.password);
|
|
698
|
+
this.allowBrowser = opts.dangerouslyAllowBrowser === true;
|
|
699
|
+
if (!this.allowBrowser && inBrowserPage() && isServiceHost(parsed.hostname)) {
|
|
700
|
+
throw new Error(BROWSER_REFUSAL);
|
|
701
|
+
}
|
|
633
702
|
this.timeoutMs = checkMs(opts.timeoutMs) ?? 30_000;
|
|
634
703
|
this.model = opts.model ?? DEFAULT_MODEL;
|
|
635
704
|
if (this.model && !MODEL_RE.test(this.model)) {
|
|
@@ -698,6 +767,7 @@ class Client {
|
|
|
698
767
|
fetch: this.fetchImpl,
|
|
699
768
|
deadlineMs: this.deadlineMs,
|
|
700
769
|
signal: this.signal,
|
|
770
|
+
dangerouslyAllowBrowser: this.allowBrowser,
|
|
701
771
|
...overrides,
|
|
702
772
|
});
|
|
703
773
|
}
|
|
@@ -729,22 +799,22 @@ class Client {
|
|
|
729
799
|
if (opts.image !== undefined)
|
|
730
800
|
body.image = normalizeImage(opts.image);
|
|
731
801
|
warnOnUnknownMetadata(metadata);
|
|
732
|
-
return this.
|
|
802
|
+
return this.write("/api/v1/memory/store", body, writeKey(opts, true));
|
|
733
803
|
}
|
|
734
804
|
async store(content, userId, metadata = {}, opts = {}) {
|
|
735
805
|
return this.add(content, userId, metadata, opts);
|
|
736
806
|
}
|
|
737
807
|
async addTurn(userMsg, assistantMsg, userId, opts = {}) {
|
|
738
|
-
return this.
|
|
808
|
+
return this.write("/api/v1/memory/store-turn", { user_id: this.uid(userId), user_msg: userMsg, assistant_msg: assistantMsg }, writeKey(opts));
|
|
739
809
|
}
|
|
740
810
|
async addBulk(content, userId, category = "general", timestamp, opts = {}) {
|
|
741
811
|
const body = { user_id: this.uid(userId), content, category: category ?? "general" };
|
|
742
812
|
if (timestamp)
|
|
743
813
|
body.timestamp = timestamp;
|
|
744
|
-
return this.
|
|
814
|
+
return this.write("/api/v1/memory/bulk-store", body, writeKey(opts));
|
|
745
815
|
}
|
|
746
816
|
async update(oldMemoryId, newContent, userId, opts = {}) {
|
|
747
|
-
return this.
|
|
817
|
+
return this.write("/api/v1/memory/supersede", { user_id: this.uid(userId), old_memory_id: oldMemoryId, new_content: newContent }, writeKey(opts));
|
|
748
818
|
}
|
|
749
819
|
async search(query, userId, limit = 10, opts = {}) {
|
|
750
820
|
checkSearchOpts(opts);
|
|
@@ -789,7 +859,7 @@ class Client {
|
|
|
789
859
|
checkContextLimit(opts.context_limit);
|
|
790
860
|
body.context_limit = opts.context_limit;
|
|
791
861
|
}
|
|
792
|
-
return this.post("/api/v1/memory/recall", withForm(body, opts.form, opts.tz));
|
|
862
|
+
return recallShape(await this.post("/api/v1/memory/recall", withForm(body, opts.form, opts.tz)));
|
|
793
863
|
}
|
|
794
864
|
async engram(name, query, userId, opts = {}) {
|
|
795
865
|
checkOpts(opts, KNOWN_ENGRAM_KEYS, "engram");
|
|
@@ -994,8 +1064,12 @@ class Client {
|
|
|
994
1064
|
warnIfStoreIdCollapses(userId);
|
|
995
1065
|
return this.post("/api/v1/memory/forget", { user_id: userId });
|
|
996
1066
|
}
|
|
997
|
-
post(path, body
|
|
998
|
-
return this.request("POST", path, body
|
|
1067
|
+
post(path, body) {
|
|
1068
|
+
return this.request("POST", path, body);
|
|
1069
|
+
}
|
|
1070
|
+
write(path, body, idempotencyKey) {
|
|
1071
|
+
const made = idempotencyKey == null;
|
|
1072
|
+
return this.request("POST", path, body, made ? newIdempotencyKey() : idempotencyKey, false, made);
|
|
999
1073
|
}
|
|
1000
1074
|
remove(path, body) {
|
|
1001
1075
|
return this.request("DELETE", path, body, undefined, true);
|
|
@@ -1113,8 +1187,9 @@ class Client {
|
|
|
1113
1187
|
}
|
|
1114
1188
|
const attempts = this.maxRetries + 1;
|
|
1115
1189
|
const upper = method.toUpperCase();
|
|
1116
|
-
const idempotent = IDEMPOTENT_METHODS.has(upper);
|
|
1117
1190
|
const logPath = path.split("?")[0];
|
|
1191
|
+
const idempotent = IDEMPOTENT_METHODS.has(upper) || reads || (upper === "POST" && READ_POSTS.has(logPath));
|
|
1192
|
+
const replaySafe = idempotent || extraHeaders?.["Idempotency-Key"] !== undefined;
|
|
1118
1193
|
const deadlineAt = this.deadlineAt();
|
|
1119
1194
|
const headers = extraHeaders ? { ...this.headers, ...extraHeaders } : this.headers;
|
|
1120
1195
|
let ambiguous = false;
|
|
@@ -1135,7 +1210,7 @@ class Client {
|
|
|
1135
1210
|
};
|
|
1136
1211
|
const cutShort = () => {
|
|
1137
1212
|
const why = att.why();
|
|
1138
|
-
if (why === "deadline" &&
|
|
1213
|
+
if (why === "deadline" && idempotent && previous) {
|
|
1139
1214
|
return previous;
|
|
1140
1215
|
}
|
|
1141
1216
|
return new APIConnectionError(0, this.abortMessage(why));
|
|
@@ -1156,7 +1231,7 @@ class Client {
|
|
|
1156
1231
|
if (timedOut)
|
|
1157
1232
|
throw cutShort();
|
|
1158
1233
|
const connect = isConnectFailure(e);
|
|
1159
|
-
if ((
|
|
1234
|
+
if ((replaySafe || connect) && !last) {
|
|
1160
1235
|
if (!connect)
|
|
1161
1236
|
ambiguous = true;
|
|
1162
1237
|
await pauseWithoutAnswer(e?.code ?? e?.name ?? "network error");
|
|
@@ -1173,7 +1248,7 @@ class Client {
|
|
|
1173
1248
|
throw cutShort();
|
|
1174
1249
|
if (e instanceof WosError)
|
|
1175
1250
|
throw e;
|
|
1176
|
-
if (
|
|
1251
|
+
if (replaySafe && !last)
|
|
1177
1252
|
return null;
|
|
1178
1253
|
throw this.networkError(e, path);
|
|
1179
1254
|
}
|
|
@@ -1200,9 +1275,10 @@ class Client {
|
|
|
1200
1275
|
throw new WosError(res.status, "unexpected redirect — refused (the API key never follows a redirect). Check baseUrl: exact host, https://.");
|
|
1201
1276
|
}
|
|
1202
1277
|
this._rateLimit = parseRateLimit(res.headers) ?? this._rateLimit;
|
|
1278
|
+
warnIfRetiring(this.model, res.headers);
|
|
1203
1279
|
if (!res.ok) {
|
|
1204
1280
|
const ra = retryAfterMs(res.headers.get("Retry-After"));
|
|
1205
|
-
let wait = RETRY_ALWAYS.has(res.status) || (RETRY_IF_IDEMPOTENT.has(res.status) &&
|
|
1281
|
+
let wait = RETRY_ALWAYS.has(res.status) || (RETRY_IF_IDEMPOTENT.has(res.status) && replaySafe)
|
|
1206
1282
|
? ra ?? this.backoffMs(attempt)
|
|
1207
1283
|
: undefined;
|
|
1208
1284
|
const retrying = wait !== undefined && !last && wait <= MAX_RETRY_WAIT_MS && this.fits(wait, deadlineAt);
|
|
@@ -1329,7 +1405,7 @@ class Client {
|
|
|
1329
1405
|
clearTimeout(timer);
|
|
1330
1406
|
}
|
|
1331
1407
|
}
|
|
1332
|
-
async request(method, path, body, idempotencyKey, removes = false) {
|
|
1408
|
+
async request(method, path, body, idempotencyKey, removes = false, madeKey = false) {
|
|
1333
1409
|
const extraHeaders = this.idemHeader(idempotencyKey);
|
|
1334
1410
|
let payload;
|
|
1335
1411
|
try {
|
|
@@ -1357,7 +1433,7 @@ class Client {
|
|
|
1357
1433
|
const got = data === null ? "null" : Array.isArray(data) ? "array" : typeof data;
|
|
1358
1434
|
throw new WosError(res.status, `expected a JSON object in the response, got ${got}`);
|
|
1359
1435
|
}
|
|
1360
|
-
if (res.headers.get("Idempotent-Replayed") === "true" && !("replayed" in data)) {
|
|
1436
|
+
if (!madeKey && res.headers.get("Idempotent-Replayed") === "true" && !("replayed" in data)) {
|
|
1361
1437
|
data.replayed = true;
|
|
1362
1438
|
}
|
|
1363
1439
|
return data;
|