@anuma/sdk 2.0.1 → 2.2.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.
@@ -1663,14 +1663,19 @@ async function upsertEntitiesOp(ctx, entities) {
1663
1663
  return out;
1664
1664
  });
1665
1665
  }
1666
- async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs) {
1666
+ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
1667
1667
  if (entityInputs.length === 0) return [];
1668
1668
  const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
1669
1669
  const byName = await upsertEntitiesOp(ctx, normalized);
1670
1670
  const entities = Array.from(byName.values());
1671
1671
  if (entities.length === 0) return [];
1672
1672
  const userId = ctx.userId;
1673
+ let skipped = false;
1673
1674
  await ctx.database.write(async () => {
1675
+ if (options?.unlessTopicsUserManaged && await autoLinkBlocked(ctx, memoryId)) {
1676
+ skipped = true;
1677
+ return;
1678
+ }
1674
1679
  const existingLinks = await ctx.memoryEntityCollection.query(Q2.where("memory_id", memoryId)).fetch();
1675
1680
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
1676
1681
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
@@ -1684,7 +1689,46 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs) {
1684
1689
  );
1685
1690
  await ctx.database.batch(...prepared);
1686
1691
  });
1687
- return entities;
1692
+ return skipped ? [] : entities;
1693
+ }
1694
+ async function autoLinkBlocked(ctx, memoryId) {
1695
+ try {
1696
+ const rows = await ctx.database.get("memory_vault").query(Q2.where("id", memoryId)).fetch();
1697
+ const row = rows[0];
1698
+ return !row || !!row.isDeleted || !!row.topicsUserManaged;
1699
+ } catch {
1700
+ return true;
1701
+ }
1702
+ }
1703
+ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
1704
+ const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
1705
+ const byName = await upsertEntitiesOp(ctx, normalized);
1706
+ const entities = Array.from(byName.values());
1707
+ const userId = ctx.userId;
1708
+ let skipped = false;
1709
+ await ctx.database.write(async () => {
1710
+ if (await autoLinkBlocked(ctx, memoryId)) {
1711
+ skipped = true;
1712
+ return;
1713
+ }
1714
+ const existingLinks = await ctx.memoryEntityCollection.query(Q2.where("memory_id", memoryId)).fetch();
1715
+ const keep = new Set(entities.map((e) => e.uniqueId));
1716
+ const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
1717
+ const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
1718
+ const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
1719
+ if (toCreate.length === 0 && toDestroy.length === 0) return;
1720
+ await ctx.database.batch(
1721
+ ...toCreate.map(
1722
+ (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
1723
+ record._setRaw("memory_id", memoryId);
1724
+ record._setRaw("entity_id", e.uniqueId);
1725
+ if (userId !== void 0) record._setRaw("user_id", userId);
1726
+ })
1727
+ ),
1728
+ ...toDestroy.map((l) => l.prepareDestroyPermanently())
1729
+ );
1730
+ });
1731
+ return skipped ? null : entities;
1688
1732
  }
1689
1733
  async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
1690
1734
  if (memoryIds.length === 0) return;
