@anuma/sdk 6.41.0 → 6.42.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.
@@ -21843,12 +21843,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
21843
21843
  interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
21844
21844
  };
21845
21845
  async function synthesizeProfile(ctx, options = {}) {
21846
- if (!ctx.vaultCtx) {
21847
- throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
21846
+ const { store } = options;
21847
+ const vaultCtx = ctx.vaultCtx;
21848
+ if (!store && !vaultCtx) {
21849
+ throw new Error(
21850
+ "synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
21851
+ );
21848
21852
  }
21849
- if (!ctx.vaultCache) {
21850
- throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
21853
+ if (!store && !ctx.vaultCache) {
21854
+ throw new Error(
21855
+ "synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
21856
+ );
21851
21857
  }
21858
+ const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
21852
21859
  const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
21853
21860
  const scopes = options.scopes ?? DEFAULT_SCOPES;
21854
21861
  const config = {
@@ -21859,11 +21866,8 @@ async function synthesizeProfile(ctx, options = {}) {
21859
21866
  reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
21860
21867
  };
21861
21868
  const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
21862
- const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
21863
- scopes,
21864
- includeDeleted: true,
21865
- includeSuperseded: true
21866
- });
21869
+ const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
21870
+ const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
21867
21871
  const watermark = computeVaultWatermark(memories);
21868
21872
  const observationTrends = summarizeObservationTrends(
21869
21873
  memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
@@ -21873,11 +21877,14 @@ async function synthesizeProfile(ctx, options = {}) {
21873
21877
  }))
21874
21878
  );
21875
21879
  const presentIds = new Set(memories.map((m) => m.uniqueId));
21880
+ const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
21881
+ const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
21882
+ const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
21876
21883
  const hasStaleSections = previous?.sections.some((s) => s.stale);
21877
21884
  const citesMissingFact = previous?.sections.some(
21878
21885
  (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
21879
21886
  );
21880
- if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
21887
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
21881
21888
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
21882
21889
  if (sameOrder) {
21883
21890
  if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
@@ -21890,12 +21897,13 @@ async function synthesizeProfile(ctx, options = {}) {
21890
21897
  };
21891
21898
  }
21892
21899
  const staleKeys = await computeStaleFacetKeys(
21893
- ctx,
21900
+ recallCtx,
21894
21901
  memories,
21895
21902
  facets,
21896
21903
  previous,
21897
21904
  watermark,
21898
- options.reviewedMemoryIds
21905
+ options.reviewedMemoryIds,
21906
+ previousUnreadable
21899
21907
  );
21900
21908
  const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
21901
21909
  const fallbackPriors = /* @__PURE__ */ new Map();
@@ -21915,7 +21923,7 @@ async function synthesizeProfile(ctx, options = {}) {
21915
21923
  if (prior && !staleKeys.has(facet.key)) {
21916
21924
  return prior;
21917
21925
  }
21918
- return synthesizeFacet(facet, ctx, options, fallbackPriors.get(facet.key));
21926
+ return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
21919
21927
  })
21920
21928
  );
21921
21929
  const sections = settled.map((r, i) => {
@@ -21930,12 +21938,18 @@ async function synthesizeProfile(ctx, options = {}) {
21930
21938
  );
21931
21939
  return fallbackSection(facet, fallbackPriors.get(facet.key));
21932
21940
  });
21941
+ if (!sameUnreadable && unreadableMemoryIds.length > 0) {
21942
+ getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
21943
+ unreadable: unreadableMemoryIds.length
21944
+ });
21945
+ }
21933
21946
  return {
21934
21947
  version: PROFILE_DOC_VERSION,
21935
21948
  sections,
21936
21949
  vaultWatermark: watermark,
21937
21950
  config,
21938
21951
  observationTrends,
21952
+ ...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
21939
21953
  generatedAt: Date.now()
21940
21954
  };
21941
21955
  }
@@ -21969,11 +21983,13 @@ function computeVaultWatermark(memories) {
21969
21983
  }
21970
21984
  return max;
21971
21985
  }
21972
- async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
21986
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
21973
21987
  const allKeys = new Set(facets.map((f) => f.key));
21974
21988
  if (previous && watermark < previous.vaultWatermark) return allKeys;
21975
21989
  if (!previous) return allKeys;
