@anuma/sdk 3.12.2 → 3.13.1

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.
@@ -1918,6 +1918,31 @@ var ENTITY_KINDS = [
1918
1918
  function normalizeEntityName(name) {
1919
1919
  return name.trim().toLowerCase();
1920
1920
  }
1921
+ function serializeTopics(topics) {
1922
+ return JSON.stringify(topics);
1923
+ }
1924
+ function parseTopics(value) {
1925
+ if (typeof value !== "string" || value.length === 0) return null;
1926
+ let parsed;
1927
+ try {
1928
+ parsed = JSON.parse(value);
1929
+ } catch {
1930
+ return null;
1931
+ }
1932
+ if (!Array.isArray(parsed)) return null;
1933
+ const out = [];
1934
+ for (const raw of parsed) {
1935
+ if (typeof raw !== "object" || raw === null) continue;
1936
+ const { name, kind, source } = raw;
1937
+ if (typeof name !== "string" || name.trim().length === 0) continue;
1938
+ out.push({
1939
+ name,
1940
+ ...typeof kind === "string" && kind.length > 0 ? { kind } : {},
1941
+ source: source === "user" ? "user" : "auto"
1942
+ });
1943
+ }
1944
+ return out;
1945
+ }
1921
1946
 
1922
1947
  // src/lib/db/entities/operations.ts
1923
1948
  function entityToStored(e) {
@@ -1967,9 +1992,62 @@ async function upsertEntitiesInWrite(ctx, entities) {
1967
1992
  for (const record of created) out.set(record.canonicalName, entityToStored(record));
1968
1993
  return { entities: out, operations: [...created, ...updated] };
1969
1994
  }
1995
+ async function findVaultRowInWrite(ctx, memoryId) {
1996
+ try {
1997
+ const rows = await ctx.database.get("memory_vault").query(Q2.where("id", memoryId)).fetch();
1998
+ return rows[0] ?? null;
1999
+ } catch {
2000
+ return null;
2001
+ }
2002
+ }
2003
+ function prepareTopicsUpdate(row, topics, now) {
2004
+ const originalUpdatedAt = row.updatedAt.getTime();
2005
+ return row.prepareUpdate((r) => {
2006
+ r._setRaw("topics", serializeTopics(topics));
2007
+ r._setRaw("topics_updated_at", now);
2008
+ r._setRaw("updated_at", originalUpdatedAt);
2009
+ });
2010
+ }
2011
+ function displayNamesOf(entities) {
2012
+ const out = /* @__PURE__ */ new Map();
2013
+ for (const e of entities) {
2014
+ const name = e.name.trim();
2015
+ const normalized = normalizeEntityName(name);
2016
+ if (normalized.length === 0 || out.has(normalized)) continue;
2017
+ out.set(normalized, name);
2018
+ }
2019
+ return out;
2020
+ }
2021
+ function topicsEqual(stored, computed) {
2022
+ if (stored === null || stored.length !== computed.length) return false;
2023
+ const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source]);
2024
+ const storedKeys = stored.map(key).sort();
2025
+ const computedKeys = computed.map(key).sort();
2026
+ return storedKeys.every((k, i) => k === computedKeys[i]);
2027
+ }
2028
+ function resolveTopicsWrite(row, linked, displayNames, source) {
2029
+ if (row === null || source === null) return null;
2030
+ const topics = topicsForEntities(linked, displayNames, source);
2031
+ if (topicsEqual(parseTopics(row.topics), topics)) return null;
2032
+ return { row, topics };
2033
+ }
2034
+ function topicsForEntities(entities, displayNames, source) {
2035
+ return entities.map((e) => {
2036
+ const name = displayNames.get(e.canonicalName) ?? e.canonicalName;
2037
+ return { name, ...e.kind ? { kind: e.kind } : {}, source };
2038
+ });
2039
+ }
2040
+ function toEntityObjects(entityInputs) {
2041
+ return entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
2042
+ }
2043
+ async function prepareMemoryTopicsUpdate(ctx, memoryId, linked, inputs, source) {
2044
+ const row = await findVaultRowInWrite(ctx, memoryId);
2045
+ const write = resolveTopicsWrite(row, linked, displayNamesOf(toEntityObjects(inputs)), source);
2046
+ return write === null ? null : prepareTopicsUpdate(write.row, write.topics, Date.now());
2047
+ }
1970
2048
  async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
