@anuma/sdk 3.1.3 → 3.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.
@@ -87,7 +87,7 @@ import {
87
87
  upsertMessageOp,
88
88
  useEncryption,
89
89
  vaultMemoryToStored
90
- } from "./chunk-JVL5PLZS.mjs";
90
+ } from "./chunk-KXIUA7ZV.mjs";
91
91
  import {
92
92
  PII_PATTERNS,
93
93
  PiiRedactor,
@@ -6391,6 +6391,9 @@ __decorateClass([
6391
6391
  __decorateClass([
6392
6392
  field3("topics_extracted_version")
6393
6393
  ], VaultMemory.prototype, "topicsExtractedVersion", 2);
6394
+ __decorateClass([
6395
+ field3("last_observed_at")
6396
+ ], VaultMemory.prototype, "lastObservedAt", 2);
6394
6397
  __decorateClass([
6395
6398
  readonly2,
6396
6399
  date3("created_at")
@@ -6796,6 +6799,8 @@ async function retain(content, ctx, options = {}) {
6796
6799
  proofCountIncrement: 1,
6797
6800
  sourceChunkIds: mergedSourceIds,
6798
6801
  preserveUpdatedAt: true,
6802
+ // C3: record the re-observation without touching updated_at.
6803
+ lastObservedAt: Date.now(),
6799
6804
  ...eventTimeUpdate && { eventTime: eventTimeUpdate }
6800
6805
  });
6801
6806
  if (updated) {
@@ -6946,6 +6951,8 @@ async function tryConsolidate(trimmed, ctx, options) {
6946
6951
  proofCountIncrement: 1,
6947
6952
  sourceChunkIds: mergedSourceIds,
6948
6953
  preserveUpdatedAt: true,
6954
+ // C3: record the re-observation without touching updated_at.
6955
+ lastObservedAt: Date.now(),
6949
6956
  ...eventTimeUpdate && { eventTime: eventTimeUpdate }
6950
6957
  });
6951
6958
  if (!updated) {
@@ -6982,6 +6989,8 @@ async function tryConsolidate(trimmed, ctx, options) {
6982
6989
  // one. Preserving updated_at keeps the recency multiplier honest
6983
6990
  // and matches the merge/noop paths above.
6984
6991
  preserveUpdatedAt: true,
6992
+ // C3: record the re-observation without touching updated_at.
6993
+ lastObservedAt: Date.now(),
6985
6994
  ...eventTimeUpdate && { eventTime: eventTimeUpdate }
6986
6995
  });
6987
6996
  if (!updated) {
@@ -7785,6 +7794,311 @@ function parseAnswer(body, base, parseSchema) {
7785
7794
  };
7786
7795
  }
7787
7796
 
7797
+ // src/lib/memory/synthesizeProfile.ts
7798
+ var DEFAULT_SYNTHESIS_MODEL = "inclusionai/ling-2.6-flash";
7799
+ var DEFAULT_FACET_RECALL_LIMIT = 20;
7800
+ var DEFAULT_FACET_MAX_TOKENS = 512;
7801
+ var DEFAULT_SCOPES = ["private"];
7802
+ var NEW_FACT_ATTRIBUTION_MIN_SCORE = 0.1;
7803
+ var PROFILE_DOC_VERSION = 1;
7804
+ var DEFAULT_PROFILE_FACETS = [
7805
+ {
7806
+ key: "bio",
7807
+ label: "Bio",
7808
+ query: "Who is this person? Their background, personality, values, and what defines them.",
7809
+ guidance: "Write a 1-2 sentence bio (max ~40 words) that captures what makes this person distinctive \u2014 their character, values, or a defining thread across the memories. Be specific and grounded; ban generic dating clich\xE9s ('loves to laugh', 'foodie', 'work hard play hard', 'living life to the fullest')."
7810
+ },
7811
+ {
7812
+ key: "interests",
7813
+ label: "Interests",
7814
+ query: "What are this person's hobbies, passions, pastimes, and interests?",
7815
+ guidance: "Return 3-6 specific interests as a comma-separated list (e.g. 'trail running, film photography, Thai cooking'). Prefer concrete activities the memories actually show over broad categories ('music', 'travel'). No sentence, just the list."
7816
+ },
7817
+ {
7818
+ key: "work_role",
7819
+ label: "Work & Role",
7820
+ query: "What does this person do for work \u2014 their profession, role, industry, or studies?",
7821
+ guidance: "State the person's current role/profession and field in one short line (e.g. 'Backend engineer at a fintech startup'). Use only what the memories support; if unclear or absent, return hasEvidence=false rather than guessing a title."
7822
+ },
7823
+ {
7824
+ key: "location_context",
7825
+ label: "Location",
7826
+ query: "Where does this person live, spend time, or come from?",
7827
+ guidance: "Summarize where the person is based or spends time at neighborhood-or-city granularity in one short line (e.g. 'Based in the Mission, SF'). PRIVACY: never emit a precise address, building, or workplace location \u2014 coarse-grain to the city/neighborhood."
7828
+ },
7829
+ {
7830
+ key: "communication_style",
7831
+ label: "Communication Style",
7832
+ query: "How does this person communicate, express themselves, and interact with others?",
7833
+ guidance: "Describe the person's communication and social style in one line using 2-4 concrete adjectives grounded in the memories (e.g. 'Direct, dry-humored, and a careful listener'). Avoid empty praise."
7834
+ },
7835
+ {
7836
+ key: "recent_activity",
7837
+ label: "Recently",
7838
+ query: "What has this person been doing, focused on, or working on recently?",
7839
+ guidance: "Summarize what the person has been up to lately in one line, favoring the most recently reinforced facts. Frame it as current/ongoing (e.g. 'Lately: training for a first half-marathon and learning Portuguese'). Omit if nothing recent stands out."
7840
+ }
7841
+ ];
7842
+ var FACET_RESPONSE_SCHEMA = {
7843
+ type: "object",
7844
+ properties: {
7845
+ summary: {
7846
+ type: "string",
7847
+ description: "The synthesized section prose. Empty string if the memories don't cover it."
7848
+ },
7849
+ hasEvidence: {
7850
+ type: "boolean",
7851
+ description: "False when the supplied memories don't support any claim for this facet."
7852
+ }
7853
+ },
7854
+ required: ["summary", "hasEvidence"],
7855
+ additionalProperties: false
7856
+ };
7857
+ async function synthesizeProfile(ctx, options = {}) {
7858
+ if (!ctx.vaultCtx) {
7859
+ throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
7860
+ }
7861
+ if (!ctx.vaultCache) {
7862
+ throw new Error("synthesizeProfile requires ctx.vaultCache (semantic fact recall).");
7863
+ }
7864
+ const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
7865
+ const scopes = options.scopes ?? DEFAULT_SCOPES;
7866
+ const config = {
7867
+ facetKeys: facets.map((f) => f.key).sort(),
7868
+ facetsSignature: facetsSignature(facets),
7869
+ scopes: [...scopes].sort(),
7870
+ redacted: options.redactor !== void 0
7871
+ };
7872
+ const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
7873
+ const memories = await getAllVaultMemoriesOp(ctx.vaultCtx, {
7874
+ // Scope the snapshot to the same scopes synthesis recalls from, so the
7875
+ // watermark, delta, and new-fact attribution all track only facts that can
7876
+ // actually appear in the profile — an out-of-scope change/new-fact must not
7877
+ // trigger a regeneration that scoped recall would never reflect.
7878
+ scopes,
7879
+ includeDeleted: true,
7880
+ includeSuperseded: true
7881
+ });
7882
+ const watermark = computeVaultWatermark(memories);
7883
+ const presentIds = new Set(memories.map((m) => m.uniqueId));
7884
+ const hasStaleSections = previous?.sections.some((s) => s.stale);
7885
+ const citesMissingFact = previous?.sections.some(
7886
+ (s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
7887
+ );
7888
+ if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
7889
+ const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
7890
+ if (sameOrder) return previous;
7891
+ return {
7892
+ ...previous,
7893
+ sections: facets.map((f) => previous.sections.find((s) => s.key === f.key))
7894
+ };
7895
+ }
7896
+ const staleKeys = await computeStaleFacetKeys(ctx, memories, facets, previous, watermark);
7897
+ const settled = await Promise.allSettled(
7898
+ facets.map(async (facet) => {
7899
+ const prior = previous?.sections.find((s) => s.key === facet.key);
7900
+ if (prior && !staleKeys.has(facet.key)) {
7901
+ return prior;
7902
+ }
7903
+ return synthesizeFacet(facet, ctx, options, prior);
7904
+ })
7905
+ );
7906
+ const sections = settled.map((r, i) => {
7907
+ if (r.status === "fulfilled") return r.value;
7908
+ const facet = facets[i];
7909
+ getLogger().warn(
7910
+ "[memory/synthesizeProfile] facet synthesis rejected; using fallback section",
7911
+ {
7912
+ facet: facet.key,
7913
+ error: r.reason instanceof Error ? r.reason.message : String(r.reason)
7914
+ }
7915
+ );
7916
+ return fallbackSection(
7917
+ facet,
7918
+ previous?.sections.find((s) => s.key === facet.key)
7919
+ );
7920
+ });
7921
+ return {
7922
+ version: PROFILE_DOC_VERSION,
7923
+ sections,
7924
+ vaultWatermark: watermark,
7925
+ config,
7926
+ generatedAt: Date.now()
7927
+ };
7928
+ }
7929
+ function configMatches(a, b) {
7930
+ if (!a) return false;
7931
+ return a.redacted === b.redacted && a.facetsSignature === b.facetsSignature && a.scopes.length === b.scopes.length && a.scopes.every((s, i) => s === b.scopes[i]);
7932
+ }
7933
+ function facetsSignature(facets) {
7934
+ return facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance])).sort().join("\n");
7935
+ }
7936
+ function changeTime(m) {
7937
+ return Math.max(m.updatedAt.getTime(), m.supersededAt ?? 0, m.lastObservedAt ?? 0);
7938
+ }
7939
+ function computeVaultWatermark(memories) {
7940
+ let max = 0;
7941
+ for (const m of memories) {
7942
+ const t = changeTime(m);
7943
+ if (t > max) max = t;
7944
+ }
7945
+ return max;
7946
+ }
7947
+ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark) {
7948
+ const allKeys = new Set(facets.map((f) => f.key));
7949
+ if (previous && watermark < previous.vaultWatermark) return allKeys;
7950
+ if (!previous) return allKeys;
7951
+ const changed = memories.filter((m) => changeTime(m) > previous.vaultWatermark);
7952
+ const changedIds = new Set(changed.map((m) => m.uniqueId));
7953
+ const presentIds = new Set(memories.map((m) => m.uniqueId));
7954
+ const stale = /* @__PURE__ */ new Set();
7955
+ for (const section of previous.sections) {
7956
+ if (section.sourceMemoryIds.some((id) => changedIds.has(id))) {
7957
+ stale.add(section.key);
7958
+ }
7959
+ if (section.sourceMemoryIds.some((id) => !presentIds.has(id))) {
7960
+ stale.add(section.key);
7961
+ }
7962
+ if (section.stale) {
7963
+ stale.add(section.key);
7964
+ }
7965
+ }
7966
+ for (const facet of facets) {
7967
+ if (!previous.sections.some((s) => s.key === facet.key)) stale.add(facet.key);
7968
+ }
7969
+ const citedIds = new Set(previous.sections.flatMap((s) => s.sourceMemoryIds));
7970
+ const toAttribute = changed.filter(
7971
+ (m) => !m.isDeleted && !m.supersededBy && !citedIds.has(m.uniqueId)
7972
+ );
7973
+ if (toAttribute.length > 0) {
7974
+ const attributed = await attributeFacts(ctx, toAttribute, facets);
7975
+ if (attributed === null) return allKeys;
7976
+ for (const k of attributed) stale.add(k);
7977
+ }
7978
+ return stale;
7979
+ }
7980
+ async function attributeFacts(ctx, candidates, facets) {
7981
+ const model = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
7982
+ const factVectors = [];
7983
+ for (const f of candidates) {
7984
+ if (!f.embedding || f.embeddingModel && f.embeddingModel !== model) {
7985
+ getLogger().warn(
7986
+ "[memory/synthesizeProfile] fact lacks a usable current-model embedding; regenerating all facets",
7987
+ { memoryId: f.uniqueId, hasEmbedding: !!f.embedding, embeddingModel: f.embeddingModel }
7988
+ );
7989
+ return null;
7990
+ }
7991
+ let vec;
7992
+ try {
7993
+ vec = JSON.parse(f.embedding);
7994
+ } catch {
7995
+ getLogger().warn(
7996
+ "[memory/synthesizeProfile] fact embedding failed to parse; regenerating all facets",
7997
+ { memoryId: f.uniqueId }
7998
+ );
7999
+ return null;
8000
+ }
8001
+ if (!Array.isArray(vec) || vec.length === 0) return null;
8002
+ factVectors.push(vec);
8003
+ }
8004
+ let queryVectors;
8005
+ try {
8006
+ queryVectors = await generateEmbeddings(
8007
+ facets.map((f) => f.query),
8008
+ ctx.embeddingOptions
8009
+ );
8010
+ } catch (err) {
8011
+ getLogger().warn(
8012
+ "[memory/synthesizeProfile] facet-query embedding failed; regenerating all facets",
8013
+ { error: err instanceof Error ? err.message : String(err) }
8014
+ );
8015
+ return null;
8016
+ }
8017
+ const keys = /* @__PURE__ */ new Set();
8018
+ for (const fv of factVectors) {
8019
+ let matchedAny = false;
8020
+ for (let i = 0; i < facets.length; i++) {
8021
+ if (cosineSimilarity(fv, queryVectors[i]) >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
8022
+ keys.add(facets[i].key);
8023
+ matchedAny = true;
8024
+ }
8025
+ }
8026
+ if (!matchedAny) return null;
8027
+ }
8028
+ return keys;
8029
+ }
8030
+ async function synthesizeFacet(facet, ctx, options, prior) {
8031
+ const result = await reflect(facet.query, ctx, {
8032
+ apiKey: options.apiKey,
8033
+ getToken: options.getToken,
8034
+ llmModel: options.llmModel ?? DEFAULT_SYNTHESIS_MODEL,
8035
+ baseUrl: options.baseUrl,
8036
+ fetchFn: options.fetchFn,
8037
+ scopes: options.scopes ?? DEFAULT_SCOPES,
8038
+ limit: options.limit ?? DEFAULT_FACET_RECALL_LIMIT,
8039
+ types: ["fact"],
8040
+ maxTokens: DEFAULT_FACET_MAX_TOKENS,
8041
+ systemPrompt: buildFacetSystemPrompt(facet),
8042
+ responseSchema: FACET_RESPONSE_SCHEMA
8043
+ });
8044
+ const noEvidence = result.basedOn.memoryIds.length === 0;
8045
+ const { text: text12, legitimateEmpty } = extractFacetText(result.structuredOutput);
8046
+ if (!text12 && !legitimateEmpty && !noEvidence) {
8047
+ getLogger().warn(
8048
+ "[memory/synthesizeProfile] facet synthesis returned degraded-empty; keeping prior section",
8049
+ { facet: facet.key, recalledCount: result.basedOn.memoryIds.length }
8050
+ );
8051
+ return fallbackSection(facet, prior);
8052
+ }
8053
+ const section = {
8054
+ key: facet.key,
8055
+ label: facet.label,
8056
+ text: text12,
8057
+ sourceMemoryIds: result.basedOn.memoryIds,
8058
+ generatedAt: Date.now()
8059
+ };
8060
+ if (options.redactor && text12) {
8061
+ const redacted = await options.redactor.redactTextAsync(text12);
8062
+ section.text = redacted.text;
8063
+ }
8064
+ return section;
8065
+ }
8066
+ function fallbackSection(facet, prior) {
8067
+ if (prior && prior.text) {
8068
+ return { ...prior, stale: true };
8069
+ }
8070
+ return {
8071
+ key: facet.key,
8072
+ label: facet.label,
8073
+ text: "",
8074
+ sourceMemoryIds: [],
8075
+ generatedAt: Date.now(),
8076
+ stale: true
8077
+ };
8078
+ }
8079
+ function buildFacetSystemPrompt(facet) {
8080
+ return `You are writing the "${facet.label}" section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth.
8081
+
8082
+ Task for this section:
8083
+ ${facet.guidance}
8084
+
8085
+ Rules:
8086
+ - Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
8087
+ - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
8088
+ - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
8089
+ - Be concise and specific; no preamble, hedging, or meta-commentary.
8090
+ - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false> }.`;
8091
+ }
8092
+ function extractFacetText(structured) {
8093
+ if (structured && typeof structured === "object") {
8094
+ const obj = structured;
8095
+ if (obj.hasEvidence === false) return { text: "", legitimateEmpty: true };
8096
+ if (typeof obj.summary === "string")
8097
+ return { text: obj.summary.trim(), legitimateEmpty: false };
8098
+ }
8099
+ return { text: "", legitimateEmpty: false };
8100
+ }
8101
+
7788
8102
  // src/lib/memory/topicExtract.ts