21976
- const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
21990
+ const changed = memories.filter(
21991
+ (m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
21992
+ );
21977
21993
  const changedIds = new Set(changed.map((m) => m.uniqueId));
21978
21994
  const presentIds = new Set(memories.map((m) => m.uniqueId));
21979
21995
  const stale = /* @__PURE__ */ new Set();
@@ -22047,7 +22063,12 @@ async function attributeFacts(ctx, candidates, facets) {
22047
22063
  for (const fv of factVectors) {
22048
22064
  let matchedAny = false;
22049
22065
  for (let i = 0; i < facets.length; i++) {
22050
- if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
22066
+ const query = queryVectors[i];
22067
+ const similarity = cosineSimilarity(
22068
+ fv,
22069
+ fv.length < query.length ? query.slice(0, fv.length) : query
22070
+ );
22071
+ if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
22051
22072
  keys.add(facets[i].key);
22052
22073
  matchedAny = true;
22053
22074
  }
@@ -9821,6 +9821,12 @@ interface ProfileDoc {
9821
9821
  * without another LLM pass. Recomputed every synthesis (not delta-cached).
9822
9822
  */
9823
9823
  observationTrends: Record<ObservationTrend, number>;
9824
+ /**
9825
+ * Sorted ids the store listed but could not decrypt at synthesis time.
9826
+ * Absent when every memory was readable. A later call regenerates only when
9827
+ * this set changes, and attributes a memory once it becomes readable.
9828
+ */
9829
+ unreadableMemoryIds?: string[];
9824
9830
  /** Unix ms this doc was produced. */
9825
9831
  generatedAt: number;
9826
9832
  }
@@ -9874,6 +9880,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
9874
9880
  * never accidentally disable the gate by finding it empty.
9875
9881
  */
9876
9882
  reviewedMemoryIds?: readonly string[];
9883
+ /**
9884
+ * Read facts and recall through this store instead of `ctx.vaultCtx` and
9885
+ * `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
9886
+ * then read from `ctx`.
9887
+ */
9888
+ store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
9877
9889
  }
9878
9890
  /**
9879
9891
  * Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
@@ -9821,6 +9821,12 @@ interface ProfileDoc {
9821
9821
  * without another LLM pass. Recomputed every synthesis (not delta-cached).
9822
9822
  */
9823
9823
  observationTrends: Record<ObservationTrend, number>;
9824
+ /**
9825
+ * Sorted ids the store listed but could not decrypt at synthesis time.
9826
+ * Absent when every memory was readable. A later call regenerates only when
9827
+ * this set changes, and attributes a memory once it becomes readable.
9828
+ */
9829
+ unreadableMemoryIds?: string[];
9824
9830
  /** Unix ms this doc was produced. */
9825
9831
  generatedAt: number;
9826
9832
  }
@@ -9874,6 +9880,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
9874
9880
  * never accidentally disable the gate by finding it empty.
9875
9881
  */
9876
9882
  reviewedMemoryIds?: readonly string[];
9883
+ /**
9884
+ * Read facts and recall through this store instead of `ctx.vaultCtx` and
9885
+ * `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
9886
+ * then read from `ctx`.
9887
+ */
9888
+ store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
9877
9889
  }
9878
9890
  /**
9879
9891
  * Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
@@ -20206,12 +20206,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
20206
20206
  interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
20207
20207
  };
20208
20208
  async function synthesizeProfile(ctx, options = {}) {
20209
- if (!ctx.vaultCtx) {
20210
- throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
20209
+ const { store } = options;
20210
+ const vaultCtx = ctx.vaultCtx;
20211
+ if (!store && !vaultCtx) {
20212
+ throw new Error(
20213
+ "synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
20214
+ );
20211
20215
  }
20212
- if (!ctx.vaultCache) {
20213
- throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
20216
+ if (!store && !ctx.vaultCache) {
20217
+ throw new Error(
20218
+ "synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
20219
+ );
20214
20220
  }
20221
+ const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
20215
20222
  const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
20216
20223
  const scopes = options.scopes ?? DEFAULT_SCOPES;
20217
20224
  const config = {
@@ -20222,11 +20229,8 @@ async function synthesizeProfile(ctx, options = {}) {
20222
20229
  reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
20223
20230
  };
20224
20231
  const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
20225
- const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
20226
- scopes,
20227
- includeDeleted: true,
20228
- includeSuperseded: true
20229
- });
20232
+ const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
20233
+ const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
20230
20234
  const watermark = computeVaultWatermark(memories);
20231
20235
  const observationTrends = summarizeObservationTrends(
20232
20236
  memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
@@ -20236,11 +20240,14 @@ async function synthesizeProfile(ctx, options = {}) {
20236
20240
  }))
20237
20241
  );
20238
20242
  const presentIds = new Set(memories.map((m) => m.uniqueId));
20243
+ const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
20244
+ const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
20245
+ const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
20239
20246
  const hasStaleSections = previous?.sections.some((s) => s.stale);
20240
20247
  const citesMissingFact = previous?.sections.some(
20241
20248
  (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
20242
20249
  );
20243
- if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
20250
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
20244
20251
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
20245
20252
  if (sameOrder) {
20246
20253
  if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
@@ -20253,12 +20260,13 @@ async function synthesizeProfile(ctx, options = {}) {
20253
20260
  };
20254
20261
  }
20255
20262
  const staleKeys = await computeStaleFacetKeys(
20256
- ctx,
20263
+ recallCtx,
20257
20264
  memories,
20258
20265
  facets,
20259
20266
  previous,
20260
20267
  watermark,
20261
- options.reviewedMemoryIds
20268
+ options.reviewedMemoryIds,
20269
+ previousUnreadable
20262
20270
  );
20263
20271
  const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
20264
20272
  const fallbackPriors = /* @__PURE__ */ new Map();
