@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.
@@ -15849,14 +15849,19 @@ async function upsertEntitiesOp(ctx, entities) {
15849
15849
  return out;
15850
15850
  });
15851
15851
  }
15852
- async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs) {
15852
+ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
15853
15853
  if (entityInputs.length === 0) return [];
15854
15854
  const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
15855
15855
  const byName = await upsertEntitiesOp(ctx, normalized);
15856
15856
  const entities = Array.from(byName.values());
15857
15857
  if (entities.length === 0) return [];
15858
15858
  const userId = ctx.userId;
15859
+ let skipped = false;
15859
15860
  await ctx.database.write(async () => {
15861
+ if (options?.unlessTopicsUserManaged && await autoLinkBlocked(ctx, memoryId)) {
15862
+ skipped = true;
15863
+ return;
15864
+ }
15860
15865
  const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb2.Q.where("memory_id", memoryId)).fetch();
15861
15866
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
15862
15867
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
@@ -15870,7 +15875,46 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs) {
15870
15875
  );
15871
15876
  await ctx.database.batch(...prepared);
15872
15877
  });
15873
- return entities;
15878
+ return skipped ? [] : entities;
15879
+ }
15880
+ async function autoLinkBlocked(ctx, memoryId) {
15881
+ try {
15882
+ const rows = await ctx.database.get("memory_vault").query(import_watermelondb2.Q.where("id", memoryId)).fetch();
15883
+ const row = rows[0];
15884
+ return !row || !!row.isDeleted || !!row.topicsUserManaged;
15885
+ } catch {
15886
+ return true;
15887
+ }
15888
+ }
15889
+ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
15890
+ const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
15891
+ const byName = await upsertEntitiesOp(ctx, normalized);
15892
+ const entities = Array.from(byName.values());
15893
+ const userId = ctx.userId;
15894
+ let skipped = false;
15895
+ await ctx.database.write(async () => {
15896
+ if (await autoLinkBlocked(ctx, memoryId)) {
15897
+ skipped = true;
15898
+ return;
15899
+ }
15900
+ const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb2.Q.where("memory_id", memoryId)).fetch();
15901
+ const keep = new Set(entities.map((e) => e.uniqueId));
15902
+ const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
15903
+ const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
15904
+ const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
15905
+ if (toCreate.length === 0 && toDestroy.length === 0) return;
15906
+ await ctx.database.batch(
15907
+ ...toCreate.map(
15908
+ (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
15909
+ record._setRaw("memory_id", memoryId);
15910
+ record._setRaw("entity_id", e.uniqueId);
15911
+ if (userId !== void 0) record._setRaw("user_id", userId);
15912
+ })
15913
+ ),
15914
+ ...toDestroy.map((l) => l.prepareDestroyPermanently())
15915
+ );
15916
+ });
15917
+ return skipped ? null : entities;
15874
15918
  }
15875
15919
  async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
15876
15920
  if (memoryIds.length === 0) return;
