@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.
@@ -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
- throw new RemoteMemoryError(
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(
@@ -13156,9 +13191,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
13156
13191
  }
13157
13192
  function baseVaultSql(ctx, options) {
13158
13193
  const clauses = [
13194
+ `"_status" is not 'deleted'`,
13159
13195
  '"is_deleted" = 0',
13160
13196
  ...options?.includeArchived ? [] : ['"archived_at" is null'],
13161
- `"trust_tier" is not 'quarantined'`,
13197
+ ...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
13162
13198
  '"superseded_by" is null'
13163
13199
  ];
13164
13200
  const args = [];
@@ -13644,8 +13680,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
13644
13680
  if (!entityCtx) {
13645
13681
  throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
13646
13682
  }
13647
- const conditions = [...baseVaultConditions(ctx), Q6.sortBy("created_at", Q6.desc)];
13648
- const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
13683
+ let rows;
13684
+ try {
13685
+ const base = baseVaultSql(ctx);
13686
+ 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`;
13687
+ rows = await ctx.vaultMemoryCollection.query(Q6.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
13688
+ } catch (err) {
13689
+ getLogger().debug(
13690
+ "memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
13691
+ );
13692
+ rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), Q6.sortBy("created_at", Q6.desc)).unsafeFetchRaw();
13693
+ }
13649
13694
  const linkedNames = await linkedEntityNamesByMemory(
13650
13695
  entityCtx,
13651
13696
  rows.map((r) => r.id)
@@ -13704,10 +13749,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
13704
13749
  }
13705
13750
  const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
13706
13751
  const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
13707
- const pending = await mapInBatches(
13708
- limitedPendingRaw,
13709
- (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
13710
- );
13752
+ const pending = [];
13753
+ for (let i = 0; i < limitedPendingRaw.length; i += 500) {
13754
+ const chunk = limitedPendingRaw.slice(i, i + 500);
13755
+ const memories = await getVaultMemoriesByIdsOp(
13756
+ ctx,
13757
+ chunk.map((raw) => raw.id)
13758
+ );
13759
+ const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
13760
+ for (const raw of chunk) {
13761
+ const memory = byId.get(raw.id);
13762
+ if (memory) pending.push(memory);
13763
+ }
13764
+ }
13711
13765
  const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
13712
13766
  return {
13713
13767
  pending,
@@ -13854,10 +13908,20 @@ function assertVaultScopeForSweep(ctx) {
13854
13908
  }
13855
13909
  async function getDecayCandidatesRawOp(ctx) {
13856
13910
  assertVaultScopeForSweep(ctx);
13857
- const results = await ctx.vaultMemoryCollection.query(
13858
- ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
13859
- Q6.where("kind", null)
13860
- ).unsafeFetchRaw();
13911
+ let results;
13912
+ try {
13913
+ const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
13914
+ 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`;
13915
+ results = await ctx.vaultMemoryCollection.query(Q6.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
13916
+ } catch (err) {
13917
+ getLogger().debug(
13918
+ "memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
13919
+ );
13920
+ results = await ctx.vaultMemoryCollection.query(
13921
+ ...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
13922
+ Q6.where("kind", null)
13923
+ ).unsafeFetchRaw();
13924
+ }
13861
13925
  return results.map((raw) => ({
13862
13926
  uniqueId: raw.id,
13863
13927
  factType: raw.fact_type ?? null,
@@ -21345,6 +21409,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
21345
21409
  function rejectsInput(error) {
21346
21410
  return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
21347
21411
  }
21412
+ function rejectsMemoryWrite(error) {
21413
+ return error instanceof RemoteMemoryError && (error.status === 400 || error.status === 422) && (error.code === void 0 || error.code === "unknown_field");
21414
+ }
21348
21415
  function warnFailed(failed) {
21349
21416
  if (failed.length)
21350
21417
  getLogger().warn(
@@ -21432,16 +21499,47 @@ function createRemoteMemoryStore(options) {
21432
21499
  const { persistence, embeddingOptions } = options;
21433
21500
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
21434
21501
  const pollIntervalMs = options.pollIntervalMs ?? 6e4;
21502
+ const activity = options.activity ?? {
21503
+ isActive: () => (typeof document === "undefined" || document.visibilityState !== "hidden") && (typeof navigator === "undefined" || navigator.onLine !== false),
21504
+ subscribe: (onChange) => {
21505
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
21506
+ };
21507
+ document.addEventListener("visibilitychange", onChange);
21508
+ window.addEventListener("online", onChange);
21509
+ window.addEventListener("offline", onChange);
21510
+ return () => {
21511
+ document.removeEventListener("visibilitychange", onChange);
21512
+ window.removeEventListener("online", onChange);
21513
+ window.removeEventListener("offline", onChange);
21514
+ };
21515
+ }
21516
+ };
21435
21517
  const pipeline = createRemoteMemoryPipeline({
21436
21518
  ...options,
21437
21519
  graphRanking: options.graphRanking ?? ((query, _traverse, recallOptions) => topicGraphRanking(query, recallOptions)),
21438
21520
  temporalRanking: options.temporalRanking ?? ((query, now) => eventTimeRanking(query, now))
21439
21521
  });
21440
21522
  const subscribers = /* @__PURE__ */ new Set();
21523
+ const retainTopics = () => pollIntervalMs > 0 && [...subscribers].some((subscriber) => subscriber.options.topics);
21441
21524
  const seen = /* @__PURE__ */ new Map();
21525
+ const reads = /* @__PURE__ */ new Map();
21526
+ const metadataReads = /* @__PURE__ */ new Map();
21527
+ let topicSnapshot;
21528
+ let topicCatalog;
21529
+ const invalidateReads = () => {
21530
+ reads.clear();
21531
+ metadataReads.clear();
21532
+ topicSnapshot = void 0;
21533
+ topicCatalog = void 0;
21534
+ };
21442
21535
  let timer;
21536
+ let scheduledAt;
21537
+ let stopActivity;
21538
+ let stopChanges;
21539
+ let changesConnected = false;
21443
21540
  const notify = (change, records = []) => {
21444
- if (timer)
21541
+ invalidateReads();
21542
+ if (subscribers.size)
21445
21543
  for (const record2 of records) {
21446
21544
  const previous = seen.get(record2.memory.memory_id);
21447
21545
  if ((previous?.version ?? 0) + 1 === record2.version)
@@ -21465,8 +21563,37 @@ function createRemoteMemoryStore(options) {
21465
21563
  } while (cursor2 && records.length < max);
21466
21564
  return records.slice(0, max);
21467
21565
  };
21468
- const decrypted = (options2) => persistence.query(options2);
21469
- const metadata3 = (options2) => persistence.queryMetadata(options2);
21566
+ const shareRead = (inflight, options2, read) => {
21567
+ if (options2.signal) return read();
21568
+ const key = JSON.stringify(Object.entries(options2).sort(([a], [b]) => a.localeCompare(b)));
21569
+ const active = inflight.get(key);
21570
+ if (active) return active;
21571
+ const pending2 = read();
21572
+ inflight.set(key, pending2);
21573
+ const clear = () => {
21574
+ if (inflight.get(key) === pending2) inflight.delete(key);
21575
+ };
21576
+ void pending2.then(clear, clear);
21577
+ return pending2;
21578
+ };
21579
+ const decrypted = (options2) => shareRead(reads, options2, () => persistence.query(options2));
21580
+ const metadata3 = (options2) => shareRead(
21581
+ metadataReads,
21582
+ options2,
21583
+ () => persistence.queryMetadata(options2)
21584
+ );
21585
+ const readTopics = () => {
21586
+ if (topicCatalog) return topicCatalog;
21587
+ const pending2 = persistence.topics();
21588
+ topicCatalog = pending2;
21589
+ const clear = () => {
21590
+ if (topicCatalog === pending2) topicCatalog = void 0;
21591
+ };
21592
+ void pending2.then(() => {
21593
+ if (!retainTopics()) clear();
21594
+ }, clear);
21595
+ return pending2;
21596
+ };
21470
21597
  const enumerate = (filters, max = Infinity) => pages(decrypted, filters, max);
21471
21598
  const memoriesByTopics = async (names) => {
21472
21599
  const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
@@ -21920,11 +22047,11 @@ function createRemoteMemoryStore(options) {
21920
22047
  };
21921
22048
  const changesSince = async (cursor2, pending2) => {
21922
22049
  const records = [];
21923
- const failed = /* @__PURE__ */ new Set();
22050
+ const failed = /* @__PURE__ */ new Map();
21924
22051
  const collect = (page) => {
21925
22052
  warnFailed(page.failed);
21926
22053
  records.push(...page.items);
21927
- for (const failure of page.failed) failed.add(failure.memory_id);
22054
+ for (const failure of page.failed) failed.set(failure.memory_id, failure.version);
21928
22055
  };
21929
22056
  let resume = cursor2;
21930
22057
  let next;
@@ -21958,25 +22085,72 @@ function createRemoteMemoryStore(options) {
21958
22085
  let ready = false;
21959
22086
  let cursor;
21960
22087
  let pending = /* @__PURE__ */ new Set();
22088
+ let failedVersions = /* @__PURE__ */ new Map();
21961
22089
  let topicFingerprint;
21962
22090
  let polling = false;
22091
+ let pollRequested = false;
22092
+ let failures = 0;
22093
+ let retryAt = 0;
22094
+ const backoff = (error) => {
22095
+ failures++;
22096
+ retryAt = Date.now() + Math.max(
22097
+ error instanceof RemoteMemoryError ? error.retryAfterMs ?? 0 : 0,
22098
+ Math.min(pollIntervalMs * 2 ** Math.min(failures - 1, 10), 15 * 6e4)
22099
+ );
22100
+ };
22101
+ const schedulePoll = (delay, keepEarlier = false) => {
22102
+ const at = Math.max(Date.now() + delay, retryAt);
22103
+ if (keepEarlier && timer && scheduledAt !== void 0 && scheduledAt <= at) return;
22104
+ if (timer) clearTimeout(timer);
22105
+ timer = void 0;
22106
+ scheduledAt = void 0;
22107
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
22108
+ scheduledAt = at;
22109
+ timer = setTimeout(poll, Math.max(0, at - Date.now()));
22110
+ };
21963
22111
  const poll = () => {
21964
- if (polling) return;
22112
+ if (timer) clearTimeout(timer);
22113
+ timer = void 0;
22114
+ scheduledAt = void 0;
22115
+ if (!subscribers.size || pollIntervalMs <= 0 || !activity.isActive()) return;
22116
+ if (Date.now() < retryAt) {
22117
+ schedulePoll(0);
22118
+ return;
22119
+ }
22120
+ if (polling) {
22121
+ pollRequested = true;
22122
+ return;
22123
+ }
21965
22124
  polling = true;
22125
+ pollRequested = false;
21966
22126
  const current = generation;
21967
22127
  changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
21968
22128
  let topicState;
22129
+ let topicFailed = false;
21969
22130
  if ([...subscribers].some((s) => s.options.topics))
21970
22131
  try {
21971
- topicState = JSON.stringify(
21972
- (await persistence.topics()).map((t) => [t.name, t.version])
21973
- );
22132
+ topicCatalog = void 0;
22133
+ topicState = JSON.stringify((await readTopics()).map((t) => [t.name, t.version]));
21974
22134
  } catch (error) {
22135
+ topicFailed = true;
22136
+ if (current === generation) backoff(error);
21975
22137
  getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
21976
22138
  }
21977
22139
  if (current !== generation) return;
22140
+ if (!topicFailed) {
22141
+ failures = 0;
22142
+ retryAt = 0;
22143
+ }
22144
+ const failedChanged = [...failed].some(
22145
+ ([id2, version]) => failedVersions.get(id2) !== version
22146
+ );
22147
+ if (records.length || failedChanged || topicState !== void 0 && topicState !== topicFingerprint) {
22148
+ reads.clear();
22149
+ metadataReads.clear();
22150
+ topicSnapshot = void 0;
22151
+ }
21978
22152
  const changed = /* @__PURE__ */ new Set();
21979
- if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
22153
+ if (ready && failedChanged || topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
21980
22154
  for (const subscriber of subscribers)
21981
22155
  if (subscriber.options.topics) changed.add(subscriber);
21982
22156
  }
@@ -21993,15 +22167,40 @@ function createRemoteMemoryStore(options) {
21993
22167
  }
21994
22168
  ready = true;
21995
22169
  cursor = resume;
21996
- pending = failed;
22170
+ pending = new Set(failed.keys());
22171
+ failedVersions = failed;
21997
22172
  for (const subscriber of changed)
21998
22173
  if (subscribers.has(subscriber)) safely(subscriber.onChange);
21999
22174
  }).catch((error) => {
22175
+ if (current === generation) backoff(error);
22000
22176
  getLogger().warn("[memory/remote-store] Subscription poll failed:", error);
22001
22177
  }).finally(() => {
22002
22178
  polling = false;
22179
+ schedulePoll(
22180
+ current !== generation ? 0 : pollRequested ? 200 : changesConnected ? Math.max(pollIntervalMs, 5 * 6e4) : pollIntervalMs
22181
+ );
22003
22182
  });
22004
22183
  };
22184
+ const refreshActivity = () => {
22185
+ if (timer) clearTimeout(timer);
22186
+ timer = void 0;
22187
+ if (!activity.isActive()) {
22188
+ stopChanges?.();
22189
+ stopChanges = void 0;
22190
+ changesConnected = false;
22191
+ return;
22192
+ }
22193
+ if (!stopChanges && persistence.supportsRealtimeChanges && options.subscribeChanges)
22194
+ stopChanges = options.subscribeChanges((connected) => {
22195
+ changesConnected = connected;
22196
+ if (polling) {
22197
+ pollRequested = true;
22198
+ return;
22199
+ }
22200
+ schedulePoll(connected ? 200 : 0, true);
22201
+ });
22202
+ poll();
22203
+ };
22005
22204
  const unrepairable = /* @__PURE__ */ new Set();
22006
22205
  let repairCursor;
22007
22206
  const repairEmbeddingsUnlocked = async ({
@@ -22057,40 +22256,78 @@ function createRemoteMemoryStore(options) {
22057
22256
  result.failed += targets.length;
22058
22257
  return finish();
22059
22258
  }
22060
- for (const [i, record2] of targets.entries()) {
22259
+ const writes = targets.flatMap((record2, i) => {
22061
22260
  const vector = vectors[i];
22062
22261
  if (!vector) {
22063
22262
  result.failed++;
22064
- continue;
22263
+ return [];
22065
22264
  }
22066
- await serialized(record2.memory.memory_id, async () => {
22067
- signal?.throwIfAborted();
22068
- try {
22069
- const [saved] = await persistence.putMany(
22070
- [
22071
- {
22072
- memory: {
22073
- ...record2.memory,
22074
- embedding: storedVector(vector),
22075
- embedding_model: model
22076
- },
22077
- expectedVersion: record2
22078
- }
22079
- ],
22080
- signal,
22081
- leaseToken
22082
- );
22083
- notify("embedding", [saved]);
22084
- repaired.add(record2.memory.memory_id);
22085
- result.reembedded++;
22086
- } catch (error) {
22265
+ try {
22266
+ return [
22267
+ {
22268
+ memory: {
22269
+ ...record2.memory,
22270
+ embedding: storedVector(vector),
22271
+ embedding_model: model
22272
+ },
22273
+ expectedVersion: record2
22274
+ }
22275
+ ];
22276
+ } catch {
22277
+ result.failed++;
22278
+ return [];
22279
+ }
22280
+ });
22281
+ for (const batch of chunks2(writes, MAX_BATCH)) {
22282
+ const continueRepair = await serializedAll(
22283
+ batch.map(({ memory }) => memory.memory_id),
22284
+ async () => {
22087
22285
  signal?.throwIfAborted();
22088
- if (isRemoteWorkLeaseExpired(error)) throw error;
22089
- if (error instanceof RemoteMemoryError && error.code === "version_conflict")
22090
- result.skippedConflict++;
22091
- else result.failed++;
22286
+ const commit = async (slice) => {
22287
+ const saved = await persistence.putMany(slice, signal, leaseToken);
22288
+ for (const record2 of saved) repaired.add(record2.memory.memory_id);
22289
+ result.reembedded += saved.length;
22290
+ notify("embedding", saved);
22291
+ };
22292
+ try {
22293
+ await commit(batch);
22294
+ } catch (error) {
22295
+ signal?.throwIfAborted();
22296
+ if (isRemoteWorkLeaseExpired(error)) throw error;
22297
+ if (!(error instanceof RemoteMemoryError && error.code === "version_conflict") && !rejectsMemoryWrite(error)) {
22298
+ result.failed += batch.length;
22299
+ return false;
22300
+ }
22301
+ if (batch.length === 1) {
22302
+ if (rejectsMemoryWrite(error)) {
22303
+ unrepairable.add(batch[0].memory.memory_id);
22304
+ result.failed++;
22305
+ } else result.skippedConflict++;
22306
+ return true;
22307
+ }
22308
+ for (const [index, write] of batch.entries()) {
22309
+ signal?.throwIfAborted();
22310
+ try {
22311
+ await commit([write]);
22312
+ } catch (error2) {
22313
+ signal?.throwIfAborted();
22314
+ if (isRemoteWorkLeaseExpired(error2)) throw error2;
22315
+ if (error2 instanceof RemoteMemoryError && error2.code === "version_conflict")
22316
+ result.skippedConflict++;
22317
+ else if (rejectsMemoryWrite(error2)) {
22318
+ unrepairable.add(write.memory.memory_id);
22319
+ result.failed++;
22320
+ } else {
22321
+ result.failed += batch.length - index;
22322
+ return false;
22323
+ }
22324
+ }
22325
+ }
22326
+ }
22327
+ return true;
22092
22328
  }
22093
- });
22329
+ );
22330
+ if (!continueRepair) break;
22094
22331
  }
22095
22332
  return finish();
22096
22333
  };
@@ -22130,7 +22367,7 @@ function createRemoteMemoryStore(options) {
22130
22367
  },
22131
22368
  importTopics: async (topics) => {
22132
22369
  if (!topics.length) return;
22133
- const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
22370
+ const existing = new Set((await readTopics()).map((topic2) => topic2.name));
22134
22371
  const missing = topics.flatMap((topic2) => {
22135
22372
  const name = normalizeEntityName(topic2.name);
22136
22373
  if (existing.has(name)) return [];
@@ -22139,6 +22376,7 @@ function createRemoteMemoryStore(options) {
22139
22376
  });
22140
22377
  if (!missing.length) return;
22141
22378
  await persistence.importTopics(missing);
22379
+ invalidateReads();
22142
22380
  for (const subscriber of [...subscribers])
22143
22381
  if (subscriber.options.topics) safely(subscriber.onChange);
22144
22382
  },
@@ -22152,40 +22390,51 @@ function createRemoteMemoryStore(options) {
22152
22390
  listProjections: async (listOptions = {}) => (await listRows(metadata3, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
22153
22391
  projection
22154
22392
  ),
22155
- listTopics: async () => {
22156
- const topics = /* @__PURE__ */ new Map();
22157
- for (const { memory } of await pages(metadata3, {
22158
- include_archived: true,
22159
- include_quarantined: true,
22160
- include_superseded: true
22161
- })) {
22162
- for (const topic2 of parseTopics(memory.topics) ?? []) {
22163
- const name = normalizeEntityName(topic2.name);
22164
- if (!name) continue;
22165
- const entry = topics.get(name) ?? {
22166
- name,
22167
- kind: null,
22168
- memoryCount: 0,
22169
- members: /* @__PURE__ */ new Set()
22170
- };
22171
- entry.kind ?? (entry.kind = topic2.kind ?? null);
22172
- entry.members.add(memory.memory_id);
22173
- topics.set(name, entry);
22393
+ listTopics: () => {
22394
+ if (topicSnapshot) return topicSnapshot.then((topics) => structuredClone(topics));
22395
+ const snapshot = (async () => {
22396
+ const topics = /* @__PURE__ */ new Map();
22397
+ for (const { memory } of await pages(metadata3, {
22398
+ include_archived: true,
22399
+ include_quarantined: true,
22400
+ include_superseded: true
22401
+ })) {
22402
+ for (const topic2 of parseTopics(memory.topics) ?? []) {
22403
+ const name = normalizeEntityName(topic2.name);
22404
+ if (!name) continue;
22405
+ const entry = topics.get(name) ?? {
22406
+ name,
22407
+ kind: null,
22408
+ memoryCount: 0,
22409
+ members: /* @__PURE__ */ new Set()
22410
+ };
22411
+ entry.kind ?? (entry.kind = topic2.kind ?? null);
22412
+ entry.members.add(memory.memory_id);
22413
+ topics.set(name, entry);
22414
+ }
22174
22415
  }
22175
- }
22176
- for (const topic2 of await persistence.topics()) {
22177
- if (topic2.is_deleted) continue;
22178
- const current = topics.get(topic2.name);
22179
- if (current) current.kind ?? (current.kind = topic2.kind ?? null);
22180
- else
22181
- topics.set(topic2.name, {
22182
- name: topic2.name,
22183
- kind: topic2.kind ?? null,
22184
- memoryCount: 0,
22185
- members: /* @__PURE__ */ new Set()
22186
- });
22187
- }
22188
- return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
22416
+ for (const topic2 of await readTopics()) {
22417
+ if (topic2.is_deleted) continue;
22418
+ const current = topics.get(topic2.name);
22419
+ if (current) current.kind ?? (current.kind = topic2.kind ?? null);
22420
+ else
22421
+ topics.set(topic2.name, {
22422
+ name: topic2.name,
22423
+ kind: topic2.kind ?? null,
22424
+ memoryCount: 0,
22425
+ members: /* @__PURE__ */ new Set()
22426
+ });
22427
+ }
22428
+ return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
22429
+ })();
22430
+ topicSnapshot = snapshot;
22431
+ const clear = () => {
22432
+ if (topicSnapshot === snapshot) topicSnapshot = void 0;
22433
+ };
22434
+ void snapshot.then(() => {
22435
+ if (!retainTopics()) clear();
22436
+ }, clear);
22437
+ return snapshot.then((topics) => structuredClone(topics));
22189
22438
  },
22190
22439
  get: async (id2) => asStored(await live(id2)),
22191
22440
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
@@ -22366,20 +22615,23 @@ function createRemoteMemoryStore(options) {
22366
22615
  subscribe: (onChange, subscribeOptions = {}) => {
22367
22616
  const subscriber = { onChange, options: subscribeOptions };
22368
22617
  subscribers.add(subscriber);
22369
- if (pollIntervalMs > 0 && !timer) {
22370
- timer = setInterval(poll, pollIntervalMs);
22371
- poll();
22618
+ if (pollIntervalMs > 0 && subscribers.size === 1) {
22619
+ stopActivity = activity.subscribe(refreshActivity);
22620
+ refreshActivity();
22372
22621
  }
22373
22622
  return () => {
22374
22623
  subscribers.delete(subscriber);
22375
- if (subscribers.size || !timer) return;
22376
- clearInterval(timer);
22624
+ if (!retainTopics()) invalidateReads();
22625
+ if (subscribers.size) return;
22626
+ if (timer) clearTimeout(timer);
22377
22627
  timer = void 0;
22628
+ stopActivity?.();
22629
+ stopActivity = void 0;
22630
+ stopChanges?.();
22631
+ stopChanges = void 0;
22632
+ changesConnected = false;
22378
22633
  generation++;
22379
- seen.clear();
22380
- ready = false;
22381
- cursor = void 0;
22382
- pending = /* @__PURE__ */ new Set();
22634
+ invalidateReads();
22383
22635
  };
22384
22636
  }
22385
22637
  };