@@ -20278,7 +20286,7 @@ async function synthesizeProfile(ctx, options = {}) {
20278
20286
  if (prior && !staleKeys.has(facet.key)) {
20279
20287
  return prior;
20280
20288
  }
20281
- return synthesizeFacet(facet, ctx, options, fallbackPriors.get(facet.key));
20289
+ return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
20282
20290
  })
20283
20291
  );
20284
20292
  const sections = settled.map((r, i) => {
@@ -20293,12 +20301,18 @@ async function synthesizeProfile(ctx, options = {}) {
20293
20301
  );
20294
20302
  return fallbackSection(facet, fallbackPriors.get(facet.key));
20295
20303
  });
20304
+ if (!sameUnreadable && unreadableMemoryIds.length > 0) {
20305
+ getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
20306
+ unreadable: unreadableMemoryIds.length
20307
+ });
20308
+ }
20296
20309
  return {
20297
20310
  version: PROFILE_DOC_VERSION,
20298
20311
  sections,
20299
20312
  vaultWatermark: watermark,
20300
20313
  config,
20301
20314
  observationTrends,
20315
+ ...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
20302
20316
  generatedAt: Date.now()
20303
20317
  };
20304
20318
  }
@@ -20332,11 +20346,13 @@ function computeVaultWatermark(memories) {
20332
20346
  }
20333
20347
  return max;
20334
20348
  }
20335
- async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
20349
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
20336
20350
  const allKeys = new Set(facets.map((f) => f.key));
20337
20351
  if (previous && watermark < previous.vaultWatermark) return allKeys;
20338
20352
  if (!previous) return allKeys;
20339
- const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
20353
+ const changed = memories.filter(
20354
+ (m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
20355
+ );
20340
20356
  const changedIds = new Set(changed.map((m) => m.uniqueId));
20341
20357
  const presentIds = new Set(memories.map((m) => m.uniqueId));
20342
20358
  const stale = /* @__PURE__ */ new Set();
@@ -20410,7 +20426,12 @@ async function attributeFacts(ctx, candidates, facets) {
20410
20426
  for (const fv of factVectors) {
20411
20427
  let matchedAny = false;
20412
20428
  for (let i = 0; i < facets.length; i++) {
20413
- if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
20429
+ const query = queryVectors[i];
20430
+ const similarity = cosineSimilarity(
20431
+ fv,
20432
+ fv.length < query.length ? query.slice(0, fv.length) : query
20433
+ );
20434
+ if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
20414
20435
  keys.add(facets[i].key);
20415
20436
  matchedAny = true;
20416
20437
  }
@@ -25216,12 +25216,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
25216
25216
  interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
25217
25217
  };
25218
25218
  async function synthesizeProfile(ctx, options = {}) {
25219
- if (!ctx.vaultCtx) {
25220
- throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
25219
+ const { store } = options;
25220
+ const vaultCtx = ctx.vaultCtx;
25221
+ if (!store && !vaultCtx) {
25222
+ throw new Error(
25223
+ "synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
25224
+ );
25221
25225
  }
25222
- if (!ctx.vaultCache) {
25223
- throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
25226
+ if (!store && !ctx.vaultCache) {
25227
+ throw new Error(
25228
+ "synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
25229
+ );
25224
25230
  }
25231
+ const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
25225
25232
  const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
25226
25233
  const scopes = options.scopes ?? DEFAULT_SCOPES;
25227
25234
  const config = {
@@ -25232,11 +25239,8 @@ async function synthesizeProfile(ctx, options = {}) {
25232
25239
  reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
25233
25240
  };
25234
25241
  const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
25235
- const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
25236
- scopes,
25237
- includeDeleted: true,
25238
- includeSuperseded: true
25239
- });
25242
+ const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
25243
+ const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
25240
25244
  const watermark = computeVaultWatermark(memories);
25241
25245
  const observationTrends = summarizeObservationTrends(
25242
25246
  memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
@@ -25246,11 +25250,14 @@ async function synthesizeProfile(ctx, options = {}) {
25246
25250
  }))
25247
25251
  );
25248
25252
  const presentIds = new Set(memories.map((m) => m.uniqueId));
25253
+ const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
25254
+ const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
25255
+ const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
25249
25256
  const hasStaleSections = previous?.sections.some((s) => s.stale);
25250
25257
  const citesMissingFact = previous?.sections.some(
25251
25258
  (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
25252
25259
  );
25253
- if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
25260
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
25254
25261
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
25255
25262
  if (sameOrder) {
25256
25263
  if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
@@ -25263,12 +25270,13 @@ async function synthesizeProfile(ctx, options = {}) {
25263
25270
  };
25264
25271
  }
25265
25272
  const staleKeys = await computeStaleFacetKeys(
25266
- ctx,
25273
+ recallCtx,
25267
25274
  memories,
25268
25275
  facets,
25269
25276
  previous,
25270
25277
  watermark,
25271
- options.reviewedMemoryIds
25278
+ options.reviewedMemoryIds,
25279
+ previousUnreadable
25272
25280
  );
25273
25281
  const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
25274
25282
  const fallbackPriors = /* @__PURE__ */ new Map();
@@ -25288,7 +25296,7 @@ async function synthesizeProfile(ctx, options = {}) {
25288
25296
  if (prior && !staleKeys.has(facet.key)) {
25289
25297
  return prior;
25290
25298
  }
25291
- return synthesizeFacet(facet, ctx, options, fallbackPriors.get(facet.key));
25299
+ return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
25292
25300
  })
25293
25301
  );
25294
25302
  const sections = settled.map((r, i) => {
@@ -25303,12 +25311,18 @@ async function synthesizeProfile(ctx, options = {}) {
25303
25311
  );
25304
25312
  return fallbackSection(facet, fallbackPriors.get(facet.key));
25305
25313
  });
25314
+ if (!sameUnreadable && unreadableMemoryIds.length > 0) {
25315
+ getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
25316
+ unreadable: unreadableMemoryIds.length
25317
+ });
25318
+ }
25306
25319
  return {
25307
25320
  version: PROFILE_DOC_VERSION,
25308
25321
  sections,
25309
25322
  vaultWatermark: watermark,
25310
25323
  config,
25311
25324
  observationTrends,
25325
+ ...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
25312
25326
  generatedAt: Date.now()
25313
25327
  };