7789
8103
  var TOPIC_EXTRACTION_BATCH_SIZE = 10;
7790
8104
  var MAX_CHARS_PER_MEMORY = 300;
@@ -11101,7 +11415,7 @@ __decorateClass([
11101
11415
  ], VaultFolder.prototype, "context", 2);
11102
11416
 
11103
11417
  // src/lib/db/schema.ts
11104
- var SDK_SCHEMA_VERSION = 38;
11418
+ var SDK_SCHEMA_VERSION = 39;
11105
11419
  var sdkSchema = appSchema2({
11106
11420
  version: SDK_SCHEMA_VERSION,
11107
11421
  tables: [
@@ -11239,7 +11553,14 @@ var sdkSchema = appSchema2({
11239
11553
  // The extraction-logic version this memory was last stamped under. Null
11240
11554
  // (pre-v38) is treated as version 0, so a bump of TOPICS_EXTRACTION_VERSION
11241
11555
  // re-extracts stale rows. See getMemoriesNeedingTopicExtractionOp.
11242
- { name: "topics_extracted_version", type: "number", isOptional: true }
11556
+ { name: "topics_extracted_version", type: "number", isOptional: true },
11557
+ // Re-observation watermark (C3). Unix ms of the last time retain() merged
11558
+ // a duplicate observation into this fact (proof_count++). Distinct from
11559
+ // updated_at, which merges deliberately preserve (preserveUpdatedAt) so a
11560
+ // re-observation doesn't reorder the vault by edit time. Null = never
11561
+ // re-observed since the column was added. Indexed so recency-weighted
11562
+ // synthesis can filter/sort on it cheaply.
11563
+ { name: "last_observed_at", type: "number", isOptional: true, isIndexed: true }
11243
11564
  ]
11244
11565
  }),
11245
11566
  // Entity table — canonical names extracted from auto-extraction (W5).
@@ -11878,6 +12199,20 @@ var sdkMigrations = schemaMigrations2({
11878
12199
  columns: [{ name: "topics_extracted_version", type: "number", isOptional: true }]
11879
12200
  })
11880
12201
  ]
12202
+ },
12203
+ // v38 -> v39: re-observation watermark on memory_vault (C3). Existing rows
12204
+ // keep NULL (= never re-observed since the column was added); synthesis
12205
+ // treats NULL as "fall back to updated_at" so no backfill is needed.
12206
+ {
12207
+ toVersion: 39,
12208
+ steps: [
12209
+ addColumns2({
12210
+ table: "memory_vault",
12211
+ columns: [
12212
+ { name: "last_observed_at", type: "number", isOptional: true, isIndexed: true }
12213
+ ]
12214
+ })
12215
+ ]
11881
12216
  }
