@anuma/sdk 9.1.0 → 9.1.1
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/expo/index.cjs +309 -86
- package/dist/expo/index.d.mts +9 -1
- package/dist/expo/index.d.ts +9 -1
- package/dist/expo/index.mjs +308 -85
- package/dist/index.cjs +37 -3
- package/dist/index.mjs +37 -3
- package/dist/react/index.cjs +309 -86
- package/dist/react/index.d.mts +9 -1
- package/dist/react/index.d.ts +9 -1
- package/dist/react/index.mjs +309 -86
- package/package.json +1 -1
package/dist/expo/index.mjs
CHANGED
|
@@ -9683,11 +9683,12 @@ function isRemoteWorkLeaseExpired(error) {
|
|
|
9683
9683
|
|
|
9684
9684
|
// src/lib/memory/store/remotePersistence.ts
|
|
9685
9685
|
var RemoteMemoryError = class extends Error {
|
|
9686
|
-
constructor(message, status, code, retryAfterMs) {
|
|
9686
|
+
constructor(message, status, code, retryAfterMs, rateLimitBucket) {
|
|
9687
9687
|
super(message);
|
|
9688
9688
|
this.status = status;
|
|
9689
9689
|
this.code = code;
|
|
9690
9690
|
this.retryAfterMs = retryAfterMs;
|
|
9691
|
+
this.rateLimitBucket = rateLimitBucket;
|
|
9691
9692
|
this.name = "RemoteMemoryError";
|
|
9692
9693
|
}
|
|
9693
9694
|
};
|
|
@@ -9782,6 +9783,14 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
|
9782
9783
|
throw new Error("timeoutMs must be a positive number");
|
|
9783
9784
|
const root = base.href.replace(/\/$/, "") + apiPath;
|
|
9784
9785
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
9786
|
+
const cooldowns = /* @__PURE__ */ new Map();
|
|
9787
|
+
const memoryBudget = (path, method) => {
|
|
9788
|
+
if (method === "PUT")
|
|
9789
|
+
return path === "/topics" ? "private-memory-import" : "private-memory-write";
|
|
9790
|
+
if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
|
|
9791
|
+
return "private-memory-import";
|
|
9792
|
+
return "private-memory-read";
|
|
9793
|
+
};
|
|
9785
9794
|
const request = async (path, init = {}, signal) => {
|
|
9786
9795
|
const controller = new AbortController();
|
|
9787
9796
|
let timedOut = false;
|
|
@@ -9805,6 +9814,25 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
|
9805
9814
|
const token = await Promise.race([options.getToken(), aborted2]);
|
|
9806
9815
|
if (controller.signal.aborted) await aborted2;
|
|
9807
9816
|
if (!token) throw new RemoteMemoryError("Authentication required", 401);
|
|
9817
|
+
const pathname = path.split("?")[0];
|
|
9818
|
+
const route = `${init.method ?? "GET"} ${pathname}`;
|
|
9819
|
+
const budgets = ["*", route];
|
|
9820
|
+
if (apiPath === "/api/private-memories")
|
|
9821
|
+
budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
|
|
9822
|
+
for (const bucket of budgets) {
|
|
9823
|
+
const cooldown = cooldowns.get(bucket);
|
|
9824
|
+
if (!cooldown) continue;
|
|
9825
|
+
const remaining = cooldown.until - Date.now();
|
|
9826
|
+
if (remaining > 0)
|
|
9827
|
+
throw new RemoteMemoryError(
|
|
9828
|
+
cooldown.error.message,
|
|
9829
|
+
429,
|
|
9830
|
+
cooldown.error.code,
|
|
9831
|
+
remaining,
|
|
9832
|
+
cooldown.error.rateLimitBucket
|
|
9833
|
+
);
|
|
9834
|
+
cooldowns.delete(bucket);
|
|
9835
|
+
}
|
|
9808
9836
|
const response = await fetcher(root + path, {
|
|
9809
9837
|
...init,
|
|
9810
9838
|
headers: {
|
|
@@ -9817,12 +9845,18 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
|
9817
9845
|
if (!response.ok) {
|
|
9818
9846
|
const body = await response.json().catch(() => null);
|
|
9819
9847
|
const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
|
|
9820
|
-
|
|
9848
|
+
const error = new RemoteMemoryError(
|
|
9821
9849
|
record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
|
|
9822
9850
|
response.status,
|
|
9823
9851
|
record(body) && typeof body.code === "string" ? body.code : void 0,
|
|
9824
|
-
retryAfter ?? void 0
|
|
9852
|
+
retryAfter ?? void 0,
|
|
9853
|
+
record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
|
|
9825
9854
|
);
|
|
9855
|
+
if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
|
|
9856
|
+
const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
|
|
9857
|
+
cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
|
|
9858
|
+
}
|
|
9859
|
+
throw error;
|
|
9826
9860
|
}
|
|
9827
9861
|
return await response.json();
|
|
9828
9862
|
} catch (error) {
|
|
@@ -10062,6 +10096,7 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
10062
10096
|
return {
|
|
10063
10097
|
...createRemoteTopicPersistence(request),
|
|
10064
10098
|
...createRemoteWorkPersistence(request),
|
|
10099
|
+
supportsRealtimeChanges: account.realtime_changes === true,
|
|
10065
10100
|
counts: async () => {
|
|
10066
10101
|
const result = await request("/counts", {});
|
|
10067
10102
|
if (!record(result) || ["live", "archived", "quarantined"].some(
|
|
@@ -21345,6 +21380,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
|
|
|
21345
21380
|
function rejectsInput(error) {
|
|
21346
21381
|
return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
|
|
21347
21382
|
}
|
|
21383
|
+
function rejectsMemoryWrite(error) {
|
|
21384
|
+
return error instanceof RemoteMemoryError && (error.status === 400 || error.status === 422) && (error.code === void 0 || error.code === "unknown_field");
|
|
21385
|
+
}
|
|
21348
21386
|
function warnFailed(failed) {
|
|
21349
21387
|
if (failed.length)
|
|
21350
21388
|
getLogger().warn(
|
|
@@ -21432,16 +21470,47 @@ function createRemoteMemoryStore(options) {
|
|
|
21432
21470
|
const { persistence, embeddingOptions } = options;
|
|
21433
21471
|
const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
21434
21472
|
const pollIntervalMs = options.pollIntervalMs ?? 6e4;
|
|
21473
|
+
const activity = options.activity ?? {
|
|
21474
|
+
isActive: () => (typeof document === "undefined" || document.visibilityState !== "hidden") && (typeof navigator === "undefined" || navigator.onLine !== false),
|
|
21475
|
+
subscribe: (onChange) => {
|
|
21476
|
+
if (typeof window === "undefined" || typeof document === "undefined") return () => {
|
|
21477
|
+
};
|
|
21478
|
+
document.addEventListener("visibilitychange", onChange);
|
|
21479
|
+
window.addEventListener("online", onChange);
|
|
21480
|
+
window.addEventListener("offline", onChange);
|
|
21481
|
+
return () => {
|
|
21482
|
+
document.removeEventListener("visibilitychange", onChange);
|
|
21483
|
+
window.removeEventListener("online", onChange);
|
|
21484
|
+
window.removeEventListener("offline", onChange);
|
|
21485
|
+
};
|
|
21486
|
+
}
|
|
21487
|
+
};
|
|
21435
21488
|
const pipeline = createRemoteMemoryPipeline({
|
|
21436
21489
|
...options,
|
|
21437
21490
|
graphRanking: options.graphRanking ?? ((query, _traverse, recallOptions) => topicGraphRanking(query, recallOptions)),
|
|
21438
21491
|
temporalRanking: options.temporalRanking ?? ((query, now) => eventTimeRanking(query, now))
|
|
21439
21492
|
});
|
|
21440
21493
|
const subscribers = /* @__PURE__ */ new Set();
|
|
21494
|
+
const retainTopics = () => pollIntervalMs > 0 && [...subscribers].some((subscriber) => subscriber.options.topics);
|
|
21441
21495
|
const seen = /* @__PURE__ */ new Map();
|
|
21496
|
+
const reads = /* @__PURE__ */ new Map();
|
|
21497
|
+
const metadataReads = /* @__PURE__ */ new Map();
|
|
21498
|
+
let topicSnapshot;
|
|
21499
|
+
let topicCatalog;
|
|
21500
|
+
const invalidateReads = () => {
|
|
21501
|
+
reads.clear();
|
|
21502
|
+
metadataReads.clear();
|
|
21503
|
+
topicSnapshot = void 0;
|
|
21504
|
+
topicCatalog = void 0;
|
|
21505
|
+
};
|
|
21442
21506
|
let timer;
|
|
21507
|
+
let scheduledAt;
|
|
21508
|
+
let stopActivity;
|
|
21509
|
+
let stopChanges;
|
|
21510
|
+
let changesConnected = false;
|
|
21443
21511
|
const notify = (change, records = []) => {
|
|
21444
|
-
|
|
21512
|
+
invalidateReads();
|
|
21513
|
+
if (subscribers.size)
|
|
21445
21514
|
for (const record2 of records) {
|
|
21446
21515
|
const previous = seen.get(record2.memory.memory_id);
|
|
21447
21516
|
if ((previous?.version ?? 0) + 1 === record2.version)
|
|
@@ -21465,8 +21534,37 @@ function createRemoteMemoryStore(options) {
|
|
|
21465
21534
|
} while (cursor2 && records.length < max);
|
|
21466
21535
|
return records.slice(0, max);
|
|
21467
21536
|
};
|
|
21468
|
-
const
|
|
21469
|
-
|
|
21537
|
+
const shareRead = (inflight, options2, read) => {
|
|
21538
|
+
if (options2.signal) return read();
|
|
21539
|
+
const key = JSON.stringify(Object.entries(options2).sort(([a], [b]) => a.localeCompare(b)));
|
|
21540
|
+
const active = inflight.get(key);
|
|
21541
|
+
if (active) return active;
|
|
21542
|
+
const pending2 = read();
|
|
21543
|
+
inflight.set(key, pending2);
|
|
21544
|
+
const clear = () => {
|
|
21545
|
+
if (inflight.get(key) === pending2) inflight.delete(key);
|
|
21546
|
+
};
|
|
21547
|
+
void pending2.then(clear, clear);
|
|
21548
|
+
return pending2;
|
|
21549
|
+
};
|
|
21550
|
+
const decrypted = (options2) => shareRead(reads, options2, () => persistence.query(options2));
|
|
21551
|
+
const metadata3 = (options2) => shareRead(
|
|
21552
|
+
metadataReads,
|
|
21553
|
+
options2,
|
|
21554
|
+
() => persistence.queryMetadata(options2)
|
|
21555
|
+
);
|
|
21556
|
+
const readTopics = () => {
|
|
21557
|
+
if (topicCatalog) return topicCatalog;
|
|
21558
|
+
const pending2 = persistence.topics();
|
|
21559
|
+
topicCatalog = pending2;
|
|
21560
|
+
const clear = () => {
|
|
21561
|
+
if (topicCatalog === pending2) topicCatalog = void 0;
|
|
21562
|
+
};
|
|
21563
|
+
void pending2.then(() => {
|
|
21564
|
+
if (!retainTopics()) clear();
|
|
21565
|
+
}, clear);
|
|
21566
|
+
return pending2;
|
|
21567
|
+
};
|
|
21470
21568
|
const enumerate = (filters, max = Infinity) => pages(decrypted, filters, max);
|
|
21471
21569
|
const memoriesByTopics = async (names) => {
|
|
21472
21570
|
const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
|
|
@@ -21920,11 +22018,11 @@ function createRemoteMemoryStore(options) {
|
|
|
21920
22018
|
};
|
|
21921
22019
|
const changesSince = async (cursor2, pending2) => {
|
|
21922
22020
|
const records = [];
|
|
21923
|
-
const failed = /* @__PURE__ */ new
|
|
22021
|
+
const failed = /* @__PURE__ */ new Map();
|
|
21924
22022
|
const collect = (page) => {
|
|
21925
22023
|
warnFailed(page.failed);
|
|
21926
22024
|
records.push(...page.items);
|
|
21927
|
-
for (const failure of page.failed) failed.
|
|
22025
|
+
for (const failure of page.failed) failed.set(failure.memory_id, failure.version);
|
|
21928
22026
|
};
|
|
21929
22027
|
let resume = cursor2;
|
|
21930
22028
|
let next;
|
|
@@ -21958,25 +22056,72 @@ function createRemoteMemoryStore(options) {
|
|
|
21958
22056
|
let ready = false;
|
|
21959
22057
|
let cursor;
|
|
21960
22058
|
let pending = /* @__PURE__ */ new Set();
|
|
22059
|
+
let failedVersions = /* @__PURE__ */ new Map();
|
|
21961
22060
|
let topicFingerprint;
|
|
21962
22061
|
let polling = false;
|
|
22062
|
+
let pollRequested = false;
|
|
22063
|
+
let failures = 0;
|
|
22064
|
+
let retryAt = 0;
|
|
22065
|
+
const backoff = (error) => {
|
|
22066
|
+
failures++;
|
|
22067
|
+
retryAt = Date.now() + Math.max(
|
|
22068
|
+
error instanceof RemoteMemoryError ? error.retryAfterMs ?? 0 : 0,
|
|
22069
|
+
Math.min(pollIntervalMs * 2 ** Math.min(failures - 1, 10), 15 * 6e4)
|
|
22070
|
+
);
|
|
22071
|
+
};
|
|
22072
|
+
const schedulePoll = (delay, keepEarlier = false) => {
|
|
22073
|
+
const at = Math.max(Date.now() + delay, retryAt);
|
|
22074
|
+
if (keepEarlier && timer && scheduledAt !== void 0 && scheduledAt <= at) return;
|
|
22075
|
+
if (timer) clearTimeout(timer);
|
|
22076
|
+
timer = void 0;
|
|
22077
|
+
scheduledAt = void 0;
|
|
22078
|
+
if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
|
|
22079
|
+
scheduledAt = at;
|
|
22080
|
+
timer = setTimeout(poll, Math.max(0, at - Date.now()));
|
|
22081
|
+
};
|
|
21963
22082
|
const poll = () => {
|
|
21964
|
-
if (
|
|
22083
|
+
if (timer) clearTimeout(timer);
|
|
22084
|
+
timer = void 0;
|
|
22085
|
+
scheduledAt = void 0;
|
|
22086
|
+
if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
|
|
22087
|
+
if (Date.now() < retryAt) {
|
|
22088
|
+
schedulePoll(0);
|
|
22089
|
+
return;
|
|
22090
|
+
}
|
|
22091
|
+
if (polling) {
|
|
22092
|
+
pollRequested = true;
|
|
22093
|
+
return;
|
|
22094
|
+
}
|
|
21965
22095
|
polling = true;
|
|
22096
|
+
pollRequested = false;
|
|
21966
22097
|
const current = generation;
|
|
21967
22098
|
changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
|
|
21968
22099
|
let topicState;
|
|
22100
|
+
let topicFailed = false;
|
|
21969
22101
|
if ([...subscribers].some((s) => s.options.topics))
|
|
21970
22102
|
try {
|
|
21971
|
-
|
|
21972
|
-
|
|
21973
|
-
);
|
|
22103
|
+
topicCatalog = void 0;
|
|
22104
|
+
topicState = JSON.stringify((await readTopics()).map((t) => [t.name, t.version]));
|
|
21974
22105
|
} catch (error) {
|
|
22106
|
+
topicFailed = true;
|
|
22107
|
+
if (current === generation) backoff(error);
|
|
21975
22108
|
getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
|
|
21976
22109
|
}
|
|
21977
22110
|
if (current !== generation) return;
|
|
22111
|
+
if (!topicFailed) {
|
|
22112
|
+
failures = 0;
|
|
22113
|
+
retryAt = 0;
|
|
22114
|
+
}
|
|
22115
|
+
const failedChanged = [...failed].some(
|
|
22116
|
+
([id2, version]) => failedVersions.get(id2) !== version
|
|
22117
|
+
);
|
|
22118
|
+
if (records.length || failedChanged || topicState !== void 0 && topicState !== topicFingerprint) {
|
|
22119
|
+
reads.clear();
|
|
22120
|
+
metadataReads.clear();
|
|
22121
|
+
topicSnapshot = void 0;
|
|
22122
|
+
}
|
|
21978
22123
|
const changed = /* @__PURE__ */ new Set();
|
|
21979
|
-
if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
|
|
22124
|
+
if (ready && failedChanged || topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
|
|
21980
22125
|
for (const subscriber of subscribers)
|
|
21981
22126
|
if (subscriber.options.topics) changed.add(subscriber);
|
|
21982
22127
|
}
|
|
@@ -21993,15 +22138,40 @@ function createRemoteMemoryStore(options) {
|
|
|
21993
22138
|
}
|
|
21994
22139
|
ready = true;
|
|
21995
22140
|
cursor = resume;
|
|
21996
|
-
pending = failed;
|
|
22141
|
+
pending = new Set(failed.keys());
|
|
22142
|
+
failedVersions = failed;
|
|
21997
22143
|
for (const subscriber of changed)
|
|
21998
22144
|
if (subscribers.has(subscriber)) safely(subscriber.onChange);
|
|
21999
22145
|
}).catch((error) => {
|
|
22146
|
+
if (current === generation) backoff(error);
|
|
22000
22147
|
getLogger().warn("[memory/remote-store] Subscription poll failed:", error);
|
|
22001
22148
|
}).finally(() => {
|
|
22002
22149
|
polling = false;
|
|
22150
|
+
schedulePoll(
|
|
22151
|
+
current !== generation ? 0 : pollRequested ? 200 : changesConnected ? Math.max(pollIntervalMs, 5 * 6e4) : pollIntervalMs
|
|
22152
|
+
);
|
|
22003
22153
|
});
|
|
22004
22154
|
};
|
|
22155
|
+
const refreshActivity = () => {
|
|
22156
|
+
if (timer) clearTimeout(timer);
|
|
22157
|
+
timer = void 0;
|
|
22158
|
+
if (!activity.isActive()) {
|
|
22159
|
+
stopChanges?.();
|
|
22160
|
+
stopChanges = void 0;
|
|
22161
|
+
changesConnected = false;
|
|
22162
|
+
return;
|
|
22163
|
+
}
|
|
22164
|
+
if (!stopChanges && persistence.supportsRealtimeChanges && options.subscribeChanges)
|
|
22165
|
+
stopChanges = options.subscribeChanges((connected) => {
|
|
22166
|
+
changesConnected = connected;
|
|
22167
|
+
if (polling) {
|
|
22168
|
+
pollRequested = true;
|
|
22169
|
+
return;
|
|
22170
|
+
}
|
|
22171
|
+
schedulePoll(connected ? 200 : 0, true);
|
|
22172
|
+
});
|
|
22173
|
+
poll();
|
|
22174
|
+
};
|
|
22005
22175
|
const unrepairable = /* @__PURE__ */ new Set();
|
|
22006
22176
|
let repairCursor;
|
|
22007
22177
|
const repairEmbeddingsUnlocked = async ({
|
|
@@ -22057,40 +22227,78 @@ function createRemoteMemoryStore(options) {
|
|
|
22057
22227
|
result.failed += targets.length;
|
|
22058
22228
|
return finish();
|
|
22059
22229
|
}
|
|
22060
|
-
|
|
22230
|
+
const writes = targets.flatMap((record2, i) => {
|
|
22061
22231
|
const vector = vectors[i];
|
|
22062
22232
|
if (!vector) {
|
|
22063
22233
|
result.failed++;
|
|
22064
|
-
|
|
22234
|
+
return [];
|
|
22065
22235
|
}
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
|
|
22069
|
-
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
|
|
22076
|
-
|
|
22077
|
-
|
|
22078
|
-
|
|
22079
|
-
|
|
22080
|
-
|
|
22081
|
-
|
|
22082
|
-
|
|
22083
|
-
|
|
22084
|
-
|
|
22085
|
-
|
|
22086
|
-
} catch (error) {
|
|
22236
|
+
try {
|
|
22237
|
+
return [
|
|
22238
|
+
{
|
|
22239
|
+
memory: {
|
|
22240
|
+
...record2.memory,
|
|
22241
|
+
embedding: storedVector(vector),
|
|
22242
|
+
embedding_model: model
|
|
22243
|
+
},
|
|
22244
|
+
expectedVersion: record2
|
|
22245
|
+
}
|
|
22246
|
+
];
|
|
22247
|
+
} catch {
|
|
22248
|
+
result.failed++;
|
|
22249
|
+
return [];
|
|
22250
|
+
}
|
|
22251
|
+
});
|
|
22252
|
+
for (const batch of chunks2(writes, MAX_BATCH)) {
|
|
22253
|
+
const continueRepair = await serializedAll(
|
|
22254
|
+
batch.map(({ memory }) => memory.memory_id),
|
|
22255
|
+
async () => {
|
|
22087
22256
|
signal?.throwIfAborted();
|
|
22088
|
-
|
|
22089
|
-
|
|
22090
|
-
|
|
22091
|
-
|
|
22257
|
+
const commit = async (slice) => {
|
|
22258
|
+
const saved = await persistence.putMany(slice, signal, leaseToken);
|
|
22259
|
+
for (const record2 of saved) repaired.add(record2.memory.memory_id);
|
|
22260
|
+
result.reembedded += saved.length;
|
|
22261
|
+
notify("embedding", saved);
|
|
22262
|
+
};
|
|
22263
|
+
try {
|
|
22264
|
+
await commit(batch);
|
|
22265
|
+
} catch (error) {
|
|
22266
|
+
signal?.throwIfAborted();
|
|
22267
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
22268
|
+
if (!(error instanceof RemoteMemoryError && error.code === "version_conflict") && !rejectsMemoryWrite(error)) {
|
|
22269
|
+
result.failed += batch.length;
|
|
22270
|
+
return false;
|
|
22271
|
+
}
|
|
22272
|
+
if (batch.length === 1) {
|
|
22273
|
+
if (rejectsMemoryWrite(error)) {
|
|
22274
|
+
unrepairable.add(batch[0].memory.memory_id);
|
|
22275
|
+
result.failed++;
|
|
22276
|
+
} else result.skippedConflict++;
|
|
22277
|
+
return true;
|
|
22278
|
+
}
|
|
22279
|
+
for (const [index, write] of batch.entries()) {
|
|
22280
|
+
signal?.throwIfAborted();
|
|
22281
|
+
try {
|
|
22282
|
+
await commit([write]);
|
|
22283
|
+
} catch (error2) {
|
|
22284
|
+
signal?.throwIfAborted();
|
|
22285
|
+
if (isRemoteWorkLeaseExpired(error2)) throw error2;
|
|
22286
|
+
if (error2 instanceof RemoteMemoryError && error2.code === "version_conflict")
|
|
22287
|
+
result.skippedConflict++;
|
|
22288
|
+
else if (rejectsMemoryWrite(error2)) {
|
|
22289
|
+
unrepairable.add(write.memory.memory_id);
|
|
22290
|
+
result.failed++;
|
|
22291
|
+
} else {
|
|
22292
|
+
result.failed += batch.length - index;
|
|
22293
|
+
return false;
|
|
22294
|
+
}
|
|
22295
|
+
}
|
|
22296
|
+
}
|
|
22297
|
+
}
|
|
22298
|
+
return true;
|
|
22092
22299
|
}
|
|
22093
|
-
|
|
22300
|
+
);
|
|
22301
|
+
if (!continueRepair) break;
|
|
22094
22302
|
}
|
|
22095
22303
|
return finish();
|
|
22096
22304
|
};
|
|
@@ -22130,7 +22338,7 @@ function createRemoteMemoryStore(options) {
|
|
|
22130
22338
|
},
|
|
22131
22339
|
importTopics: async (topics) => {
|
|
22132
22340
|
if (!topics.length) return;
|
|
22133
|
-
const existing = new Set((await
|
|
22341
|
+
const existing = new Set((await readTopics()).map((topic2) => topic2.name));
|
|
22134
22342
|
const missing = topics.flatMap((topic2) => {
|
|
22135
22343
|
const name = normalizeEntityName(topic2.name);
|
|
22136
22344
|
if (existing.has(name)) return [];
|
|
@@ -22139,6 +22347,7 @@ function createRemoteMemoryStore(options) {
|
|
|
22139
22347
|
});
|
|
22140
22348
|
if (!missing.length) return;
|
|
22141
22349
|
await persistence.importTopics(missing);
|
|
22350
|
+
invalidateReads();
|
|
22142
22351
|
for (const subscriber of [...subscribers])
|
|
22143
22352
|
if (subscriber.options.topics) safely(subscriber.onChange);
|
|
22144
22353
|
},
|
|
@@ -22152,40 +22361,51 @@ function createRemoteMemoryStore(options) {
|
|
|
22152
22361
|
listProjections: async (listOptions = {}) => (await listRows(metadata3, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
|
|
22153
22362
|
projection
|
|
22154
22363
|
),
|
|
22155
|
-
listTopics:
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
|
|
22160
|
-
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
name
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
|
|
22171
|
-
|
|
22172
|
-
|
|
22173
|
-
|
|
22364
|
+
listTopics: () => {
|
|
22365
|
+
if (topicSnapshot) return topicSnapshot.then((topics) => structuredClone(topics));
|
|
22366
|
+
const snapshot = (async () => {
|
|
22367
|
+
const topics = /* @__PURE__ */ new Map();
|
|
22368
|
+
for (const { memory } of await pages(metadata3, {
|
|
22369
|
+
include_archived: true,
|
|
22370
|
+
include_quarantined: true,
|
|
22371
|
+
include_superseded: true
|
|
22372
|
+
})) {
|
|
22373
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
22374
|
+
const name = normalizeEntityName(topic2.name);
|
|
22375
|
+
if (!name) continue;
|
|
22376
|
+
const entry = topics.get(name) ?? {
|
|
22377
|
+
name,
|
|
22378
|
+
kind: null,
|
|
22379
|
+
memoryCount: 0,
|
|
22380
|
+
members: /* @__PURE__ */ new Set()
|
|
22381
|
+
};
|
|
22382
|
+
entry.kind ?? (entry.kind = topic2.kind ?? null);
|
|
22383
|
+
entry.members.add(memory.memory_id);
|
|
22384
|
+
topics.set(name, entry);
|
|
22385
|
+
}
|
|
22174
22386
|
}
|
|
22175
|
-
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22181
|
-
|
|
22182
|
-
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22387
|
+
for (const topic2 of await readTopics()) {
|
|
22388
|
+
if (topic2.is_deleted) continue;
|
|
22389
|
+
const current = topics.get(topic2.name);
|
|
22390
|
+
if (current) current.kind ?? (current.kind = topic2.kind ?? null);
|
|
22391
|
+
else
|
|
22392
|
+
topics.set(topic2.name, {
|
|
22393
|
+
name: topic2.name,
|
|
22394
|
+
kind: topic2.kind ?? null,
|
|
22395
|
+
memoryCount: 0,
|
|
22396
|
+
members: /* @__PURE__ */ new Set()
|
|
22397
|
+
});
|
|
22398
|
+
}
|
|
22399
|
+
return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
|
|
22400
|
+
})();
|
|
22401
|
+
topicSnapshot = snapshot;
|
|
22402
|
+
const clear = () => {
|
|
22403
|
+
if (topicSnapshot === snapshot) topicSnapshot = void 0;
|
|
22404
|
+
};
|
|
22405
|
+
void snapshot.then(() => {
|
|
22406
|
+
if (!retainTopics()) clear();
|
|
22407
|
+
}, clear);
|
|
22408
|
+
return snapshot.then((topics) => structuredClone(topics));
|
|
22189
22409
|
},
|
|
22190
22410
|
get: async (id2) => asStored(await live(id2)),
|
|
22191
22411
|
listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
|
|
@@ -22366,20 +22586,23 @@ function createRemoteMemoryStore(options) {
|
|
|
22366
22586
|
subscribe: (onChange, subscribeOptions = {}) => {
|
|
22367
22587
|
const subscriber = { onChange, options: subscribeOptions };
|
|
22368
22588
|
subscribers.add(subscriber);
|
|
22369
|
-
if (pollIntervalMs > 0 &&
|
|
22370
|
-
|
|
22371
|
-
|
|
22589
|
+
if (pollIntervalMs > 0 && subscribers.size === 1) {
|
|
22590
|
+
stopActivity = activity.subscribe(refreshActivity);
|
|
22591
|
+
refreshActivity();
|
|
22372
22592
|
}
|
|
22373
22593
|
return () => {
|
|
22374
22594
|
subscribers.delete(subscriber);
|
|
22375
|
-
if (
|
|
22376
|
-
|
|
22595
|
+
if (!retainTopics()) invalidateReads();
|
|
22596
|
+
if (subscribers.size) return;
|
|
22597
|
+
if (timer) clearTimeout(timer);
|
|
22377
22598
|
timer = void 0;
|
|
22599
|
+
stopActivity?.();
|
|
22600
|
+
stopActivity = void 0;
|
|
22601
|
+
stopChanges?.();
|
|
22602
|
+
stopChanges = void 0;
|
|
22603
|
+
changesConnected = false;
|
|
22378
22604
|
generation++;
|
|
22379
|
-
|
|
22380
|
-
ready = false;
|
|
22381
|
-
cursor = void 0;
|
|
22382
|
-
pending = /* @__PURE__ */ new Set();
|
|
22605
|
+
invalidateReads();
|
|
22383
22606
|
};
|
|
22384
22607
|
}
|
|
22385
22608
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1826,11 +1826,12 @@ function parseRetryAfterMs(header) {
|
|
|
1826
1826
|
|
|
1827
1827
|
// src/lib/memory/store/remotePersistence.ts
|
|
1828
1828
|
var RemoteMemoryError = class extends Error {
|
|
1829
|
-
constructor(message, status, code, retryAfterMs) {
|
|
1829
|
+
constructor(message, status, code, retryAfterMs, rateLimitBucket) {
|
|
1830
1830
|
super(message);
|
|
1831
1831
|
this.status = status;
|
|
1832
1832
|
this.code = code;
|
|
1833
1833
|
this.retryAfterMs = retryAfterMs;
|
|
1834
|
+
this.rateLimitBucket = rateLimitBucket;
|
|
1834
1835
|
this.name = "RemoteMemoryError";
|
|
1835
1836
|
}
|
|
1836
1837
|
};
|
|
@@ -1849,6 +1850,14 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
|
1849
1850
|
throw new Error("timeoutMs must be a positive number");
|
|
1850
1851
|
const root = base.href.replace(/\/$/, "") + apiPath;
|
|
1851
1852
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
1853
|
+
const cooldowns = /* @__PURE__ */ new Map();
|
|
1854
|
+
const memoryBudget = (path, method) => {
|
|
1855
|
+
if (method === "PUT")
|
|
1856
|
+
return path === "/topics" ? "private-memory-import" : "private-memory-write";
|
|
1857
|
+
if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
|
|
1858
|
+
return "private-memory-import";
|
|
1859
|
+
return "private-memory-read";
|
|
1860
|
+
};
|
|
1852
1861
|
const request = async (path, init = {}, signal) => {
|
|
1853
1862
|
const controller = new AbortController();
|
|
1854
1863
|
let timedOut = false;
|
|
@@ -1872,6 +1881,25 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
|
1872
1881
|
const token = await Promise.race([options.getToken(), aborted]);
|
|
1873
1882
|
if (controller.signal.aborted) await aborted;
|
|
1874
1883
|
if (!token) throw new RemoteMemoryError("Authentication required", 401);
|
|
1884
|
+
const pathname = path.split("?")[0];
|
|
1885
|
+
const route = `${init.method ?? "GET"} ${pathname}`;
|
|
1886
|
+
const budgets = ["*", route];
|
|
1887
|
+
if (apiPath === "/api/private-memories")
|
|
1888
|
+
budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
|
|
1889
|
+
for (const bucket of budgets) {
|
|
1890
|
+
const cooldown = cooldowns.get(bucket);
|
|
1891
|
+
if (!cooldown) continue;
|
|
1892
|
+
const remaining = cooldown.until - Date.now();
|
|
1893
|
+
if (remaining > 0)
|
|
1894
|
+
throw new RemoteMemoryError(
|
|
1895
|
+
cooldown.error.message,
|
|
1896
|
+
429,
|
|
1897
|
+
cooldown.error.code,
|
|
1898
|
+
remaining,
|
|
1899
|
+
cooldown.error.rateLimitBucket
|
|
1900
|
+
);
|
|
1901
|
+
cooldowns.delete(bucket);
|
|
1902
|
+
}
|
|
1875
1903
|
const response = await fetcher(root + path, {
|
|
1876
1904
|
...init,
|
|
1877
1905
|
headers: {
|
|
@@ -1884,12 +1912,18 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
|
1884
1912
|
if (!response.ok) {
|
|
1885
1913
|
const body = await response.json().catch(() => null);
|
|
1886
1914
|
const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
|
|
1887
|
-
|
|
1915
|
+
const error = new RemoteMemoryError(
|
|
1888
1916
|
record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
|
|
1889
1917
|
response.status,
|
|
1890
1918
|
record(body) && typeof body.code === "string" ? body.code : void 0,
|
|
1891
|
-
retryAfter ?? void 0
|
|
1919
|
+
retryAfter ?? void 0,
|
|
1920
|
+
record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
|
|
1892
1921
|
);
|
|
1922
|
+
if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
|
|
1923
|
+
const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
|
|
1924
|
+
cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
|
|
1925
|
+
}
|
|
1926
|
+
throw error;
|
|
1893
1927
|
}
|
|
1894
1928
|
return await response.json();
|
|
1895
1929
|
} catch (error) {
|