@anuma/sdk 9.1.0 → 9.1.2

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.
@@ -12118,11 +12118,12 @@ function isRemoteWorkLeaseExpired(error) {
12118
12118
 
12119
12119
  // src/lib/memory/store/remotePersistence.ts
12120
12120
  var RemoteMemoryError = class extends Error {
12121
- constructor(message, status, code, retryAfterMs) {
12121
+ constructor(message, status, code, retryAfterMs, rateLimitBucket) {
12122
12122
  super(message);
12123
12123
  this.status = status;
12124
12124
  this.code = code;
12125
12125
  this.retryAfterMs = retryAfterMs;
12126
+ this.rateLimitBucket = rateLimitBucket;
12126
12127
  this.name = "RemoteMemoryError";
12127
12128
  }
12128
12129
  };
@@ -12217,6 +12218,14 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
12217
12218
  throw new Error("timeoutMs must be a positive number");
12218
12219
  const root = base.href.replace(/\/$/, "") + apiPath;
12219
12220
  const fetcher = options.fetch ?? globalThis.fetch;
12221
+ const cooldowns = /* @__PURE__ */ new Map();
12222
+ const memoryBudget = (path, method) => {
12223
+ if (method === "PUT")
12224
+ return path === "/topics" ? "private-memory-import" : "private-memory-write";
12225
+ if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
12226
+ return "private-memory-import";
12227
+ return "private-memory-read";
12228
+ };
12220
12229
  const request = async (path, init = {}, signal) => {
12221
12230
  const controller = new AbortController();
12222
12231
  let timedOut = false;
@@ -12240,6 +12249,25 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
12240
12249
  const token = await Promise.race([options.getToken(), aborted2]);
12241
12250
  if (controller.signal.aborted) await aborted2;
12242
12251
  if (!token) throw new RemoteMemoryError("Authentication required", 401);
12252
+ const pathname = path.split("?")[0];
12253
+ const route = `${init.method ?? "GET"} ${pathname}`;
12254
+ const budgets = ["*", route];
12255
+ if (apiPath === "/api/private-memories")
12256
+ budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
12257
+ for (const bucket of budgets) {
12258
+ const cooldown = cooldowns.get(bucket);
12259
+ if (!cooldown) continue;
12260
+ const remaining = cooldown.until - Date.now();
12261
+ if (remaining > 0)
12262
+ throw new RemoteMemoryError(
12263
+ cooldown.error.message,
12264
+ 429,
12265
+ cooldown.error.code,
12266
+ remaining,
12267
+ cooldown.error.rateLimitBucket
12268
+ );
12269
+ cooldowns.delete(bucket);
12270
+ }
12243
12271
  const response = await fetcher(root + path, {
12244
12272
  ...init,
12245
12273
  headers: {
@@ -12252,12 +12280,18 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
12252
12280
  if (!response.ok) {
12253
12281
  const body = await response.json().catch(() => null);
12254
12282
  const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
12255
- throw new RemoteMemoryError(
12283
+ const error = new RemoteMemoryError(
12256
12284
  record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
12257
12285
  response.status,
12258
12286
  record(body) && typeof body.code === "string" ? body.code : void 0,
12259
- retryAfter ?? void 0
12287
+ retryAfter ?? void 0,
12288
+ record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
12260
12289
  );
12290
+ if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
12291
+ const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
12292
+ cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
12293
+ }
12294
+ throw error;
12261
12295
  }
12262
12296
  return await response.json();
12263
12297
  } catch (error) {
@@ -12497,6 +12531,7 @@ async function createRemoteMemoryPersistence(options) {
12497
12531
  return {
12498
12532
  ...createRemoteTopicPersistence(request),
12499
12533
  ...createRemoteWorkPersistence(request),
12534
+ supportsRealtimeChanges: account.realtime_changes === true,
12500
12535
  counts: async () => {
12501
12536
  const result = await request("/counts", {});
12502
12537
  if (!record(result) || ["live", "archived", "quarantined"].some(
@@ -16599,9 +16634,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
16599
16634
  }
16600
16635
  function baseVaultSql(ctx, options) {
16601
16636
  const clauses = [
16637
+ `"_status" is not 'deleted'`,
16602
16638
  '"is_deleted" = 0',
16603
16639
  ...options?.includeArchived ? [] : ['"archived_at" is null'],
16604
- `"trust_tier" is not 'quarantined'`,
16640
+ ...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
16605
16641
  '"superseded_by" is null'
16606
16642
  ];
16607
16643
  const args = [];
@@ -17087,8 +17123,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
17087
17123
  if (!entityCtx) {
17088
17124
  throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
17089
17125
  }
17090
- const conditions = [...baseVaultConditions(ctx), import_watermelondb24.Q.sortBy("created_at", import_watermelondb24.Q.desc)];
17091
- const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
17126
+ let rows;
17127
+ try {
17128
+ const base = baseVaultSql(ctx);
17129
+ const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
17130
+ rows = await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
17131
+ } catch (err) {
17132
+ getLogger().debug(
17133
+ "memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
17134
+ );
17135
+ rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb24.Q.sortBy("created_at", import_watermelondb24.Q.desc)).unsafeFetchRaw();
17136
+ }
17092
17137
  const linkedNames = await linkedEntityNamesByMemory(
17093
17138
  entityCtx,
17094
17139
  rows.map((r) => r.id)
@@ -17147,10 +17192,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
17147
17192
  }
17148
17193
  const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
17149
17194
  const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
17150
- const pending = await mapInBatches(
17151
- limitedPendingRaw,
17152
- (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
17153
- );
17195
+ const pending = [];
17196
+ for (let i = 0; i < limitedPendingRaw.length; i += 500) {
17197
+ const chunk = limitedPendingRaw.slice(i, i + 500);
17198
+ const memories = await getVaultMemoriesByIdsOp(
17199
+ ctx,
17200
+ chunk.map((raw) => raw.id)
17201
+ );
17202
+ const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
17203
+ for (const raw of chunk) {
17204
+ const memory = byId.get(raw.id);
17205
+ if (memory) pending.push(memory);
17206
+ }
17207
+ }
17154
17208
  const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
17155
17209
  return {
17156
17210
  pending,
@@ -17297,10 +17351,20 @@ function assertVaultScopeForSweep(ctx) {
17297
17351
  }
17298
17352
  async function getDecayCandidatesRawOp(ctx) {
17299
17353
  assertVaultScopeForSweep(ctx);
17300
- const results = await ctx.vaultMemoryCollection.query(
17301
- ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
17302
- import_watermelondb24.Q.where("kind", null)
17303
- ).unsafeFetchRaw();
17354
+ let results;
17355
+ try {
17356
+ const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
17357
+ const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
17358
+ results = await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
17359
+ } catch (err) {
17360
+ getLogger().debug(
17361
+ "memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
17362
+ );
17363
+ results = await ctx.vaultMemoryCollection.query(
17364
+ ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
17365
+ import_watermelondb24.Q.where("kind", null)
17366
+ ).unsafeFetchRaw();
17367
+ }
17304
17368
  return results.map((raw) => ({
17305
17369
  uniqueId: raw.id,
17306
17370
  factType: raw.fact_type ?? null,
@@ -25334,6 +25398,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
25334
25398
  function rejectsInput(error) {
25335
25399
  return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
25336
25400
  }
25401
+ function rejectsMemoryWrite(error) {
25402
+ return error instanceof RemoteMemoryError && (error.status === 400 || error.status === 422) && (error.code === void 0 || error.code === "unknown_field");
25403
+ }
25337
25404
  function warnFailed(failed) {
25338
25405
  if (failed.length)
25339
25406
  getLogger().warn(
@@ -25421,16 +25488,47 @@ function createRemoteMemoryStore(options) {
25421
25488
  const { persistence, embeddingOptions } = options;
25422
25489
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
25423
25490
  const pollIntervalMs = options.pollIntervalMs ?? 6e4;
25491
+ const activity = options.activity ?? {
25492
+ isActive: () => (typeof document === "undefined" || document.visibilityState !== "hidden") && (typeof navigator === "undefined" || navigator.onLine !== false),
25493
+ subscribe: (onChange) => {
25494
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
25495
+ };
25496
+ document.addEventListener("visibilitychange", onChange);
25497
+ window.addEventListener("online", onChange);
25498
+ window.addEventListener("offline", onChange);
25499
+ return () => {
25500
+ document.removeEventListener("visibilitychange", onChange);
25501
+ window.removeEventListener("online", onChange);
25502
+ window.removeEventListener("offline", onChange);
25503
+ };
25504
+ }
25505
+ };
25424
25506
  const pipeline = createRemoteMemoryPipeline({
25425
25507
  ...options,
25426
25508
  graphRanking: options.graphRanking ?? ((query, _traverse, recallOptions) => topicGraphRanking(query, recallOptions)),
25427
25509
  temporalRanking: options.temporalRanking ?? ((query, now) => eventTimeRanking(query, now))
25428
25510
  });
25429
25511
  const subscribers = /* @__PURE__ */ new Set();
25512
+ const retainTopics = () => pollIntervalMs > 0 && [...subscribers].some((subscriber) => subscriber.options.topics);
25430
25513
  const seen = /* @__PURE__ */ new Map();
25514
+ const reads = /* @__PURE__ */ new Map();
25515
+ const metadataReads = /* @__PURE__ */ new Map();
25516
+ let topicSnapshot;
25517
+ let topicCatalog;
25518
+ const invalidateReads = () => {
25519
+ reads.clear();
25520
+ metadataReads.clear();
25521
+ topicSnapshot = void 0;
25522
+ topicCatalog = void 0;
25523
+ };
25431
25524
  let timer;
25525
+ let scheduledAt;
25526
+ let stopActivity;
25527
+ let stopChanges;
25528
+ let changesConnected = false;
25432
25529
  const notify = (change, records = []) => {
25433
- if (timer)
25530
+ invalidateReads();
25531
+ if (subscribers.size)
25434
25532
  for (const record2 of records) {
25435
25533
  const previous = seen.get(record2.memory.memory_id);
25436
25534
  if ((previous?.version ?? 0) + 1 === record2.version)
@@ -25454,8 +25552,37 @@ function createRemoteMemoryStore(options) {
25454
25552
  } while (cursor2 && records.length < max);
25455
25553
  return records.slice(0, max);
25456
25554
  };
25457
- const decrypted = (options2) => persistence.query(options2);
25458
- const metadata3 = (options2) => persistence.queryMetadata(options2);
25555
+ const shareRead = (inflight, options2, read) => {
25556
+ if (options2.signal) return read();
25557
+ const key = JSON.stringify(Object.entries(options2).sort(([a], [b]) => a.localeCompare(b)));
25558
+ const active = inflight.get(key);
25559
+ if (active) return active;
25560
+ const pending2 = read();
25561
+ inflight.set(key, pending2);
25562
+ const clear = () => {
25563
+ if (inflight.get(key) === pending2) inflight.delete(key);
25564
+ };
25565
+ void pending2.then(clear, clear);
25566
+ return pending2;
25567
+ };
25568
+ const decrypted = (options2) => shareRead(reads, options2, () => persistence.query(options2));
25569
+ const metadata3 = (options2) => shareRead(
25570
+ metadataReads,
25571
+ options2,
25572
+ () => persistence.queryMetadata(options2)
25573
+ );
25574
+ const readTopics = () => {
25575
+ if (topicCatalog) return topicCatalog;
25576
+ const pending2 = persistence.topics();
25577
+ topicCatalog = pending2;
25578
+ const clear = () => {
25579
+ if (topicCatalog === pending2) topicCatalog = void 0;
25580
+ };
25581
+ void pending2.then(() => {
25582
+ if (!retainTopics()) clear();
25583
+ }, clear);
25584
+ return pending2;
25585
+ };
25459
25586
  const enumerate = (filters, max = Infinity) => pages(decrypted, filters, max);
25460
25587
  const memoriesByTopics = async (names) => {
25461
25588
  const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
@@ -25909,11 +26036,11 @@ function createRemoteMemoryStore(options) {
25909
26036
  };
25910
26037
  const changesSince = async (cursor2, pending2) => {
25911
26038
  const records = [];
25912
- const failed = /* @__PURE__ */ new Set();
26039
+ const failed = /* @__PURE__ */ new Map();
25913
26040
  const collect = (page) => {
25914
26041
  warnFailed(page.failed);
25915
26042
  records.push(...page.items);
25916
- for (const failure of page.failed) failed.add(failure.memory_id);
26043
+ for (const failure of page.failed) failed.set(failure.memory_id, failure.version);
25917
26044
  };
25918
26045
  let resume = cursor2;
25919
26046
  let next;
@@ -25947,25 +26074,72 @@ function createRemoteMemoryStore(options) {
25947
26074
  let ready = false;
25948
26075
  let cursor;
25949
26076
  let pending = /* @__PURE__ */ new Set();
26077
+ let failedVersions = /* @__PURE__ */ new Map();
25950
26078
  let topicFingerprint;
25951
26079
  let polling = false;
26080
+ let pollRequested = false;
26081
+ let failures = 0;
26082
+ let retryAt = 0;
26083
+ const backoff = (error) => {
26084
+ failures++;
26085
+ retryAt = Date.now() + Math.max(
26086
+ error instanceof RemoteMemoryError ? error.retryAfterMs ?? 0 : 0,
26087
+ Math.min(pollIntervalMs * 2 ** Math.min(failures - 1, 10), 15 * 6e4)
26088
+ );
26089
+ };
26090
+ const schedulePoll = (delay, keepEarlier = false) => {
26091
+ const at = Math.max(Date.now() + delay, retryAt);
26092
+ if (keepEarlier && timer && scheduledAt !== void 0 && scheduledAt <= at) return;
26093
+ if (timer) clearTimeout(timer);
26094
+ timer = void 0;
26095
+ scheduledAt = void 0;
26096
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
26097
+ scheduledAt = at;
26098
+ timer = setTimeout(poll, Math.max(0, at - Date.now()));
26099
+ };
25952
26100
  const poll = () => {
25953
- if (polling) return;
26101
+ if (timer) clearTimeout(timer);
26102
+ timer = void 0;
26103
+ scheduledAt = void 0;
26104
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
26105
+ if (Date.now() < retryAt) {
26106
+ schedulePoll(0);
26107
+ return;
26108
+ }
26109
+ if (polling) {
26110
+ pollRequested = true;
26111
+ return;
26112
+ }
25954
26113
  polling = true;
26114
+ pollRequested = false;
25955
26115
  const current = generation;
25956
26116
  changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
25957
26117
  let topicState;
26118
+ let topicFailed = false;
25958
26119
  if ([...subscribers].some((s) => s.options.topics))
25959
26120
  try {
25960
- topicState = JSON.stringify(
25961
- (await persistence.topics()).map((t) => [t.name, t.version])
25962
- );
26121
+ topicCatalog = void 0;
26122
+ topicState = JSON.stringify((await readTopics()).map((t) => [t.name, t.version]));
25963
26123
  } catch (error) {
26124
+ topicFailed = true;
26125
+ if (current === generation) backoff(error);
25964
26126
  getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
25965
26127
  }
25966
26128
  if (current !== generation) return;
26129
+ if (!topicFailed) {
26130
+ failures = 0;
26131
+ retryAt = 0;
26132
+ }
26133
+ const failedChanged = [...failed].some(
26134
+ ([id2, version]) => failedVersions.get(id2) !== version
26135
+ );
26136
+ if (records.length || failedChanged || topicState !== void 0 && topicState !== topicFingerprint) {
26137
+ reads.clear();
26138
+ metadataReads.clear();
26139
+ topicSnapshot = void 0;
26140
+ }
25967
26141
  const changed = /* @__PURE__ */ new Set();
25968
- if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
26142
+ if (ready && failedChanged || topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
25969
26143
  for (const subscriber of subscribers)
25970
26144
  if (subscriber.options.topics) changed.add(subscriber);
25971
26145
  }
@@ -25982,15 +26156,40 @@ function createRemoteMemoryStore(options) {
25982
26156
  }
25983
26157
  ready = true;
25984
26158
  cursor = resume;
25985
- pending = failed;
26159
+ pending = new Set(failed.keys());
26160
+ failedVersions = failed;
25986
26161
  for (const subscriber of changed)
25987
26162
  if (subscribers.has(subscriber)) safely(subscriber.onChange);
25988
26163
  }).catch((error) => {
26164
+ if (current === generation) backoff(error);
25989
26165
  getLogger().warn("[memory/remote-store] Subscription poll failed:", error);
25990
26166
  }).finally(() => {
25991
26167
  polling = false;
26168
+ schedulePoll(
26169
+ current !== generation ? 0 : pollRequested ? 200 : changesConnected ? Math.max(pollIntervalMs, 5 * 6e4) : pollIntervalMs
26170
+ );
25992
26171
  });
25993
26172
  };
26173
+ const refreshActivity = () => {
26174
+ if (timer) clearTimeout(timer);
26175
+ timer = void 0;
26176
+ if (!activity.isActive()) {
26177
+ stopChanges?.();
26178
+ stopChanges = void 0;
26179
+ changesConnected = false;
26180
+ return;
26181
+ }
26182
+ if (!stopChanges && persistence.supportsRealtimeChanges && options.subscribeChanges)
26183
+ stopChanges = options.subscribeChanges((connected) => {
26184
+ changesConnected = connected;
26185
+ if (polling) {
26186
+ pollRequested = true;
26187
+ return;
26188
+ }
26189
+ schedulePoll(connected ? 200 : 0, true);
26190
+ });
26191
+ poll();
26192
+ };
25994
26193
  const unrepairable = /* @__PURE__ */ new Set();
25995
26194
  let repairCursor;
25996
26195
  const repairEmbeddingsUnlocked = async ({
@@ -26046,40 +26245,78 @@ function createRemoteMemoryStore(options) {
26046
26245
  result.failed += targets.length;
26047
26246
  return finish();
26048
26247
  }
26049
- for (const [i, record2] of targets.entries()) {
26248
+ const writes = targets.flatMap((record2, i) => {
26050
26249
  const vector = vectors[i];
26051
26250
  if (!vector) {
26052
26251
  result.failed++;
26053
- continue;
26252
+ return [];
26054
26253
  }
26055
- await serialized(record2.memory.memory_id, async () => {
26056
- signal?.throwIfAborted();
26057
- try {
26058
- const [saved] = await persistence.putMany(
26059
- [
26060
- {
26061
- memory: {
26062
- ...record2.memory,
26063
- embedding: storedVector(vector),
26064
- embedding_model: model
26065
- },
26066
- expectedVersion: record2
26067
- }
26068
- ],
26069
- signal,
26070
- leaseToken
26071
- );
26072
- notify("embedding", [saved]);
26073
- repaired.add(record2.memory.memory_id);
26074
- result.reembedded++;
26075
- } catch (error) {
26254
+ try {
26255
+ return [
26256
+ {
26257
+ memory: {
26258
+ ...record2.memory,
26259
+ embedding: storedVector(vector),
26260
+ embedding_model: model
26261
+ },
26262
+ expectedVersion: record2
26263
+ }
26264
+ ];
26265
+ } catch {
26266
+ result.failed++;
26267
+ return [];
26268
+ }
26269
+ });
26270
+ for (const batch of chunks2(writes, MAX_BATCH)) {
26271
+ const continueRepair = await serializedAll(
26272
+ batch.map(({ memory }) => memory.memory_id),
26273
+ async () => {
26076
26274
  signal?.throwIfAborted();
26077
- if (isRemoteWorkLeaseExpired(error)) throw error;
26078
- if (error instanceof RemoteMemoryError && error.code === "version_conflict")
26079
- result.skippedConflict++;
26080
- else result.failed++;
26275
+ const commit = async (slice) => {
26276
+ const saved = await persistence.putMany(slice, signal, leaseToken);
26277
+ for (const record2 of saved) repaired.add(record2.memory.memory_id);
26278
+ result.reembedded += saved.length;
26279
+ notify("embedding", saved);
26280
+ };
26281
+ try {
26282
+ await commit(batch);
26283
+ } catch (error) {
26284
+ signal?.throwIfAborted();
26285
+ if (isRemoteWorkLeaseExpired(error)) throw error;
26286
+ if (!(error instanceof RemoteMemoryError && error.code === "version_conflict") && !rejectsMemoryWrite(error)) {
26287
+ result.failed += batch.length;
26288
+ return false;
26289
+ }
26290
+ if (batch.length === 1) {
26291
+ if (rejectsMemoryWrite(error)) {
26292
+ unrepairable.add(batch[0].memory.memory_id);
26293
+ result.failed++;
26294
+ } else result.skippedConflict++;
26295
+ return true;
26296
+ }
26297
+ for (const [index, write] of batch.entries()) {
26298
+ signal?.throwIfAborted();
26299
+ try {
26300
+ await commit([write]);
26301
+ } catch (error2) {
26302
+ signal?.throwIfAborted();
26303
+ if (isRemoteWorkLeaseExpired(error2)) throw error2;
26304
+ if (error2 instanceof RemoteMemoryError && error2.code === "version_conflict")
26305
+ result.skippedConflict++;
26306
+ else if (rejectsMemoryWrite(error2)) {
26307
+ unrepairable.add(write.memory.memory_id);
26308
+ result.failed++;
26309
+ } else {
26310
+ result.failed += batch.length - index;
26311
+ return false;
26312
+ }
26313
+ }
26314
+ }
26315
+ }
26316
+ return true;
26081
26317
  }
26082
- });
26318
+ );
26319
+ if (!continueRepair) break;
26083
26320
  }
26084
26321
  return finish();
26085
26322
  };
@@ -26119,7 +26356,7 @@ function createRemoteMemoryStore(options) {
26119
26356
  },
26120
26357
  importTopics: async (topics) => {
26121
26358
  if (!topics.length) return;
26122
- const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
26359
+ const existing = new Set((await readTopics()).map((topic2) => topic2.name));
26123
26360
  const missing = topics.flatMap((topic2) => {
26124
26361
  const name = normalizeEntityName(topic2.name);
26125
26362
  if (existing.has(name)) return [];
@@ -26128,6 +26365,7 @@ function createRemoteMemoryStore(options) {
26128
26365
  });
26129
26366
  if (!missing.length) return;
26130
26367
  await persistence.importTopics(missing);
26368
+ invalidateReads();
26131
26369
  for (const subscriber of [...subscribers])
26132
26370
  if (subscriber.options.topics) safely(subscriber.onChange);
26133
26371
  },
@@ -26141,40 +26379,51 @@ function createRemoteMemoryStore(options) {
26141
26379
  listProjections: async (listOptions = {}) => (await listRows(metadata3, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
26142
26380
  projection
26143
26381
  ),
26144
- listTopics: async () => {
26145
- const topics = /* @__PURE__ */ new Map();
26146
- for (const { memory } of await pages(metadata3, {
26147
- include_archived: true,
26148
- include_quarantined: true,
26149
- include_superseded: true
26150
- })) {
26151
- for (const topic2 of parseTopics(memory.topics) ?? []) {
26152
- const name = normalizeEntityName(topic2.name);
26153
- if (!name) continue;
26154
- const entry = topics.get(name) ?? {
26155
- name,
26156
- kind: null,
26157
- memoryCount: 0,
26158
- members: /* @__PURE__ */ new Set()
26159
- };
26160
- entry.kind ?? (entry.kind = topic2.kind ?? null);
26161
- entry.members.add(memory.memory_id);
26162
- topics.set(name, entry);
26163
- }
26164
- }
26165
- for (const topic2 of await persistence.topics()) {
26166
- if (topic2.is_deleted) continue;
26167
- const current = topics.get(topic2.name);
26168
- if (current) current.kind ?? (current.kind = topic2.kind ?? null);
26169
- else
26170
- topics.set(topic2.name, {
26171
- name: topic2.name,
26172
- kind: topic2.kind ?? null,
26173
- memoryCount: 0,
26174
- members: /* @__PURE__ */ new Set()
26175
- });
26176
- }
26177
- return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
26382
+ listTopics: () => {
26383
+ if (topicSnapshot) return topicSnapshot.then((topics) => structuredClone(topics));
26384
+ const snapshot = (async () => {
26385
+ const topics = /* @__PURE__ */ new Map();
26386
+ for (const { memory } of await pages(metadata3, {
26387
+ include_archived: true,
26388
+ include_quarantined: true,
26389
+ include_superseded: true
26390
+ })) {
26391
+ for (const topic2 of parseTopics(memory.topics) ?? []) {
26392
+ const name = normalizeEntityName(topic2.name);
26393
+ if (!name) continue;
26394
+ const entry = topics.get(name) ?? {
26395
+ name,
26396
+ kind: null,
26397
+ memoryCount: 0,
26398
+ members: /* @__PURE__ */ new Set()
26399
+ };
26400
+ entry.kind ?? (entry.kind = topic2.kind ?? null);
26401
+ entry.members.add(memory.memory_id);
26402
+ topics.set(name, entry);
26403
+ }
26404
+ }
26405
+ for (const topic2 of await readTopics()) {
26406
+ if (topic2.is_deleted) continue;
26407
+ const current = topics.get(topic2.name);
26408
+ if (current) current.kind ?? (current.kind = topic2.kind ?? null);
26409
+ else
26410
+ topics.set(topic2.name, {
26411
+ name: topic2.name,
26412
+ kind: topic2.kind ?? null,
26413
+ memoryCount: 0,
26414
+ members: /* @__PURE__ */ new Set()
26415
+ });
26416
+ }
26417
+ return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
26418
+ })();
26419
+ topicSnapshot = snapshot;
26420
+ const clear = () => {
26421
+ if (topicSnapshot === snapshot) topicSnapshot = void 0;
26422
+ };
26423
+ void snapshot.then(() => {
26424
+ if (!retainTopics()) clear();
26425
+ }, clear);
26426
+ return snapshot.then((topics) => structuredClone(topics));
26178
26427
  },
26179
26428
  get: async (id2) => asStored(await live(id2)),
26180
26429
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
@@ -26355,20 +26604,23 @@ function createRemoteMemoryStore(options) {
26355
26604
  subscribe: (onChange, subscribeOptions = {}) => {
26356
26605
  const subscriber = { onChange, options: subscribeOptions };
26357
26606
  subscribers.add(subscriber);
26358
- if (pollIntervalMs > 0 && !timer) {
26359
- timer = setInterval(poll, pollIntervalMs);
26360
- poll();
26607
+ if (pollIntervalMs > 0 && subscribers.size === 1) {
26608
+ stopActivity = activity.subscribe(refreshActivity);
26609
+ refreshActivity();
26361
26610
  }
26362
26611
  return () => {
26363
26612
  subscribers.delete(subscriber);
26364
- if (subscribers.size || !timer) return;
26365
- clearInterval(timer);
26613
+ if (!retainTopics()) invalidateReads();
26614
+ if (subscribers.size) return;
26615
+ if (timer) clearTimeout(timer);
26366
26616
  timer = void 0;
26617
+ stopActivity?.();
26618
+ stopActivity = void 0;
26619
+ stopChanges?.();
26620
+ stopChanges = void 0;
26621
+ changesConnected = false;
26367
26622
  generation++;
26368
- seen.clear();
26369
- ready = false;
26370
- cursor = void 0;
26371
- pending = /* @__PURE__ */ new Set();
26623
+ invalidateReads();
26372
26624
  };
26373
26625
  }
26374
26626
  };