@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.
@@ -10287,11 +10287,12 @@ function isRemoteWorkLeaseExpired(error) {
10287
10287
 
10288
10288
  // src/lib/memory/store/remotePersistence.ts
10289
10289
  var RemoteMemoryError = class extends Error {
10290
- constructor(message, status, code, retryAfterMs) {
10290
+ constructor(message, status, code, retryAfterMs, rateLimitBucket) {
10291
10291
  super(message);
10292
10292
  this.status = status;
10293
10293
  this.code = code;
10294
10294
  this.retryAfterMs = retryAfterMs;
10295
+ this.rateLimitBucket = rateLimitBucket;
10295
10296
  this.name = "RemoteMemoryError";
10296
10297
  }
10297
10298
  };
@@ -10386,6 +10387,14 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
10386
10387
  throw new Error("timeoutMs must be a positive number");
10387
10388
  const root = base.href.replace(/\/$/, "") + apiPath;
10388
10389
  const fetcher = options.fetch ?? globalThis.fetch;
10390
+ const cooldowns = /* @__PURE__ */ new Map();
10391
+ const memoryBudget = (path, method) => {
10392
+ if (method === "PUT")
10393
+ return path === "/topics" ? "private-memory-import" : "private-memory-write";
10394
+ if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
10395
+ return "private-memory-import";
10396
+ return "private-memory-read";
10397
+ };
10389
10398
  const request = async (path, init = {}, signal) => {
10390
10399
  const controller = new AbortController();
10391
10400
  let timedOut = false;
@@ -10409,6 +10418,25 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
10409
10418
  const token = await Promise.race([options.getToken(), aborted2]);
10410
10419
  if (controller.signal.aborted) await aborted2;
10411
10420
  if (!token) throw new RemoteMemoryError("Authentication required", 401);
10421
+ const pathname = path.split("?")[0];
10422
+ const route = `${init.method ?? "GET"} ${pathname}`;
10423
+ const budgets = ["*", route];
10424
+ if (apiPath === "/api/private-memories")
10425
+ budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
10426
+ for (const bucket of budgets) {
10427
+ const cooldown = cooldowns.get(bucket);
10428
+ if (!cooldown) continue;
10429
+ const remaining = cooldown.until - Date.now();
10430
+ if (remaining > 0)
10431
+ throw new RemoteMemoryError(
10432
+ cooldown.error.message,
10433
+ 429,
10434
+ cooldown.error.code,
10435
+ remaining,
10436
+ cooldown.error.rateLimitBucket
10437
+ );
10438
+ cooldowns.delete(bucket);
10439
+ }
10412
10440
  const response = await fetcher(root + path, {
10413
10441
  ...init,
10414
10442
  headers: {
@@ -10421,12 +10449,18 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
10421
10449
  if (!response.ok) {
10422
10450
  const body = await response.json().catch(() => null);
10423
10451
  const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
10424
- throw new RemoteMemoryError(
10452
+ const error = new RemoteMemoryError(
10425
10453
  record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
10426
10454
  response.status,
10427
10455
  record(body) && typeof body.code === "string" ? body.code : void 0,
10428
- retryAfter ?? void 0
10456
+ retryAfter ?? void 0,
10457
+ record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
10429
10458
  );
10459
+ if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
10460
+ const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
10461
+ cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
10462
+ }
10463
+ throw error;
10430
10464
  }
10431
10465
  return await response.json();
10432
10466
  } catch (error) {
@@ -10666,6 +10700,7 @@ async function createRemoteMemoryPersistence(options) {
10666
10700
  return {
10667
10701
  ...createRemoteTopicPersistence(request),
10668
10702
  ...createRemoteWorkPersistence(request),
10703
+ supportsRealtimeChanges: account.realtime_changes === true,
10669
10704
  counts: async () => {
10670
10705
  const result = await request("/counts", {});
10671
10706
  if (!record(result) || ["live", "archived", "quarantined"].some(
@@ -14768,9 +14803,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
14768
14803
  }
14769
14804
  function baseVaultSql(ctx, options) {
14770
14805
  const clauses = [
14806
+ `"_status" is not 'deleted'`,
14771
14807
  '"is_deleted" = 0',
14772
14808
  ...options?.includeArchived ? [] : ['"archived_at" is null'],
14773
- `"trust_tier" is not 'quarantined'`,
14809
+ ...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
14774
14810
  '"superseded_by" is null'
14775
14811
  ];
14776
14812
  const args = [];
@@ -15256,8 +15292,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
15256
15292
  if (!entityCtx) {
15257
15293
  throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
15258
15294
  }
15259
- const conditions = [...baseVaultConditions(ctx), Q9.sortBy("created_at", Q9.desc)];
15260
- const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
15295
+ let rows;
15296
+ try {
15297
+ const base = baseVaultSql(ctx);
15298
+ 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`;
15299
+ rows = await ctx.vaultMemoryCollection.query(Q9.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
15300
+ } catch (err) {
15301
+ getLogger().debug(
15302
+ "memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
15303
+ );
15304
+ rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), Q9.sortBy("created_at", Q9.desc)).unsafeFetchRaw();
15305
+ }
15261
15306
  const linkedNames = await linkedEntityNamesByMemory(
15262
15307
  entityCtx,
15263
15308
  rows.map((r) => r.id)
@@ -15316,10 +15361,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
15316
15361
  }
15317
15362
  const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
15318
15363
  const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
15319
- const pending = await mapInBatches(
15320
- limitedPendingRaw,
15321
- (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
15322
- );
15364
+ const pending = [];
15365
+ for (let i = 0; i < limitedPendingRaw.length; i += 500) {
15366
+ const chunk = limitedPendingRaw.slice(i, i + 500);
15367
+ const memories = await getVaultMemoriesByIdsOp(
15368
+ ctx,
15369
+ chunk.map((raw) => raw.id)
15370
+ );
15371
+ const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
15372
+ for (const raw of chunk) {
15373
+ const memory = byId.get(raw.id);
15374
+ if (memory) pending.push(memory);
15375
+ }
15376
+ }
15323
15377
  const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
15324
15378
  return {
15325
15379
  pending,
@@ -15466,10 +15520,20 @@ function assertVaultScopeForSweep(ctx) {
15466
15520
  }
15467
15521
  async function getDecayCandidatesRawOp(ctx) {
15468
15522
  assertVaultScopeForSweep(ctx);
15469
- const results = await ctx.vaultMemoryCollection.query(
15470
- ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
15471
- Q9.where("kind", null)
15472
- ).unsafeFetchRaw();
15523
+ let results;
15524
+ try {
15525
+ const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
15526
+ 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`;
15527
+ results = await ctx.vaultMemoryCollection.query(Q9.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
15528
+ } catch (err) {
15529
+ getLogger().debug(
15530
+ "memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
15531
+ );
15532
+ results = await ctx.vaultMemoryCollection.query(
15533
+ ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
15534
+ Q9.where("kind", null)
15535
+ ).unsafeFetchRaw();
15536
+ }
15473
15537
  return results.map((raw) => ({
15474
15538
  uniqueId: raw.id,
15475
15539
  factType: raw.fact_type ?? null,
@@ -23450,6 +23514,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
23450
23514
  function rejectsInput(error) {
23451
23515
  return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
23452
23516
  }
23517
+ function rejectsMemoryWrite(error) {
23518
+ return error instanceof RemoteMemoryError && (error.status === 400 || error.status === 422) && (error.code === void 0 || error.code === "unknown_field");
23519
+ }
23453
23520
  function warnFailed(failed) {
23454
23521
  if (failed.length)
23455
23522
  getLogger().warn(
@@ -23537,16 +23604,47 @@ function createRemoteMemoryStore(options) {
23537
23604
  const { persistence, embeddingOptions } = options;
23538
23605
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
23539
23606
  const pollIntervalMs = options.pollIntervalMs ?? 6e4;
23607
+ const activity = options.activity ?? {
23608
+ isActive: () => (typeof document === "undefined" || document.visibilityState !== "hidden") && (typeof navigator === "undefined" || navigator.onLine !== false),
23609
+ subscribe: (onChange) => {
23610
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
23611
+ };
23612
+ document.addEventListener("visibilitychange", onChange);
23613
+ window.addEventListener("online", onChange);
23614
+ window.addEventListener("offline", onChange);
23615
+ return () => {
23616
+ document.removeEventListener("visibilitychange", onChange);
23617
+ window.removeEventListener("online", onChange);
23618
+ window.removeEventListener("offline", onChange);
23619
+ };
23620
+ }
23621
+ };
23540
23622
  const pipeline = createRemoteMemoryPipeline({
23541
23623
  ...options,
23542
23624
  graphRanking: options.graphRanking ?? ((query, _traverse, recallOptions) => topicGraphRanking(query, recallOptions)),
23543
23625
  temporalRanking: options.temporalRanking ?? ((query, now) => eventTimeRanking(query, now))
23544
23626
  });
23545
23627
  const subscribers = /* @__PURE__ */ new Set();
23628
+ const retainTopics = () => pollIntervalMs > 0 && [...subscribers].some((subscriber) => subscriber.options.topics);
23546
23629
  const seen = /* @__PURE__ */ new Map();
23630
+ const reads = /* @__PURE__ */ new Map();
23631
+ const metadataReads = /* @__PURE__ */ new Map();
23632
+ let topicSnapshot;
23633
+ let topicCatalog;
23634
+ const invalidateReads = () => {
23635
+ reads.clear();
23636
+ metadataReads.clear();
23637
+ topicSnapshot = void 0;
23638
+ topicCatalog = void 0;
23639
+ };
23547
23640
  let timer;
23641
+ let scheduledAt;
23642
+ let stopActivity;
23643
+ let stopChanges;
23644
+ let changesConnected = false;
23548
23645
  const notify = (change, records = []) => {
23549
- if (timer)
23646
+ invalidateReads();
23647
+ if (subscribers.size)
23550
23648
  for (const record2 of records) {
23551
23649
  const previous = seen.get(record2.memory.memory_id);
23552
23650
  if ((previous?.version ?? 0) + 1 === record2.version)
@@ -23570,8 +23668,37 @@ function createRemoteMemoryStore(options) {
23570
23668
  } while (cursor2 && records.length < max);
23571
23669
  return records.slice(0, max);
23572
23670
  };
23573
- const decrypted = (options2) => persistence.query(options2);
23574
- const metadata3 = (options2) => persistence.queryMetadata(options2);
23671
+ const shareRead = (inflight, options2, read) => {
23672
+ if (options2.signal) return read();
23673
+ const key = JSON.stringify(Object.entries(options2).sort(([a], [b]) => a.localeCompare(b)));
23674
+ const active = inflight.get(key);
23675
+ if (active) return active;
23676
+ const pending2 = read();
23677
+ inflight.set(key, pending2);
23678
+ const clear = () => {
23679
+ if (inflight.get(key) === pending2) inflight.delete(key);
23680
+ };
23681
+ void pending2.then(clear, clear);
23682
+ return pending2;
23683
+ };
23684
+ const decrypted = (options2) => shareRead(reads, options2, () => persistence.query(options2));
23685
+ const metadata3 = (options2) => shareRead(
23686
+ metadataReads,
23687
+ options2,
23688
+ () => persistence.queryMetadata(options2)
23689
+ );
23690
+ const readTopics = () => {
23691
+ if (topicCatalog) return topicCatalog;
23692
+ const pending2 = persistence.topics();
23693
+ topicCatalog = pending2;
23694
+ const clear = () => {
23695
+ if (topicCatalog === pending2) topicCatalog = void 0;
23696
+ };
23697
+ void pending2.then(() => {
23698
+ if (!retainTopics()) clear();
23699
+ }, clear);
23700
+ return pending2;
23701
+ };
23575
23702
  const enumerate = (filters, max = Infinity) => pages(decrypted, filters, max);
23576
23703
  const memoriesByTopics = async (names) => {
23577
23704
  const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
@@ -24025,11 +24152,11 @@ function createRemoteMemoryStore(options) {
24025
24152
  };
24026
24153
  const changesSince = async (cursor2, pending2) => {
24027
24154
  const records = [];
24028
- const failed = /* @__PURE__ */ new Set();
24155
+ const failed = /* @__PURE__ */ new Map();
24029
24156
  const collect = (page) => {
24030
24157
  warnFailed(page.failed);
24031
24158
  records.push(...page.items);
24032
- for (const failure of page.failed) failed.add(failure.memory_id);
24159
+ for (const failure of page.failed) failed.set(failure.memory_id, failure.version);
24033
24160
  };
24034
24161
  let resume = cursor2;
24035
24162
  let next;
@@ -24063,25 +24190,72 @@ function createRemoteMemoryStore(options) {
24063
24190
  let ready = false;
24064
24191
  let cursor;
24065
24192
  let pending = /* @__PURE__ */ new Set();
24193
+ let failedVersions = /* @__PURE__ */ new Map();
24066
24194
  let topicFingerprint;
24067
24195
  let polling = false;
24196
+ let pollRequested = false;
24197
+ let failures = 0;
24198
+ let retryAt = 0;
24199
+ const backoff = (error) => {
24200
+ failures++;
24201
+ retryAt = Date.now() + Math.max(
24202
+ error instanceof RemoteMemoryError ? error.retryAfterMs ?? 0 : 0,
24203
+ Math.min(pollIntervalMs * 2 ** Math.min(failures - 1, 10), 15 * 6e4)
24204
+ );
24205
+ };
24206
+ const schedulePoll = (delay, keepEarlier = false) => {
24207
+ const at = Math.max(Date.now() + delay, retryAt);
24208
+ if (keepEarlier && timer && scheduledAt !== void 0 && scheduledAt <= at) return;
24209
+ if (timer) clearTimeout(timer);
24210
+ timer = void 0;
24211
+ scheduledAt = void 0;
24212
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
24213
+ scheduledAt = at;
24214
+ timer = setTimeout(poll, Math.max(0, at - Date.now()));
24215
+ };
24068
24216
  const poll = () => {
24069
- if (polling) return;
24217
+ if (timer) clearTimeout(timer);
24218
+ timer = void 0;
24219
+ scheduledAt = void 0;
24220
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
24221
+ if (Date.now() < retryAt) {
24222
+ schedulePoll(0);
24223
+ return;
24224
+ }
24225
+ if (polling) {
24226
+ pollRequested = true;
24227
+ return;
24228
+ }
24070
24229
  polling = true;
24230
+ pollRequested = false;
24071
24231
  const current = generation;
24072
24232
  changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
24073
24233
  let topicState;
24234
+ let topicFailed = false;
24074
24235
  if ([...subscribers].some((s) => s.options.topics))
24075
24236
  try {
24076
- topicState = JSON.stringify(
24077
- (await persistence.topics()).map((t) => [t.name, t.version])
24078
- );
24237
+ topicCatalog = void 0;
24238
+ topicState = JSON.stringify((await readTopics()).map((t) => [t.name, t.version]));
24079
24239
  } catch (error) {
24240
+ topicFailed = true;
24241
+ if (current === generation) backoff(error);
24080
24242
  getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
24081
24243
  }
24082
24244
  if (current !== generation) return;
24245
+ if (!topicFailed) {
24246
+ failures = 0;
24247
+ retryAt = 0;
24248
+ }
24249
+ const failedChanged = [...failed].some(
24250
+ ([id2, version]) => failedVersions.get(id2) !== version
24251
+ );
24252
+ if (records.length || failedChanged || topicState !== void 0 && topicState !== topicFingerprint) {
24253
+ reads.clear();
24254
+ metadataReads.clear();
24255
+ topicSnapshot = void 0;
24256
+ }
24083
24257
  const changed = /* @__PURE__ */ new Set();
24084
- if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
24258
+ if (ready && failedChanged || topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
24085
24259
  for (const subscriber of subscribers)
24086
24260
  if (subscriber.options.topics) changed.add(subscriber);
24087
24261
  }
@@ -24098,15 +24272,40 @@ function createRemoteMemoryStore(options) {
24098
24272
  }
24099
24273
  ready = true;
24100
24274
  cursor = resume;
24101
- pending = failed;
24275
+ pending = new Set(failed.keys());
24276
+ failedVersions = failed;
24102
24277
  for (const subscriber of changed)
24103
24278
  if (subscribers.has(subscriber)) safely(subscriber.onChange);
24104
24279
  }).catch((error) => {
24280
+ if (current === generation) backoff(error);
24105
24281
  getLogger().warn("[memory/remote-store] Subscription poll failed:", error);
24106
24282
  }).finally(() => {
24107
24283
  polling = false;
24284
+ schedulePoll(
24285
+ current !== generation ? 0 : pollRequested ? 200 : changesConnected ? Math.max(pollIntervalMs, 5 * 6e4) : pollIntervalMs
24286
+ );
24108
24287
  });
24109
24288
  };
24289
+ const refreshActivity = () => {
24290
+ if (timer) clearTimeout(timer);
24291
+ timer = void 0;
24292
+ if (!activity.isActive()) {
24293
+ stopChanges?.();
24294
+ stopChanges = void 0;
24295
+ changesConnected = false;
24296
+ return;
24297
+ }
24298
+ if (!stopChanges && persistence.supportsRealtimeChanges && options.subscribeChanges)
24299
+ stopChanges = options.subscribeChanges((connected) => {
24300
+ changesConnected = connected;
24301
+ if (polling) {
24302
+ pollRequested = true;
24303
+ return;
24304
+ }
24305
+ schedulePoll(connected ? 200 : 0, true);
24306
+ });
24307
+ poll();
24308
+ };
24110
24309
  const unrepairable = /* @__PURE__ */ new Set();
24111
24310
  let repairCursor;
24112
24311
  const repairEmbeddingsUnlocked = async ({
@@ -24162,40 +24361,78 @@ function createRemoteMemoryStore(options) {
24162
24361
  result.failed += targets.length;
24163
24362
  return finish();
24164
24363
  }
24165
- for (const [i, record2] of targets.entries()) {
24364
+ const writes = targets.flatMap((record2, i) => {
24166
24365
  const vector = vectors[i];
24167
24366
  if (!vector) {
24168
24367
  result.failed++;
24169
- continue;
24368
+ return [];
24170
24369
  }
24171
- await serialized(record2.memory.memory_id, async () => {
24172
- signal?.throwIfAborted();
24173
- try {
24174
- const [saved] = await persistence.putMany(
24175
- [
24176
- {
24177
- memory: {
24178
- ...record2.memory,
24179
- embedding: storedVector(vector),
24180
- embedding_model: model
24181
- },
24182
- expectedVersion: record2
24183
- }
24184
- ],
24185
- signal,
24186
- leaseToken
24187
- );
24188
- notify("embedding", [saved]);
24189
- repaired.add(record2.memory.memory_id);
24190
- result.reembedded++;
24191
- } catch (error) {
24370
+ try {
24371
+ return [
24372
+ {
24373
+ memory: {
24374
+ ...record2.memory,
24375
+ embedding: storedVector(vector),
24376
+ embedding_model: model
24377
+ },
24378
+ expectedVersion: record2
24379
+ }
24380
+ ];
24381
+ } catch {
24382
+ result.failed++;
24383
+ return [];
24384
+ }
24385
+ });
24386
+ for (const batch of chunks2(writes, MAX_BATCH)) {
24387
+ const continueRepair = await serializedAll(
24388
+ batch.map(({ memory }) => memory.memory_id),
24389
+ async () => {
24192
24390
  signal?.throwIfAborted();
24193
- if (isRemoteWorkLeaseExpired(error)) throw error;
24194
- if (error instanceof RemoteMemoryError && error.code === "version_conflict")
24195
- result.skippedConflict++;
24196
- else result.failed++;
24391
+ const commit = async (slice) => {
24392
+ const saved = await persistence.putMany(slice, signal, leaseToken);
24393
+ for (const record2 of saved) repaired.add(record2.memory.memory_id);
24394
+ result.reembedded += saved.length;
24395
+ notify("embedding", saved);
24396
+ };
24397
+ try {
24398
+ await commit(batch);
24399
+ } catch (error) {
24400
+ signal?.throwIfAborted();
24401
+ if (isRemoteWorkLeaseExpired(error)) throw error;
24402
+ if (!(error instanceof RemoteMemoryError && error.code === "version_conflict") && !rejectsMemoryWrite(error)) {
24403
+ result.failed += batch.length;
24404
+ return false;
24405
+ }
24406
+ if (batch.length === 1) {
24407
+ if (rejectsMemoryWrite(error)) {
24408
+ unrepairable.add(batch[0].memory.memory_id);
24409
+ result.failed++;
24410
+ } else result.skippedConflict++;
24411
+ return true;
24412
+ }
24413
+ for (const [index, write] of batch.entries()) {
24414
+ signal?.throwIfAborted();
24415
+ try {
24416
+ await commit([write]);
24417
+ } catch (error2) {
24418
+ signal?.throwIfAborted();
24419
+ if (isRemoteWorkLeaseExpired(error2)) throw error2;
24420
+ if (error2 instanceof RemoteMemoryError && error2.code === "version_conflict")
24421
+ result.skippedConflict++;
24422
+ else if (rejectsMemoryWrite(error2)) {
24423
+ unrepairable.add(write.memory.memory_id);
24424
+ result.failed++;
24425
+ } else {
24426
+ result.failed += batch.length - index;
24427
+ return false;
24428
+ }
24429
+ }
24430
+ }
24431
+ }
24432
+ return true;
24197
24433
  }
24198
- });
24434
+ );
24435
+ if (!continueRepair) break;
24199
24436
  }
24200
24437
  return finish();
24201
24438
  };
@@ -24235,7 +24472,7 @@ function createRemoteMemoryStore(options) {
24235
24472
  },
24236
24473
  importTopics: async (topics) => {
24237
24474
  if (!topics.length) return;
24238
- const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
24475
+ const existing = new Set((await readTopics()).map((topic2) => topic2.name));
24239
24476
  const missing = topics.flatMap((topic2) => {
24240
24477
  const name = normalizeEntityName(topic2.name);
24241
24478
  if (existing.has(name)) return [];
@@ -24244,6 +24481,7 @@ function createRemoteMemoryStore(options) {
24244
24481
  });
24245
24482
  if (!missing.length) return;
24246
24483
  await persistence.importTopics(missing);
24484
+ invalidateReads();
24247
24485
  for (const subscriber of [...subscribers])
24248
24486
  if (subscriber.options.topics) safely(subscriber.onChange);
24249
24487
  },
@@ -24257,40 +24495,51 @@ function createRemoteMemoryStore(options) {
24257
24495
  listProjections: async (listOptions = {}) => (await listRows(metadata3, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
24258
24496
  projection
24259
24497
  ),
24260
- listTopics: async () => {
24261
- const topics = /* @__PURE__ */ new Map();
24262
- for (const { memory } of await pages(metadata3, {
24263
- include_archived: true,
24264
- include_quarantined: true,
24265
- include_superseded: true
24266
- })) {
24267
- for (const topic2 of parseTopics(memory.topics) ?? []) {
24268
- const name = normalizeEntityName(topic2.name);
24269
- if (!name) continue;
24270
- const entry = topics.get(name) ?? {
24271
- name,
24272
- kind: null,
24273
- memoryCount: 0,
24274
- members: /* @__PURE__ */ new Set()
24275
- };
24276
- entry.kind ?? (entry.kind = topic2.kind ?? null);
24277
- entry.members.add(memory.memory_id);
24278
- topics.set(name, entry);
24279
- }
24280
- }
24281
- for (const topic2 of await persistence.topics()) {
24282
- if (topic2.is_deleted) continue;
24283
- const current = topics.get(topic2.name);
24284
- if (current) current.kind ?? (current.kind = topic2.kind ?? null);
24285
- else
24286
- topics.set(topic2.name, {
24287
- name: topic2.name,
24288
- kind: topic2.kind ?? null,
24289
- memoryCount: 0,
24290
- members: /* @__PURE__ */ new Set()
24291
- });
24292
- }
24293
- return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
24498
+ listTopics: () => {
24499
+ if (topicSnapshot) return topicSnapshot.then((topics) => structuredClone(topics));
24500
+ const snapshot = (async () => {
24501
+ const topics = /* @__PURE__ */ new Map();
24502
+ for (const { memory } of await pages(metadata3, {
24503
+ include_archived: true,
24504
+ include_quarantined: true,
24505
+ include_superseded: true
24506
+ })) {
24507
+ for (const topic2 of parseTopics(memory.topics) ?? []) {
24508
+ const name = normalizeEntityName(topic2.name);
24509
+ if (!name) continue;
24510
+ const entry = topics.get(name) ?? {
24511
+ name,
24512
+ kind: null,
24513
+ memoryCount: 0,
24514
+ members: /* @__PURE__ */ new Set()
24515
+ };
24516
+ entry.kind ?? (entry.kind = topic2.kind ?? null);
24517
+ entry.members.add(memory.memory_id);
24518
+ topics.set(name, entry);
24519
+ }
24520
+ }
24521
+ for (const topic2 of await readTopics()) {
24522
+ if (topic2.is_deleted) continue;
24523
+ const current = topics.get(topic2.name);
24524
+ if (current) current.kind ?? (current.kind = topic2.kind ?? null);
24525
+ else
24526
+ topics.set(topic2.name, {
24527
+ name: topic2.name,
24528
+ kind: topic2.kind ?? null,
24529
+ memoryCount: 0,
24530
+ members: /* @__PURE__ */ new Set()
24531
+ });
24532
+ }
24533
+ return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
24534
+ })();
24535
+ topicSnapshot = snapshot;
24536
+ const clear = () => {
24537
+ if (topicSnapshot === snapshot) topicSnapshot = void 0;
24538
+ };
24539
+ void snapshot.then(() => {
24540
+ if (!retainTopics()) clear();
24541
+ }, clear);
24542
+ return snapshot.then((topics) => structuredClone(topics));
24294
24543
  },
24295
24544
  get: async (id2) => asStored(await live(id2)),
24296
24545
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
@@ -24471,20 +24720,23 @@ function createRemoteMemoryStore(options) {
24471
24720
  subscribe: (onChange, subscribeOptions = {}) => {
24472
24721
  const subscriber = { onChange, options: subscribeOptions };
24473
24722
  subscribers.add(subscriber);
24474
- if (pollIntervalMs > 0 && !timer) {
24475
- timer = setInterval(poll, pollIntervalMs);
24476
- poll();
24723
+ if (pollIntervalMs > 0 && subscribers.size === 1) {
24724
+ stopActivity = activity.subscribe(refreshActivity);
24725
+ refreshActivity();
24477
24726
  }
24478
24727
  return () => {
24479
24728
  subscribers.delete(subscriber);
24480
- if (subscribers.size || !timer) return;
24481
- clearInterval(timer);
24729
+ if (!retainTopics()) invalidateReads();
24730
+ if (subscribers.size) return;
24731
+ if (timer) clearTimeout(timer);
24482
24732
  timer = void 0;
24733
+ stopActivity?.();
24734
+ stopActivity = void 0;
24735
+ stopChanges?.();
24736
+ stopChanges = void 0;
24737
+ changesConnected = false;
24483
24738
  generation++;
24484
- seen.clear();
24485
- ready = false;
24486
- cursor = void 0;
24487
- pending = /* @__PURE__ */ new Set();
24739
+ invalidateReads();
24488
24740
  };
24489
24741
  }
24490
24742
  };