@anuma/sdk 7.1.0 → 7.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.
@@ -18992,10 +18992,18 @@ function admitVaultProjections(queryEmbedding, vectored, k) {
18992
18992
  }
18993
18993
  async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, queryOpts, opts) {
18994
18994
  let rowsDecrypted = 0;
18995
+ const keysStart = nowMs();
18995
18996
  const keys = await getVaultCandidateKeysOp(
18996
18997
  vaultCtx,
18997
18998
  Object.keys(queryOpts).length > 0 ? queryOpts : void 0
18998
18999
  );
19000
+ const corpusStats = {
19001
+ keysMs: nowMs() - keysStart,
19002
+ embeddingsFetchMs: 0,
19003
+ rowsFetchMs: 0,
19004
+ cacheHits: 0,
19005
+ cacheMisses: 0
19006
+ };
18999
19007
  const hydrateOpts = queryOpts.includeArchived ? { includeArchived: true } : void 0;
19000
19008
  const vaultSize = keys.length;
19001
19009
  if (keys.length === 0) {
@@ -19005,7 +19013,8 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19005
19013
  queryEmbedding: [],
19006
19014
  vaultSize: 0,
19007
19015
  laneEmbedFailed: false,
19008
- rowsDecrypted: 0
19016
+ rowsDecrypted: 0,
19017
+ corpusStats
19009
19018
  };
19010
19019
  }
19011
19020
  const queryEmbedStart = nowMs();
@@ -19027,16 +19036,20 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19027
19036
  for (const k2 of keys) {
19028
19037
  const cached = cachedRowVector(cache, k2.uniqueId, k2.updatedAt);
19029
19038
  if (cached && cached.length === queryEmbedding.length) {
19039
+ corpusStats.cacheHits++;
19030
19040
  vectored.push({ uniqueId: k2.uniqueId, embedding: cached, updatedAt: k2.updatedAt });
19031
19041
  continue;
19032
19042
  }
19043
+ corpusStats.cacheMisses++;
19033
19044
  const modelCompatible = (k2.embeddingModel ?? currentModel) === currentModel;
19034
19045
  if (modelCompatible) missIds.push(k2.uniqueId);
19035
19046
  else noVectorIds.push(k2.uniqueId);
19036
19047
  }
19037
19048
  }
19038
19049
  if (missIds.length > 0) {
19050
+ const embeddingsStart = nowMs();
19039
19051
  const rows = await getVaultEmbeddingsByIdsOp(vaultCtx, missIds, hydrateOpts);
19052
+ corpusStats.embeddingsFetchMs += nowMs() - embeddingsStart;
19040
19053
  const gotVector = /* @__PURE__ */ new Set();
19041
19054
  for (const r of rows) {
19042
19055
  if (!r.embedding) continue;
@@ -19064,7 +19077,9 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19064
19077
  `memoryVault: projected search un-embedded lane capped at ${laneIds.length}/${noVectorIds.length}`
19065
19078
  );
19066
19079
  }
19080
+ const laneStart = nowMs();
19067
19081
  const laneFetched = await getVaultMemoriesByIdsOp(vaultCtx, laneIds, hydrateOpts);
19082
+ corpusStats.rowsFetchMs += nowMs() - laneStart;
19068
19083
  rowsDecrypted += laneFetched.length;
19069
19084
  for (const row of laneFetched) laneById.set(row.uniqueId, row);
19070
19085
  const laneRows = laneFetched.filter((m) => !isEncrypted(m.content));
@@ -19116,7 +19131,9 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19116
19131
  }
19117
19132
  }
19118
19133
  const idsToFetch = [...admissionSet].filter((id) => !laneById.has(id));
19134
+ const admittedStart = nowMs();
19119
19135
  const fetched = idsToFetch.length > 0 ? await getVaultMemoriesByIdsOp(vaultCtx, idsToFetch, hydrateOpts) : [];
19136
+ corpusStats.rowsFetchMs += nowMs() - admittedStart;
19120
19137
  rowsDecrypted += fetched.length;
19121
19138
  const admittedRows = [
19122
19139
  ...fetched,
@@ -19145,7 +19162,10 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19145
19162
  }