1971
2049
  if (entityInputs.length === 0) return [];
1972
- const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
2050
+ const normalized = toEntityObjects(entityInputs);
1973
2051
  const userId = ctx.userId;
1974
2052
  let skipped = false;
1975
2053
  let entities = [];
@@ -1979,7 +2057,8 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
1979
2057
  normalized
1980
2058
  );
1981
2059
  entities = Array.from(byName.values());
1982
- if (options?.unlessTopicsUserManaged && await autoLinkBlocked(ctx, memoryId)) {
2060
+ const vaultRow = await findVaultRowInWrite(ctx, memoryId);
2061
+ if (options?.unlessTopicsUserManaged && autoLinkBlocked(vaultRow)) {
1983
2062
  if (entityOps.length > 0) {
1984
2063
  await ctx.database.batch(...entityOps);
1985
2064
  }
@@ -1990,7 +2069,18 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
1990
2069
  const existingLinks = await ctx.memoryEntityCollection.query(Q2.where("memory_id", memoryId)).fetch();
1991
2070
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
1992
2071
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
1993
- if (entityOps.length === 0 && toCreate.length === 0) return;
2072
+ const keptIds = entities.map((e) => e.uniqueId);
2073
+ const carriedOver = await resolveEntitiesByIds(
2074
+ ctx,
2075
+ [...existingEntityIds].filter((id) => !keptIds.includes(id))
2076
+ );
2077
+ const topicsWrite = resolveTopicsWrite(
2078
+ vaultRow,
2079
+ [...carriedOver, ...entities],
2080
+ displayNamesOf(normalized),
2081
+ options?.topicsSource ?? "auto"
2082
+ );
2083
+ if (entityOps.length === 0 && toCreate.length === 0 && topicsWrite === null) return;
1994
2084
  const linkOps = toCreate.map(
1995
2085
  (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
1996
2086
  record._setRaw("memory_id", memoryId);
@@ -1998,26 +2088,42 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
1998
2088
  if (userId !== void 0) record._setRaw("user_id", userId);
1999
2089
  })
2000
2090
  );
2001
- await ctx.database.batch(...entityOps, ...linkOps);
2091
+ await ctx.database.batch(
2092
+ ...entityOps,
2093
+ ...linkOps,
2094
+ ...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
2095
+ );
2002
2096
  });
2003
2097
  return skipped ? [] : entities;
2004
2098
  }
2005
- async function autoLinkBlocked(ctx, memoryId) {
2006
- try {
2007
- const rows = await ctx.database.get("memory_vault").query(Q2.where("id", memoryId)).fetch();
2008
- const row = rows[0];
2009
- return !row || !!row.isDeleted || !!row.topicsUserManaged;
2010
- } catch {
2011
- return true;
2012
- }
2099
+ async function resolveEntitiesByIds(ctx, entityIds) {
2100
+ if (entityIds.length === 0) return [];
2101
+ const rows = await ctx.entityCollection.query(Q2.where("id", Q2.oneOf(Array.from(entityIds)))).fetch();
2102
+ return rows.map(entityToStored);
2103
+ }
2104
+ function autoLinkBlocked(row) {
2105
+ return !row || !!row.isDeleted || !!row.topicsUserManaged;
2013
2106
  }
2014
2107
  async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
2015
- const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
2108
+ return replaceMemoryEntities(ctx, memoryId, entityInputs, {
2109
+ guarded: true,
2110
+ topicsSource: "auto"
2111
+ });
2112
+ }
2113
+ async function relinkMemoryEntitiesFromTopicsOp(ctx, memoryId, topics) {
2114
+ return replaceMemoryEntities(ctx, memoryId, topics, {
2115
+ guarded: false,
2116
+ topicsSource: null
2117
+ });
2118
+ }
2119
+ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
2120
+ const normalized = toEntityObjects(entityInputs);
2016
2121
  const userId = ctx.userId;
