@anuma/sdk 3.8.3 → 3.8.5

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.
@@ -149,7 +149,7 @@ import {
149
149
  updateVaultMemoryOp,
150
150
  useEncryption,
151
151
  vaultMemoryToStored
152
- } from "./chunk-35BHTCK4.mjs";
152
+ } from "./chunk-NLVSQAYH.mjs";
153
153
  import {
154
154
  PiiRedactor,
155
155
  createStreamingDeAnonymizer,
@@ -6743,6 +6743,162 @@ __decorateClass([
6743
6743
  // src/lib/memory/autoExtract.ts
6744
6744
  import { Q as Q4 } from "@nozbe/watermelondb";
6745
6745
 
6746
+ // src/lib/memory/entitySalience.ts
6747
+ var GENERIC_ENTITY_NAMES = /* @__PURE__ */ new Set([
6748
+ // Relative and absolute time
6749
+ "today",
6750
+ "tomorrow",
6751
+ "yesterday",
6752
+ "tonight",
6753
+ "morning",
6754
+ "afternoon",
6755
+ "evening",
6756
+ "night",
6757
+ "day",
6758
+ "date",
6759
+ "time",
6760
+ "week",
6761
+ "weekend",
6762
+ "weekday",
6763
+ "month",
6764
+ "year",
6765
+ "monday",
6766
+ "tuesday",
6767
+ "wednesday",
6768
+ "thursday",
6769
+ "friday",
6770
+ "saturday",
6771
+ "sunday",
6772
+ "january",
6773
+ "february",
6774
+ "march",
6775
+ "april",
6776
+ "may",
6777
+ "june",
6778
+ "july",
6779
+ "august",
6780
+ "september",
6781
+ "october",
6782
+ "november",
6783
+ "december",
6784
+ // Generic containers and venues — the named ones ("Blue Bottle on Valencia",
6785
+ // "Stanford") are unaffected because they are not bare tokens.
6786
+ "home",
6787
+ "house",
6788
+ "apartment",
6789
+ "room",
6790
+ "office",
6791
+ "work",
6792
+ "workplace",
6793
+ "school",
6794
+ "gym",
6795
+ "store",
6796
+ "shop",
6797
+ "hospital",
6798
+ "airport",
6799
+ "hotel",
6800
+ "city",
6801
+ "town",
6802
+ "country",
6803
+ "place",
6804
+ // Generic calendar entries — "Chicago Marathon" survives, "meeting" doesn't.
6805
+ "meeting",
6806
+ "call",
6807
+ "appointment",
6808
+ "reminder",
6809
+ "event",
6810
+ "birthday",
6811
+ "breakfast",
6812
+ "lunch",
6813
+ "dinner",
6814
+ "brunch",
6815
+ "vacation",
6816
+ "holiday",
6817
+ "trip",
6818
+ // Plurals are listed explicitly rather than stemmed: naive `s`-stripping
6819
+ // would fold real titles into generics ("The Times" → "time"). Only plurals
6820
+ // that are junk as topics in their own right belong here — note the absence
6821
+ // of "times", "places", and anything else that reads as a name.
6822
+ "days",
6823
+ "weeks",
6824
+ "weekends",
6825
+ "months",
6826
+ "years",
6827
+ "rooms",
6828
+ "offices",
6829
+ "houses",
6830
+ "apartments",
6831
+ "stores",
6832
+ "shops",
6833
+ "hotels",
6834
+ "meetings",
6835
+ "calls",
6836
+ "appointments",
6837
+ "reminders",
6838
+ "events",
6839
+ "birthdays",
6840
+ "trips",
6841
+ "vacations",
6842
+ "holidays",
6843
+ // Meta / filler the models occasionally echo back as an entity
6844
+ "user",
6845
+ "assistant",
6846
+ "memory",
6847
+ "memories",
6848
+ "note",
6849
+ "notes",
6850
+ "thing",
6851
+ "things",
6852
+ "stuff",
6853
+ "something",
6854
+ "someone",
6855
+ "anything",
6856
+ "everything",
6857
+ "none",
6858
+ "other",
6859
+ "misc",
6860
+ "general",
6861
+ "unknown"
6862
+ ]);
6863
+ function isGenericEntityName(name) {
6864
+ const tokens = name.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
6865
+ if (tokens.length === 0) return true;
6866
+ const distinctive = tokens.filter(
6867
+ (token) => !GENERIC_ENTITY_NAMES.has(token) && !ARTICLES.has(token) && !WEAK_MODIFIERS.has(token)
6868
+ );
6869
+ if (distinctive.length > 0) return false;
6870
+ const generic = tokens.filter((token) => GENERIC_ENTITY_NAMES.has(token));
6871
+ if (generic.length === 0) return true;
6872
+ const titleShaped = generic.length === 1 && tokens.some((token) => ARTICLES.has(token)) && !tokens.some((token) => WEAK_MODIFIERS.has(token));
6873
+ return !titleShaped;
6874
+ }
6875
+ var ARTICLES = /* @__PURE__ */ new Set(["the", "a", "an"]);
6876
+ var WEAK_MODIFIERS = /* @__PURE__ */ new Set([
6877
+ "my",
6878
+ "our",
6879
+ "their",
6880
+ "his",
6881
+ "her",
6882
+ "its",
6883
+ "this",
6884
+ "that",
6885
+ "next",
6886
+ "last",
6887
+ "upcoming",
6888
+ "every",
6889
+ "at",
6890
+ "in",
6891
+ "on",
6892
+ "of",
6893
+ "for",
6894
+ "with",
6895
+ "from",
6896
+ "to",
6897
+ "by",
6898
+ "and",
6899
+ "or"
6900
+ ]);
6901
+
6746
6902
  // src/lib/memory/injectionClassifier.ts
6747
6903
  var DEFAULT_MODEL = "inclusionai/ling-2.6-flash";
6748
6904
  var DEFAULT_ATTEMPTS = 2;
@@ -7671,13 +7827,13 @@ function parseEntities(raw) {
7671
7827
  for (const e of raw) {
7672
7828
  if (typeof e === "string") {
7673
7829
  const name2 = e.trim();
7674
- if (name2.length > 0) out.push({ name: name2 });
7830
+ if (name2.length > 0 && !isGenericEntityName(name2)) out.push({ name: name2 });
7675
7831
  continue;
7676
7832
  }
7677
7833
  if (typeof e !== "object" || e === null) continue;
7678
7834
  const obj = e;
7679
7835
  const name = typeof obj.name === "string" ? obj.name.trim() : "";
7680
- if (name.length === 0) continue;
7836
+ if (name.length === 0 || isGenericEntityName(name)) continue;
7681
7837
  const kind = typeof obj.kind === "string" && ENTITY_KINDS.includes(obj.kind) ? obj.kind : void 0;
7682
7838
  out.push(kind !== void 0 ? { name, kind } : { name });
7683
7839
  }
@@ -7897,9 +8053,11 @@ function createAutoExtractor(options) {
7897
8053
  }
7898
8054
  }
7899
8055
  );
7900
- const advancedTo = window2[window2.length - 1].id;
7901
- stateFor(conversationId).watermark = advancedTo;
7902
- persistCursor(conversationId, messages, advancedTo, contiguous);
8056
+ if (outcome !== "empty-after-retry") {
8057
+ const advancedTo = window2[window2.length - 1].id;
8058
+ stateFor(conversationId).watermark = advancedTo;
8059
+ persistCursor(conversationId, messages, advancedTo, contiguous);
8060
+ }
7903
8061
  for (let i = 0; i < results.length; i++) {
7904
8062
  if (results[i].action === "suppressed") continue;
7905
8063
  options.onMemoryExtracted?.({
@@ -8980,7 +9138,11 @@ var SYSTEM_PROMPT4 = `You assign topics to saved memories for a personal memory
8980
9138
 
8981
9139
  Each memory is a short statement about the user. For each memory, list the NAMED entities it mentions \u2014 these become the memory's topics, used to connect related memories in a knowledge graph.
8982
9140
 
8983
- Include only NAMED entities, skip generic/common nouns. ${ENTITY_KIND_GUIDELINES}
9141
+ Include only NAMED entities, skip generic/common nouns. A topic must identify a specific thing the user could recognize by name; if it would be equally true of thousands of unrelated memories, leave it out:
9142
+ - SKIP bare generic nouns even when the memory capitalizes them or a calendar entry is titled that way \u2014 "Home", "Work", "Meeting", "Lunch", "Birthday", "Appointment", "Trip", "Today", "Friday", "March" are NOT topics.
9143
+ - KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
9144
+ - Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
9145
+ ${ENTITY_KIND_GUIDELINES}
8984
9146
 
8985
9147
  Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
8986
9148
  - exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
@@ -8992,7 +9154,7 @@ async function extractEntitiesForMemories(memories, options) {
8992
9154
  const redactor = resolvePiiRedactor(options.piiRedaction);
8993
9155
  const vocabulary = (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES).map((n) => redactor ? redactor.redactText(n).text : n);
8994
9156
  const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
8995
- When a memory refers to one of these, reuse the EXACT existing name (same spelling and casing) instead of a variant.
9157
+ When a memory refers to one of these, reuse the EXACT existing name (same spelling, casing, spacing and punctuation) instead of a variant \u2014 if "Foo.ai" is listed, never emit "Foo ai" or "Foo AI", and if "Jane Roe" is listed, emit that rather than a bare "Jane".
8996
9158
 
8997
9159
  ` : "";
8998
9160
  for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-35BHTCK4.mjs";
3
+ } from "./chunk-NLVSQAYH.mjs";
4
4
  import "./chunk-BOY27A2C.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -16003,7 +16003,7 @@ function entityToStored(e) {
16003
16003
  updatedAt: e.updatedAt
16004
16004
  };
16005
16005
  }
16006
- async function upsertEntitiesOp(ctx, entities) {
16006
+ async function upsertEntitiesInWrite(ctx, entities) {
16007
16007
  const kindByName = /* @__PURE__ */ new Map();
16008
16008
  for (const e of entities) {
16009
16009
  const name = normalizeEntityName(e.name);
@@ -16017,60 +16017,62 @@ async function upsertEntitiesOp(ctx, entities) {
16017
16017
  }
16018
16018
  const unique = Array.from(kindByName.keys());
16019
16019
  const out = /* @__PURE__ */ new Map();
16020
- if (unique.length === 0) return out;
16021
- return await ctx.database.write(async () => {
16022
- const existing = await ctx.entityCollection.query(import_watermelondb2.Q.where("canonical_name", import_watermelondb2.Q.oneOf(unique))).fetch();
16023
- const existingNames = new Set(existing.map((e) => e.canonicalName));
16024
- const updates = existing.filter((e) => {
16025
- const incoming = kindByName.get(e.canonicalName);
16026
- return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
16027
- });
16028
- const missing = unique.filter((n) => !existingNames.has(n));
16029
- const created = missing.map(
16030
- (name) => ctx.entityCollection.prepareCreate((record) => {
16031
- record._setRaw("canonical_name", name);
16032
- const kind = kindByName.get(name);
16033
- if (kind !== void 0) record._setRaw("kind", kind);
16034
- })
16035
- );
16036
- const updated = updates.map(
16037
- (e) => e.prepareUpdate((record) => {
16038
- record._setRaw("kind", kindByName.get(e.canonicalName));
16039
- })
16040
- );
16041
- if (created.length > 0 || updated.length > 0) {
16042
- await ctx.database.batch(...created, ...updated);
16043
- }
16044
- for (const e of existing) out.set(e.canonicalName, entityToStored(e));
16045
- for (const record of created) out.set(record.canonicalName, entityToStored(record));
16046
- return out;
16020
+ if (unique.length === 0) return { entities: out, operations: [] };
16021
+ const existing = await ctx.entityCollection.query(import_watermelondb2.Q.where("canonical_name", import_watermelondb2.Q.oneOf(unique))).fetch();
16022
+ const existingNames = new Set(existing.map((e) => e.canonicalName));
16023
+ const updates = existing.filter((e) => {
16024
+ const incoming = kindByName.get(e.canonicalName);
16025
+ return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
16047
16026
  });
16027
+ const missing = unique.filter((n) => !existingNames.has(n));
16028
+ const created = missing.map(
16029
+ (name) => ctx.entityCollection.prepareCreate((record) => {
16030
+ record._setRaw("canonical_name", name);
16031
+ const kind = kindByName.get(name);
16032
+ if (kind !== void 0) record._setRaw("kind", kind);
16033
+ })
16034
+ );
16035
+ const updated = updates.map(
16036
+ (e) => e.prepareUpdate((record) => {
16037
+ record._setRaw("kind", kindByName.get(e.canonicalName));
16038
+ })
16039
+ );
16040
+ for (const e of existing) out.set(e.canonicalName, entityToStored(e));
16041
+ for (const record of created) out.set(record.canonicalName, entityToStored(record));
16042
+ return { entities: out, operations: [...created, ...updated] };
16048
16043
  }
16049
16044
  async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
16050
16045
  if (entityInputs.length === 0) return [];
16051
16046
  const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
16052
- const byName = await upsertEntitiesOp(ctx, normalized);
16053
- const entities = Array.from(byName.values());
16054
- if (entities.length === 0) return [];
16055
16047
  const userId = ctx.userId;
16056
16048
  let skipped = false;
16049
+ let entities = [];
16057
16050
  await ctx.database.write(async () => {
16051
+ const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
16052
+ ctx,
16053
+ normalized
16054
+ );
16055
+ entities = Array.from(byName.values());
16058
16056
  if (options?.unlessTopicsUserManaged && await autoLinkBlocked(ctx, memoryId)) {
16057
+ if (entityOps.length > 0) {
16058
+ await ctx.database.batch(...entityOps);
16059
+ }
16059
16060
  skipped = true;
16060
16061
  return;
16061
16062
  }
16063
+ if (entities.length === 0) return;
16062
16064
  const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb2.Q.where("memory_id", memoryId)).fetch();
16063
16065
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
16064
16066
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
16065
- if (toCreate.length === 0) return;
16066
- const prepared = toCreate.map(
16067
+ if (entityOps.length === 0 && toCreate.length === 0) return;
16068
+ const linkOps = toCreate.map(
16067
16069
  (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
16068
16070
  record._setRaw("memory_id", memoryId);
16069
16071
  record._setRaw("entity_id", e.uniqueId);
16070
16072
  if (userId !== void 0) record._setRaw("user_id", userId);
16071
16073
  })
16072
16074
  );
16073
- await ctx.database.batch(...prepared);
16075
+ await ctx.database.batch(...entityOps, ...linkOps);
16074
16076
  });
16075
16077
  return skipped ? [] : entities;
16076
16078
  }
@@ -16085,22 +16087,28 @@ async function autoLinkBlocked(ctx, memoryId) {
16085
16087
  }
16086
16088
  async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
16087
16089
  const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
16088
- const byName = await upsertEntitiesOp(ctx, normalized);
16089
- const entities = Array.from(byName.values());
16090
16090
  const userId = ctx.userId;
16091
16091
  let skipped = false;
16092
+ let entities = [];
16092
16093
  await ctx.database.write(async () => {
16093
16094
  if (await autoLinkBlocked(ctx, memoryId)) {
16094
16095
  skipped = true;
16095
16096
  return;
16096
16097
  }
16098
+ const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
16099
+ ctx,
16100
+ normalized
16101
+ );
16102
+ entities = Array.from(byName.values());
16097
16103
  const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb2.Q.where("memory_id", memoryId)).fetch();
16098
16104
  const keep = new Set(entities.map((e) => e.uniqueId));
16099
16105
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
16100
16106
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
16101
16107
  const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
16102
- if (toCreate.length === 0 && toDestroy.length === 0) return;
16108
+ if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0) return;
16109
+ const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
16103
16110
  await ctx.database.batch(
16111
+ ...entityOps,
16104
16112
  ...toCreate.map(
16105
16113
  (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
16106
16114
  record._setRaw("memory_id", memoryId);
@@ -16108,11 +16116,23 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
16108
16116
  if (userId !== void 0) record._setRaw("user_id", userId);
16109
16117
  })
16110
16118
  ),
16111
- ...toDestroy.map((l) => l.prepareDestroyPermanently())
16119
+ ...toDestroy.map((l) => l.prepareDestroyPermanently()),
16120
+ ...orphans.map((e) => e.prepareDestroyPermanently())
16112
16121
  );
16113
16122
  });