25314
25328
  }
@@ -25342,11 +25356,13 @@ function computeVaultWatermark(memories) {
25342
25356
  }
25343
25357
  return max;
25344
25358
  }
25345
- async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
25359
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
25346
25360
  const allKeys = new Set(facets.map((f) => f.key));
25347
25361
  if (previous && watermark < previous.vaultWatermark) return allKeys;
25348
25362
  if (!previous) return allKeys;
25349
- const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
25363
+ const changed = memories.filter(
25364
+ (m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
25365
+ );
25350
25366
  const changedIds = new Set(changed.map((m) => m.uniqueId));
25351
25367
  const presentIds = new Set(memories.map((m) => m.uniqueId));
25352
25368
  const stale = /* @__PURE__ */ new Set();
@@ -25420,7 +25436,12 @@ async function attributeFacts(ctx, candidates, facets) {
25420
25436
  for (const fv of factVectors) {
25421
25437
  let matchedAny = false;
25422
25438
  for (let i = 0; i < facets.length; i++) {
25423
- if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
25439
+ const query = queryVectors[i];
25440
+ const similarity = cosineSimilarity(
25441
+ fv,
25442
+ fv.length < query.length ? query.slice(0, fv.length) : query
25443
+ );
25444
+ if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
25424
25445
  keys.add(facets[i].key);
25425
25446
  matchedAny = true;
25426
25447
  }
@@ -12057,6 +12057,12 @@ interface ProfileDoc {
12057
12057
  * without another LLM pass. Recomputed every synthesis (not delta-cached).
12058
12058
  */
12059
12059
  observationTrends: Record<ObservationTrend, number>;
12060
+ /**
12061
+ * Sorted ids the store listed but could not decrypt at synthesis time.
12062
+ * Absent when every memory was readable. A later call regenerates only when
12063
+ * this set changes, and attributes a memory once it becomes readable.
12064
+ */
12065
+ unreadableMemoryIds?: string[];
12060
12066
  /** Unix ms this doc was produced. */
12061
12067
  generatedAt: number;
12062
12068
  }
@@ -12110,6 +12116,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
12110
12116
  * never accidentally disable the gate by finding it empty.
12111
12117
  */
12112
12118
  reviewedMemoryIds?: readonly string[];
12119
+ /**
12120
+ * Read facts and recall through this store instead of `ctx.vaultCtx` and
12121
+ * `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
12122
+ * then read from `ctx`.
12123
+ */
12124
+ store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
12113
12125
  }
12114
12126
  /**
12115
12127
  * Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
@@ -12057,6 +12057,12 @@ interface ProfileDoc {
12057
12057
  * without another LLM pass. Recomputed every synthesis (not delta-cached).
12058
12058
  */
12059
12059
  observationTrends: Record<ObservationTrend, number>;
12060
+ /**
12061
+ * Sorted ids the store listed but could not decrypt at synthesis time.
12062
+ * Absent when every memory was readable. A later call regenerates only when
12063
+ * this set changes, and attributes a memory once it becomes readable.
12064
+ */
12065
+ unreadableMemoryIds?: string[];
12060
12066
  /** Unix ms this doc was produced. */
12061
12067
  generatedAt: number;
12062
12068
  }
@@ -12110,6 +12116,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
12110
12116
  * never accidentally disable the gate by finding it empty.
12111
12117
  */
12112
12118
  reviewedMemoryIds?: readonly string[];
12119
+ /**
12120
+ * Read facts and recall through this store instead of `ctx.vaultCtx` and
12121
+ * `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
12122
+ * then read from `ctx`.
12123
+ */
12124
+ store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
12113
12125
  }
