@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.
@@ -11312,11 +11312,12 @@ function isRemoteWorkLeaseExpired(error) {
11312
11312
 
11313
11313
  // src/lib/memory/store/remotePersistence.ts
11314
11314
  var RemoteMemoryError = class extends Error {
11315
- constructor(message, status, code, retryAfterMs) {
11315
+ constructor(message, status, code, retryAfterMs, rateLimitBucket) {
11316
11316
  super(message);
11317
11317
  this.status = status;
11318
11318
  this.code = code;
11319
11319
  this.retryAfterMs = retryAfterMs;
11320
+ this.rateLimitBucket = rateLimitBucket;
11320
11321
  this.name = "RemoteMemoryError";
11321
11322
  }
11322
11323
  };
@@ -11411,6 +11412,14 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
11411
11412
  throw new Error("timeoutMs must be a positive number");
11412
11413
  const root = base.href.replace(/\/$/, "") + apiPath;
11413
11414
  const fetcher = options.fetch ?? globalThis.fetch;
11415
+ const cooldowns = /* @__PURE__ */ new Map();
11416
+ const memoryBudget = (path, method) => {
11417
+ if (method === "PUT")
11418
+ return path === "/topics" ? "private-memory-import" : "private-memory-write";
11419
+ if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
11420
+ return "private-memory-import";
11421
+ return "private-memory-read";
11422
+ };
11414
11423
  const request = async (path, init = {}, signal) => {
11415
11424
  const controller = new AbortController();
11416
11425
  let timedOut = false;
@@ -11434,6 +11443,25 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
11434
11443
  const token = await Promise.race([options.getToken(), aborted2]);
11435
11444
  if (controller.signal.aborted) await aborted2;
11436
11445
  if (!token) throw new RemoteMemoryError("Authentication required", 401);
11446
+ const pathname = path.split("?")[0];
11447
+ const route = `${init.method ?? "GET"} ${pathname}`;
11448
+ const budgets = ["*", route];
11449
+ if (apiPath === "/api/private-memories")
11450
+ budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
11451
+ for (const bucket of budgets) {
11452
+ const cooldown = cooldowns.get(bucket);
11453
+ if (!cooldown) continue;
11454
+ const remaining = cooldown.until - Date.now();
11455
+ if (remaining > 0)
11456
+ throw new RemoteMemoryError(
11457
+ cooldown.error.message,
11458
+ 429,
11459
+ cooldown.error.code,
11460
+ remaining,
11461
+ cooldown.error.rateLimitBucket
11462
+ );
11463
+ cooldowns.delete(bucket);
11464
+ }
11437
11465
  const response = await fetcher(root + path, {
11438
11466
  ...init,
11439
11467
  headers: {
@@ -11446,12 +11474,18 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
11446
11474
  if (!response.ok) {
11447
11475
  const body = await response.json().catch(() => null);
11448
11476
  const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
11449
- throw new RemoteMemoryError(
11477
+ const error = new RemoteMemoryError(
11450
11478
  record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
11451
11479
  response.status,
11452
11480
  record(body) && typeof body.code === "string" ? body.code : void 0,
11453
- retryAfter ?? void 0
11481
+ retryAfter ?? void 0,
11482
+ record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
11454
11483
  );
11484
+ if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
11485
+ const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
11486
+ cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
11487
+ }
11488
+ throw error;
11455
11489
  }
11456
11490
  return await response.json();
11457
11491
  } catch (error) {
@@ -11691,6 +11725,7 @@ async function createRemoteMemoryPersistence(options) {
11691
11725
  return {
11692
11726
  ...createRemoteTopicPersistence(request),
11693
11727
  ...createRemoteWorkPersistence(request),
11728
+ supportsRealtimeChanges: account.realtime_changes === true,
11694
11729
  counts: async () => {
11695
11730
  const result = await request("/counts", {});
11696
11731
  if (!record(result) || ["live", "archived", "quarantined"].some(
@@ -14782,9 +14817,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
14782
14817
  }
14783
14818
  function baseVaultSql(ctx, options) {
14784
14819
  const clauses = [
14820
+ `"_status" is not 'deleted'`,
14785
14821
  '"is_deleted" = 0',
14786
14822
  ...options?.includeArchived ? [] : ['"archived_at" is null'],
14787
- `"trust_tier" is not 'quarantined'`,
14823
+ ...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
14788
14824
  '"superseded_by" is null'
14789
14825
  ];
14790
14826
  const args = [];
@@ -15270,8 +15306,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
15270
15306
  if (!entityCtx) {
15271
15307
  throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
15272
15308
  }
15273
- const conditions = [...baseVaultConditions(ctx), import_watermelondb21.Q.sortBy("created_at", import_watermelondb21.Q.desc)];
15274
- const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
15309
+ let rows;
15310
+ try {
15311
+ const base = baseVaultSql(ctx);
15312
+ 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`;
15313
+ rows = await ctx.vaultMemoryCollection.query(import_watermelondb21.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
15314
+ } catch (err) {
15315
+ getLogger().debug(
15316
+ "memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
15317
+ );
15318
+ rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb21.Q.sortBy("created_at", import_watermelondb21.Q.desc)).unsafeFetchRaw();
15319
+ }
15275
15320
  const linkedNames = await linkedEntityNamesByMemory(
15276
15321
  entityCtx,
15277
15322
  rows.map((r) => r.id)
@@ -15330,10 +15375,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
15330
15375
  }
15331
15376
  const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
15332
15377
  const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
15333
- const pending = await mapInBatches(
15334
- limitedPendingRaw,
15335
- (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
15336
- );
15378
+ const pending = [];
15379
+ for (let i = 0; i < limitedPendingRaw.length; i += 500) {
15380
+ const chunk = limitedPendingRaw.slice(i, i + 500);
15381
+ const memories = await getVaultMemoriesByIdsOp(
15382
+ ctx,
15383
+ chunk.map((raw) => raw.id)
15384
+ );
15385
+ const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
15386
+ for (const raw of chunk) {
15387
+ const memory = byId.get(raw.id);
15388
+ if (memory) pending.push(memory);
15389
+ }
15390
+ }
15337
15391
  const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
15338
15392
  return {
15339
15393
  pending,
@@ -15480,10 +15534,20 @@ function assertVaultScopeForSweep(ctx) {
15480
15534
  }
15481
15535
  async function getDecayCandidatesRawOp(ctx) {
15482
15536
  assertVaultScopeForSweep(ctx);
15483
- const results = await ctx.vaultMemoryCollection.query(
15484
- ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
15485
- import_watermelondb21.Q.where("kind", null)
15486
- ).unsafeFetchRaw();
15537
+ let results;
15538
+ try {
15539
+ const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
15540
+ 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`;
15541
+ results = await ctx.vaultMemoryCollection.query(import_watermelondb21.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
15542
+ } catch (err) {
15543
+ getLogger().debug(
15544
+ "memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
15545
+ );
15546
+ results = await ctx.vaultMemoryCollection.query(
15547
+ ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
15548
+ import_watermelondb21.Q.where("kind", null)
15549
+ ).unsafeFetchRaw();
15550
+ }
15487
15551
  return results.map((raw) => ({
15488
15552
  uniqueId: raw.id,
15489
15553
  factType: raw.fact_type ?? null,
@@ -23021,6 +23085,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
23021
23085
  function rejectsInput(error) {
23022
23086
  return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
23023
23087
  }
23088
+ function rejectsMemoryWrite(error) {
23089
+ return error instanceof RemoteMemoryError && (error.status === 400 || error.status === 422) && (error.code === void 0 || error.code === "unknown_field");
23090
+ }
23024
23091
  function warnFailed(failed) {
23025
23092
  if (failed.length)
23026
23093
  getLogger().warn(
@@ -23108,16 +23175,47 @@ function createRemoteMemoryStore(options) {
23108
23175
  const { persistence, embeddingOptions } = options;
23109
23176
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
23110
23177
  const pollIntervalMs = options.pollIntervalMs ?? 6e4;
23178
+ const activity = options.activity ?? {
23179
+ isActive: () => (typeof document === "undefined" || document.visibilityState !== "hidden") && (typeof navigator === "undefined" || navigator.onLine !== false),
23180
+ subscribe: (onChange) => {
23181
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
23182
+ };
23183
+ document.addEventListener("visibilitychange", onChange);
23184
+ window.addEventListener("online", onChange);
23185
+ window.addEventListener("offline", onChange);
23186
+ return () => {
23187
+ document.removeEventListener("visibilitychange", onChange);
23188
+ window.removeEventListener("online", onChange);
23189
+ window.removeEventListener("offline", onChange);
23190
+ };
23191
+ }
23192
+ };
23111
23193
  const pipeline = createRemoteMemoryPipeline({
23112
23194
  ...options,
23113
23195
  graphRanking: options.graphRanking ?? ((query, _traverse, recallOptions) => topicGraphRanking(query, recallOptions)),
23114
23196
  temporalRanking: options.temporalRanking ?? ((query, now) => eventTimeRanking(query, now))
23115
23197
  });
23116
23198
  const subscribers = /* @__PURE__ */ new Set();
23199
+ const retainTopics = () => pollIntervalMs > 0 && [...subscribers].some((subscriber) => subscriber.options.topics);
23117
23200
  const seen = /* @__PURE__ */ new Map();
23201
+ const reads = /* @__PURE__ */ new Map();
23202
+ const metadataReads = /* @__PURE__ */ new Map();
23203
+ let topicSnapshot;
23204
+ let topicCatalog;
23205
+ const invalidateReads = () => {
23206
+ reads.clear();
23207
+ metadataReads.clear();
23208
+ topicSnapshot = void 0;
23209
+ topicCatalog = void 0;
23210
+ };
23118
23211
  let timer;
23212
+ let scheduledAt;
23213
+ let stopActivity;
23214
+ let stopChanges;
23215
+ let changesConnected = false;
23119
23216
  const notify = (change, records = []) => {
23120
- if (timer)
23217
+ invalidateReads();
23218
+ if (subscribers.size)
23121
23219
  for (const record2 of records) {
23122
23220
  const previous = seen.get(record2.memory.memory_id);
23123
23221
  if ((previous?.version ?? 0) + 1 === record2.version)
@@ -23141,8 +23239,37 @@ function createRemoteMemoryStore(options) {
23141
23239
  } while (cursor2 && records.length < max);
23142
23240
  return records.slice(0, max);
23143
23241
  };
23144
- const decrypted = (options2) => persistence.query(options2);
23145
- const metadata3 = (options2) => persistence.queryMetadata(options2);
23242
+ const shareRead = (inflight, options2, read) => {
23243
+ if (options2.signal) return read();
23244
+ const key = JSON.stringify(Object.entries(options2).sort(([a], [b]) => a.localeCompare(b)));
23245
+ const active = inflight.get(key);
23246
+ if (active) return active;
23247
+ const pending2 = read();
23248
+ inflight.set(key, pending2);
23249
+ const clear = () => {
23250
+ if (inflight.get(key) === pending2) inflight.delete(key);
23251
+ };
23252
+ void pending2.then(clear, clear);
23253
+ return pending2;
23254
+ };
23255
+ const decrypted = (options2) => shareRead(reads, options2, () => persistence.query(options2));
23256
+ const metadata3 = (options2) => shareRead(
23257
+ metadataReads,
23258
+ options2,
23259
+ () => persistence.queryMetadata(options2)
23260
+ );
23261
+ const readTopics = () => {
23262
+ if (topicCatalog) return topicCatalog;
23263
+ const pending2 = persistence.topics();
23264
+ topicCatalog = pending2;
23265
+ const clear = () => {
23266
+ if (topicCatalog === pending2) topicCatalog = void 0;
23267
+ };
23268
+ void pending2.then(() => {
23269
+ if (!retainTopics()) clear();
23270
+ }, clear);
23271
+ return pending2;
23272
+ };
23146
23273
  const enumerate = (filters, max = Infinity) => pages(decrypted, filters, max);
23147
23274
  const memoriesByTopics = async (names) => {
23148
23275
  const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
@@ -23596,11 +23723,11 @@ function createRemoteMemoryStore(options) {
23596
23723
  };
23597
23724
  const changesSince = async (cursor2, pending2) => {
23598
23725
  const records = [];
23599
- const failed = /* @__PURE__ */ new Set();
23726
+ const failed = /* @__PURE__ */ new Map();
23600
23727
  const collect = (page) => {
23601
23728
  warnFailed(page.failed);
23602
23729
  records.push(...page.items);
23603
- for (const failure of page.failed) failed.add(failure.memory_id);
23730
+ for (const failure of page.failed) failed.set(failure.memory_id, failure.version);
23604
23731
  };
23605
23732
  let resume = cursor2;
23606
23733
  let next;
@@ -23634,25 +23761,72 @@ function createRemoteMemoryStore(options) {
23634
23761
  let ready = false;
23635
23762
  let cursor;
23636
23763
  let pending = /* @__PURE__ */ new Set();
23764
+ let failedVersions = /* @__PURE__ */ new Map();
23637
23765
  let topicFingerprint;
23638
23766
  let polling = false;
23767
+ let pollRequested = false;
23768
+ let failures = 0;
23769
+ let retryAt = 0;
23770
+ const backoff = (error) => {
23771
+ failures++;
23772
+ retryAt = Date.now() + Math.max(
23773
+ error instanceof RemoteMemoryError ? error.retryAfterMs ?? 0 : 0,
23774
+ Math.min(pollIntervalMs * 2 ** Math.min(failures - 1, 10), 15 * 6e4)
23775
+ );
23776
+ };
23777
+ const schedulePoll = (delay, keepEarlier = false) => {
23778
+ const at = Math.max(Date.now() + delay, retryAt);
23779
+ if (keepEarlier && timer && scheduledAt !== void 0 && scheduledAt <= at) return;
23780
+ if (timer) clearTimeout(timer);
23781
+ timer = void 0;
23782
+ scheduledAt = void 0;
23783
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
23784
+ scheduledAt = at;
23785
+ timer = setTimeout(poll, Math.max(0, at - Date.now()));
23786
+ };
23639
23787
  const poll = () => {
23640
- if (polling) return;
23788
+ if (timer) clearTimeout(timer);
23789
+ timer = void 0;
23790
+ scheduledAt = void 0;
23791
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
23792
+ if (Date.now() < retryAt) {
23793
+ schedulePoll(0);
23794
+ return;
23795
+ }
23796
+ if (polling) {
23797
+ pollRequested = true;
23798
+ return;
23799
+ }
23641
23800
  polling = true;
23801
+ pollRequested = false;
23642
23802
  const current = generation;
23643
23803
  changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
23644
23804
  let topicState;
23805
+ let topicFailed = false;
23645
23806
  if ([...subscribers].some((s) => s.options.topics))
23646
23807
  try {
23647
- topicState = JSON.stringify(
23648
- (await persistence.topics()).map((t) => [t.name, t.version])
23649
- );
23808
+ topicCatalog = void 0;
23809
+ topicState = JSON.stringify((await readTopics()).map((t) => [t.name, t.version]));
23650
23810
  } catch (error) {
23811
+ topicFailed = true;
23812
+ if (current === generation) backoff(error);
23651
23813
  getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
23652
23814
  }
23653
23815
  if (current !== generation) return;
23816
+ if (!topicFailed) {
23817
+ failures = 0;
23818
+ retryAt = 0;
23819
+ }
23820
+ const failedChanged = [...failed].some(
23821
+ ([id2, version]) => failedVersions.get(id2) !== version
23822
+ );
23823
+ if (records.length || failedChanged || topicState !== void 0 && topicState !== topicFingerprint) {
23824
+ reads.clear();
23825
+ metadataReads.clear();
23826
+ topicSnapshot = void 0;
23827
+ }
23654
23828
  const changed = /* @__PURE__ */ new Set();
23655
- if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
23829
+ if (ready && failedChanged || topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
23656
23830
  for (const subscriber of subscribers)
23657
23831
  if (subscriber.options.topics) changed.add(subscriber);
23658
23832
  }
@@ -23669,15 +23843,40 @@ function createRemoteMemoryStore(options) {
23669
23843
  }
23670
23844
  ready = true;
23671
23845
  cursor = resume;
23672
- pending = failed;
23846
+ pending = new Set(failed.keys());
23847
+ failedVersions = failed;
23673
23848
  for (const subscriber of changed)
23674
23849
  if (subscribers.has(subscriber)) safely(subscriber.onChange);
23675
23850
  }).catch((error) => {
23851
+ if (current === generation) backoff(error);
23676
23852
  getLogger().warn("[memory/remote-store] Subscription poll failed:", error);
23677
23853
  }).finally(() => {
23678
23854
  polling = false;
23855
+ schedulePoll(
23856
+ current !== generation ? 0 : pollRequested ? 200 : changesConnected ? Math.max(pollIntervalMs, 5 * 6e4) : pollIntervalMs
23857
+ );
23679
23858
  });
23680
23859
  };
23860
+ const refreshActivity = () => {
23861
+ if (timer) clearTimeout(timer);
23862
+ timer = void 0;
23863
+ if (!activity.isActive()) {
23864
+ stopChanges?.();
23865
+ stopChanges = void 0;
23866
+ changesConnected = false;
23867
+ return;
23868
+ }
23869
+ if (!stopChanges && persistence.supportsRealtimeChanges && options.subscribeChanges)
23870
+ stopChanges = options.subscribeChanges((connected) => {
23871
+ changesConnected = connected;
23872
+ if (polling) {
23873
+ pollRequested = true;
23874
+ return;
23875
+ }
23876
+ schedulePoll(connected ? 200 : 0, true);
23877
+ });
23878
+ poll();
23879
+ };
23681
23880
  const unrepairable = /* @__PURE__ */ new Set();
23682
23881
  let repairCursor;
23683
23882
  const repairEmbeddingsUnlocked = async ({
@@ -23733,40 +23932,78 @@ function createRemoteMemoryStore(options) {
23733
23932
  result.failed += targets.length;
23734
23933
  return finish();
23735
23934
  }
23736
- for (const [i, record2] of targets.entries()) {
23935
+ const writes = targets.flatMap((record2, i) => {
23737
23936
  const vector = vectors[i];
23738
23937
  if (!vector) {
23739
23938
  result.failed++;
23740
- continue;
23939
+ return [];
23741
23940
  }
23742
- await serialized(record2.memory.memory_id, async () => {
23743
- signal?.throwIfAborted();
23744
- try {
23745
- const [saved] = await persistence.putMany(
23746
- [
23747
- {
23748
- memory: {
23749
- ...record2.memory,
23750
- embedding: storedVector(vector),
23751
- embedding_model: model
23752
- },
23753
- expectedVersion: record2
23754
- }
23755
- ],
23756
- signal,
23757
- leaseToken
23758
- );
23759
- notify("embedding", [saved]);
23760
- repaired.add(record2.memory.memory_id);
23761
- result.reembedded++;
23762
- } catch (error) {
23941
+ try {
23942
+ return [
23943
+ {
23944
+ memory: {
23945
+ ...record2.memory,
23946
+ embedding: storedVector(vector),
23947
+ embedding_model: model
23948
+ },
23949
+ expectedVersion: record2
23950
+ }
23951
+ ];
23952
+ } catch {
23953
+ result.failed++;
23954
+ return [];
23955
+ }
23956
+ });
23957
+ for (const batch of chunks2(writes, MAX_BATCH)) {
23958
+ const continueRepair = await serializedAll(
23959
+ batch.map(({ memory }) => memory.memory_id),
23960
+ async () => {
23763
23961
  signal?.throwIfAborted();
23764
- if (isRemoteWorkLeaseExpired(error)) throw error;
23765
- if (error instanceof RemoteMemoryError && error.code === "version_conflict")
23766
- result.skippedConflict++;
23767
- else result.failed++;
23962
+ const commit = async (slice) => {
23963
+ const saved = await persistence.putMany(slice, signal, leaseToken);
23964
+ for (const record2 of saved) repaired.add(record2.memory.memory_id);
23965
+ result.reembedded += saved.length;
23966
+ notify("embedding", saved);
23967
+ };
23968
+ try {
23969
+ await commit(batch);
23970
+ } catch (error) {
23971
+ signal?.throwIfAborted();
23972
+ if (isRemoteWorkLeaseExpired(error)) throw error;
23973
+ if (!(error instanceof RemoteMemoryError && error.code === "version_conflict") && !rejectsMemoryWrite(error)) {
23974
+ result.failed += batch.length;
23975
+ return false;
23976
+ }
23977
+ if (batch.length === 1) {
23978
+ if (rejectsMemoryWrite(error)) {
23979
+ unrepairable.add(batch[0].memory.memory_id);
23980
+ result.failed++;
23981
+ } else result.skippedConflict++;
23982
+ return true;
23983
+ }
23984
+ for (const [index, write] of batch.entries()) {
23985
+ signal?.throwIfAborted();
23986
+ try {
23987
+ await commit([write]);
23988
+ } catch (error2) {
23989
+ signal?.throwIfAborted();
23990
+ if (isRemoteWorkLeaseExpired(error2)) throw error2;
23991
+ if (error2 instanceof RemoteMemoryError && error2.code === "version_conflict")
23992
+ result.skippedConflict++;
23993
+ else if (rejectsMemoryWrite(error2)) {
23994
+ unrepairable.add(write.memory.memory_id);
23995
+ result.failed++;
23996
+ } else {
23997
+ result.failed += batch.length - index;
23998
+ return false;
23999
+ }
24000
+ }
24001
+ }
24002
+ }
24003
+ return true;
23768
24004
  }
23769
- });
24005
+ );
24006
+ if (!continueRepair) break;
23770
24007
  }
23771
24008
  return finish();
23772
24009
  };
@@ -23806,7 +24043,7 @@ function createRemoteMemoryStore(options) {
23806
24043
  },
23807
24044
  importTopics: async (topics) => {
23808
24045
  if (!topics.length) return;
23809
- const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
24046
+ const existing = new Set((await readTopics()).map((topic2) => topic2.name));
23810
24047
  const missing = topics.flatMap((topic2) => {
23811
24048
  const name = normalizeEntityName(topic2.name);
23812
24049
  if (existing.has(name)) return [];
@@ -23815,6 +24052,7 @@ function createRemoteMemoryStore(options) {
23815
24052
  });
23816
24053
  if (!missing.length) return;
23817
24054
  await persistence.importTopics(missing);
24055
+ invalidateReads();
23818
24056
  for (const subscriber of [...subscribers])
23819
24057
  if (subscriber.options.topics) safely(subscriber.onChange);
23820
24058
  },
@@ -23828,40 +24066,51 @@ function createRemoteMemoryStore(options) {
23828
24066
  listProjections: async (listOptions = {}) => (await listRows(metadata3, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
23829
24067
  projection
23830
24068
  ),
23831
- listTopics: async () => {
23832
- const topics = /* @__PURE__ */ new Map();
23833
- for (const { memory } of await pages(metadata3, {
23834
- include_archived: true,
23835
- include_quarantined: true,
23836
- include_superseded: true
23837
- })) {
23838
- for (const topic2 of parseTopics(memory.topics) ?? []) {
23839
- const name = normalizeEntityName(topic2.name);
23840
- if (!name) continue;
23841
- const entry = topics.get(name) ?? {
23842
- name,
23843
- kind: null,
23844
- memoryCount: 0,
23845
- members: /* @__PURE__ */ new Set()
23846
- };
23847
- entry.kind ?? (entry.kind = topic2.kind ?? null);
23848
- entry.members.add(memory.memory_id);
23849
- topics.set(name, entry);
23850
- }
23851
- }
23852
- for (const topic2 of await persistence.topics()) {
23853
- if (topic2.is_deleted) continue;
23854
- const current = topics.get(topic2.name);
23855
- if (current) current.kind ?? (current.kind = topic2.kind ?? null);
23856
- else
23857
- topics.set(topic2.name, {
23858
- name: topic2.name,
23859
- kind: topic2.kind ?? null,
23860
- memoryCount: 0,
23861
- members: /* @__PURE__ */ new Set()
23862
- });
23863
- }
23864
- return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
24069
+ listTopics: () => {
24070
+ if (topicSnapshot) return topicSnapshot.then((topics) => structuredClone(topics));
24071
+ const snapshot = (async () => {
24072
+ const topics = /* @__PURE__ */ new Map();
24073
+ for (const { memory } of await pages(metadata3, {
24074
+ include_archived: true,
24075
+ include_quarantined: true,
24076
+ include_superseded: true
24077
+ })) {
24078
+ for (const topic2 of parseTopics(memory.topics) ?? []) {
24079
+ const name = normalizeEntityName(topic2.name);
24080
+ if (!name) continue;
24081
+ const entry = topics.get(name) ?? {
24082
+ name,
24083
+ kind: null,
24084
+ memoryCount: 0,
24085
+ members: /* @__PURE__ */ new Set()
24086
+ };
24087
+ entry.kind ?? (entry.kind = topic2.kind ?? null);
24088
+ entry.members.add(memory.memory_id);
24089
+ topics.set(name, entry);
24090
+ }
24091
+ }
24092
+ for (const topic2 of await readTopics()) {
24093
+ if (topic2.is_deleted) continue;
24094
+ const current = topics.get(topic2.name);
24095
+ if (current) current.kind ?? (current.kind = topic2.kind ?? null);
24096
+ else
24097
+ topics.set(topic2.name, {
24098
+ name: topic2.name,
24099
+ kind: topic2.kind ?? null,
24100
+ memoryCount: 0,
24101
+ members: /* @__PURE__ */ new Set()
24102
+ });
24103
+ }
24104
+ return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
24105
+ })();
24106
+ topicSnapshot = snapshot;
24107
+ const clear = () => {
24108
+ if (topicSnapshot === snapshot) topicSnapshot = void 0;
24109
+ };
24110
+ void snapshot.then(() => {
24111
+ if (!retainTopics()) clear();
24112
+ }, clear);
24113
+ return snapshot.then((topics) => structuredClone(topics));
23865
24114
  },
23866
24115
  get: async (id2) => asStored(await live(id2)),
23867
24116
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
@@ -24042,20 +24291,23 @@ function createRemoteMemoryStore(options) {
24042
24291
  subscribe: (onChange, subscribeOptions = {}) => {
24043
24292
  const subscriber = { onChange, options: subscribeOptions };
24044
24293
  subscribers.add(subscriber);
24045
- if (pollIntervalMs > 0 && !timer) {
24046
- timer = setInterval(poll, pollIntervalMs);
24047
- poll();
24294
+ if (pollIntervalMs > 0 && subscribers.size === 1) {
24295
+ stopActivity = activity.subscribe(refreshActivity);
24296
+ refreshActivity();
24048
24297
  }
24049
24298
  return () => {
24050
24299
  subscribers.delete(subscriber);
24051
- if (subscribers.size || !timer) return;
24052
- clearInterval(timer);
24300
+ if (!retainTopics()) invalidateReads();
24301
+ if (subscribers.size) return;
24302
+ if (timer) clearTimeout(timer);
24053
24303
  timer = void 0;
24304
+ stopActivity?.();
24305
+ stopActivity = void 0;
24306
+ stopChanges?.();
24307
+ stopChanges = void 0;
24308
+ changesConnected = false;
24054
24309
  generation++;
24055
- seen.clear();
24056
- ready = false;
24057
- cursor = void 0;
24058
- pending = /* @__PURE__ */ new Set();
24310
+ invalidateReads();
24059
24311
  };
24060
24312
  }
24061
24313
  };