19146
19163
  if (staleIds.length > 0) {
19147
19164
  const contentById = new Map(memories.map((m) => [m.uniqueId, m]));
19148
- for (const r of await getVaultEmbeddingsByIdsOp(vaultCtx, staleIds, hydrateOpts)) {
19165
+ const staleStart = nowMs();
19166
+ const staleRows = await getVaultEmbeddingsByIdsOp(vaultCtx, staleIds, hydrateOpts);
19167
+ corpusStats.embeddingsFetchMs += nowMs() - staleStart;
19168
+ for (const r of staleRows) {
19149
19169
  if (!r.embedding) continue;
19150
19170
  try {
19151
19171
  const parsed = JSON.parse(r.embedding);
@@ -19177,7 +19197,15 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19177
19197
  eventTimeKind: normalizeEventTimeKind(m.eventTimeKind),
19178
19198
  factType: m.factType
19179
19199
  }));
19180
- return { memories, embeddedItems, queryEmbedding, vaultSize, laneEmbedFailed, rowsDecrypted };
19200
+ return {
19201
+ memories,
19202
+ embeddedItems,
19203
+ queryEmbedding,
19204
+ vaultSize,
19205
+ laneEmbedFailed,
19206
+ rowsDecrypted,
19207
+ corpusStats
19208
+ };
19181
19209
  }
19182
19210
  async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache, searchOptions) {
19183
19211
  if (!query || typeof query !== "string") {
@@ -19218,6 +19246,7 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19218
19246
  embeddingFailure = true;
19219
19247
  };
19220
19248
  const embedStats = { queryMs: 0, rowsEmbedded: 0 };
19249
+ let corpusStats;
19221
19250
  if (searchOptions?.decryptLast) {
19222
19251
  const forceIncludeIds = [
19223
19252
  ...searchOptions.entityRanking ?? [],
@@ -19238,6 +19267,7 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19238
19267
  onEmbeddingDegraded,
19239
19268
  embedStats
19240
19269
  });
19270
+ corpusStats = corpus.corpusStats;
19241
19271
  if (corpus.vaultSize === 0) {
19242
19272
  return {
19243
19273
  memories: [],
@@ -19249,7 +19279,8 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19249
19279
  decryptLast: true,
19250
19280
  rowsDecrypted: corpus.rowsDecrypted,
19251
19281
  queryEmbedMs: embedStats.queryMs,
19252
- rowsEmbedded: embedStats.rowsEmbedded
19282
+ rowsEmbedded: embedStats.rowsEmbedded,
19283
+ corpusStats
19253
19284
  };
19254
19285
  }
19255
19286
  ({ memories, embeddedItems, queryEmbedding, vaultSize } = corpus);
@@ -19266,7 +19297,8 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19266
19297
  decryptLast: true,
19267
19298
  rowsDecrypted: corpus.rowsDecrypted,
19268
19299
  queryEmbedMs: embedStats.queryMs,
19269
- rowsEmbedded: embedStats.rowsEmbedded
19300
+ rowsEmbedded: embedStats.rowsEmbedded,
19301
+ corpusStats
19270
19302
  };
19271
19303
  }
19272
19304
  } else {
@@ -19399,7 +19431,8 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19399
19431
  embeddingsUnavailable,
19400
19432
  embeddingFailure,
19401
19433
  decryptLast: !!searchOptions?.decryptLast,
19402
- rowsDecrypted
19434
+ rowsDecrypted,
19435
+ ...corpusStats && { corpusStats }
19403
19436
  };
19404
19437
  }
19405
19438
  async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, searchOptions) {
@@ -19616,7 +19649,8 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
19616
19649
  decryptLast: prepared.decryptLast,
19617
19650
  rowsDecrypted: prepared.rowsDecrypted,
19618
19651
  queryEmbedMs: prepared.queryEmbedMs,
19619
- rowsEmbedded: prepared.rowsEmbedded
19652
+ rowsEmbedded: prepared.rowsEmbedded,
19653
+ ...prepared.corpusStats && { corpusStats: prepared.corpusStats }
19620
19654
  };
19621
19655
  }
19622
19656
  if (prepared.memories.length === 0) {
@@ -19631,7 +19665,8 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
19631
19665
  decryptLast: prepared.decryptLast,
19632
19666
  rowsDecrypted: prepared.rowsDecrypted,
19633
19667
  queryEmbedMs: prepared.queryEmbedMs,
19634
- rowsEmbedded: prepared.rowsEmbedded
19668
+ rowsEmbedded: prepared.rowsEmbedded,
19669
+ ...prepared.corpusStats && { corpusStats: prepared.corpusStats }
19635
19670
  };
19636
19671
  }