12114
12126
  /**
12115
12127
  * Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
@@ -23308,12 +23308,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
23308
23308
  interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
23309
23309
  };
23310
23310
  async function synthesizeProfile(ctx, options = {}) {
23311
- if (!ctx.vaultCtx) {
23312
- throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
23311
+ const { store } = options;
23312
+ const vaultCtx = ctx.vaultCtx;
23313
+ if (!store && !vaultCtx) {
23314
+ throw new Error(
23315
+ "synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
23316
+ );
23313
23317
  }
23314
- if (!ctx.vaultCache) {
23315
- throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
23318
+ if (!store && !ctx.vaultCache) {
23319
+ throw new Error(
23320
+ "synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
23321
+ );
23316
23322
  }
23323
+ const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
23317
23324
  const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
23318
23325
  const scopes = options.scopes ?? DEFAULT_SCOPES;
23319
23326
  const config = {
@@ -23324,11 +23331,8 @@ async function synthesizeProfile(ctx, options = {}) {
23324
23331
  reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
23325
23332
  };
23326
23333
  const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
23327
- const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
23328
- scopes,
23329
- includeDeleted: true,
23330
- includeSuperseded: true
23331
- });
23334
+ const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
23335
+ const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
23332
23336
  const watermark = computeVaultWatermark(memories);
23333
23337
  const observationTrends = summarizeObservationTrends(
23334
23338
  memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
@@ -23338,11 +23342,14 @@ async function synthesizeProfile(ctx, options = {}) {
23338
23342
  }))
23339
23343
  );
23340
23344
  const presentIds = new Set(memories.map((m) => m.uniqueId));
23345
+ const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
23346
+ const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
23347
+ const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
23341
23348
  const hasStaleSections = previous?.sections.some((s) => s.stale);
23342
23349
  const citesMissingFact = previous?.sections.some(
23343
23350
  (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
23344
23351
  );
23345
- if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
23352
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
23346
23353
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
23347
23354
  if (sameOrder) {
23348
23355
  if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
@@ -23355,12 +23362,13 @@ async function synthesizeProfile(ctx, options = {}) {
23355
23362
  };
23356
23363
  }
23357
23364
  const staleKeys = await computeStaleFacetKeys(
23358
- ctx,
23365
+ recallCtx,
23359
23366
  memories,
23360
23367
  facets,
23361
23368
  previous,
23362
23369
  watermark,
23363
- options.reviewedMemoryIds
23370
+ options.reviewedMemoryIds,
23371
+ previousUnreadable
23364
23372
  );
23365
23373
  const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
23366
23374
  const fallbackPriors = /* @__PURE__ */ new Map();
@@ -23380,7 +23388,7 @@ async function synthesizeProfile(ctx, options = {}) {
23380
23388
  if (prior && !staleKeys.has(facet.key)) {
23381
23389
  return prior;
23382
23390
  }
23383
- return synthesizeFacet(facet, ctx, options, fallbackPriors.get(facet.key));
23391
+ return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
23384
23392
  })
23385
23393
  );
23386
23394
  const sections = settled.map((r, i) => {
@@ -23395,12 +23403,18 @@ async function synthesizeProfile(ctx, options = {}) {
23395
23403
  );
23396
23404
  return fallbackSection(facet, fallbackPriors.get(facet.key));
23397
23405
  });
23406
+ if (!sameUnreadable && unreadableMemoryIds.length > 0) {
23407
+ getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
23408
+ unreadable: unreadableMemoryIds.length
23409
+ });
23410
+ }
23398
23411
  return {
23399
23412
  version: PROFILE_DOC_VERSION,
23400
23413
  sections,
23401
23414
  vaultWatermark: watermark,
23402
23415
  config,
23403
23416
  observationTrends,
23417
+ ...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
23404
23418
  generatedAt: Date.now()
23405
23419
  };
