@anuma/sdk 4.1.0 → 4.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.
@@ -29893,21 +29893,63 @@ var DEFAULT_PROFILE_FACETS = [
29893
29893
  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."
29894
29894
  }
29895
29895
  ];
29896
+ var NEARBY_MAX_OCCUPATION_CODEPOINTS = 80;
29897
+ var NEARBY_MAX_INTERESTS = 12;
29898
+ var NEARBY_MAX_INTEREST_CODEPOINTS = 40;
29899
+ var MAX_RAW_INTERESTS = NEARBY_MAX_INTERESTS * 2;
29900
+ function codePointLength(value) {
29901
+ return [...value].length;
29902
+ }
29903
+ var FACET_BASE_PROPERTIES = {
29904
+ summary: {
29905
+ type: "string",
29906
+ description: "The synthesized section prose. Empty string if the memories don't cover it."
29907
+ },
29908
+ hasEvidence: {
29909
+ type: "boolean",
29910
+ description: "False when the supplied memories don't support any claim for this facet."
29911
+ }
29912
+ };
29896
29913
  var FACET_RESPONSE_SCHEMA = {
29914
+ type: "object",
29915
+ properties: FACET_BASE_PROPERTIES,
29916
+ required: ["summary", "hasEvidence"],
29917
+ additionalProperties: false
29918
+ };
29919
+ var WORK_ROLE_RESPONSE_SCHEMA = {
29897
29920
  type: "object",
29898
29921
  properties: {
29899
- summary: {
29922
+ ...FACET_BASE_PROPERTIES,
29923
+ occupation: {
29900
29924
  type: "string",
29901
- description: "The synthesized section prose. Empty string if the memories don't cover it."
29902
- },
29903
- hasEvidence: {
29904
- type: "boolean",
29905
- description: "False when the supplied memories don't support any claim for this facet."
29925
+ description: `The same role as a standalone phrase for a profile field, at most ${NEARBY_MAX_OCCUPATION_CODEPOINTS} characters (e.g. "Backend engineer, fintech"). Empty string when hasEvidence is false.`
29906
29926
  }
29907
29927
  },
29908
- required: ["summary", "hasEvidence"],
29928
+ required: ["summary", "hasEvidence", "occupation"],
29929
+ additionalProperties: false
29930
+ };
29931
+ var INTERESTS_RESPONSE_SCHEMA = {
29932
+ type: "object",
29933
+ properties: {
29934
+ ...FACET_BASE_PROPERTIES,
29935
+ interests: {
29936
+ type: "array",
29937
+ items: { type: "string" },
29938
+ description: `The same interests as discrete strings \u2014 at most ${NEARBY_MAX_INTERESTS} items, each at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters (e.g. ["trail running", "film photography"]). Empty array when hasEvidence is false.`
29939
+ }
29940
+ },
29941
+ required: ["summary", "hasEvidence", "interests"],
29909
29942
  additionalProperties: false
29910
29943
  };
29944
+ function facetResponseSchema(key) {
29945
+ if (key === "work_role") return WORK_ROLE_RESPONSE_SCHEMA;
29946
+ if (key === "interests") return INTERESTS_RESPONSE_SCHEMA;
29947
+ return FACET_RESPONSE_SCHEMA;
29948
+ }
29949
+ var FACET_STRUCTURED_RESPONSE_HINT = {
29950
+ work_role: `, "occupation": <the role as a standalone phrase of at most ${NEARBY_MAX_OCCUPATION_CODEPOINTS} characters, e.g. "Backend engineer, fintech", or "">`,
29951
+ 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 []>`
29952
+ };
29911
29953
  async function synthesizeProfile(ctx, options = {}) {
29912
29954
  if (!ctx.vaultCtx) {
29913
29955
  throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
@@ -30003,7 +30045,7 @@ function reviewedMemoryIdsSignature(ids) {
30003
30045
  return [...new Set(ids)].sort().join("\n");
30004
30046
  }
30005
30047
  function facetsSignature(facets) {
30006
- return facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance])).sort().join("\n");
30048
+ return facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance, facetResponseSchema(f.key)])).sort().join("\n");
30007
30049
  }
30008
30050
  function trendsEqual(a, b) {
30009
30051
  if (!a) return false;
@@ -30151,7 +30193,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
30151
30193
  types: ["fact"],
30152
30194
  maxTokens: DEFAULT_FACET_MAX_TOKENS,
30153
30195
  systemPrompt: buildFacetSystemPrompt(facet),
30154
- responseSchema: FACET_RESPONSE_SCHEMA,
30196
+ responseSchema: facetResponseSchema(facet.key),
30155
30197
  memories
30156
30198
  });
30157
30199
  const noEvidence = result.basedOn.memoryIds.length === 0;
@@ -30174,6 +30216,25 @@ async function synthesizeFacet(facet, ctx, options, prior) {
30174
30216
  const redacted = await options.redactor.redactTextAsync(text12);
30175
30217
  section.text = redacted.text;
30176
30218
  }
30219
+ if (!legitimateEmpty && !noEvidence) {
30220
+ const values = extractStructuredValues(facet.key, result.structuredOutput);
30221
+ if (options.redactor) {
30222
+ if (values.occupation) {
30223
+ values.occupation = (await options.redactor.redactTextAsync(values.occupation)).text;
30224
+ }
30225
+ if (values.interests) {
30226
+ const gated = [];
30227
+ for (const interest of values.interests) {
30228
+ gated.push((await options.redactor.redactTextAsync(interest)).text);
30229
+ }
30230
+ values.interests = gated;
30231
+ }
30232
+ }
30233
+ const occupation = normalizeOccupation(values.occupation);
30234
+ if (occupation !== void 0) section.occupation = occupation;
30235
+ const interests = normalizeInterests(values.interests);
30236
+ if (interests !== void 0) section.interests = interests;
30237
+ }
30177
30238
  return section;
30178
30239
  }
30179
30240
  function fallbackSection(facet, prior) {
@@ -30190,6 +30251,7 @@ function fallbackSection(facet, prior) {
30190
30251
  };
30191
30252
  }
30192
30253
  function buildFacetSystemPrompt(facet) {
30254
+ const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
30193
30255
  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.
30194
30256
 
30195
30257
  Task for this section:
@@ -30200,7 +30262,7 @@ Rules:
30200
30262
  - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
30201
30263
  - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
30202
30264
  - Be concise and specific; no preamble, hedging, or meta-commentary.
30203
- - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false> }.`;
30265
+ - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
30204
30266
  }
30205
30267
  function extractFacetText(structured) {
30206
30268
  if (structured && typeof structured === "object") {
@@ -30211,6 +30273,73 @@ function extractFacetText(structured) {
30211
30273
  }
30212
30274
  return { text: "", legitimateEmpty: false };
30213
30275
  }
30276
+ function extractStructuredValues(key, structured) {
30277
+ if (!structured || typeof structured !== "object") return {};
30278
+ const obj = structured;
30279
+ if (key === "work_role") {
30280
+ return typeof obj.occupation === "string" ? { occupation: obj.occupation } : {};
30281
+ }
30282
+ if (key === "interests") {
30283
+ const list = coerceInterestList(obj.interests);
30284
+ if (list) return { interests: list.slice(0, MAX_RAW_INTERESTS) };
30285
+ }
30286
+ return {};
30287
+ }
30288
+ function coerceInterestList(value) {
30289
+ if (Array.isArray(value)) return value.filter((i) => typeof i === "string");
30290
+ if (typeof value !== "string") return void 0;
30291
+ const trimmed = value.trim();
30292
+ if (trimmed.startsWith("[")) {
30293
+ try {
30294
+ const parsed = JSON.parse(trimmed);
30295
+ if (Array.isArray(parsed)) return parsed.filter((i) => typeof i === "string");
30296
+ } catch {
30297
+ if (trimmed.endsWith("]")) return splitInterestFragments(trimmed.slice(1, -1));
30298
+ }
30299
+ }
30300
+ return splitInterestFragments(trimmed);
30301
+ }
30302
+ function splitInterestFragments(source) {
30303
+ return source.split(",").map((fragment) => {
30304
+ let entry = fragment.trim();
30305
+ if (entry.startsWith("[")) entry = entry.slice(1);
30306
+ if (entry.endsWith("]")) entry = entry.slice(0, -1);
30307
+ entry = entry.trim();
30308
+ const quote = entry[0];
30309
+ if ((quote === '"' || quote === "'") && entry.endsWith(quote)) entry = entry.slice(1, -1);
30310
+ return entry;
30311
+ });
30312
+ }
30313
+ function normalizeOccupation(value) {
30314
+ if (value === void 0) return void 0;
30315
+ const trimmed = value.trim();
30316
+ if (!trimmed) return void 0;
30317
+ const length = codePointLength(trimmed);
30318
+ if (length > NEARBY_MAX_OCCUPATION_CODEPOINTS) {
30319
+ getLogger().warn(
30320
+ "[memory/synthesizeProfile] occupation exceeded the publishable length; dropping it",
30321
+ { length, max: NEARBY_MAX_OCCUPATION_CODEPOINTS }
30322
+ );
30323
+ return void 0;
30324
+ }
30325
+ return trimmed;
30326
+ }
30327
+ function normalizeInterests(values) {
30328
+ if (values === void 0) return void 0;
30329
+ const seen = /* @__PURE__ */ new Set();
30330
+ const out = [];
30331
+ for (const value of values) {
30332
+ const trimmed = value.trim();
30333
+ if (!trimmed) continue;
30334
+ if (codePointLength(trimmed) > NEARBY_MAX_INTEREST_CODEPOINTS) continue;
30335
+ const key = trimmed.toLowerCase();
30336
+ if (seen.has(key)) continue;
30337
+ seen.add(key);
30338
+ out.push(trimmed);
30339
+ if (out.length === NEARBY_MAX_INTERESTS) break;
30340
+ }
30341
+ return out.length > 0 ? out : void 0;
30342
+ }
30214
30343
 
30215
30344
  // src/lib/memory/topicExtract.ts
30216
30345
  init_operations2();
@@ -8277,6 +8277,23 @@ interface ProfileSection {
8277
8277
  text: string;
8278
8278
  /** Vault memory ids this section was grounded on — provenance + delta refresh. */
8279
8279
  sourceMemoryIds: string[];
8280
+ /**
8281
+ * Structured occupation — the `work_role` facet only. A short role phrase
8282
+ * (at most 80 code points, PII-gated alongside {@link ProfileSection.text})
8283
+ * that a profile store's `occupation` column takes verbatim.
8284
+ *
8285
+ * Absent when the facet found no evidence, when the model didn't return one,
8286
+ * or when the value it returned couldn't be made publishable. `text` is
8287
+ * unaffected either way, so the prose is never blocked on this.
8288
+ */
8289
+ occupation?: string;
8290
+ /**
8291
+ * Structured interests — the `interests` facet only. Discrete entries,
8292
+ * trimmed and deduped case- and space-insensitively (first spelling wins), at
8293
+ * most 12 items of at most 40 code points each, ready for a profile store's
8294
+ * `interests` column. Absent when nothing survived normalization.
8295
+ */
8296
+ interests?: string[];
8280
8297
  /** Unix ms this section was generated. */
8281
8298
  generatedAt: number;
8282
8299
  /** True when regeneration failed and a prior section value was carried
@@ -8293,10 +8310,12 @@ interface ProfileConfigFingerprint {
8293
8310
  /** Facet keys present in the doc, sorted. */
8294
8311
  facetKeys: ProfileFacetKey[];
8295
8312
  /** Order-independent digest of each facet's full definition (key + label +
8296
- * query + guidance). Reuse must invalidate when a facet's PROMPT changes, not
8297
- * just its key set — otherwise reused sections carry text generated under the
8298
- * old definition. Facet display order does NOT invalidate (sections are
8299
- * rebuilt in facet order and reused by key). */
8313
+ * query + guidance) and the response schema it is synthesized under. Reuse
8314
+ * must invalidate when a facet's PROMPT changes, not just its key set —
8315
+ * otherwise reused sections carry text generated under the old definition,
8316
+ * or under an output schema that predates a field the caller now reads.
8317
+ * Facet display order does NOT invalidate (sections are rebuilt in facet
8318
+ * order and reused by key). */
8300
8319
  facetsSignature: string;
8301
8320
  /** Scopes the facts were drawn from, sorted. */
8302
8321
  scopes: string[];
@@ -8277,6 +8277,23 @@ interface ProfileSection {
8277
8277
  text: string;
8278
8278
  /** Vault memory ids this section was grounded on — provenance + delta refresh. */
8279
8279
  sourceMemoryIds: string[];
8280
+ /**
8281
+ * Structured occupation — the `work_role` facet only. A short role phrase
8282
+ * (at most 80 code points, PII-gated alongside {@link ProfileSection.text})
8283
+ * that a profile store's `occupation` column takes verbatim.
8284
+ *
8285
+ * Absent when the facet found no evidence, when the model didn't return one,
8286
+ * or when the value it returned couldn't be made publishable. `text` is
8287
+ * unaffected either way, so the prose is never blocked on this.
8288
+ */
8289
+ occupation?: string;
8290
+ /**
8291
+ * Structured interests — the `interests` facet only. Discrete entries,
8292
+ * trimmed and deduped case- and space-insensitively (first spelling wins), at
8293
+ * most 12 items of at most 40 code points each, ready for a profile store's
8294
+ * `interests` column. Absent when nothing survived normalization.
8295
+ */
8296
+ interests?: string[];
8280
8297
  /** Unix ms this section was generated. */
8281
8298
  generatedAt: number;
8282
8299
  /** True when regeneration failed and a prior section value was carried
@@ -8293,10 +8310,12 @@ interface ProfileConfigFingerprint {
8293
8310
  /** Facet keys present in the doc, sorted. */
8294
8311
  facetKeys: ProfileFacetKey[];
8295
8312
  /** Order-independent digest of each facet's full definition (key + label +
8296
- * query + guidance). Reuse must invalidate when a facet's PROMPT changes, not
8297
- * just its key set — otherwise reused sections carry text generated under the
8298
- * old definition. Facet display order does NOT invalidate (sections are
8299
- * rebuilt in facet order and reused by key). */
8313
+ * query + guidance) and the response schema it is synthesized under. Reuse
8314
+ * must invalidate when a facet's PROMPT changes, not just its key set —
8315
+ * otherwise reused sections carry text generated under the old definition,
8316
+ * or under an output schema that predates a field the caller now reads.
8317
+ * Facet display order does NOT invalidate (sections are rebuilt in facet
8318
+ * order and reused by key). */
8300
8319
  facetsSignature: string;
8301
8320
  /** Scopes the facts were drawn from, sorted. */
8302
8321
  scopes: string[];
@@ -6886,21 +6886,63 @@ var DEFAULT_PROFILE_FACETS = [
6886
6886
  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."
6887
6887
  }
6888
6888
  ];
6889
+ var NEARBY_MAX_OCCUPATION_CODEPOINTS = 80;
6890
+ var NEARBY_MAX_INTERESTS = 12;
6891
+ var NEARBY_MAX_INTEREST_CODEPOINTS = 40;
6892
+ var MAX_RAW_INTERESTS = NEARBY_MAX_INTERESTS * 2;
6893
+ function codePointLength(value) {
6894
+ return [...value].length;
6895
+ }
6896
+ var FACET_BASE_PROPERTIES = {
6897
+ summary: {
6898
+ type: "string",
6899
+ description: "The synthesized section prose. Empty string if the memories don't cover it."
6900
+ },
6901
+ hasEvidence: {
6902
+ type: "boolean",
6903
+ description: "False when the supplied memories don't support any claim for this facet."
6904
+ }
6905
+ };
6889
6906
  var FACET_RESPONSE_SCHEMA = {
6907
+ type: "object",
6908
+ properties: FACET_BASE_PROPERTIES,
6909
+ required: ["summary", "hasEvidence"],
6910
+ additionalProperties: false
6911
+ };
6912
+ var WORK_ROLE_RESPONSE_SCHEMA = {
6890
6913
  type: "object",
6891
6914
  properties: {
6892
- summary: {
6915
+ ...FACET_BASE_PROPERTIES,
6916
+ occupation: {
6893
6917
  type: "string",
6894
- description: "The synthesized section prose. Empty string if the memories don't cover it."
6895
- },
6896
- hasEvidence: {
6897
- type: "boolean",
6898
- description: "False when the supplied memories don't support any claim for this facet."
6918
+ description: `The same role as a standalone phrase for a profile field, at most ${NEARBY_MAX_OCCUPATION_CODEPOINTS} characters (e.g. "Backend engineer, fintech"). Empty string when hasEvidence is false.`
6899
6919
  }
6900
6920
  },
6901
- required: ["summary", "hasEvidence"],
6921
+ required: ["summary", "hasEvidence", "occupation"],
6922
+ additionalProperties: false
6923
+ };
6924
+ var INTERESTS_RESPONSE_SCHEMA = {
6925
+ type: "object",
6926
+ properties: {
6927
+ ...FACET_BASE_PROPERTIES,
6928
+ interests: {
6929
+ type: "array",
6930
+ items: { type: "string" },
6931
+ description: `The same interests as discrete strings \u2014 at most ${NEARBY_MAX_INTERESTS} items, each at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters (e.g. ["trail running", "film photography"]). Empty array when hasEvidence is false.`
6932
+ }
6933
+ },
6934
+ required: ["summary", "hasEvidence", "interests"],
6902
6935
  additionalProperties: false
6903
6936
  };
6937
+ function facetResponseSchema(key) {
6938
+ if (key === "work_role") return WORK_ROLE_RESPONSE_SCHEMA;
6939
+ if (key === "interests") return INTERESTS_RESPONSE_SCHEMA;
6940
+ return FACET_RESPONSE_SCHEMA;
6941
+ }
6942
+ var FACET_STRUCTURED_RESPONSE_HINT = {
6943
+ work_role: `, "occupation": <the role as a standalone phrase of at most ${NEARBY_MAX_OCCUPATION_CODEPOINTS} characters, e.g. "Backend engineer, fintech", or "">`,
6944
+ 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 []>`
6945
+ };
6904
6946
  async function synthesizeProfile(ctx, options = {}) {
6905
6947
  if (!ctx.vaultCtx) {
6906
6948
  throw new Error("synthesizeProfile requires ctx.vaultCtx (vault-backed facts).");
@@ -6996,7 +7038,7 @@ function reviewedMemoryIdsSignature(ids) {
6996
7038
  return [...new Set(ids)].sort().join("\n");
6997
7039
  }
6998
7040
  function facetsSignature(facets) {
6999
- return facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance])).sort().join("\n");
7041
+ return facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance, facetResponseSchema(f.key)])).sort().join("\n");
7000
7042
  }
7001
7043
  function trendsEqual(a, b) {
7002
7044
  if (!a) return false;
@@ -7144,7 +7186,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
7144
7186
  types: ["fact"],
7145
7187
  maxTokens: DEFAULT_FACET_MAX_TOKENS,
7146
7188
  systemPrompt: buildFacetSystemPrompt(facet),
7147
- responseSchema: FACET_RESPONSE_SCHEMA,
7189
+ responseSchema: facetResponseSchema(facet.key),
7148
7190
  memories
7149
7191
  });
7150
7192
  const noEvidence = result.basedOn.memoryIds.length === 0;
@@ -7167,6 +7209,25 @@ async function synthesizeFacet(facet, ctx, options, prior) {
7167
7209
  const redacted = await options.redactor.redactTextAsync(text12);
7168
7210
  section.text = redacted.text;
7169
7211
  }
7212
+ if (!legitimateEmpty && !noEvidence) {
7213
+ const values = extractStructuredValues(facet.key, result.structuredOutput);
7214
+ if (options.redactor) {
7215
+ if (values.occupation) {
7216
+ values.occupation = (await options.redactor.redactTextAsync(values.occupation)).text;
7217
+ }
7218
+ if (values.interests) {
7219
+ const gated = [];
7220
+ for (const interest of values.interests) {
7221
+ gated.push((await options.redactor.redactTextAsync(interest)).text);
7222
+ }
7223
+ values.interests = gated;
7224
+ }
7225
+ }
7226
+ const occupation = normalizeOccupation(values.occupation);
7227
+ if (occupation !== void 0) section.occupation = occupation;
7228
+ const interests = normalizeInterests(values.interests);
7229
+ if (interests !== void 0) section.interests = interests;
7230
+ }
7170
7231
  return section;
7171
7232
  }
7172
7233
  function fallbackSection(facet, prior) {
@@ -7183,6 +7244,7 @@ function fallbackSection(facet, prior) {
7183
7244
  };
7184
7245
  }
7185
7246
  function buildFacetSystemPrompt(facet) {
7247
+ const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
7186
7248
  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.
7187
7249
 
7188
7250
  Task for this section:
@@ -7193,7 +7255,7 @@ Rules:
7193
7255
  - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
7194
7256
  - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
7195
7257
  - Be concise and specific; no preamble, hedging, or meta-commentary.
7196
- - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false> }.`;
7258
+ - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
7197
7259
  }
7198
7260
  function extractFacetText(structured) {
7199
7261
  if (structured && typeof structured === "object") {
@@ -7204,6 +7266,73 @@ function extractFacetText(structured) {
7204
7266
  }
7205
7267
  return { text: "", legitimateEmpty: false };
7206
7268
  }
7269
+ function extractStructuredValues(key, structured) {
7270
+ if (!structured || typeof structured !== "object") return {};
7271
+ const obj = structured;
7272
+ if (key === "work_role") {
7273
+ return typeof obj.occupation === "string" ? { occupation: obj.occupation } : {};
7274
+ }
7275
+ if (key === "interests") {
7276
+ const list = coerceInterestList(obj.interests);
7277
+ if (list) return { interests: list.slice(0, MAX_RAW_INTERESTS) };
7278
+ }
7279
+ return {};
7280
+ }
7281
+ function coerceInterestList(value) {
7282
+ if (Array.isArray(value)) return value.filter((i) => typeof i === "string");
7283
+ if (typeof value !== "string") return void 0;
7284
+ const trimmed = value.trim();
7285
+ if (trimmed.startsWith("[")) {
7286
+ try {
7287
+ const parsed = JSON.parse(trimmed);
7288
+ if (Array.isArray(parsed)) return parsed.filter((i) => typeof i === "string");
7289
+ } catch {
7290
+ if (trimmed.endsWith("]")) return splitInterestFragments(trimmed.slice(1, -1));
7291
+ }
7292
+ }
7293
+ return splitInterestFragments(trimmed);
7294
+ }
7295
+ function splitInterestFragments(source) {
7296
+ return source.split(",").map((fragment) => {
7297
+ let entry = fragment.trim();
7298
+ if (entry.startsWith("[")) entry = entry.slice(1);
7299
+ if (entry.endsWith("]")) entry = entry.slice(0, -1);
7300
+ entry = entry.trim();
7301
+ const quote = entry[0];
7302
+ if ((quote === '"' || quote === "'") && entry.endsWith(quote)) entry = entry.slice(1, -1);
7303
+ return entry;
7304
+ });
7305
+ }
7306
+ function normalizeOccupation(value) {
7307
+ if (value === void 0) return void 0;
7308
+ const trimmed = value.trim();
7309
+ if (!trimmed) return void 0;
7310
+ const length = codePointLength(trimmed);
7311
+ if (length > NEARBY_MAX_OCCUPATION_CODEPOINTS) {
7312
+ getLogger().warn(
7313
+ "[memory/synthesizeProfile] occupation exceeded the publishable length; dropping it",
7314
+ { length, max: NEARBY_MAX_OCCUPATION_CODEPOINTS }
7315
+ );
7316
+ return void 0;
7317
+ }
7318
+ return trimmed;
7319
+ }
7320
+ function normalizeInterests(values) {
7321
+ if (values === void 0) return void 0;
7322
+ const seen = /* @__PURE__ */ new Set();
7323
+ const out = [];
7324
+ for (const value of values) {
7325
+ const trimmed = value.trim();
7326
+ if (!trimmed) continue;
7327
+ if (codePointLength(trimmed) > NEARBY_MAX_INTEREST_CODEPOINTS) continue;
7328
+ const key = trimmed.toLowerCase();
7329
+ if (seen.has(key)) continue;
7330
+ seen.add(key);
7331
+ out.push(trimmed);
7332
+ if (out.length === NEARBY_MAX_INTERESTS) break;
7333
+ }
7334
+ return out.length > 0 ? out : void 0;
7335
+ }
7207
7336
 
7208
7337
  // src/lib/memory/topicExtract.ts
7209
7338
  var TOPIC_EXTRACTION_BATCH_SIZE = 10;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",