@anuma/sdk 6.39.3 → 6.40.0

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.
@@ -18246,6 +18246,14 @@ function createLocalMemoryStore(options) {
18246
18246
  getLogger().warn("[memory/store] Failed to embed memory:", err);
18247
18247
  });
18248
18248
  };
18249
+ const maintenance = {
18250
+ createDecaySweeper: (sweeperOptions) => createDecaySweeper({ ...sweeperOptions, vaultCtx }),
18251
+ getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
18252
+ extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
18253
+ stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
18254
+ relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
18255
+ backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
18256
+ };
18249
18257
  return {
18250
18258
  masksEmbeddingInput: options.embeddingOptions.maskInput !== void 0,
18251
18259
  list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
@@ -18259,21 +18267,22 @@ function createLocalMemoryStore(options) {
18259
18267
  memoriesByTopics: (names) => getMemoriesByEntityNamesOp(entityCtx, names),
18260
18268
  topicsByMemories: (memoryIds) => getEntitiesByMemoryIdsOp(entityCtx, memoryIds),
18261
18269
  listTopics: async () => {
18262
- const links = await entityCtx.memoryEntityCollection.query(...linksOwnedBy).fetch();
18270
+ const links = await entityCtx.memoryEntityCollection.query(...linksOwnedBy).unsafeFetchRaw();
18263
18271
  const members = /* @__PURE__ */ new Map();
18264
18272
  for (const link of links) {
18265
- const entityId = String(link.entityId);
18273
+ const entityId = String(link.entity_id);
18266
18274
  const set = members.get(entityId) ?? /* @__PURE__ */ new Set();
18267
- set.add(String(link.memoryId));
18275
+ set.add(String(link.memory_id));
18268
18276
  members.set(entityId, set);
18269
18277
  }
18270
- const entities = userId === void 0 ? await entityCtx.entityCollection.query().fetch() : members.size ? await entityCtx.entityCollection.query(Q8.where("id", Q8.oneOf([...members.keys()]))).fetch() : [];
18278
+ const entities = userId === void 0 ? await entityCtx.entityCollection.query().unsafeFetchRaw() : members.size ? await entityCtx.entityCollection.query(Q8.where("id", Q8.oneOf([...members.keys()]))).unsafeFetchRaw() : [];
18271
18279
  const byName = /* @__PURE__ */ new Map();
18272
18280
  for (const entity of entities) {
18273
- const count2 = members.get(entity.id)?.size ?? 0;
18274
- const existing = byName.get(entity.canonicalName);
18275
- byName.set(entity.canonicalName, {
18276
- name: entity.canonicalName,
18281
+ const name = String(entity.canonical_name);
18282
+ const count2 = members.get(String(entity.id))?.size ?? 0;
18283
+ const existing = byName.get(name);
18284
+ byName.set(name, {
18285
+ name,
18277
18286
  kind: existing?.kind ?? entity.kind ?? null,
18278
18287
  memoryCount: (existing?.memoryCount ?? 0) + count2
18279
18288
  });
@@ -18364,14 +18373,8 @@ function createLocalMemoryStore(options) {
18364
18373
  }
18365
18374
  return () => subscriptions.forEach((s) => s.unsubscribe());
18366
18375
  },
18367
- maintenance: {
18368
- createDecaySweeper: (sweeperOptions) => createDecaySweeper({ ...sweeperOptions, vaultCtx }),
18369
- getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
18370
- extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
18371
- stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
18372
- relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
18373
- backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
18374
- }
18376
+ maintenance,
18377
+ topicMaintenance: maintenance
18375
18378
  };
18376
18379
  }
18377
18380
 
@@ -19155,7 +19158,7 @@ function createRemoteMemoryPipeline(options) {
19155
19158
  const memory = {
19156
19159
  ...snapshot.memory,
19157
19160
  content: patch.content,
19158
- updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Date.now()
19161
+ updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
19159
19162
  };
19160
19163
  if (patch.scope !== void 0) memory.scope = patch.scope;
19161
19164
  if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
@@ -19288,6 +19291,7 @@ function projection({ memory }) {
19288
19291
  };
19289
19292
  }
19290
19293
  function relevant(change, options) {
19294
+ if (change === "metadata") return false;
19291
19295
  return options.includeDeleted ? change === "membership" : change !== "embedding" || !!options.embeddings;
19292
19296
  }
19293
19297
  function safely(onChange) {
@@ -19379,6 +19383,17 @@ function mergeTopics(existing, added) {
19379
19383
  }
19380
19384
  return [...byName.values()];
19381
19385
  }
19386
+ function sameTopics(stored2, next) {
19387
+ const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source]);
19388
+ if (stored2 === null || stored2.length !== next.length) return false;
19389
+ const keys = new Set(stored2.map(key));
19390
+ return next.every((t) => keys.has(key(t)));
19391
+ }
19392
+ function extractionDue(memory) {
19393
+ const stamp = memory.topics_extracted_at;
19394
+ if (stamp === void 0 || stamp === null) return "fresh";
19395
+ return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
19396
+ }
19382
19397
  function canonicalTopics(row) {
19383
19398
  return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
19384
19399
  }
@@ -19391,7 +19406,12 @@ function createRemoteMemoryStore(options) {
19391
19406
  const seen = /* @__PURE__ */ new Map();
19392
19407
  let timer;
19393
19408
  const notify = (change, records = []) => {
19394
- if (timer) for (const record2 of records) seen.set(record2.memory.memory_id, record2);
19409
+ if (timer)
19410
+ for (const record2 of records) {
19411
+ const previous = seen.get(record2.memory.memory_id);
19412
+ if ((previous?.version ?? 0) + 1 === record2.version)
19413
+ seen.set(record2.memory.memory_id, record2);
19414
+ }
19395
19415
  for (const subscriber of [...subscribers])
19396
19416
  if (relevant(change, subscriber.options)) safely(subscriber.onChange);
19397
19417
  };
@@ -19452,15 +19472,72 @@ function createRemoteMemoryStore(options) {
19452
19472
  const record2 = await persistence.get(id);
19453
19473
  return record2 && !record2.memory.is_deleted ? record2 : null;
19454
19474
  };
19455
- const mutate = async (id, change, edit) => serialized(id, async () => {
19475
+ const mutateLocked = async (id, change, edit) => {
19456
19476
  const record2 = await live(id);
19457
19477
  if (!record2) return null;
19458
19478
  const next = edit(structuredClone(record2.memory));
19459
19479
  if (!next) return null;
19460
19480
  const saved = await persistence.put(next, record2);
19461
- notify(change, [saved]);
19481
+ notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
19462
19482
  return saved;
19463
- });
19483
+ };
19484
+ const mutate = (id, change, edit) => serialized(id, () => mutateLocked(id, change, edit));
19485
+ const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
19486
+ const mutateMany = async (ids, change, edit) => {
19487
+ const saved = /* @__PURE__ */ new Map();
19488
+ const failed = /* @__PURE__ */ new Map();
19489
+ for (const slice of chunks([...new Set(ids)], MAX_BATCH)) {
19490
+ await serializedAll(slice, async () => {
19491
+ const writes = [];
19492
+ let read;
19493
+ try {
19494
+ read = await readEveryState(slice);
19495
+ } catch (error) {
19496
+ for (const id of slice) failed.set(id, error);
19497
+ return;
19498
+ }
19499
+ const { records, unreadable } = read;
19500
+ for (const id of slice) {
19501
+ const record2 = records.get(id);
19502
+ if (unreadable.has(id)) failed.set(id, new Error(`Memory ${id} could not be decrypted`));
19503
+ else if (!record2) failed.set(id, new Error(`Memory ${id} was not found`));
19504
+ else if (!record2.memory.is_deleted) {
19505
+ const memory = edit(structuredClone(record2.memory));
19506
+ if (memory) writes.push({ before: record2, memory });
19507
+ }
19508
+ }
19509
+ if (writes.length === 0) return;
19510
+ try {
19511
+ const written = await persistence.putMany(
19512
+ writes.map(({ before, memory }) => ({ memory, expectedVersion: before }))
19513
+ );
19514
+ const byChange = /* @__PURE__ */ new Map();
19515
+ written.forEach((record2, i) => {
19516
+ saved.set(record2.memory.memory_id, record2);
19517
+ const kind = change(writes[i].before.memory);
19518
+ byChange.set(kind, [...byChange.get(kind) ?? [], record2]);
19519
+ });
19520
+ for (const [kind, changed] of byChange) notify(kind, changed);
19521
+ } catch (error) {
19522
+ const perRow = error instanceof RemoteMemoryError && (error.code === "version_conflict" || error.status === 429);
19523
+ for (const { before } of writes) {
19524
+ const id = before.memory.memory_id;
19525
+ if (!perRow) {
19526
+ failed.set(id, error);
19527
+ continue;
19528
+ }
19529
+ try {
19530
+ const record2 = await mutateLocked(id, change, edit);
19531
+ if (record2) saved.set(id, record2);
19532
+ } catch (rowError) {
19533
+ failed.set(id, rowError);
19534
+ }
19535
+ }
19536
+ }
19537
+ });
19538
+ }
19539
+ return { saved, failed };
19540
+ };
19464
19541
  const embedInBackground = ({ memory: { memory_id: id, content } }) => {
19465
19542
  generateEmbedding(content, embeddingOptions).then(
19466
19543
  (vector) => serialized(id, async () => {
@@ -19516,11 +19593,10 @@ function createRemoteMemoryStore(options) {
19516
19593
  return records.map((record2) => stored(record2.memory));
19517
19594
  };
19518
19595
  const asStored = (record2) => record2 ? stored(record2.memory) : null;
19519
- const withKnownKinds = async (topics) => {
19596
+ const knownKinds = async (topics) => {
19520
19597
  const unkinded = [
19521
19598
  ...new Set(topics.filter((t) => !t.kind).map((t) => normalizeEntityName(t.name)))
19522
19599
  ];
19523
- if (unkinded.length === 0) return topics;
19524
19600
  const kinds = /* @__PURE__ */ new Map();
19525
19601
  for (const names of chunks(unkinded, MAX_QUERY_TOPICS)) {
19526
19602
  for (const { memory } of await pages(metadata, {
@@ -19535,10 +19611,136 @@ function createRemoteMemoryStore(options) {
19535
19611
  }
19536
19612
  }
19537
19613
  }
19538
- return topics.map((topic) => {
19614
+ return (topic) => {
19539
19615
  const kind = topic.kind ?? kinds.get(normalizeEntityName(topic.name));
19540
19616
  return kind ? { ...topic, kind } : topic;
19541
- });
19617
+ };
19618
+ };
19619
+ const withKnownKinds = async (topics) => topics.map(await knownKinds(topics));
19620
+ const readEveryState = async (ids) => {
19621
+ const records = /* @__PURE__ */ new Map();
19622
+ const unreadable = /* @__PURE__ */ new Set();
19623
+ for (const memory_ids of chunks(ids, MAX_QUERY_IDS)) {
19624
+ let cursor2;
19625
+ do {
19626
+ const page = await persistence.query({
19627
+ memory_ids,
19628
+ include_deleted: true,
19629
+ include_archived: true,
19630
+ include_quarantined: true,
19631
+ include_superseded: true,
19632
+ limit: 200,
19633
+ ...cursor2 && { cursor: cursor2 }
19634
+ });
19635
+ warnFailed(page.failed);
19636
+ for (const record2 of page.items) records.set(record2.memory.memory_id, record2);
19637
+ for (const failure of page.failed) unreadable.add(failure.memory_id);
19638
+ cursor2 = page.next_cursor;
19639
+ } while (cursor2);
19640
+ }
19641
+ return { records, unreadable };
19642
+ };
19643
+ const topicMaintenance = {
19644
+ getTopicBacklog: async (backlogOptions = {}) => {
19645
+ const stale = [];
19646
+ const fresh = [];
19647
+ for (const { memory } of await pages(metadata, { order: "created" })) {
19648
+ if (memory.topics_user_managed) continue;
19649
+ const due = extractionDue(memory);
19650
+ if (due === "stale") stale.push(memory.memory_id);
19651
+ else if (due === "fresh") fresh.push(memory.memory_id);
19652
+ }
19653
+ const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
19654
+ return {
19655
+ pending: [...stale, ...fresh].slice(0, limit).map((uniqueId) => ({ uniqueId })),
19656
+ linkedUnstamped: [],
19657
+ topicsToRelink: [],
19658
+ topicsBackfill: []
19659
+ };
19660
+ },
19661
+ extractTopics: async (memoryIds, extractOptions) => {
19662
+ const extractedAt = extractOptions.now ?? Date.now();
19663
+ const skippedIds = [];
19664
+ const skippedReasons = /* @__PURE__ */ new Map();
19665
+ const skip = (id, reason) => {
19666
+ if (skippedReasons.has(id)) return;
19667
+ skippedIds.push(id);
19668
+ skippedReasons.set(id, reason);
19669
+ };
19670
+ const ids = [...new Set(memoryIds)];
19671
+ const { records, unreadable } = await readEveryState(ids);
19672
+ const inputs = [];
19673
+ for (const id of ids) {
19674
+ const memory = records.get(id)?.memory;
19675
+ if (unreadable.has(id)) skip(id, "unreadable");
19676
+ else if (!memory) skip(id, "not-found");
19677
+ else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
19678
+ else inputs.push({ id, content: memory.content });
19679
+ }
19680
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, extractOptions);
19681
+ const answered = /* @__PURE__ */ new Map();
19682
+ for (const input of inputs) {
19683
+ const entities = entitiesByMemory.get(input.id);
19684
+ if (entities === void 0) skip(input.id, "llm-unanswered");
19685
+ else
19686
+ answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
19687
+ }
19688
+ const stampedIds = [];
19689
+ const fail = (id, reason) => {
19690
+ skip(id, reason);
19691
+ entitiesByMemory.delete(id);
19692
+ };
19693
+ try {
19694
+ const kinded = await knownKinds([...answered.values()].flatMap((a) => a.topics));
19695
+ const topics = new Map(
19696
+ [...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
19697
+ );
19698
+ const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
19699
+ const { saved, failed } = await mutateMany(
19700
+ [...answered.keys()],
19701
+ (before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
19702
+ (memory) => {
19703
+ if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
19704
+ return null;
19705
+ return {
19706
+ ...memory,
19707
+ ...!unchanged(memory) && {
19708
+ topics: serializeTopics(topics.get(memory.memory_id)),
19709
+ topics_updated_at: Date.now()
19710
+ },
19711
+ topics_extracted_at: Math.max(extractedAt, memory.updated_at),
19712
+ topics_extracted_version: TOPICS_EXTRACTION_VERSION
19713
+ };
19714
+ }
19715
+ );
19716
+ for (const id of answered.keys()) {
19717
+ if (saved.has(id)) stampedIds.push(id);
19718
+ else fail(id, failed.has(id) ? "link-failed" : "link-declined");
19719
+ }
19720
+ if (failed.size)
19721
+ getLogger().warn(
19722
+ "[memory/remote-store] Failed to write extracted topics:",
19723
+ failed.values().next().value
19724
+ );
19725
+ } catch (error) {
19726
+ getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
19727
+ for (const id of answered.keys()) fail(id, "link-failed");
19728
+ }
19729
+ return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
19730
+ },
19731
+ stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
19732
+ const ids = [...new Set(memoryIds)];
19733
+ const { saved, failed } = await mutateMany(
19734
+ ids,
19735
+ () => "metadata",
19736
+ (memory) => memory.topics_user_managed ? null : { ...memory, topics_extracted_at: extractedAt, topics_extracted_version: version }
19737
+ );
19738
+ if (failed.size)
19739
+ getLogger().warn("[memory/remote-store] Failed to stamp topics:", [...failed.values()]);
19740
+ return ids.filter((id) => saved.has(id));
19741
+ },
19742
+ relinkTopics: () => Promise.resolve([]),
19743
+ backfillTopics: () => Promise.resolve([])
19542
19744
  };
19543
19745
  const everyState = {
19544
19746
  include_deleted: true,
@@ -19617,6 +19819,7 @@ function createRemoteMemoryStore(options) {
19617
19819
  };
19618
19820
  return {
19619
19821
  masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
19822
+ topicMaintenance,
19620
19823
  list: async (listOptions = {}) => (await listRows(decrypted, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
19621
19824
  (r) => stored(r.memory)
19622
19825
  ),
@@ -19701,7 +19904,7 @@ function createRemoteMemoryStore(options) {
19701
19904
  const kind = patch.kind !== void 0 ? patch.kind : memory.kind;
19702
19905
  assertValidKindLevel(kind, level);
19703
19906
  memory.content = patch.content;
19704
- memory.updated_at = Date.now();
19907
+ memory.updated_at = Math.max(Date.now(), (memory.topics_extracted_at ?? 0) + 1);
19705
19908
  if (patch.level !== void 0) {
19706
19909
  memory.level = level;
19707
19910
  memory.scope = scopeForLevel(level);