23406
23420
  }
@@ -23434,11 +23448,13 @@ function computeVaultWatermark(memories) {
23434
23448
  }
23435
23449
  return max;
23436
23450
  }
23437
- async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
23451
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
23438
23452
  const allKeys = new Set(facets.map((f) => f.key));
23439
23453
  if (previous && watermark < previous.vaultWatermark) return allKeys;
23440
23454
  if (!previous) return allKeys;
23441
- const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
23455
+ const changed = memories.filter(
23456
+ (m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
23457
+ );
23442
23458
  const changedIds = new Set(changed.map((m) => m.uniqueId));
23443
23459
  const presentIds = new Set(memories.map((m) => m.uniqueId));
23444
23460
  const stale = /* @__PURE__ */ new Set();
@@ -23512,7 +23528,12 @@ async function attributeFacts(ctx, candidates, facets) {
23512
23528
  for (const fv of factVectors) {
23513
23529
  let matchedAny = false;
23514
23530
  for (let i = 0; i < facets.length; i++) {
23515
- if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
23531
+ const query = queryVectors[i];
23532
+ const similarity = cosineSimilarity(
23533
+ fv,
23534
+ fv.length < query.length ? query.slice(0, fv.length) : query
23535
+ );
23536
+ if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
23516
23537
  keys.add(facets[i].key);
23517
23538
  matchedAny = true;
23518
23539
  }
@@ -35411,12 +35411,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
35411
35411
  interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
35412
35412
  };
35413
35413
  async function synthesizeProfile(ctx, options = {}) {
35414
- if (!ctx.vaultCtx) {
35415
- throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
35414
+ const { store } = options;
35415
+ const vaultCtx = ctx.vaultCtx;
35416
+ if (!store && !vaultCtx) {
35417
+ throw new Error(
35418
+ "synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
35419
+ );
35416
35420
  }
35417
- if (!ctx.vaultCache) {
35418
- throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
35421
+ if (!store && !ctx.vaultCache) {
35422
+ throw new Error(
35423
+ "synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
35424
+ );
35419
35425
  }
35426
+ const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
35420
35427
  const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
35421
35428
  const scopes = options.scopes ?? DEFAULT_SCOPES;
35422
35429
  const config = {
@@ -35427,11 +35434,8 @@ async function synthesizeProfile(ctx, options = {}) {
35427
35434
  reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
35428
35435
  };
35429
35436
  const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
35430
- const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
35431
- scopes,
35432
- includeDeleted: true,
35433
- includeSuperseded: true
35434
- });
35437
+ const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
35438
+ const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
35435
35439
  const watermark = computeVaultWatermark(memories);
35436
35440
  const observationTrends = summarizeObservationTrends(
35437
35441
  memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
@@ -35441,11 +35445,14 @@ async function synthesizeProfile(ctx, options = {}) {
35441
35445
  }))
35442
35446
  );
35443
35447
  const presentIds = new Set(memories.map((m) => m.uniqueId));
35448
+ const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
35449
+ const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
35450
+ const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
35444
35451
  const hasStaleSections = previous?.sections.some((s) => s.stale);
35445
35452
  const citesMissingFact = previous?.sections.some(
35446
35453
  (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
35447
35454
  );
35448
- if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
35455
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
35449
35456
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
35450
35457
  if (sameOrder) {
35451
35458
  if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
@@ -35458,12 +35465,13 @@ async function synthesizeProfile(ctx, options = {}) {
35458
35465
  };
35459
35466
  }
35460
35467
  const staleKeys = await computeStaleFacetKeys(
35461
- ctx,
35468
+ recallCtx,
35462
35469
  memories,
35463
35470
  facets,
35464
35471
  previous,
35465
35472
  watermark,
35466
- options.reviewedMemoryIds
35473
+ options.reviewedMemoryIds,
35474
+ previousUnreadable
35467
35475
  );
35468
35476
  const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
35469
35477
  const fallbackPriors = /* @__PURE__ */ new Map();
@@ -35483,7 +35491,7 @@ async function synthesizeProfile(ctx, options = {}) {
35483
35491
  if (prior && !staleKeys.has(facet.key)) {
35484
35492
  return prior;
35485
35493
  }
35486
- return synthesizeFacet(facet, ctx, options, fallbackPriors.get(facet.key));
35494
+ return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
35487
35495
  })
35488
35496
  );
35489
35497
  const sections = settled.map((r, i) => {
@@ -35498,12 +35506,18 @@ async function synthesizeProfile(ctx, options = {}) {
35498
35506
  );
35499
35507
  return fallbackSection(facet, fallbackPriors.get(facet.key));
35500
35508
  });
35509
+ if (!sameUnreadable && unreadableMemoryIds.length > 0) {
35510
+ getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
35511
+ unreadable: unreadableMemoryIds.length
35512
+ });
35513
+ }
35501
35514
  return {
35502
35515
  version: PROFILE_DOC_VERSION,
35503
35516
  sections,
35504
35517
  vaultWatermark: watermark,
35505
35518
  config,
35506
35519
  observationTrends,
35520
+ ...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
35507
35521
  generatedAt: Date.now()
35508
35522
  };