@@ -1807,6 +1851,7 @@ function vaultMemoryToStoredRaw(memory) {
1807
1851
  eventTimeEnd: memory.eventTimeEnd ?? null,
1808
1852
  eventTimeKind: memory.eventTimeKind ?? null,
1809
1853
  topicsUserManaged: memory.topicsUserManaged ?? false,
1854
+ topicsExtractedAt: memory.topicsExtractedAt ?? null,
1810
1855
  createdAt: memory.createdAt,
1811
1856
  updatedAt: memory.updatedAt,
1812
1857
  isDeleted: memory.isDeleted
@@ -1990,6 +2035,7 @@ function vaultMemoryRawToStoredRaw(raw) {
1990
2035
  eventTimeKind: raw.event_time_kind ?? null,
1991
2036
  // SQLite stores booleans as 0/1, LokiJS as true/false — coerce both.
1992
2037
  topicsUserManaged: raw.topics_user_managed === true || raw.topics_user_managed === 1,
2038
+ topicsExtractedAt: raw.topics_extracted_at ?? null,
1993
2039
  createdAt: new Date(raw.created_at),
1994
2040
  updatedAt: new Date(raw.updated_at),
1995
2041
  isDeleted: raw.is_deleted === true || raw.is_deleted === 1
@@ -2132,6 +2178,87 @@ async function getUnfiledVaultMemoriesOp(ctx) {
2132
2178
  (record) => vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
2133
2179
  );
2134
2180
  }
2181
+ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
2182
+ const entityCtx = ctx.entityCtx;
2183
+ if (!entityCtx) {
2184
+ throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
2185
+ }
2186
+ const conditions = [
2187
+ // Boolean optional column: null (legacy) and false both mean auto-managed.
2188
+ // Q.notEq(true) is a NULL trap on SQLite (NULL <> TRUE is NULL) — enumerate.
2189
+ Q3.or(Q3.where("topics_user_managed", null), Q3.where("topics_user_managed", false)),
2190
+ ...baseVaultConditions(ctx),
2191
+ Q3.sortBy("created_at", Q3.desc)
2192
+ ];
2193
+ const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
2194
+ const stampedPending = [];
2195
+ const unstamped = [];
2196
+ for (const raw of rows) {
2197
+ const stamp = raw.topics_extracted_at ?? null;
2198
+ if (stamp !== null) {
2199
+ if (raw.updated_at > stamp) stampedPending.push(raw);
2200
+ } else {
2201
+ unstamped.push(raw);
2202
+ }
2203
+ }
2204
+ const linkedIds = /* @__PURE__ */ new Set();
2205
+ const CHUNK = 500;
2206
+ for (let i = 0; i < unstamped.length; i += CHUNK) {
2207
+ const ids = unstamped.slice(i, i + CHUNK).map((r) => r.id);
2208
+ const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", Q3.oneOf(ids))).unsafeFetchRaw();
2209
+ for (const link of links) linkedIds.add(String(link.memory_id));
2210
+ }
2211
+ const linkedUnstampedAll = [];
2212
+ const pendingRaw = [...stampedPending];
2213
+ for (const raw of unstamped) {
2214
+ if (linkedIds.has(raw.id)) {
2215
+ linkedUnstampedAll.push(raw.id);
2216
+ } else {
2217
+ pendingRaw.push(raw);
2218
+ }
2219
+ }
2220
+ const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
2221
+ const limitedPendingRaw = cap !== void 0 ? pendingRaw.slice(0, cap) : pendingRaw;
2222
+ const linkedUnstamped = cap !== void 0 ? linkedUnstampedAll.slice(0, cap) : linkedUnstampedAll;
2223
+ const pending = await mapInBatches(
2224
+ limitedPendingRaw,
2225
+ (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
2226
+ );
2227
+ return { pending, linkedUnstamped };
2228
+ }
2229
+ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt) {
2230
+ if (memoryIds.length === 0) return [];
2231
+ const uniqueIds = Array.from(new Set(memoryIds));
2232
+ const CHUNK = 500;
2233
+ const stamped = [];
2234
+ for (let i = 0; i < uniqueIds.length; i += CHUNK) {
2235
+ const chunkIds = uniqueIds.slice(i, i + CHUNK);
2236
+ await ctx.database.write(async () => {
2237
+ const prepared = [];
2238
+ for (const id of chunkIds) {
2239
+ let record;
2240
+ try {
2241
+ record = await ctx.vaultMemoryCollection.find(id);
2242
+ } catch {
2243
+ continue;
2244
+ }
2245
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record) || record.topicsUserManaged) {
2246
+ continue;
2247
+ }
2248
+ const originalUpdatedAt = record.updatedAt.getTime();
2249
+ prepared.push(
2250
+ record.prepareUpdate((r) => {
2251
+ r._setRaw("topics_extracted_at", extractedAt);
2252
+ r._setRaw("updated_at", originalUpdatedAt);
2253
+ })
2254
+ );
2255
+ stamped.push(id);
2256
+ }
2257
+ if (prepared.length > 0) await ctx.database.batch(...prepared);
2258
+ });
2259
+ }
2260
+ return stamped;
2261
+ }
2135
2262
  async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
2136
2263
  if (ctx.userId !== void 0 && ctx.userId !== userId) return 0;
2137
2264
  const records = await ctx.vaultMemoryCollection.query(Q3.where("user_id", userId), Q3.where("is_deleted", false)).fetch();
@@ -4690,7 +4817,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
4690
4817
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
4691
4818
  );
4692
4819
  }
4693
- const { recall: recall2 } = await import("./recall-DHOR5WWW.mjs");
4820
+ const { recall: recall2 } = await import("./recall-62ZPSDHR.mjs");
4694
4821
  const result = await recall2(
4695
4822
  query,
4696
4823
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -5315,6 +5442,8 @@ export {
5315
5442
  chunkAndEmbedAllMessages,
5316
5443
  ENTITY_KINDS,
5317
5444
  linkMemoryEntitiesOp,
5445
+ replaceMemoryEntitiesGuardedOp,
5446
+ vaultMemoryToStored,
5318
5447
  createVaultMemoryOp,
5319
5448
  createVaultMemoriesBatchOp,
5320
5449
  getVaultMemoryOp,
@@ -5323,6 +5452,8 @@ export {
5323
5452
  updateVaultMemoryOp,
5324
5453
  deleteVaultMemoryOp,
5325
5454
  getUnfiledVaultMemoriesOp,
5455
+ getMemoriesNeedingTopicExtractionOp,
5456
+ stampTopicsExtractedAtOp,
5326
5457
  deleteAllVaultMemoriesForUserOp,
5327
5458
  updateVaultMemoryEmbeddingOp,
5328
5459
  DEFAULT_VAULT_CACHE_SIZE,