@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.
@@ -14915,6 +14915,17 @@ async function decryptVaultMemoryFields(memory, address, signMessage, embeddedWa
14915
14915
  };
14916
14916
  }
14917
14917
 
14918
+ // src/lib/db/memoryVault/sourceChunkIds.ts
14919
+ var MAX_SOURCE_CHUNK_IDS_BYTES = 16 << 10;
14920
+ var utf8 = new TextEncoder();
14921
+ function serializeSourceChunkIds(ids) {
14922
+ const sizes = ids.map((id) => utf8.encode(JSON.stringify(id)).length + 1);
14923
+ let total = sizes.reduce((sum, size) => sum + size, 1);
14924
+ let start = 0;
14925
+ while (total > MAX_SOURCE_CHUNK_IDS_BYTES && start < ids.length) total -= sizes[start++];
14926
+ return JSON.stringify(ids.slice(start));
14927
+ }
14928
+
14918
14929
  // src/lib/db/memoryVault/types.ts
14919
14930
  var MEMORY_KINDS = [
14920
14931
  "display_name",
@@ -15062,15 +15073,15 @@ async function mapInBatches(items, fn) {
15062
15073
  return results;
15063
15074
  }
15064
15075
  function vaultMemoryToStoredRaw(memory) {
15065
- let sourceChunkIds = null;
15076
+ let sourceChunkIds2 = null;
15066
15077
  if (memory.sourceChunkIds) {
15067
15078
  try {
15068
15079
  const parsed = JSON.parse(memory.sourceChunkIds);
15069
15080
  if (Array.isArray(parsed)) {
15070
- sourceChunkIds = parsed.filter((s) => typeof s === "string");
15081
+ sourceChunkIds2 = parsed.filter((s) => typeof s === "string");
15071
15082
  }
15072
15083
  } catch {
15073
- sourceChunkIds = null;
15084
+ sourceChunkIds2 = null;
15074
15085
  }
15075
15086
  }
15076
15087
  return {
@@ -15084,7 +15095,7 @@ function vaultMemoryToStoredRaw(memory) {
15084
15095
  userId: memory.userId ?? null,
15085
15096
  embedding: memory.embedding ?? null,
15086
15097
  embeddingModel: memory.embeddingModel ?? null,
15087
- sourceChunkIds,
15098
+ sourceChunkIds: sourceChunkIds2,
15088
15099
  proofCount: memory.proofCount ?? null,
15089
15100
  source: memory.source ?? null,
15090
15101
  media: parseMedia(memory.media),
@@ -15133,7 +15144,7 @@ function populateNewVaultMemory(record2, ctx, opts, encryptedContent, encryptedK
15133
15144
  record2._setRaw("embedding_model", opts.embeddingModel ?? null);
15134
15145
  }
15135
15146
  if (opts.sourceChunkIds !== void 0) {
15136
- record2._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
15147
+ record2._setRaw("source_chunk_ids", serializeSourceChunkIds(opts.sourceChunkIds));
15137
15148
  }
15138
15149
  record2._setRaw("proof_count", opts.proofCount ?? 1);
15139
15150
  record2._setRaw("source", opts.source ?? "manual");
@@ -15311,16 +15322,16 @@ async function getVaultMemoryOp(ctx, id) {
15311
15322
  }
15312
15323
  }
15313
15324
  function vaultMemoryRawToStoredRaw(raw) {
15314
- let sourceChunkIds = null;
15325
+ let sourceChunkIds2 = null;
15315
15326
  const rawChunks = raw.source_chunk_ids;
15316
15327
  if (typeof rawChunks === "string" && rawChunks) {
15317
15328
  try {
15318
15329
  const parsed = JSON.parse(rawChunks);
15319
15330
  if (Array.isArray(parsed)) {
15320
- sourceChunkIds = parsed.filter((s) => typeof s === "string");
15331
+ sourceChunkIds2 = parsed.filter((s) => typeof s === "string");
15321
15332
  }
15322
15333
  } catch {
15323
- sourceChunkIds = null;
15334
+ sourceChunkIds2 = null;
15324
15335
  }
15325
15336
  }
15326
15337
  return {
@@ -15334,7 +15345,7 @@ function vaultMemoryRawToStoredRaw(raw) {
15334
15345
  userId: raw.user_id ?? null,
15335
15346
  embedding: raw.embedding ?? null,
15336
15347
  embeddingModel: raw.embedding_model ?? null,
15337
- sourceChunkIds,
15348
+ sourceChunkIds: sourceChunkIds2,
15338
15349
  proofCount: raw.proof_count ?? null,
15339
15350
  source: raw.source ?? null,
15340
15351
  eventTimeStart: raw.event_time_start ?? null,
@@ -15631,7 +15642,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
15631
15642
  if (opts.sourceChunkIds !== void 0) {
15632
15643
  r._setRaw(
15633
15644
  "source_chunk_ids",
15634
- JSON.stringify(
15645
+ serializeSourceChunkIds(
15635
15646
  opts.observationSourceIds?.length ? [.../* @__PURE__ */ new Set([...observedSources, ...opts.sourceChunkIds])] : opts.sourceChunkIds
15636
15647
  )
15637
15648
  );
@@ -23017,13 +23028,20 @@ function memoryCipher(canonical) {
23017
23028
  };
23018
23029
  }
23019
23030
  async function reencryptMemoryField(value, canonical, fallbacks = []) {
23020
- const { encrypt } = memoryCipher(canonical);
23021
- if (!value.startsWith("enc:")) return encrypt(value);
23031
+ return memoryCipher(canonical).encrypt(
23032
+ await decryptMemoryField(value, [canonical, ...fallbacks])
23033
+ );
23034
+ }
23035
+ async function decryptMemoryField(value, rings) {
23036
+ if (!value.startsWith("enc:")) return value;
23022
23037
  if (!isEncrypted(value))
23023
23038
  throw new MemoryKeyError("Field has an encryption prefix but an invalid payload");
23024
- return encrypt(await decryptWith(value, [canonical, ...fallbacks]));
23039
+ return decryptWith(value, rings);
23025
23040
  }
23026
23041
 
23042
+ // src/lib/memory/store/remoteMigration.ts
23043
+ init_logger();
23044
+
23027
23045
  // src/lib/memory/store/remotePersistence.ts
23028
23046
  init_portalLlm();
23029
23047
  var RemoteMemoryError = class extends Error {
@@ -23716,7 +23734,7 @@ function createRemoteMemoryPipeline(options) {
23716
23734
  embedding: storedVector(JSON.parse(input.embedding)),
23717
23735
  embedding_model: input.embeddingModel
23718
23736
  },
23719
- source_chunk_ids: JSON.stringify(input.sourceChunkIds ?? []),
23737
+ source_chunk_ids: serializeSourceChunkIds(input.sourceChunkIds ?? []),
23720
23738
  proof_count: input.proofCount ?? 1,
23721
23739
  source: input.source ?? "manual",
23722
23740
  ...input.eventTime && {
@@ -23775,7 +23793,7 @@ function createRemoteMemoryPipeline(options) {
23775
23793
  const observed = sourceIds(memory.source_chunk_ids);
23776
23794
  const replay = !!patch.observationSourceIds?.length && patch.observationSourceIds.every((id2) => observed.includes(id2));
23777
23795
  if (patch.sourceChunkIds !== void 0)
23778
- memory.source_chunk_ids = JSON.stringify(
23796
+ memory.source_chunk_ids = serializeSourceChunkIds(
23779
23797
  patch.observationSourceIds?.length ? [.../* @__PURE__ */ new Set([...observed, ...patch.sourceChunkIds])] : patch.sourceChunkIds
23780
23798
  );
23781
23799
  if (!replay) {
@@ -23882,6 +23900,8 @@ function createRemoteMemoryPipeline(options) {
23882
23900
 
23883
23901
  // src/lib/memory/store/remoteMigration.ts
23884
23902
  var IMPORT_BATCH = 50;
23903
+ var EMBED_BATCH = 100;
23904
+ var LOCAL_VECTOR_DIMS = 4096;
23885
23905
  var VERIFY_BATCH = 1e3;
23886
23906
  var QUERY_IDS = 1e3;
23887
23907
  var QUERY_PAGE = 200;
@@ -23907,7 +23927,7 @@ var SHORT_FIELDS = [
23907
23927
  "visibility",
23908
23928
  "geohash"
23909
23929
  ];
23910
- var utf8 = new TextEncoder();
23930
+ var utf82 = new TextEncoder();
23911
23931
  var METADATA_FIELDS = [
23912
23932
  "scope",
23913
23933
  "kind",
@@ -23958,17 +23978,34 @@ function revision(raw) {
23958
23978
  last_observed_at: num(raw.last_observed_at) ?? null
23959
23979
  };
23960
23980
  }
23961
- function embedding(raw) {
23981
+ function embedding(raw, model) {
23962
23982
  const value = text13(raw.embedding);
23963
23983
  if (!value) return void 0;
23964
23984
  try {
23965
- return storedVector(JSON.parse(value));
23985
+ const vector = JSON.parse(value);
23986
+ const stored2 = text13(raw.embedding_model);
23987
+ if (stored2 === void 0 ? vector.length !== LOCAL_VECTOR_DIMS : stored2 !== model)
23988
+ return void 0;
23989
+ return storedVector(vector);
23990
+ } catch {
23991
+ return void 0;
23992
+ }
23993
+ }
23994
+ function stringArray(value) {
23995
+ try {
23996
+ const parsed = JSON.parse(value);
23997
+ return Array.isArray(parsed) && parsed.every((id) => typeof id === "string") ? parsed : void 0;
23966
23998
  } catch {
23967
23999
  return void 0;
23968
24000
  }
23969
24001
  }
24002
+ function sourceChunkIds(raw) {
24003
+ const value = text13(raw.source_chunk_ids);
24004
+ const ids = value && stringArray(value);
24005
+ return ids ? serializeSourceChunkIds(ids) : value;
24006
+ }
23970
24007
  function bytes(value) {
23971
- return value === void 0 ? 0 : utf8.encode(value).length;
24008
+ return value === void 0 ? 0 : utf82.encode(value).length;
23972
24009
  }
23973
24010
  function fitsNearby(row) {
23974
24011
  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);
@@ -23977,16 +24014,16 @@ function invalid(raw) {
23977
24014
  const id = raw.id;
23978
24015
  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;
23979
24016
  }
23980
- function metadata(raw) {
23981
- const vector = embedding(raw);
24017
+ function metadata(raw, model) {
24018
+ const vector = embedding(raw, model);
23982
24019
  return {
23983
24020
  scope: raw.scope,
23984
24021
  kind: text13(raw.kind),
23985
24022
  level: text13(raw.level),
23986
24023
  folder_id: text13(raw.folder_id),
23987
24024
  is_deleted: bool(raw.is_deleted) ?? false,
23988
- ...vector && { embedding: vector, embedding_model: text13(raw.embedding_model) },
23989
- source_chunk_ids: text13(raw.source_chunk_ids),
24025
+ ...vector && { embedding: vector, embedding_model: model },
24026
+ source_chunk_ids: sourceChunkIds(raw),
23990
24027
  proof_count: num(raw.proof_count),
23991
24028
  source: text13(raw.source),
23992
24029
  event_time_start: num(raw.event_time_start),
@@ -24015,30 +24052,63 @@ function defined(value) {
24015
24052
  Object.entries(value).filter(([, field10]) => field10 !== void 0)
24016
24053
  );
24017
24054
  }
24018
- async function remoteRow(raw, canonical, fallbacks, updatedAt) {
24019
- const content = await reencryptMemoryField(
24020
- typeof raw.content === "string" ? raw.content : "",
24021
- canonical,
24022
- fallbacks
24023
- );
24024
- const kindValue = text13(raw.kind_value);
24025
- const encryptedKindValue = kindValue ? await reencryptMemoryField(kindValue, canonical, fallbacks) : void 0;
24055
+ async function prepareRow(raw, canonical, rings, model, updatedAt) {
24056
+ const deleted = bool(raw.is_deleted) ?? false;
24057
+ const { encrypt } = memoryCipher(canonical);
24058
+ const localKindValue = text13(raw.kind_value);
24059
+ let plaintext;
24060
+ let kindValue;
24061
+ try {
24062
+ plaintext = await decryptMemoryField(typeof raw.content === "string" ? raw.content : "", rings);
24063
+ kindValue = localKindValue && await decryptMemoryField(localKindValue, rings);
24064
+ } catch (error) {
24065
+ if (!deleted || !(error instanceof MemoryKeyError)) throw error;
24066
+ plaintext = void 0;
24067
+ kindValue = void 0;
24068
+ }
24026
24069
  const row = {
24027
- ...defined({ ...metadata(raw), kind_value: encryptedKindValue }),
24070
+ ...defined({
24071
+ ...metadata(raw, model),
24072
+ kind_value: kindValue === void 0 ? void 0 : await encrypt(kindValue)
24073
+ }),
24028
24074
  memory_id: raw.id,
24029
- content,
24075
+ content: await encrypt(plaintext ?? ""),
24030
24076
  scope: raw.scope,
24031
24077
  created_at: raw.created_at,
24032
24078
  updated_at: updatedAt,
24033
- is_deleted: bool(raw.is_deleted) ?? false
24079
+ is_deleted: deleted
24034
24080
  };
24035
- return fitsNearby(row) ? row : null;
24081
+ return { row, plaintext };
24082
+ }
24083
+ async function embedMissing(prepared, options, model, signal) {
24084
+ const needed = prepared.filter((p) => !p.row.embedding && p.plaintext);
24085
+ for (const batch of chunks(needed, EMBED_BATCH)) {
24086
+ if (signal?.aborted) throw aborted(signal);
24087
+ try {
24088
+ const vectors = await generateEmbeddings(
24089
+ batch.map((p) => p.plaintext),
24090
+ { ...options, taskType: "memory_retain_embed" }
24091
+ );
24092
+ if (vectors.length !== batch.length) throw new Error("Incomplete migration embeddings");
24093
+ const stored2 = vectors.map(storedVector);
24094
+ batch.forEach((p, i) => {
24095
+ p.row.embedding = stored2[i];
24096
+ p.row.embedding_model = model;
24097
+ });
24098
+ } catch (error) {
24099
+ getLogger().warn(
24100
+ `[memory/migration] Uploading ${batch.length} row(s) without a vector: embedding failed`,
24101
+ error
24102
+ );
24103
+ }
24104
+ }
24036
24105
  }
24037
- function sameMetadata(local, remote) {
24038
- const mine = metadata(local);
24106
+ function sameMetadata(local, remote, model) {
24107
+ const mine = metadata(local, model);
24039
24108
  return METADATA_FIELDS.every((field10) => {
24040
24109
  const a = mine[field10] ?? null;
24041
24110
  const b = remote[field10] ?? null;
24111
+ if (field10 === "embedding_model" && a === null) return true;
24042
24112
  if (field10 === "visibility") return (a ?? "private") === (b ?? "private");
24043
24113
  if (field10 === "is_deleted" || field10 === "topics_user_managed" || field10 === "twin_opt_in")
24044
24114
  return (a ?? false) === (b ?? false);
@@ -24134,7 +24204,8 @@ async function backfillTopics(ctx) {
24134
24204
  }
24135
24205
  async function migrateMemoriesToRemote(options) {
24136
24206
  const { canonical, signal } = options;
24137
- const fallbacks = options.fallbacks ?? [];
24207
+ const rings = [canonical, ...options.fallbacks ?? []];
24208
+ const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
24138
24209
  const request = createNearbyRequest(options);
24139
24210
  const account = await request("/account", {}, signal);
24140
24211
  if (typeof account.key_id === "string" && account.key_id !== canonical.keyId)
@@ -24145,8 +24216,13 @@ async function migrateMemoriesToRemote(options) {
24145
24216
  );
24146
24217
  const active = account.state === "active";
24147
24218
  let claimed = account.key_id === canonical.keyId;
24219
+ if (!active) await createDecaySweeper({ vaultCtx: options.vaultCtx }).runSweep();
24148
24220
  await backfillTopics(options.vaultCtx);
24149
24221
  const skipped = /* @__PURE__ */ new Map();
24222
+ const reembedded = /* @__PURE__ */ new Set();
24223
+ const unembedded = /* @__PURE__ */ new Set();
24224
+ const placeholders = /* @__PURE__ */ new Set();
24225
+ const truncated = /* @__PURE__ */ new Set();
24150
24226
  let imported = 0;
24151
24227
  for (let pass = 0; ; pass++) {
24152
24228
  const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
@@ -24162,9 +24238,10 @@ async function migrateMemoriesToRemote(options) {
24162
24238
  candidates.map((raw) => raw.id),
24163
24239
  signal
24164
24240
  );
24241
+ const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
24165
24242
  for (const raw of candidates) {
24166
24243
  const server = remote.get(raw.id);
24167
- if (server && server.updated_at === raw.updated_at && !sameMetadata(raw, server))
24244
+ if (server && server.updated_at === raw.updated_at && (!sameMetadata(raw, server, model) || needsVector(raw, server)))
24168
24245
  pending.set(raw.id, raw.updated_at + 1);
24169
24246
  }
24170
24247
  }
@@ -24173,7 +24250,11 @@ async function migrateMemoriesToRemote(options) {
24173
24250
  state,
24174
24251
  total: rows.length,
24175
24252
  imported,
24176
- skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason }))
24253
+ skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
24254
+ reembedded: reembedded.size,
24255
+ unembedded: unembedded.size,
24256
+ placeholderTombstones: placeholders.size,
24257
+ truncatedSourceChunkIds: truncated.size
24177
24258
  });
24178
24259
  if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
24179
24260
  if (!claimed) await importBatch(request, canonical.keyId, [], signal);
@@ -24191,19 +24272,35 @@ async function migrateMemoriesToRemote(options) {
24191
24272
  409,
24192
24273
  "migration_incomplete"
24193
24274
  );
24194
- const upload = [];
24275
+ const prepared = [];
24195
24276
  for (const raw of candidates) {
24196
24277
  const updatedAt = pending.get(raw.id);
24197
24278
  if (updatedAt === void 0) continue;
24198
24279
  try {
24199
- const row = await remoteRow(raw, canonical, fallbacks, updatedAt);
24200
- if (row) upload.push(row);
24201
- else skipped.set(raw.id, "invalid");
24280
+ prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
24202
24281
  } catch (error) {
24203
24282
  if (!(error instanceof MemoryKeyError)) throw error;
24204
24283
  skipped.set(raw.id, "unreadable");
24205
24284
  }
24206
24285
  }
24286
+ const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
24287
+ if (options.embeddingOptions)
24288
+ await embedMissing(prepared, options.embeddingOptions, model, signal);
24289
+ const local = new Map(candidates.map((raw) => [raw.id, raw]));
24290
+ const upload = [];
24291
+ for (const { row, plaintext } of prepared) {
24292
+ const id = row.memory_id;
24293
+ if (!fitsNearby(row)) {
24294
+ skipped.set(id, "invalid");
24295
+ continue;
24296
+ }
24297
+ upload.push(row);
24298
+ const track = (set, on) => on ? set.add(id) : set.delete(id);
24299
+ track(reembedded, !!row.embedding && !usable.has(id));
24300
+ track(unembedded, !row.embedding);
24301
+ track(placeholders, plaintext === void 0);
24302
+ track(truncated, row.source_chunk_ids !== text13(local.get(id).source_chunk_ids));
24303
+ }
24207
24304
  let sent = 0;
24208
24305
  options.onProgress?.({ imported: sent, toImport: upload.length });
24209
24306
  for (const batch of chunks(upload, IMPORT_BATCH)) {
@@ -11458,6 +11458,8 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
11458
11458
  fallbacks?: readonly MemoryKeyRing[];
11459
11459
  /** Activate even when some rows could not be migrated. Default false. */
11460
11460
  allowSkipped?: boolean;
11461
+ /** Re-embeds rows whose local vector nearby could not rank; without it those rows upload unembedded. */
11462
+ embeddingOptions?: EmbeddingOptions;
11461
11463
  signal?: AbortSignal;
11462
11464
  onProgress?: (progress: MemoryMigrationProgress) => void;
11463
11465
  }
@@ -11484,6 +11486,14 @@ interface MemoryMigrationResult {
11484
11486
  total: number;
11485
11487
  imported: number;
11486
11488
  skipped: MemoryMigrationSkip[];
11489
+ /** Rows uploaded with a vector embedded during migration. */
11490
+ reembedded: number;
11491
+ /** Rows uploaded without a vector nearby can rank. */
11492
+ unembedded: number;
11493
+ /** Deleted rows no supplied key decrypts, uploaded with placeholder content. */
11494
+ placeholderTombstones: number;
11495
+ /** Rows whose oldest source ids were dropped to fit nearby's limit. */
11496
+ truncatedSourceChunkIds: number;
11487
11497
  }
11488
11498
  /**
11489
11499
  * Upload this device's local memories to nearby and, once nearby holds every one, make it authoritative.
@@ -11458,6 +11458,8 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
11458
11458
  fallbacks?: readonly MemoryKeyRing[];
11459
11459
  /** Activate even when some rows could not be migrated. Default false. */
11460
11460
  allowSkipped?: boolean;
11461
+ /** Re-embeds rows whose local vector nearby could not rank; without it those rows upload unembedded. */
11462
+ embeddingOptions?: EmbeddingOptions;
11461
11463
  signal?: AbortSignal;
11462
11464
  onProgress?: (progress: MemoryMigrationProgress) => void;
11463
11465
  }
@@ -11484,6 +11486,14 @@ interface MemoryMigrationResult {
11484
11486
  total: number;
11485
11487
  imported: number;
11486
11488
  skipped: MemoryMigrationSkip[];
11489
+ /** Rows uploaded with a vector embedded during migration. */
11490
+ reembedded: number;
11491
+ /** Rows uploaded without a vector nearby can rank. */
11492
+ unembedded: number;
11493
+ /** Deleted rows no supplied key decrypts, uploaded with placeholder content. */
11494
+ placeholderTombstones: number;
11495
+ /** Rows whose oldest source ids were dropped to fit nearby's limit. */
11496
+ truncatedSourceChunkIds: number;
11487
11497
  }
11488
11498
  /**
11489
11499
  * Upload this device's local memories to nearby and, once nearby holds every one, make it authoritative.