2017
2122
  let skipped = false;
2018
2123
  let entities = [];
2019
2124
  await ctx.database.write(async () => {
2020
- if (await autoLinkBlocked(ctx, memoryId)) {
2125
+ const vaultRow = await findVaultRowInWrite(ctx, memoryId);
2126
+ if (options.guarded && autoLinkBlocked(vaultRow)) {
2021
2127
  skipped = true;
2022
2128
  return;
2023
2129
  }
@@ -2031,7 +2137,15 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
2031
2137
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
2032
2138
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
2033
2139
  const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
2034
- if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0) return;
2140
+ const topicsWrite = resolveTopicsWrite(
2141
+ vaultRow,
2142
+ entities,
2143
+ displayNamesOf(normalized),
2144
+ options.topicsSource
2145
+ );
2146
+ if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
2147
+ return;
2148
+ }
2035
2149
  const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
2036
2150
  await ctx.database.batch(
2037
2151
  ...entityOps,
@@ -2043,7 +2157,8 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
2043
2157
  })
2044
2158
  ),
2045
2159
  ...toDestroy.map((l) => l.prepareDestroyPermanently()),
2046
- ...orphans.map((e) => e.prepareDestroyPermanently())
2160
+ ...orphans.map((e) => e.prepareDestroyPermanently()),
2161
+ ...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
2047
2162
  );
2048
2163
  });
2049
2164
  return skipped ? null : entities;
@@ -2253,6 +2368,8 @@ function vaultMemoryToStoredRaw(memory) {
2253
2368
  eventTimeEnd: memory.eventTimeEnd ?? null,
2254
2369
  eventTimeKind: memory.eventTimeKind ?? null,
2255
2370
  topicsUserManaged: memory.topicsUserManaged ?? false,
2371
+ topics: parseTopics(memory.topics),
2372
+ topicsUpdatedAt: memory.topicsUpdatedAt ?? null,
2256
2373
  topicsExtractedAt: memory.topicsExtractedAt ?? null,
2257
2374
  topicsExtractedVersion: memory.topicsExtractedVersion ?? null,
2258
2375
  supersededBy: memory.supersededBy ?? null,
@@ -2529,6 +2646,8 @@ function vaultMemoryRawToStoredRaw(raw) {
2529
2646
  eventTimeKind: raw.event_time_kind ?? null,
2530
2647
  // SQLite stores booleans as 0/1, LokiJS as true/false — coerce both.
2531
2648
  topicsUserManaged: raw.topics_user_managed === true || raw.topics_user_managed === 1,
2649
+ topics: parseTopics(raw.topics),
2650
+ topicsUpdatedAt: raw.topics_updated_at ?? null,
2532
2651
  topicsExtractedAt: raw.topics_extracted_at ?? null,
2533
2652
  topicsExtractedVersion: raw.topics_extracted_version ?? null,
2534
2653
  supersededBy: raw.superseded_by ?? null,
@@ -2819,13 +2938,23 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
2819
2938
  });
2820
2939
  });
2821
2940
  if (stale) return null;
2822
- const linked = entities.length > 0 ? await linkMemoryEntitiesOp(entityCtx, memoryId, entities) : [];
2941
+ const linked = entities.length > 0 ? await linkMemoryEntitiesOp(entityCtx, memoryId, entities, { topicsSource: "user" }) : [];
2823
2942
  const keep = new Set(linked.map((e) => e.uniqueId));