16114
16123
  return skipped ? null : entities;
16115
16124
  }
16125
+ async function findOrphanedEntities(ctx, memoryId, toDestroy) {
16126
+ const candidateIds = [...new Set(toDestroy.map((l) => String(l.entityId)))];
16127
+ if (candidateIds.length === 0) return [];
16128
+ const links = await ctx.memoryEntityCollection.query(import_watermelondb2.Q.where("entity_id", import_watermelondb2.Q.oneOf(candidateIds))).fetch();
16129
+ const stillLinked = new Set(
16130
+ links.filter((l) => String(l.memoryId) !== memoryId).map((l) => String(l.entityId))
16131
+ );
16132
+ const orphanIds = candidateIds.filter((id) => !stillLinked.has(id));
16133
+ if (orphanIds.length === 0) return [];
16134
+ return await ctx.entityCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(orphanIds))).fetch();
16135
+ }
16116
16136
  async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
16117
16137
  if (memoryIds.length === 0) return;
16118
16138
  const links = await ctx.memoryEntityCollection.query(import_watermelondb2.Q.where("memory_id", import_watermelondb2.Q.oneOf(Array.from(memoryIds)))).fetch();
@@ -16733,7 +16753,7 @@ async function getUnfiledVaultMemoriesOp(ctx) {
16733
16753
  (record) => vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
16734
16754
  );
16735
16755
  }
16736
- var TOPICS_EXTRACTION_VERSION = 2;
16756
+ var TOPICS_EXTRACTION_VERSION = 3;
16737
16757
  async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
16738
16758
  const entityCtx = ctx.entityCtx;
16739
16759
  if (!entityCtx) {
@@ -19554,7 +19574,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
19554
19574
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
19555
19575
  );
19556
19576
  }
19557
- const { recall: recall2 } = await import("./recall-XZ6WLFPJ.mjs");
19577
+ const { recall: recall2 } = await import("./recall-VDMDNQNB.mjs");
19558
19578
  const result = await recall2(
19559
19579
  query,
19560
19580
  { vaultCtx, embeddingOptions, vaultCache: cache },