35509
35523
  }
@@ -35537,11 +35551,13 @@ function computeVaultWatermark(memories) {
35537
35551
  }
35538
35552
  return max;
35539
35553
  }
35540
- async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
35554
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
35541
35555
  const allKeys = new Set(facets.map((f) => f.key));
35542
35556
  if (previous && watermark < previous.vaultWatermark) return allKeys;
35543
35557
  if (!previous) return allKeys;
35544
- const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
35558
+ const changed = memories.filter(
35559
+ (m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
35560
+ );
35545
35561
  const changedIds = new Set(changed.map((m) => m.uniqueId));
35546
35562
  const presentIds = new Set(memories.map((m) => m.uniqueId));
35547
35563
  const stale = /* @__PURE__ */ new Set();
@@ -35615,7 +35631,12 @@ async function attributeFacts(ctx, candidates, facets) {
35615
35631
  for (const fv of factVectors) {
35616
35632
  let matchedAny = false;
35617
35633
  for (let i = 0; i < facets.length; i++) {
35618
- if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
35634
+ const query = queryVectors[i];
35635
+ const similarity = cosineSimilarity(
35636
+ fv,
35637
+ fv.length < query.length ? query.slice(0, fv.length) : query
35638
+ );
35639
+ if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
35619
35640
  keys.add(facets[i].key);
35620
35641
  matchedAny = true;
35621
35642
  }
@@ -9766,6 +9766,12 @@ interface ProfileDoc {
9766
9766
  * without another LLM pass. Recomputed every synthesis (not delta-cached).
9767
9767
  */
9768
9768
  observationTrends: Record<ObservationTrend, number>;
9769
+ /**
9770
+ * Sorted ids the store listed but could not decrypt at synthesis time.
9771
+ * Absent when every memory was readable. A later call regenerates only when
9772
+ * this set changes, and attributes a memory once it becomes readable.
9773
+ */
9774
+ unreadableMemoryIds?: string[];
9769
9775
  /** Unix ms this doc was produced. */
9770
9776
  generatedAt: number;
9771
9777
  }
@@ -9819,6 +9825,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
9819
9825
  * never accidentally disable the gate by finding it empty.
9820
9826
  */
9821
9827
  reviewedMemoryIds?: readonly string[];
9828
+ /**
9829
+ * Read facts and recall through this store instead of `ctx.vaultCtx` and
9830
+ * `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
9831
+ * then read from `ctx`.
9832
+ */
9833
+ store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
9822
9834
  }
9823
9835
  /**
9824
9836
  * Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
@@ -9766,6 +9766,12 @@ interface ProfileDoc {
9766
9766
  * without another LLM pass. Recomputed every synthesis (not delta-cached).
9767
9767
  */
9768
9768
  observationTrends: Record<ObservationTrend, number>;
9769
+ /**
9770
+ * Sorted ids the store listed but could not decrypt at synthesis time.
9771
+ * Absent when every memory was readable. A later call regenerates only when
9772
+ * this set changes, and attributes a memory once it becomes readable.
9773
+ */
9774
+ unreadableMemoryIds?: string[];
9769
9775
  /** Unix ms this doc was produced. */
9770
9776
  generatedAt: number;
9771
9777
  }
@@ -9819,6 +9825,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
9819
9825
  * never accidentally disable the gate by finding it empty.
9820
9826
  */
9821
9827
  reviewedMemoryIds?: readonly string[];
9828
+ /**
9829
+ * Read facts and recall through this store instead of `ctx.vaultCtx` and
9830
+ * `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
9831
+ * then read from `ctx`.
9832
+ */
9833
+ store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
9822
9834
  }
