@anuma/sdk 7.0.6 → 7.0.7

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.
@@ -11505,6 +11505,17 @@ async function decryptVaultMemoryFields(memory, address, signMessage, embeddedWa
11505
11505
  };
11506
11506
  }
11507
11507
 
11508
+ // src/lib/db/memoryVault/sourceChunkIds.ts
11509
+ var MAX_SOURCE_CHUNK_IDS_BYTES = 16 << 10;
11510
+ var utf8 = new TextEncoder();
11511
+ function serializeSourceChunkIds(ids) {
11512
+ const sizes = ids.map((id) => utf8.encode(JSON.stringify(id)).length + 1);
11513
+ let total = sizes.reduce((sum, size) => sum + size, 1);
11514
+ let start = 0;
11515
+ while (total > MAX_SOURCE_CHUNK_IDS_BYTES && start < ids.length) total -= sizes[start++];
11516
+ return JSON.stringify(ids.slice(start));
11517
+ }
11518
+
11508
11519
  // src/lib/db/memoryVault/types.ts
11509
11520
  var MEMORY_KINDS = [
11510
11521
  "display_name",
@@ -11652,15 +11663,15 @@ async function mapInBatches(items, fn) {
11652
11663
  return results;
11653
11664
  }
11654
11665
  function vaultMemoryToStoredRaw(memory) {
11655
- let sourceChunkIds = null;
11666
+ let sourceChunkIds2 = null;
11656
11667
  if (memory.sourceChunkIds) {
11657
11668
  try {
11658
11669
  const parsed = JSON.parse(memory.sourceChunkIds);
11659
11670
  if (Array.isArray(parsed)) {
11660
- sourceChunkIds = parsed.filter((s) => typeof s === "string");
11671
+ sourceChunkIds2 = parsed.filter((s) => typeof s === "string");
11661
11672
  }
11662
11673
  } catch {
11663
- sourceChunkIds = null;
11674
+ sourceChunkIds2 = null;
11664
11675
  }
11665
11676
  }
11666
11677
  return {
@@ -11674,7 +11685,7 @@ function vaultMemoryToStoredRaw(memory) {
11674
11685
  userId: memory.userId ?? null,
11675
11686
  embedding: memory.embedding ?? null,
11676
11687
  embeddingModel: memory.embeddingModel ?? null,
11677
- sourceChunkIds,
11688
+ sourceChunkIds: sourceChunkIds2,
11678
11689
  proofCount: memory.proofCount ?? null,
11679
11690
  source: memory.source ?? null,
11680
11691
  media: parseMedia(memory.media),
@@ -11723,7 +11734,7 @@ function populateNewVaultMemory(record2, ctx, opts, encryptedContent, encryptedK
11723
11734
  record2._setRaw("embedding_model", opts.embeddingModel ?? null);
11724
11735
  }
11725
11736
  if (opts.sourceChunkIds !== void 0) {
11726
- record2._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
11737
+ record2._setRaw("source_chunk_ids", serializeSourceChunkIds(opts.sourceChunkIds));
11727
11738
  }
11728
11739
  record2._setRaw("proof_count", opts.proofCount ?? 1);
11729
11740
  record2._setRaw("source", opts.source ?? "manual");
@@ -11901,16 +11912,16 @@ async function getVaultMemoryOp(ctx, id) {
11901
11912
  }
11902
11913
  }
11903
11914
  function vaultMemoryRawToStoredRaw(raw) {
11904
- let sourceChunkIds = null;
11915
+ let sourceChunkIds2 = null;
11905
11916
  const rawChunks = raw.source_chunk_ids;
11906
11917
  if (typeof rawChunks === "string" && rawChunks) {
11907
11918
  try {
11908
11919
  const parsed = JSON.parse(rawChunks);
11909
11920
  if (Array.isArray(parsed)) {
11910
- sourceChunkIds = parsed.filter((s) => typeof s === "string");
11921
+ sourceChunkIds2 = parsed.filter((s) => typeof s === "string");
11911
11922
  }
11912
11923
  } catch {
11913
- sourceChunkIds = null;
11924
+ sourceChunkIds2 = null;
11914
11925
  }
11915
11926
  }
11916
11927
  return {
@@ -11924,7 +11935,7 @@ function vaultMemoryRawToStoredRaw(raw) {
11924
11935
  userId: raw.user_id ?? null,
11925
11936
  embedding: raw.embedding ?? null,
11926
11937
  embeddingModel: raw.embedding_model ?? null,
11927
- sourceChunkIds,
11938
+ sourceChunkIds: sourceChunkIds2,
11928
11939
  proofCount: raw.proof_count ?? null,
11929
11940
  source: raw.source ?? null,
11930
11941
  eventTimeStart: raw.event_time_start ?? null,
@@ -12221,7 +12232,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
12221
12232
  if (opts.sourceChunkIds !== void 0) {
12222
12233
  r._setRaw(
12223
12234
  "source_chunk_ids",
12224
- JSON.stringify(
12235
+ serializeSourceChunkIds(
12225
12236
  opts.observationSourceIds?.length ? [.../* @__PURE__ */ new Set([...observedSources, ...opts.sourceChunkIds])] : opts.sourceChunkIds
12226
12237
  )
12227
12238
  );
@@ -19076,11 +19087,15 @@ function memoryCipher(canonical) {
19076
19087
  };
19077
19088
  }
19078
19089
  async function reencryptMemoryField(value, canonical, fallbacks = []) {
19079
- const { encrypt } = memoryCipher(canonical);
19080
- if (!value.startsWith("enc:")) return encrypt(value);
19090
+ return memoryCipher(canonical).encrypt(
19091
+ await decryptMemoryField(value, [canonical, ...fallbacks])
19092
+ );
19093
+ }
19094
+ async function decryptMemoryField(value, rings) {
19095
+ if (!value.startsWith("enc:")) return value;
19081
19096
  if (!isEncrypted(value))
19082
19097
  throw new MemoryKeyError("Field has an encryption prefix but an invalid payload");
19083
- return encrypt(await decryptWith(value, [canonical, ...fallbacks]));
19098
+ return decryptWith(value, rings);
19084
19099
  }
19085
19100
 
19086
19101
  // src/lib/memory/store/remotePersistence.ts
@@ -19773,7 +19788,7 @@ function createRemoteMemoryPipeline(options) {
19773
19788
  embedding: storedVector(JSON.parse(input.embedding)),
19774
19789
  embedding_model: input.embeddingModel
19775
19790
  },
19776
- source_chunk_ids: JSON.stringify(input.sourceChunkIds ?? []),
19791
+ source_chunk_ids: serializeSourceChunkIds(input.sourceChunkIds ?? []),
19777
19792
  proof_count: input.proofCount ?? 1,
19778
19793
  source: input.source ?? "manual",
19779
19794
  ...input.eventTime && {
@@ -19832,7 +19847,7 @@ function createRemoteMemoryPipeline(options) {
19832
19847
  const observed = sourceIds(memory.source_chunk_ids);
19833
19848
  const replay = !!patch.observationSourceIds?.length && patch.observationSourceIds.every((id2) => observed.includes(id2));
19834
19849
  if (patch.sourceChunkIds !== void 0)
19835
- memory.source_chunk_ids = JSON.stringify(
19850
+ memory.source_chunk_ids = serializeSourceChunkIds(
19836
19851
  patch.observationSourceIds?.length ? [.../* @__PURE__ */ new Set([...observed, ...patch.sourceChunkIds])] : patch.sourceChunkIds
19837
19852
  );
19838
19853
  if (!replay) {
@@ -19939,6 +19954,8 @@ function createRemoteMemoryPipeline(options) {
19939
19954
 
19940
19955
  // src/lib/memory/store/remoteMigration.ts
19941
19956
  var IMPORT_BATCH = 50;
19957
+ var EMBED_BATCH = 100;
19958
+ var LOCAL_VECTOR_DIMS = 4096;
19942
19959
  var VERIFY_BATCH = 1e3;
19943
19960
  var QUERY_IDS = 1e3;
19944
19961
  var QUERY_PAGE = 200;
@@ -19964,7 +19981,7 @@ var SHORT_FIELDS = [
19964
19981
  "visibility",
19965
19982
  "geohash"
19966
19983
  ];
19967
- var utf8 = new TextEncoder();
19984
+ var utf82 = new TextEncoder();
19968
19985
  var METADATA_FIELDS = [
19969
19986
  "scope",
19970
19987
  "kind",
@@ -20015,17 +20032,34 @@ function revision(raw) {
20015
20032
  last_observed_at: num(raw.last_observed_at) ?? null
20016
20033
  };
20017
20034
  }
20018
- function embedding(raw) {
20035
+ function embedding(raw, model) {
20019
20036
  const value = text13(raw.embedding);
20020
20037
  if (!value) return void 0;
20021
20038
  try {
20022
- return storedVector(JSON.parse(value));
20039
+ const vector = JSON.parse(value);
20040
+ const stored2 = text13(raw.embedding_model);
20041
+ if (stored2 === void 0 ? vector.length !== LOCAL_VECTOR_DIMS : stored2 !== model)
20042
+ return void 0;
20043
+ return storedVector(vector);
20023
20044
  } catch {
20024
20045
  return void 0;
20025
20046
  }
20026
20047
  }
20048
+ function stringArray(value) {
20049
+ try {
20050
+ const parsed = JSON.parse(value);
20051
+ return Array.isArray(parsed) && parsed.every((id) => typeof id === "string") ? parsed : void 0;
20052
+ } catch {
20053
+ return void 0;
20054
+ }
20055
+ }
20056
+ function sourceChunkIds(raw) {
20057
+ const value = text13(raw.source_chunk_ids);
20058
+ const ids = value && stringArray(value);
20059
+ return ids ? serializeSourceChunkIds(ids) : value;
20060
+ }
20027
20061
  function bytes(value) {
20028
- return value === void 0 ? 0 : utf8.encode(value).length;
20062
+ return value === void 0 ? 0 : utf82.encode(value).length;
20029
20063
  }
20030
20064
  function fitsNearby(row) {
20031
20065
  return bytes(row.memory_id) <= MAX_ID_BYTES && bytes(row.content) <= MAX_CONTENT_BYTES && bytes(row.kind_value) <= MAX_CONTENT_BYTES && TEXT_FIELDS.every((field10) => bytes(row[field10]) <= MAX_TEXT_BYTES) && SHORT_FIELDS.every((field10) => bytes(row[field10]) <= MAX_SHORT_BYTES);
@@ -20034,16 +20068,16 @@ function invalid(raw) {
20034
20068
  const id = raw.id;
20035
20069
  return typeof id !== "string" || id === "" || bytes(id) > MAX_ID_BYTES || !text13(raw.scope) || (num(raw.created_at) ?? 0) <= 0 || (num(raw.updated_at) ?? 0) <= 0;
20036
20070
  }
20037
- function metadata(raw) {
20038
- const vector = embedding(raw);
20071
+ function metadata(raw, model) {
20072
+ const vector = embedding(raw, model);
20039
20073
  return {
20040
20074
  scope: raw.scope,
20041
20075
  kind: text13(raw.kind),
20042
20076
  level: text13(raw.level),
20043
20077
  folder_id: text13(raw.folder_id),
20044
20078
  is_deleted: bool(raw.is_deleted) ?? false,
20045
- ...vector && { embedding: vector, embedding_model: text13(raw.embedding_model) },
20046
- source_chunk_ids: text13(raw.source_chunk_ids),
20079
+ ...vector && { embedding: vector, embedding_model: model },
20080
+ source_chunk_ids: sourceChunkIds(raw),
20047
20081
  proof_count: num(raw.proof_count),
20048
20082
  source: text13(raw.source),
20049
20083
  event_time_start: num(raw.event_time_start),
@@ -20072,30 +20106,63 @@ function defined(value) {
20072
20106
  Object.entries(value).filter(([, field10]) => field10 !== void 0)
20073
20107
  );
20074
20108
  }
20075
- async function remoteRow(raw, canonical, fallbacks, updatedAt) {
20076
- const content = await reencryptMemoryField(
20077
- typeof raw.content === "string" ? raw.content : "",
20078
- canonical,
20079
- fallbacks
20080
- );
20081
- const kindValue = text13(raw.kind_value);
20082
- const encryptedKindValue = kindValue ? await reencryptMemoryField(kindValue, canonical, fallbacks) : void 0;
20109
+ async function prepareRow(raw, canonical, rings, model, updatedAt) {
20110
+ const deleted = bool(raw.is_deleted) ?? false;
20111
+ const { encrypt } = memoryCipher(canonical);
20112
+ const localKindValue = text13(raw.kind_value);
20113
+ let plaintext;
20114
+ let kindValue;
20115
+ try {
20116
+ plaintext = await decryptMemoryField(typeof raw.content === "string" ? raw.content : "", rings);
20117
+ kindValue = localKindValue && await decryptMemoryField(localKindValue, rings);
20118
+ } catch (error) {
20119
+ if (!deleted || !(error instanceof MemoryKeyError)) throw error;
20120
+ plaintext = void 0;
20121
+ kindValue = void 0;
20122
+ }
20083
20123
  const row = {
20084
- ...defined({ ...metadata(raw), kind_value: encryptedKindValue }),
20124
+ ...defined({
20125
+ ...metadata(raw, model),
20126
+ kind_value: kindValue === void 0 ? void 0 : await encrypt(kindValue)
20127
+ }),
20085
20128
  memory_id: raw.id,
20086
- content,
20129
+ content: await encrypt(plaintext ?? ""),
20087
20130
  scope: raw.scope,
20088
20131
  created_at: raw.created_at,
20089
20132
  updated_at: updatedAt,
20090
- is_deleted: bool(raw.is_deleted) ?? false
20133
+ is_deleted: deleted
20091
20134
  };
20092
- return fitsNearby(row) ? row : null;
20135
+ return { row, plaintext };
20093
20136
  }
20094
- function sameMetadata(local, remote) {
20095
- const mine = metadata(local);
20137
+ async function embedMissing(prepared, options, model, signal) {
20138
+ const needed = prepared.filter((p) => !p.row.embedding && p.plaintext);
20139
+ for (const batch of chunks(needed, EMBED_BATCH)) {
20140
+ if (signal?.aborted) throw aborted(signal);
20141
+ try {
20142
+ const vectors = await generateEmbeddings(
20143
+ batch.map((p) => p.plaintext),
20144
+ { ...options, taskType: "memory_retain_embed" }
20145
+ );
20146
+ if (vectors.length !== batch.length) throw new Error("Incomplete migration embeddings");
20147
+ const stored2 = vectors.map(storedVector);
20148
+ batch.forEach((p, i) => {
20149
+ p.row.embedding = stored2[i];
20150
+ p.row.embedding_model = model;
20151
+ });
20152
+ } catch (error) {
20153
+ getLogger().warn(
20154
+ `[memory/migration] Uploading ${batch.length} row(s) without a vector: embedding failed`,
20155
+ error
20156
+ );
20157
+ }
20158
+ }
20159
+ }
20160
+ function sameMetadata(local, remote, model) {
20161
+ const mine = metadata(local, model);
20096
20162
  return METADATA_FIELDS.every((field10) => {
20097
20163
  const a = mine[field10] ?? null;
20098
20164
  const b = remote[field10] ?? null;
20165
+ if (field10 === "embedding_model" && a === null) return true;
20099
20166
  if (field10 === "visibility") return (a ?? "private") === (b ?? "private");
20100
20167
  if (field10 === "is_deleted" || field10 === "topics_user_managed" || field10 === "twin_opt_in")
20101
20168
  return (a ?? false) === (b ?? false);
@@ -20191,7 +20258,8 @@ async function backfillTopics(ctx) {
20191
20258
  }
20192
20259
  async function migrateMemoriesToRemote(options) {
20193
20260
  const { canonical, signal } = options;
20194
- const fallbacks = options.fallbacks ?? [];
20261
+ const rings = [canonical, ...options.fallbacks ?? []];
20262
+ const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
20195
20263
  const request = createNearbyRequest(options);
20196
20264
  const account = await request("/account", {}, signal);
20197
20265
  if (typeof account.key_id === "string" && account.key_id !== canonical.keyId)
@@ -20202,8 +20270,13 @@ async function migrateMemoriesToRemote(options) {
20202
20270
  );
20203
20271
  const active = account.state === "active";
20204
20272
  let claimed = account.key_id === canonical.keyId;
20273
+ if (!active) await createDecaySweeper({ vaultCtx: options.vaultCtx }).runSweep();
20205
20274
  await backfillTopics(options.vaultCtx);
20206
20275
  const skipped = /* @__PURE__ */ new Map();
20276
+ const reembedded = /* @__PURE__ */ new Set();
20277
+ const unembedded = /* @__PURE__ */ new Set();
20278
+ const placeholders = /* @__PURE__ */ new Set();
20279
+ const truncated = /* @__PURE__ */ new Set();
20207
20280
  let imported = 0;
20208
20281
  for (let pass = 0; ; pass++) {
20209
20282
  const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
@@ -20219,9 +20292,10 @@ async function migrateMemoriesToRemote(options) {
20219
20292
  candidates.map((raw) => raw.id),
20220
20293
  signal
20221
20294
  );
20295
+ const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
20222
20296
  for (const raw of candidates) {
20223
20297
  const server = remote.get(raw.id);
20224
- if (server && server.updated_at === raw.updated_at && !sameMetadata(raw, server))
20298
+ if (server && server.updated_at === raw.updated_at && (!sameMetadata(raw, server, model) || needsVector(raw, server)))
20225
20299
  pending.set(raw.id, raw.updated_at + 1);
20226
20300
  }
20227
20301
  }
@@ -20230,7 +20304,11 @@ async function migrateMemoriesToRemote(options) {
20230
20304
  state,
20231
20305
  total: rows.length,
20232
20306
  imported,
20233
- skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason }))
20307
+ skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
20308
+ reembedded: reembedded.size,
20309
+ unembedded: unembedded.size,
20310
+ placeholderTombstones: placeholders.size,
20311
+ truncatedSourceChunkIds: truncated.size
20234
20312
  });
20235
20313
  if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
20236
20314
  if (!claimed) await importBatch(request, canonical.keyId, [], signal);
@@ -20248,19 +20326,35 @@ async function migrateMemoriesToRemote(options) {
20248
20326
  409,
20249
20327
  "migration_incomplete"
20250
20328
  );
20251
- const upload = [];
20329
+ const prepared = [];
20252
20330
  for (const raw of candidates) {
20253
20331
  const updatedAt = pending.get(raw.id);
20254
20332
  if (updatedAt === void 0) continue;
20255
20333
  try {
20256
- const row = await remoteRow(raw, canonical, fallbacks, updatedAt);
20257
- if (row) upload.push(row);
20258
- else skipped.set(raw.id, "invalid");
20334
+ prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
20259
20335
  } catch (error) {
20260
20336
  if (!(error instanceof MemoryKeyError)) throw error;
20261
20337
  skipped.set(raw.id, "unreadable");
20262
20338
  }
20263
20339
  }
20340
+ const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
20341
+ if (options.embeddingOptions)
20342
+ await embedMissing(prepared, options.embeddingOptions, model, signal);
20343
+ const local = new Map(candidates.map((raw) => [raw.id, raw]));
20344
+ const upload = [];
20345
+ for (const { row, plaintext } of prepared) {
20346
+ const id = row.memory_id;
20347
+ if (!fitsNearby(row)) {
20348
+ skipped.set(id, "invalid");
20349
+ continue;
20350
+ }
20351
+ upload.push(row);
20352
+ const track = (set, on) => on ? set.add(id) : set.delete(id);
20353
+ track(reembedded, !!row.embedding && !usable.has(id));
20354
+ track(unembedded, !row.embedding);
20355
+ track(placeholders, plaintext === void 0);
20356
+ track(truncated, row.source_chunk_ids !== text13(local.get(id).source_chunk_ids));
20357
+ }
20264
20358
  let sent = 0;
20265
20359
  options.onProgress?.({ imported: sent, toImport: upload.length });
20266
20360
  for (const batch of chunks(upload, IMPORT_BATCH)) {