11882
12217
  ]
11883
12218
  });
@@ -13219,12 +13554,14 @@ export {
13219
13554
  DEFAULT_CHUNK_OVERLAP,
13220
13555
  DEFAULT_CHUNK_SIZE,
13221
13556
  DEFAULT_MIN_CHUNK_SIZE,
13557
+ DEFAULT_PROFILE_FACETS,
13222
13558
  DEFAULT_VAULT_CACHE_SIZE,
13223
13559
  DatabaseManager,
13224
13560
  INFERENCE_ID_HEADER,
13225
13561
  LoggerProvider,
13226
13562
  NOTION_OAUTH_CONFIG,
13227
13563
  PII_PATTERNS,
13564
+ PROFILE_DOC_VERSION,
13228
13565
  PiiRedactor,
13229
13566
  QueueManager,
13230
13567
  RECALL_MAX_LIMIT,
@@ -13339,6 +13676,7 @@ export {
13339
13676
  streamCancelPath,
13340
13677
  streamReplayPath,
13341
13678
  supersedeVaultMemoryOp,
13679
+ synthesizeProfile,
13342
13680
  updateVaultFolderContextOp,
13343
13681
  updateVaultFolderOp,
13344
13682
  updateVaultMemoryEmbeddingOp,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-JVL5PLZS.mjs";
3
+ } from "./chunk-KXIUA7ZV.mjs";
4
4
  import "./chunk-DFVCS3QQ.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -2186,6 +2186,7 @@ function vaultMemoryToStoredRaw(memory) {
2186
2186
  topicsExtractedVersion: memory.topicsExtractedVersion ?? null,
2187
2187
  supersededBy: memory.supersededBy ?? null,
2188
2188
  supersededAt: memory.supersededAt ?? null,
2189
+ lastObservedAt: memory.lastObservedAt ?? null,
2189
2190
  createdAt: memory.createdAt,
2190
2191
  updatedAt: memory.updatedAt,
2191
2192
  isDeleted: memory.isDeleted
@@ -2426,6 +2427,7 @@ function vaultMemoryRawToStoredRaw(raw) {
2426
2427
  topicsExtractedVersion: raw.topics_extracted_version ?? null,
2427
2428
  supersededBy: raw.superseded_by ?? null,
2428
2429
  supersededAt: raw.superseded_at ?? null,
2430
+ lastObservedAt: raw.last_observed_at ?? null,
2429
2431
  createdAt: new Date(raw.created_at),
2430
2432
  updatedAt: new Date(raw.updated_at),
2431
2433
  isDeleted: raw.is_deleted === true || raw.is_deleted === 1
@@ -2514,6 +2516,9 @@ async function updateVaultMemoryOp(ctx, id, opts) {
2514
2516
  if (opts.topicsUserManaged !== void 0) {
2515
2517
  r._setRaw("topics_user_managed", opts.topicsUserManaged);
2516
2518
  }
2519
+ if (opts.lastObservedAt !== void 0) {
2520
+ r._setRaw("last_observed_at", opts.lastObservedAt);
2521
+ }
2517
2522
  if (opts.preserveUpdatedAt) {
2518
2523
  r._setRaw("updated_at", originalUpdatedAt);
2519
2524
  }
@@ -4529,7 +4534,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
4529
4534
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
4530
4535
  );
4531
4536
  }
4532
- const { recall: recall2 } = await import("./recall-NG2P5ZQ7.mjs");
4537
+ const { recall: recall2 } = await import("./recall-GYNSL2DS.mjs");
4533
4538
  const result = await recall2(
4534
4539
  query,
4535
4540
  { vaultCtx, embeddingOptions, vaultCache: cache },