2824
2943
  const existing = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", memoryId)).fetch();
2825
2944
  const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
2826
- if (staleLinks.length > 0) {
2945
+ if (staleLinks.length > 0 || entities.length === 0) {
2827
2946
  await ctx.database.write(async () => {
2828
- await ctx.database.batch(...staleLinks.map((l) => l.prepareDestroyPermanently()));
2947
+ const topicsOp = await prepareMemoryTopicsUpdate(
2948
+ entityCtx,
2949
+ memoryId,
2950
+ linked,
2951
+ entities,
2952
+ "user"
2953
+ );
2954
+ await ctx.database.batch(
2955
+ ...staleLinks.map((l) => l.prepareDestroyPermanently()),
2956
+ ...topicsOp ? [topicsOp] : []
2957
+ );
2829
2958
  });
2830
2959
  }
2831
2960
  return vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
@@ -2956,56 +3085,111 @@ async function getUnfiledVaultMemoriesOp(ctx) {
2956
3085
  );
2957
3086
  }
2958
3087
  var TOPICS_EXTRACTION_VERSION = 3;
3088
+ async function linkedEntityNamesByMemory(entityCtx, memoryIds) {
3089
+ const CHUNK = 500;
3090
+ const entityIdByMemory = /* @__PURE__ */ new Map();
3091
+ const allEntityIds = /* @__PURE__ */ new Set();
3092
+ for (let i = 0; i < memoryIds.length; i += CHUNK) {
3093
+ const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", Q3.oneOf(memoryIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
3094
+ for (const link of links) {
3095
+ const memoryId = String(link.memory_id);
3096
+ const entityId = String(link.entity_id);
3097
+ allEntityIds.add(entityId);
3098
+ let bucket = entityIdByMemory.get(memoryId);
3099
+ if (!bucket) {
3100
+ bucket = /* @__PURE__ */ new Set();
3101
+ entityIdByMemory.set(memoryId, bucket);
3102
+ }
3103
+ bucket.add(entityId);
3104
+ }
3105
+ }
3106
+ const entityIds = Array.from(allEntityIds);
3107
+ const nameById = /* @__PURE__ */ new Map();
3108
+ for (let i = 0; i < entityIds.length; i += CHUNK) {
3109
+ const entities = await entityCtx.entityCollection.query(Q3.where("id", Q3.oneOf(entityIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
3110
+ for (const e of entities) {
3111
+ if (typeof e.canonical_name === "string") nameById.set(String(e.id), e.canonical_name);
3112
+ }
3113
+ }
3114
+ const out = /* @__PURE__ */ new Map();
3115
+ for (const [memoryId, ids] of entityIdByMemory) {
3116
+ const names = /* @__PURE__ */ new Set();
3117
+ for (const id of ids) {
3118
+ const name = nameById.get(id);
3119
+ if (name !== void 0) names.add(name);
3120
+ }
3121
+ if (names.size > 0) out.set(memoryId, names);
3122
+ }
3123
+ return out;
3124
+ }
3125
+ function linksDivergeFromTopics(topics, linked) {
3126
+ const wanted = new Set(
3127
+ topics.map((t) => normalizeEntityName(t.name)).filter((n) => n.length > 0)
3128
+ );
3129
+ if (wanted.size !== linked.size) return true;
3130
+ for (const name of wanted) if (!linked.has(name)) return true;
3131
+ return false;
3132
+ }
2959
3133
  async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
2960
3134
  const entityCtx = ctx.entityCtx;
2961
3135
  if (!entityCtx) {
2962
3136
  throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
2963
3137
  }
2964
- const conditions = [
2965
- // Boolean optional column: null (legacy) and false both mean auto-managed.
2966
- // Q.notEq(true) is a NULL trap on SQLite (NULL <> TRUE is NULL) — enumerate.
2967
- Q3.or(Q3.where("topics_user_managed", null), Q3.where("topics_user_managed", false)),
2968
- ...baseVaultConditions(ctx),
2969
- Q3.sortBy("created_at", Q3.desc)
2970
- ];
3138
+ const conditions = [...baseVaultConditions(ctx), Q3.sortBy("created_at", Q3.desc)];
2971
3139
  const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
2972
- const stampedPending = [];
2973
- const unstamped = [];
3140
+ const linkedNames = await linkedEntityNamesByMemory(
3141
+ entityCtx,
3142
+ rows.map((r) => r.id)
3143
+ );
3144
+ const pendingRaw = [];
3145
+ const stampedPendingRaw = [];
3146
+ const linkedUnstampedAll = [];
3147
+ const topicsToRelinkAll = [];
3148
+ const topicsBackfillAll = [];
2974
3149
  for (const raw of rows) {
3150
+ const id = raw.id;
3151
+ const topics = parseTopics(raw.topics);
3152
+ const linked = linkedNames.get(id);
3153
+ if (topics !== null && topics.length > 0 && linksDivergeFromTopics(topics, linked ?? /* @__PURE__ */ new Set())) {
3154
+ topicsToRelinkAll.push(id);
3155
+ continue;
3156
+ }
3157
+ if (raw.topics_user_managed) {
3158
+ if (topics === null && linked !== void 0) topicsBackfillAll.push(id);
3159
+ continue;
3160
+ }
2975
3161
  const stamp = raw.topics_extracted_at ?? null;
3162
+ let isPending = false;
2976
3163
  if (stamp !== null) {
2977
3164
  const version = raw.topics_extracted_version ?? 0;
2978
3165
  if (raw.updated_at > stamp || version < TOPICS_EXTRACTION_VERSION) {
2979
- stampedPending.push(raw);
3166
+ stampedPendingRaw.push(raw);
3167
+ isPending = true;
2980
3168
  }
2981
- } else {
2982
- unstamped.push(raw);
2983
- }
2984
- }
2985
- const linkedIds = /* @__PURE__ */ new Set();
2986
- const CHUNK = 500;
2987
- for (let i = 0; i < unstamped.length; i += CHUNK) {
2988
- const ids = unstamped.slice(i, i + CHUNK).map((r) => r.id);
2989
- const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", Q3.oneOf(ids))).unsafeFetchRaw();
2990
- for (const link of links) linkedIds.add(String(link.memory_id));
2991
- }
2992
- const linkedUnstampedAll = [];
2993
- const pendingRaw = [...stampedPending];
2994
- for (const raw of unstamped) {
2995
- if (linkedIds.has(raw.id)) {
2996
- linkedUnstampedAll.push(raw.id);
3169
+ } else if (linked !== void 0) {
3170
+ linkedUnstampedAll.push(id);
2997
3171
  } else {
2998
3172
  pendingRaw.push(raw);
3173
+ isPending = true;
3174
+ }
3175
+ if (!isPending && topics === null && linked !== void 0) {
3176
+ topicsBackfillAll.push(id);
2999
3177
  }
3000
3178
  }
3001
3179
  const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
3002
- const limitedPendingRaw = cap !== void 0 ? pendingRaw.slice(0, cap) : pendingRaw;
3003
- const linkedUnstamped = cap !== void 0 ? linkedUnstampedAll.slice(0, cap) : linkedUnstampedAll;
3180
+ const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
3181
+ const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
3004
3182
  const pending = await mapInBatches(
3005
3183
  limitedPendingRaw,
3006
3184
  (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
3007
3185
  );
3008
- return { pending, linkedUnstamped };
3186
+ const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
3187
+ return {
3188
+ pending,
3189
+ linkedUnstamped: applyCap(linkedUnstampedAll),
3190
+ topicsToRelink: applyCap(topicsToRelinkAll),
3191
+ topicsBackfill: applyCap(topicsBackfillAll)
3192
+ };
3009
3193
  }
3010
3194
  async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) {
3011
3195
  if (memoryIds.length === 0) return [];
@@ -3039,6 +3223,61 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = T
3039
3223
  }
3040
3224
  return stamped;
3041
3225
  }
3226
+ async function relinkMemoryTopicsOp(ctx, memoryIds) {
3227
+ const entityCtx = ctx.entityCtx;
3228
+ if (!entityCtx) {
3229
+ throw new Error("relinkMemoryTopicsOp requires ctx.entityCtx");
3230
+ }
3231
+ const relinked = [];
3232
+ for (const id of Array.from(new Set(memoryIds))) {
3233
+ let record;
3234
+ try {
3235
+ record = await ctx.vaultMemoryCollection.find(id);
3236
+ } catch {
3237
+ continue;
3238
+ }
3239
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
3240
+ const topics = parseTopics(record.topics);
3241
+ if (topics === null || topics.length === 0) continue;
3242
+ try {
3243
+ await relinkMemoryEntitiesFromTopicsOp(entityCtx, id, topics);
3244
+ relinked.push(id);
3245
+ } catch (err) {
3246
+ getLogger().warn("[memory/topics] relink failed", err);
3247
+ }
3248
+ }
3249
+ return relinked;
3250
+ }
3251
+ async function backfillMemoryTopicsOp(ctx, memoryIds) {
3252
+ const entityCtx = ctx.entityCtx;
3253
+ if (!entityCtx) {
3254
+ throw new Error("backfillMemoryTopicsOp requires ctx.entityCtx");
3255
+ }
3256
+ const filled = [];
3257
+ for (const id of Array.from(new Set(memoryIds))) {
3258
+ let record;
3259
+ try {
3260
+ record = await ctx.vaultMemoryCollection.find(id);
3261
+ } catch {
3262
+ continue;
3263
+ }
3264
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
3265
+ if (parseTopics(record.topics) !== null) continue;
3266
+ const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", id)).unsafeFetchRaw();
3267
+ const entityIds = links.map((l) => String(l.entity_id));
3268
+ if (entityIds.length === 0) continue;
3269
+ const entities = await entityCtx.entityCollection.query(Q3.where("id", Q3.oneOf(entityIds))).fetch();
3270
+ if (entities.length === 0) continue;
3271
+ const source = record.topicsUserManaged ? "user" : "auto";
3272
+ await ctx.database.write(async () => {
3273
+ const topicsOp = await prepareMemoryTopicsUpdate(entityCtx, id, entities, [], source);
3274
+ if (!topicsOp) return;
3275
+ await ctx.database.batch(topicsOp);
3276
+ filled.push(id);
3277
+ });
3278
+ }
3279
+ return filled;
3280
+ }
3042
3281
  async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
3043
3282
  if (ctx.userId !== void 0 && ctx.userId !== userId) return 0;
3044
3283
  const records = await ctx.vaultMemoryCollection.query(Q3.where("user_id", userId), Q3.where("is_deleted", false)).fetch();
@@ -6191,7 +6430,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
6191
6430
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
6192
6431
  );
6193
6432
  }
6194
- const { recall: recall2 } = await import("./recall-NQNZDPNU.mjs");
6433
+ const { recall: recall2 } = await import("./recall-KTJCUAXN.mjs");
6195
6434
  let recallDegraded = [];
6196
6435
  const result = await recall2(
6197
6436
  query,
@@ -7257,6 +7496,8 @@ export {
7257
7496
  TOPICS_EXTRACTION_VERSION,
7258
7497
  getMemoriesNeedingTopicExtractionOp,
7259
7498
  stampTopicsExtractedAtOp,
7499
+ relinkMemoryTopicsOp,
7500
+ backfillMemoryTopicsOp,
7260
7501
  deleteAllVaultMemoriesForUserOp,
7261
7502
  assertVaultScopeForSweep,
7262
7503
  getDecayCandidatesRawOp,