9823
9835
  /**
9824
9836
  * Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
@@ -33686,12 +33686,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
33686
33686
  interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
33687
33687
  };
33688
33688
  async function synthesizeProfile(ctx, options = {}) {
33689
- if (!ctx.vaultCtx) {
33690
- throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
33689
+ const { store } = options;
33690
+ const vaultCtx = ctx.vaultCtx;
33691
+ if (!store && !vaultCtx) {
33692
+ throw new Error(
33693
+ "synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
33694
+ );
33691
33695
  }
33692
- if (!ctx.vaultCache) {
33693
- throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
33696
+ if (!store && !ctx.vaultCache) {
33697
+ throw new Error(
33698
+ "synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
33699
+ );
33694
33700
  }
33701
+ const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
33695
33702
  const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
33696
33703
  const scopes = options.scopes ?? DEFAULT_SCOPES;
33697
33704
  const config = {
@@ -33702,11 +33709,8 @@ async function synthesizeProfile(ctx, options = {}) {
33702
33709
  reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
33703
33710
  };
33704
33711
  const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
33705
- const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
33706
- scopes,
33707
- includeDeleted: true,
33708
- includeSuperseded: true
33709
- });
33712
+ const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
33713
+ const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
33710
33714
  const watermark = computeVaultWatermark(memories);
33711
33715
  const observationTrends = summarizeObservationTrends(
33712
33716
  memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
@@ -33716,11 +33720,14 @@ async function synthesizeProfile(ctx, options = {}) {
33716
33720
  }))
33717
33721
  );
33718
33722
  const presentIds = new Set(memories.map((m) => m.uniqueId));
33723
+ const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
33724
+ const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
33725
+ const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
33719
33726
  const hasStaleSections = previous?.sections.some((s) => s.stale);
33720
33727
  const citesMissingFact = previous?.sections.some(
33721
33728
  (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
33722
33729
  );
33723
- if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
33730
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
33724
33731
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
33725
33732
  if (sameOrder) {
33726
33733
  if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
@@ -33733,12 +33740,13 @@ async function synthesizeProfile(ctx, options = {}) {
33733
33740
  };
33734
33741
  }
33735
33742
  const staleKeys = await computeStaleFacetKeys(
33736
- ctx,
33743
+ recallCtx,
33737
33744
  memories,
33738
33745
  facets,
33739
33746
  previous,
33740
33747
  watermark,
33741
- options.reviewedMemoryIds
33748
+ options.reviewedMemoryIds,
33749
+ previousUnreadable
33742
33750
  );
33743
33751
  const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
33744
33752
  const fallbackPriors = /* @__PURE__ */ new Map();
@@ -33758,7 +33766,7 @@ async function synthesizeProfile(ctx, options = {}) {
33758
33766
  if (prior && !staleKeys.has(facet.key)) {
33759
33767
  return prior;
33760
33768
  }
33761
- return synthesizeFacet(facet, ctx, options, fallbackPriors.get(facet.key));
33769
+ return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
33762
33770
  })
33763
33771
  );
33764
33772
  const sections = settled.map((r, i) => {
@@ -33773,12 +33781,18 @@ async function synthesizeProfile(ctx, options = {}) {
33773
33781
  );
33774
33782
  return fallbackSection(facet, fallbackPriors.get(facet.key));
33775
33783
  });
33784
+ if (!sameUnreadable && unreadableMemoryIds.length > 0) {
33785
+ getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
33786
+ unreadable: unreadableMemoryIds.length
33787
+ });
33788
+ }
33776
33789
  return {
33777
33790
  version: PROFILE_DOC_VERSION,
33778
33791
  sections,
33779
33792
  vaultWatermark: watermark,
33780
33793
  config,
33781
33794
  observationTrends,
33795
+ ...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
33782
33796
  generatedAt: Date.now()
33783
33797
  };
33784
33798
  }
@@ -33812,11 +33826,13 @@ function computeVaultWatermark(memories) {
33812
33826
  }
33813
33827
  return max;
33814
33828
  }
33815
- async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
33829
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
33816
33830
  const allKeys = new Set(facets.map((f) => f.key));
33817
33831
  if (previous && watermark < previous.vaultWatermark) return allKeys;
33818
33832
  if (!previous) return allKeys;
33819
- const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
33833
+ const changed = memories.filter(
33834
+ (m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
33835
+ );
33820
33836
  const changedIds = new Set(changed.map((m) => m.uniqueId));
33821
33837
  const presentIds = new Set(memories.map((m) => m.uniqueId));
33822
33838
  const stale = /* @__PURE__ */ new Set();
@@ -33890,7 +33906,12 @@ async function attributeFacts(ctx, candidates, facets) {
33890
33906
  for (const fv of factVectors) {
33891
33907
  let matchedAny = false;
33892
33908
  for (let i = 0; i < facets.length; i++) {
33893
- if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
33909
+ const query = queryVectors[i];
33910
+ const similarity = cosineSimilarity(
33911
+ fv,
33912
+ fv.length < query.length ? query.slice(0, fv.length) : query
33913
+ );
33914
+ if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
33894
33915
  keys.add(facets[i].key);
33895
33916
  matchedAny = true;
33896
33917
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "6.41.0",
3
+ "version": "6.42.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",