@@ -15993,6 +16037,7 @@ function vaultMemoryToStoredRaw(memory) {
15993
16037
  eventTimeEnd: memory.eventTimeEnd ?? null,
15994
16038
  eventTimeKind: memory.eventTimeKind ?? null,
15995
16039
  topicsUserManaged: memory.topicsUserManaged ?? false,
16040
+ topicsExtractedAt: memory.topicsExtractedAt ?? null,
15996
16041
  createdAt: memory.createdAt,
15997
16042
  updatedAt: memory.updatedAt,
15998
16043
  isDeleted: memory.isDeleted
@@ -16176,6 +16221,7 @@ function vaultMemoryRawToStoredRaw(raw) {
16176
16221
  eventTimeKind: raw.event_time_kind ?? null,
16177
16222
  // SQLite stores booleans as 0/1, LokiJS as true/false — coerce both.
16178
16223
  topicsUserManaged: raw.topics_user_managed === true || raw.topics_user_managed === 1,
16224
+ topicsExtractedAt: raw.topics_extracted_at ?? null,
16179
16225
  createdAt: new Date(raw.created_at),
16180
16226
  updatedAt: new Date(raw.updated_at),
16181
16227
  isDeleted: raw.is_deleted === true || raw.is_deleted === 1
@@ -16318,6 +16364,87 @@ async function getUnfiledVaultMemoriesOp(ctx) {
16318
16364
  (record) => vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
16319
16365
  );
16320
16366
  }
16367
+ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
16368
+ const entityCtx = ctx.entityCtx;
16369
+ if (!entityCtx) {
16370
+ throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
16371
+ }
16372
+ const conditions = [
16373
+ // Boolean optional column: null (legacy) and false both mean auto-managed.
16374
+ // Q.notEq(true) is a NULL trap on SQLite (NULL <> TRUE is NULL) — enumerate.
16375
+ import_watermelondb3.Q.or(import_watermelondb3.Q.where("topics_user_managed", null), import_watermelondb3.Q.where("topics_user_managed", false)),
16376
+ ...baseVaultConditions(ctx),
16377
+ import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc)
16378
+ ];
16379
+ const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
16380
+ const stampedPending = [];
16381
+ const unstamped = [];
16382
+ for (const raw of rows) {
16383
+ const stamp = raw.topics_extracted_at ?? null;
16384
+ if (stamp !== null) {
16385
+ if (raw.updated_at > stamp) stampedPending.push(raw);
16386
+ } else {
16387
+ unstamped.push(raw);
16388
+ }
16389
+ }
16390
+ const linkedIds = /* @__PURE__ */ new Set();
16391
+ const CHUNK = 500;
16392
+ for (let i = 0; i < unstamped.length; i += CHUNK) {
16393
+ const ids = unstamped.slice(i, i + CHUNK).map((r) => r.id);
16394
+ const links = await entityCtx.memoryEntityCollection.query(import_watermelondb3.Q.where("memory_id", import_watermelondb3.Q.oneOf(ids))).unsafeFetchRaw();
16395
+ for (const link of links) linkedIds.add(String(link.memory_id));
16396
+ }
16397
+ const linkedUnstampedAll = [];
16398
+ const pendingRaw = [...stampedPending];
16399
+ for (const raw of unstamped) {
16400
+ if (linkedIds.has(raw.id)) {
16401
+ linkedUnstampedAll.push(raw.id);
16402
+ } else {
16403
+ pendingRaw.push(raw);
16404
+ }
16405
+ }
16406
+ const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
16407
+ const limitedPendingRaw = cap !== void 0 ? pendingRaw.slice(0, cap) : pendingRaw;
16408
+ const linkedUnstamped = cap !== void 0 ? linkedUnstampedAll.slice(0, cap) : linkedUnstampedAll;
16409
+ const pending = await mapInBatches(
16410
+ limitedPendingRaw,
16411
+ (raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
16412
+ );
16413
+ return { pending, linkedUnstamped };
16414
+ }
16415
+ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt) {
16416
+ if (memoryIds.length === 0) return [];
16417
+ const uniqueIds = Array.from(new Set(memoryIds));
16418
+ const CHUNK = 500;
16419
+ const stamped = [];
16420
+ for (let i = 0; i < uniqueIds.length; i += CHUNK) {
16421
+ const chunkIds = uniqueIds.slice(i, i + CHUNK);
16422
+ await ctx.database.write(async () => {
16423
+ const prepared = [];
16424
+ for (const id of chunkIds) {
16425
+ let record;
16426
+ try {
16427
+ record = await ctx.vaultMemoryCollection.find(id);
16428
+ } catch {
16429
+ continue;
16430
+ }
16431
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record) || record.topicsUserManaged) {
16432
+ continue;
16433
+ }
16434
+ const originalUpdatedAt = record.updatedAt.getTime();
16435
+ prepared.push(
16436
+ record.prepareUpdate((r) => {
16437
+ r._setRaw("topics_extracted_at", extractedAt);
16438
+ r._setRaw("updated_at", originalUpdatedAt);
16439
+ })
16440
+ );
16441
+ stamped.push(id);
16442
+ }
16443
+ if (prepared.length > 0) await ctx.database.batch(...prepared);
16444
+ });
16445
+ }
16446
+ return stamped;
16447
+ }
16321
16448
  async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
16322
16449
  if (ctx.userId !== void 0 && ctx.userId !== userId) return 0;
16323
16450
  const records = await ctx.vaultMemoryCollection.query(import_watermelondb3.Q.where("user_id", userId), import_watermelondb3.Q.where("is_deleted", false)).fetch();
@@ -18848,7 +18975,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
18848
18975
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
18849
18976
  );
18850
18977
  }
18851
- const { recall: recall2 } = await import("./recall-RMBNCEG6.mjs");
18978
+ const { recall: recall2 } = await import("./recall-K2YHLBBV.mjs");
18852
18979
  const result = await recall2(
18853
18980
  query,
18854
18981
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -19476,6 +19603,8 @@ export {
19476
19603
  makeSyntheticStoredConversation,
19477
19604
  ENTITY_KINDS,
19478
19605
  linkMemoryEntitiesOp,
19606
+ replaceMemoryEntitiesGuardedOp,
19607
+ vaultMemoryToStored,
19479
19608
  createVaultMemoryOp,
19480
19609
  createVaultMemoriesBatchOp,
19481
19610
  getVaultMemoryOp,
@@ -19484,6 +19613,8 @@ export {
19484
19613
  updateVaultMemoryOp,
19485
19614
  deleteVaultMemoryOp,
19486
19615
  getUnfiledVaultMemoriesOp,
19616
+ getMemoriesNeedingTopicExtractionOp,
19617
+ stampTopicsExtractedAtOp,
19487
19618
  deleteAllVaultMemoriesForUserOp,
19488
19619
  updateVaultMemoryEmbeddingOp,
19489
19620
  DEFAULT_CHUNK_SIZE,