19637
19672
  const ranked = await rankPreparedVaultCandidates(
@@ -19646,7 +19681,8 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
19646
19681
  decryptLast: prepared.decryptLast,
19647
19682
  rowsDecrypted: prepared.rowsDecrypted,
19648
19683
  queryEmbedMs: prepared.queryEmbedMs,
19649
- rowsEmbedded: prepared.rowsEmbedded
19684
+ rowsEmbedded: prepared.rowsEmbedded,
19685
+ ...prepared.corpusStats && { corpusStats: prepared.corpusStats }
19650
19686
  };
19651
19687
  }
19652
19688
  async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
@@ -21661,6 +21697,7 @@ async function recall(query, ctx, options = {}) {
21661
21697
  let factLaneRankedOnCosine = false;
21662
21698
  let decryptLastRan;
21663
21699
  let vaultRowsDecrypted;
21700
+ let corpusStats;
21664
21701
  let graphLaneCount = 0;
21665
21702
  let temporalLaneCount = 0;
21666
21703
  let graphLaneFailed = false;
@@ -21701,6 +21738,10 @@ async function recall(query, ctx, options = {}) {
21701
21738
  ...decryptLastRan !== void 0 && { decryptLast: decryptLastRan },
21702
21739
  ...vaultRowsDecrypted !== void 0 && { vaultRowsDecrypted },
21703
21740
  ...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
21741
+ ...corpusStats && {
21742
+ vaultCacheHits: corpusStats.cacheHits,
21743
+ vaultCacheMisses: corpusStats.cacheMisses
21744
+ },
21704
21745
  ...chunkStats && {
21705
21746
  chunkRowsScanned: chunkStats.rowsScanned,
21706
21747
  chunkRowsFetched: chunkStats.rowsFetched,
@@ -21723,7 +21764,12 @@ async function recall(query, ctx, options = {}) {
21723
21764
  rerank: rerankMs,
21724
21765
  queryEmbed: queryEmbedMs,
21725
21766
  chunkLane: chunkLaneMs,
21726
- fuse: fuseMs
21767
+ fuse: fuseMs,
21768
+ ...corpusStats && {
21769
+ corpusKeys: corpusStats.keysMs,
21770
+ corpusEmbeddingsFetch: corpusStats.embeddingsFetchMs,
21771
+ corpusRowsFetch: corpusStats.rowsFetchMs
21772
+ }
21727
21773
  },
21728
21774
  degraded
21729
21775
  };
@@ -21797,7 +21843,8 @@ async function recall(query, ctx, options = {}) {
21797
21843
  embeddingsUnavailable: factEmbeddingsUnavailable,
21798
21844
  rankedOnCosine: factRankedOnCosine,
21799
21845
  decryptLast: factDecryptLast,
21800
- rowsDecrypted: factRowsDecrypted
21846
+ rowsDecrypted: factRowsDecrypted,
21847
+ corpusStats: factCorpusStats
21801
21848
  } = await (ctx.factSource?.search.bind(ctx.factSource) ?? ((searchQuery, searchOptions) => searchVaultMemoriesWithSize(
21802
21849
  searchQuery,
21803
21850
  ctx.vaultCtx,
@@ -21863,6 +21910,7 @@ async function recall(query, ctx, options = {}) {
21863
21910
  factLaneRankedOnCosine = factRankedOnCosine;
21864
21911
  decryptLastRan = factDecryptLast;
21865
21912
  vaultRowsDecrypted = factRowsDecrypted;
21913
+ corpusStats = factCorpusStats;
21866
21914
  factLaneMs = nowMs() - factStart;
21867
21915
  }
21868
21916
  if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
@@ -30968,7 +31016,7 @@ async function requestICloudSignIn() {
30968
31016
  }
30969
31017
  return container.whenUserSignsIn();
30970
31018
  }
30971
- async function uploadFileToICloud(filename, content) {
31019
+ async function uploadFileToICloud(filename, content, backupRevision) {
30972
31020
  const container = await getContainer();
30973
31021
  const database = container.privateCloudDatabase;
30974
31022
  const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
@@ -30981,7 +31029,8 @@ async function uploadFileToICloud(filename, content) {
30981
31029
  filename: { value: filename },
30982
31030
  data: { value: base64Data },
30983
31031
  size: { value: content.size },
30984
- contentType: { value: content.type || "application/json" }
31032
+ contentType: { value: content.type || "application/json" },
31033
+ ...backupRevision ? { backupRevision: { value: backupRevision } } : {}
30985
31034
  }
30986
31035
  };
30987
31036
  const response = await database.saveRecords(record2);
@@ -30993,7 +31042,8 @@ async function uploadFileToICloud(filename, content) {
30993
31042
  recordName: savedRecord.recordName,
30994
31043
  filename,
30995
31044
  modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
30996
- size: content.size
31045
+ size: content.size,
31046
+ backupRevision: savedRecord.fields.backupRevision?.value
30997
31047
  };
30998
31048
  }
30999
31049
  async function listICloudFiles() {
@@ -31014,6 +31064,7 @@ async function listICloudFiles() {
31014
31064
  recordName: record2.recordName,
31015
31065
  filename: record2.fields.filename?.value ?? "",
31016
31066
  modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
31067
+ backupRevision: record2.fields.backupRevision?.value,
31017
31068
  size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
31018
31069
  }));
31019
31070
  return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
@@ -31067,35 +31118,53 @@ async function findICloudFile(filename) {
31067
31118
  recordName: record2.recordName,
31068
31119
  filename: record2.fields.filename?.value ?? "",
31069
31120
  modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
31121
+ backupRevision: record2.fields.backupRevision?.value,
31070
31122
  size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
31071
31123
  };
31072
31124
  }
31073
31125
 
31074
31126
  // src/lib/backup/icloud/backup.ts
31075
31127
  var isAuthError = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
31128
+ var UNREVISIONED_COLUMNS = /* @__PURE__ */ new Set([
31129
+ "updated_at",
31130
+ "vector",
31131
+ "embedding_model",
31132
+ "chunks",
31133
+ "origin"
31134
+ ]);
31135
+ async function getBackupRevision(database, conversationId) {
31136
+ const serialized = await database.read(async () => {
31137
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
31138
+ const tables = await Promise.all(
31139
+ ["conversations", "history"].map(
31140
+ (table) => database.get(table).query(Q20.where("conversation_id", conversationId), Q20.sortBy("id", Q20.asc)).unsafeFetchRaw()
31141
+ )
31142
+ );
31143
+ return JSON.stringify(
31144
+ tables.map(
31145
+ (rows) => rows.map(
31146
+ (row) => Object.entries(row).filter(([key]) => !key.startsWith("_") && !UNREVISIONED_COLUMNS.has(key)).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
31147
+ )
31148
+ )
31149
+ );
31150
+ });
31151
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(serialized));
31152
+ return "v1:" + Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
31153
+ }
31076
31154
  async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
31077
31155
  try {
31078
31156
  await deps.requestEncryptionKey(userAddress);
31079
31157
  const filename = `${conversationId}.json`;
31080
31158
  const existingFile = await findICloudFile(filename);
31081
- if (existingFile) {
31082
- const { Q: Q20 } = await import("@nozbe/watermelondb");
31083
- const conversationsCollection = database.get("conversations");
31084
- const records = await conversationsCollection.query(Q20.where("conversation_id", conversationId)).fetch();
31085
- if (records.length > 0) {
31086
- const conversation = conversationToStoredRaw(records[0]);
31087
- const localUpdated = conversation.updatedAt.getTime();
31088
- const remoteModified = existingFile.modifiedAt.getTime();
31089
- if (localUpdated <= remoteModified) {
31090
- return "skipped";
31091
- }
31092
- }
31159
+ const backupRevision = await getBackupRevision(database, conversationId);
31160
+ if (existingFile?.backupRevision === backupRevision) {
31161
+ return "skipped";
31093
31162
  }
31094
31163
  const exportResult = await deps.exportConversation(conversationId, userAddress);
31095
31164
  if (!exportResult.success || !exportResult.blob) {
31096
31165
  return "failed";
31097
31166
  }
31098
- await uploadFileToICloud(filename, exportResult.blob);
31167
+ await uploadFileToICloud(filename, exportResult.blob, backupRevision);
31099
31168
  return "uploaded";
31100
31169
  } catch (err) {
31101
31170
  if (isAuthError(err) && !_retried) {
@@ -109,6 +109,9 @@ interface CloudKitRecord {
109
109
  size: number;
110
110
  };
111
111
  };
112
+ backupRevision?: {
113
+ value: string;
114
+ };
112
115
  };
113
116
  modified?: {
114
117
  timestamp: number;
@@ -4508,6 +4511,13 @@ declare function eagerEmbedContent(content: string, embeddingOptions: EmbeddingO
4508
4511
  * trusting it (see `vectorVersion.ts`).
4509
4512
  */
4510
4513
  updatedAt?: Date): Promise<void>;
4514
+ interface CorpusStats {
4515
+ keysMs: number;
4516
+ embeddingsFetchMs: number;
4517
+ rowsFetchMs: number;
4518
+ cacheHits: number;
4519
+ cacheMisses: number;
4520
+ }
4511
4521
  /**
4512
4522
  * A single vault search result with its similarity score.
4513
4523
  */
@@ -4586,6 +4596,7 @@ declare function searchVaultMemoriesWithSize(query: string, vaultCtx: VaultMemor
4586
4596
  */
4587
4597
  queryEmbedMs: number;
4588
4598
  rowsEmbedded: number;
4599
+ corpusStats?: CorpusStats;
4589
4600
  }>;
4590
4601
  /**
4591
4602
  * Search vault memories by semantic similarity. Returns structured results
@@ -5166,6 +5177,8 @@ interface RecallDiagnostics {
5166
5177
  * LEGACY read path this batch is uncapped, so it can be the whole vault.
5167
5178
  */
5168
5179
  vaultRowsEmbedded?: number;
5180
+ vaultCacheHits?: number;
5181
+ vaultCacheMisses?: number;
5169
5182
  /** Embedded messages the chunk lane considered; absent when the lane did not run. */
5170
5183
  chunkRowsScanned?: number;
5171
5184
  /** Messages whose vectors the chunk lane read from storage on a cache miss. */
@@ -5268,6 +5281,9 @@ interface RecallDiagnostics {
5268
5281
  chunkLane: number;
5269
5282
  /** Cross-lane RRF fusion + provenance dedup after both lanes. */
5270
5283
  fuse: number;
5284
+ corpusKeys?: number;
5285
+ corpusEmbeddingsFetch?: number;
5286
+ corpusRowsFetch?: number;
5271
5287
  };
5272
5288
  /** Soft-degradation signals that fired this call (empty when clean). */
5273
5289
  degraded: RecallDegradation[];
@@ -109,6 +109,9 @@ interface CloudKitRecord {
109
109
  size: number;
110
110
  };
111
111
  };
112
+ backupRevision?: {
113
+ value: string;
114
+ };
112
115
  };
113
116
  modified?: {
114
117
  timestamp: number;
@@ -4508,6 +4511,13 @@ declare function eagerEmbedContent(content: string, embeddingOptions: EmbeddingO
4508
4511
  * trusting it (see `vectorVersion.ts`).
4509
4512
  */
4510
4513
  updatedAt?: Date): Promise<void>;
4514
+ interface CorpusStats {
4515
+ keysMs: number;
4516
+ embeddingsFetchMs: number;
4517
+ rowsFetchMs: number;
4518
+ cacheHits: number;
4519
+ cacheMisses: number;
4520
+ }
4511
4521
  /**
4512
4522
  * A single vault search result with its similarity score.
4513
4523
  */
@@ -4586,6 +4596,7 @@ declare function searchVaultMemoriesWithSize(query: string, vaultCtx: VaultMemor
4586
4596
  */
4587
4597
  queryEmbedMs: number;
4588
4598
  rowsEmbedded: number;
4599
+ corpusStats?: CorpusStats;
4589
4600
  }>;
4590
4601
  /**
4591
4602
  * Search vault memories by semantic similarity. Returns structured results
@@ -5166,6 +5177,8 @@ interface RecallDiagnostics {
5166
5177
  * LEGACY read path this batch is uncapped, so it can be the whole vault.
5167
5178
  */
5168
5179
  vaultRowsEmbedded?: number;
5180
+ vaultCacheHits?: number;
5181
+ vaultCacheMisses?: number;
5169
5182
  /** Embedded messages the chunk lane considered; absent when the lane did not run. */
5170
5183
  chunkRowsScanned?: number;
5171
5184
  /** Messages whose vectors the chunk lane read from storage on a cache miss. */
@@ -5268,6 +5281,9 @@ interface RecallDiagnostics {
5268
5281
  chunkLane: number;
5269
5282
  /** Cross-lane RRF fusion + provenance dedup after both lanes. */
5270
5283
  fuse: number;
5284
+ corpusKeys?: number;
5285
+ corpusEmbeddingsFetch?: number;
5286
+ corpusRowsFetch?: number;
5271
5287
  };
5272
5288
  /** Soft-degradation signals that fired this call (empty when clean). */
5273
5289
  degraded: RecallDegradation[];