@anuma/sdk 3.13.2 → 4.0.1

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.
@@ -18,19 +18,21 @@ A "memory" is a self-contained natural-language fact about the user. Multiple me
18
18
  Decide one of four actions:
19
19
 
20
20
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
21
- - "update": the new memory describes the SAME FACET as exactly one existing memory and is COMPATIBLE with it \u2014 it adds detail, fixes an incomplete value, or rephrases the same still-true fact. Return targetId of that existing memory + the consolidated content (richest version of the two).
22
- - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory \u2014 the old fact was true before but is false now (a state change). Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
23
- - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
21
+ - "update": the new memory describes the SAME FACET of the SAME SUBJECT as exactly one existing memory, is COMPATIBLE with it, and ADDS INFORMATION \u2014 it adds detail or fills in an incomplete value. Return targetId of that existing memory + the consolidated content (richest version of the two). A pure rewording that adds nothing is NOT an update; it is "noop".
22
+ - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory ABOUT THE SAME SUBJECT \u2014 the old fact was true before but is false now (a state change). Before choosing this, name the subject of the new memory and the subject of each candidate out loud to yourself; if they are different people, the old fact is still TRUE and superseding it would delete a correct memory \u2014 choose "create" instead. Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
23
+ - "noop": the new memory is already fully captured by an existing memory \u2014 same facet of the same subject, no new information. This INCLUDES a restatement in different words ("has two kids" / "has two children"): if the existing memory already tells you everything the new one does, the answer is noop no matter how differently it is worded. Skip the write.
24
24
 
25
25
  RULES (carry these strictly):
26
26
 
27
27
  1. ONE OBSERVATION PER DISTINCT FACET. If the new memory is about "user's aunt's twins" and an existing memory is also about "user's aunt's twins", they are the same facet \u2192 update, never create.
28
+ 1a. SAME SUBJECT REQUIRED \u2014 check this FIRST, before you look at the facet. The subject is WHO the fact is about: the user, or a specific person or thing in their life (a relative, their manager, their dog Biscuit). "update", "supersede" and "noop" all require the new memory and the existing one to be about the SAME subject. Two memories can share a facet and still have different subjects, and then they are simply two different facts: "User's daughter is allergic to peanuts" and "User is allergic to peanuts" are both about a peanut allergy, but one is about the DAUGHTER and one is about the USER \u2192 create, keep both. This holds for EVERY attribute, not just the one in this example \u2014 where someone lives, who they work for, what they own, how old they are. A possessive like "user's X" makes X the subject, not the user; strip it and ask "whose fact is this?" before comparing anything. Never retire the user's own value because a fact about somebody else resembles it \u2014 the user's fact is still true, and retiring it destroys it.
28
29
  2. MATCH BY FACET, NOT TOPIC. Two memories both mentioning "Zara" is not enough to merge \u2014 only merge if they describe the same property (e.g. both about "user's pending Zara boot exchange"). Two memories about Zara on different topics (e.g. "user shops at Zara" + "user returned a sweater to Zara last week") are separate facets.
29
30
  3. NO COMPUTATION. Do not sum counts, decrement quantities, or derive new facts from existing ones. If the new memory says "user spent $200 today" and an existing memory says "user spent $150 yesterday", these are TWO SEPARATE EVENTS \u2014 create.
30
31
  4. EVENTS vs STANDING STATE.
31
32
  - Distinct EVENTS (episodic \u2014 something that happened on a date) are separate memories, even if the same activity: "user went to gym Monday" vs "user went to gym Friday" \u2192 create; keep both.
32
- - A STANDING ATTRIBUTE (an ongoing state: where the user lives/works, relationship status, current role) has ONE current value. When the new memory changes that value ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true).
33
- 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory; then choose supersede. Over-merging loses information; a live contradiction is worse than either.
33
+ - A STANDING ATTRIBUTE (an ongoing state: where someone lives/works, relationship status, current role) has ONE current value PER SUBJECT. When the new memory changes that value FOR THE SAME SUBJECT ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true). A different subject holding the same attribute is not a value change at all \u2192 create.
34
+ 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory ABOUT THE SAME SUBJECT; then choose supersede. Two facts about different subjects never contradict each other, however similarly they read. Over-merging loses information; a live contradiction is worse than either.
35
+ 6. When in doubt between update and noop, ask whether the existing memory ALONE already answers everything the new one would. If it does, choose noop \u2014 a write that changes only the wording costs a re-embed and makes an old fact look newly observed.
34
36
 
35
37
  OUTPUT \u2014 strict JSON, no prose:
36
38
  {
@@ -8847,19 +8847,21 @@ A "memory" is a self-contained natural-language fact about the user. Multiple me
8847
8847
  Decide one of four actions:
8848
8848
 
8849
8849
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
8850
- - "update": the new memory describes the SAME FACET as exactly one existing memory and is COMPATIBLE with it \u2014 it adds detail, fixes an incomplete value, or rephrases the same still-true fact. Return targetId of that existing memory + the consolidated content (richest version of the two).
8851
- - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory \u2014 the old fact was true before but is false now (a state change). Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
8852
- - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
8850
+ - "update": the new memory describes the SAME FACET of the SAME SUBJECT as exactly one existing memory, is COMPATIBLE with it, and ADDS INFORMATION \u2014 it adds detail or fills in an incomplete value. Return targetId of that existing memory + the consolidated content (richest version of the two). A pure rewording that adds nothing is NOT an update; it is "noop".
8851
+ - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory ABOUT THE SAME SUBJECT \u2014 the old fact was true before but is false now (a state change). Before choosing this, name the subject of the new memory and the subject of each candidate out loud to yourself; if they are different people, the old fact is still TRUE and superseding it would delete a correct memory \u2014 choose "create" instead. Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
8852
+ - "noop": the new memory is already fully captured by an existing memory \u2014 same facet of the same subject, no new information. This INCLUDES a restatement in different words ("has two kids" / "has two children"): if the existing memory already tells you everything the new one does, the answer is noop no matter how differently it is worded. Skip the write.
8853
8853
 
8854
8854
  RULES (carry these strictly):
8855
8855
 
8856
8856
  1. ONE OBSERVATION PER DISTINCT FACET. If the new memory is about "user's aunt's twins" and an existing memory is also about "user's aunt's twins", they are the same facet \u2192 update, never create.
8857
+ 1a. SAME SUBJECT REQUIRED \u2014 check this FIRST, before you look at the facet. The subject is WHO the fact is about: the user, or a specific person or thing in their life (a relative, their manager, their dog Biscuit). "update", "supersede" and "noop" all require the new memory and the existing one to be about the SAME subject. Two memories can share a facet and still have different subjects, and then they are simply two different facts: "User's daughter is allergic to peanuts" and "User is allergic to peanuts" are both about a peanut allergy, but one is about the DAUGHTER and one is about the USER \u2192 create, keep both. This holds for EVERY attribute, not just the one in this example \u2014 where someone lives, who they work for, what they own, how old they are. A possessive like "user's X" makes X the subject, not the user; strip it and ask "whose fact is this?" before comparing anything. Never retire the user's own value because a fact about somebody else resembles it \u2014 the user's fact is still true, and retiring it destroys it.
8857
8858
  2. MATCH BY FACET, NOT TOPIC. Two memories both mentioning "Zara" is not enough to merge \u2014 only merge if they describe the same property (e.g. both about "user's pending Zara boot exchange"). Two memories about Zara on different topics (e.g. "user shops at Zara" + "user returned a sweater to Zara last week") are separate facets.
8858
8859
  3. NO COMPUTATION. Do not sum counts, decrement quantities, or derive new facts from existing ones. If the new memory says "user spent $200 today" and an existing memory says "user spent $150 yesterday", these are TWO SEPARATE EVENTS \u2014 create.
8859
8860
  4. EVENTS vs STANDING STATE.
8860
8861
  - Distinct EVENTS (episodic \u2014 something that happened on a date) are separate memories, even if the same activity: "user went to gym Monday" vs "user went to gym Friday" \u2192 create; keep both.
8861
- - A STANDING ATTRIBUTE (an ongoing state: where the user lives/works, relationship status, current role) has ONE current value. When the new memory changes that value ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true).
8862
- 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory; then choose supersede. Over-merging loses information; a live contradiction is worse than either.
8862
+ - A STANDING ATTRIBUTE (an ongoing state: where someone lives/works, relationship status, current role) has ONE current value PER SUBJECT. When the new memory changes that value FOR THE SAME SUBJECT ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true). A different subject holding the same attribute is not a value change at all \u2192 create.
8863
+ 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory ABOUT THE SAME SUBJECT; then choose supersede. Two facts about different subjects never contradict each other, however similarly they read. Over-merging loses information; a live contradiction is worse than either.
8864
+ 6. When in doubt between update and noop, ask whether the existing memory ALONE already answers everything the new one would. If it does, choose noop \u2014 a write that changes only the wording costs a re-embed and makes an old fact look newly observed.
8863
8865
 
8864
8866
  OUTPUT \u2014 strict JSON, no prose:
8865
8867
  {
@@ -18265,7 +18267,8 @@ function configMatches(a, b) {
18265
18267
  return a.redacted === b.redacted && a.facetsSignature === b.facetsSignature && (a.reviewedMemoryIdsSignature ?? "") === b.reviewedMemoryIdsSignature && a.scopes.length === b.scopes.length && a.scopes.every((s, i) => s === b.scopes[i]);
18266
18268
  }
18267
18269
  function reviewedMemoryIdsSignature(ids) {
18268
- if (ids === void 0 || ids.length === 0) return "";
18270
+ if (ids === void 0) return "";
18271
+ if (ids.length === 0) return "\0gated-empty";
18269
18272
  return [...new Set(ids)].sort().join("\n");
18270
18273
  }
18271
18274
  function facetsSignature(facets) {
@@ -18375,7 +18378,16 @@ async function synthesizeFacet(facet, ctx, options, prior) {
18375
18378
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
18376
18379
  const proofCountAlpha = options.proofCountAlpha ?? DEFAULT_PROFILE_PROOF_ALPHA;
18377
18380
  const reviewed = options.reviewedMemoryIds;
18378
- const hasReviewGate = reviewed !== void 0 && reviewed.length > 0;
18381
+ const hasReviewGate = reviewed !== void 0;
18382
+ if (hasReviewGate && reviewed.length === 0) {
18383
+ return {
18384
+ key: facet.key,
18385
+ label: facet.label,
18386
+ text: "",
18387
+ sourceMemoryIds: [],
18388
+ generatedAt: Date.now()
18389
+ };
18390
+ }
18379
18391
  const recalled = await recall(facet.query, ctx, {
18380
18392
  scopes,
18381
18393
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
@@ -8949,10 +8949,20 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
8949
8949
  */
8950
8950
  proofCountAlpha?: number;
8951
8951
  /**
8952
- * When non-empty, intersect each facet's recalled evidence with this id set
8953
- * before the LLM runs (publish-review gate). Empty intersection → empty
8954
- * section (legitimate no-evidence), not a stale fallback. Omit / empty →
8955
- * no gate.
8952
+ * Publish-review gate: when SUPPLIED, each facet's recalled evidence is
8953
+ * intersected with this id set before the LLM runs, so synthesis can only draw
8954
+ * on memories the user approved for publication. Empty intersection → empty
8955
+ * section (legitimate no-evidence), not a stale fallback.
8956
+ *
8957
+ * Pass the user's published set (e.g. `getAllVaultMemoriesOp(ctx, { visibility:
8958
+ * ["public"] })`) to keep a published profile derivable only from published
8959
+ * memories — People Nearby's two-tier model treats `private` memories as never
8960
+ * leaving the device, and a summary derived from them is a derivative that does.
8961
+ *
8962
+ * `[]` means "nothing approved" and gates everything OUT (no recall, no LLM
8963
+ * call, empty sections). **Omitting the field is the only way to run ungated**
8964
+ * — that asymmetry is deliberate, so a caller computing a published set can
8965
+ * never accidentally disable the gate by finding it empty.
8956
8966
  */
8957
8967
  reviewedMemoryIds?: readonly string[];
8958
8968
  }
@@ -8949,10 +8949,20 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
8949
8949
  */
8950
8950
  proofCountAlpha?: number;
8951
8951
  /**
8952
- * When non-empty, intersect each facet's recalled evidence with this id set
8953
- * before the LLM runs (publish-review gate). Empty intersection → empty
8954
- * section (legitimate no-evidence), not a stale fallback. Omit / empty →
8955
- * no gate.
8952
+ * Publish-review gate: when SUPPLIED, each facet's recalled evidence is
8953
+ * intersected with this id set before the LLM runs, so synthesis can only draw
8954
+ * on memories the user approved for publication. Empty intersection → empty
8955
+ * section (legitimate no-evidence), not a stale fallback.
8956
+ *
8957
+ * Pass the user's published set (e.g. `getAllVaultMemoriesOp(ctx, { visibility:
8958
+ * ["public"] })`) to keep a published profile derivable only from published
8959
+ * memories — People Nearby's two-tier model treats `private` memories as never
8960
+ * leaving the device, and a summary derived from them is a derivative that does.
8961
+ *
8962
+ * `[]` means "nothing approved" and gates everything OUT (no recall, no LLM
8963
+ * call, empty sections). **Omitting the field is the only way to run ungated**
8964
+ * — that asymmetry is deliberate, so a caller computing a published set can
8965
+ * never accidentally disable the gate by finding it empty.
8956
8966
  */
8957
8967
  reviewedMemoryIds?: readonly string[];
8958
8968
  }
@@ -161,7 +161,7 @@ import {
161
161
  updateVaultMemoryOp,
162
162
  useEncryption,
163
163
  vaultMemoryToStored
164
- } from "./chunk-XXCZVG4Z.mjs";
164
+ } from "./chunk-BAJL46NX.mjs";
165
165
  import {
166
166
  PiiRedactor,
167
167
  createStreamingDeAnonymizer,
@@ -7490,7 +7490,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
7490
7490
  content: m.content,
7491
7491
  similarity: m.similarity
7492
7492
  }));
7493
- const { consolidateMemory: doConsolidate } = await import("./consolidate-R6FH7XMV.mjs");
7493
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-IFIKT25Z.mjs");
7494
7494
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
7495
7495
  if (decision.action === "create") return null;
7496
7496
  if (decision.action === "supersede" && decision.content) {
@@ -9036,7 +9036,8 @@ function configMatches(a, b) {
9036
9036
  return a.redacted === b.redacted && a.facetsSignature === b.facetsSignature && (a.reviewedMemoryIdsSignature ?? "") === b.reviewedMemoryIdsSignature && a.scopes.length === b.scopes.length && a.scopes.every((s, i) => s === b.scopes[i]);
9037
9037
  }
9038
9038
  function reviewedMemoryIdsSignature(ids) {
9039
- if (ids === void 0 || ids.length === 0) return "";
9039
+ if (ids === void 0) return "";
9040
+ if (ids.length === 0) return "\0gated-empty";
9040
9041
  return [...new Set(ids)].sort().join("\n");
9041
9042
  }
9042
9043
  function facetsSignature(facets) {
@@ -9146,7 +9147,16 @@ async function synthesizeFacet(facet, ctx, options, prior) {
9146
9147
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
9147
9148
  const proofCountAlpha = options.proofCountAlpha ?? DEFAULT_PROFILE_PROOF_ALPHA;
9148
9149
  const reviewed = options.reviewedMemoryIds;
9149
- const hasReviewGate = reviewed !== void 0 && reviewed.length > 0;
9150
+ const hasReviewGate = reviewed !== void 0;
9151
+ if (hasReviewGate && reviewed.length === 0) {
9152
+ return {
9153
+ key: facet.key,
9154
+ label: facet.label,
9155
+ text: "",
9156
+ sourceMemoryIds: [],
9157
+ generatedAt: Date.now()
9158
+ };
9159
+ }
9150
9160
  const recalled = await recall(facet.query, ctx, {
9151
9161
  scopes,
9152
9162
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-XXCZVG4Z.mjs";
3
+ } from "./chunk-BAJL46NX.mjs";
4
4
  import "./chunk-ILAWDQDE.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -18,19 +18,21 @@ A "memory" is a self-contained natural-language fact about the user. Multiple me
18
18
  Decide one of four actions:
19
19
 
20
20
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
21
- - "update": the new memory describes the SAME FACET as exactly one existing memory and is COMPATIBLE with it \u2014 it adds detail, fixes an incomplete value, or rephrases the same still-true fact. Return targetId of that existing memory + the consolidated content (richest version of the two).
22
- - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory \u2014 the old fact was true before but is false now (a state change). Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
23
- - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
21
+ - "update": the new memory describes the SAME FACET of the SAME SUBJECT as exactly one existing memory, is COMPATIBLE with it, and ADDS INFORMATION \u2014 it adds detail or fills in an incomplete value. Return targetId of that existing memory + the consolidated content (richest version of the two). A pure rewording that adds nothing is NOT an update; it is "noop".
22
+ - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory ABOUT THE SAME SUBJECT \u2014 the old fact was true before but is false now (a state change). Before choosing this, name the subject of the new memory and the subject of each candidate out loud to yourself; if they are different people, the old fact is still TRUE and superseding it would delete a correct memory \u2014 choose "create" instead. Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
23
+ - "noop": the new memory is already fully captured by an existing memory \u2014 same facet of the same subject, no new information. This INCLUDES a restatement in different words ("has two kids" / "has two children"): if the existing memory already tells you everything the new one does, the answer is noop no matter how differently it is worded. Skip the write.
24
24
 
25
25
  RULES (carry these strictly):
26
26
 
27
27
  1. ONE OBSERVATION PER DISTINCT FACET. If the new memory is about "user's aunt's twins" and an existing memory is also about "user's aunt's twins", they are the same facet \u2192 update, never create.
28
+ 1a. SAME SUBJECT REQUIRED \u2014 check this FIRST, before you look at the facet. The subject is WHO the fact is about: the user, or a specific person or thing in their life (a relative, their manager, their dog Biscuit). "update", "supersede" and "noop" all require the new memory and the existing one to be about the SAME subject. Two memories can share a facet and still have different subjects, and then they are simply two different facts: "User's daughter is allergic to peanuts" and "User is allergic to peanuts" are both about a peanut allergy, but one is about the DAUGHTER and one is about the USER \u2192 create, keep both. This holds for EVERY attribute, not just the one in this example \u2014 where someone lives, who they work for, what they own, how old they are. A possessive like "user's X" makes X the subject, not the user; strip it and ask "whose fact is this?" before comparing anything. Never retire the user's own value because a fact about somebody else resembles it \u2014 the user's fact is still true, and retiring it destroys it.
28
29
  2. MATCH BY FACET, NOT TOPIC. Two memories both mentioning "Zara" is not enough to merge \u2014 only merge if they describe the same property (e.g. both about "user's pending Zara boot exchange"). Two memories about Zara on different topics (e.g. "user shops at Zara" + "user returned a sweater to Zara last week") are separate facets.
29
30
  3. NO COMPUTATION. Do not sum counts, decrement quantities, or derive new facts from existing ones. If the new memory says "user spent $200 today" and an existing memory says "user spent $150 yesterday", these are TWO SEPARATE EVENTS \u2014 create.
30
31
  4. EVENTS vs STANDING STATE.
31
32
  - Distinct EVENTS (episodic \u2014 something that happened on a date) are separate memories, even if the same activity: "user went to gym Monday" vs "user went to gym Friday" \u2192 create; keep both.
32
- - A STANDING ATTRIBUTE (an ongoing state: where the user lives/works, relationship status, current role) has ONE current value. When the new memory changes that value ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true).
33
- 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory; then choose supersede. Over-merging loses information; a live contradiction is worse than either.
33
+ - A STANDING ATTRIBUTE (an ongoing state: where someone lives/works, relationship status, current role) has ONE current value PER SUBJECT. When the new memory changes that value FOR THE SAME SUBJECT ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true). A different subject holding the same attribute is not a value change at all \u2192 create.
34
+ 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory ABOUT THE SAME SUBJECT; then choose supersede. Two facts about different subjects never contradict each other, however similarly they read. Over-merging loses information; a live contradiction is worse than either.
35
+ 6. When in doubt between update and noop, ask whether the existing memory ALONE already answers everything the new one would. If it does, choose noop \u2014 a write that changes only the wording costs a re-embed and makes an old fact look newly observed.
34
36
 
35
37
  OUTPUT \u2014 strict JSON, no prose:
36
38
  {
@@ -23361,19 +23361,21 @@ A "memory" is a self-contained natural-language fact about the user. Multiple me
23361
23361
  Decide one of four actions:
23362
23362
 
23363
23363
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
23364
- - "update": the new memory describes the SAME FACET as exactly one existing memory and is COMPATIBLE with it \u2014 it adds detail, fixes an incomplete value, or rephrases the same still-true fact. Return targetId of that existing memory + the consolidated content (richest version of the two).
23365
- - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory \u2014 the old fact was true before but is false now (a state change). Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
23366
- - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
23364
+ - "update": the new memory describes the SAME FACET of the SAME SUBJECT as exactly one existing memory, is COMPATIBLE with it, and ADDS INFORMATION \u2014 it adds detail or fills in an incomplete value. Return targetId of that existing memory + the consolidated content (richest version of the two). A pure rewording that adds nothing is NOT an update; it is "noop".
23365
+ - "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory ABOUT THE SAME SUBJECT \u2014 the old fact was true before but is false now (a state change). Before choosing this, name the subject of the new memory and the subject of each candidate out loud to yourself; if they are different people, the old fact is still TRUE and superseding it would delete a correct memory \u2014 choose "create" instead. Return "targetIds": an array of the ids of EVERY existing candidate that describes the SAME standing attribute now being changed \u2014 retire ALL stale duplicates of the old value, not just one (the candidate list may contain several paraphrases of the same old fact) + content = the NEW fact. The old memories are kept as history, not overwritten or deleted; do not re-state them.
23366
+ - "noop": the new memory is already fully captured by an existing memory \u2014 same facet of the same subject, no new information. This INCLUDES a restatement in different words ("has two kids" / "has two children"): if the existing memory already tells you everything the new one does, the answer is noop no matter how differently it is worded. Skip the write.
23367
23367
 
23368
23368
  RULES (carry these strictly):
23369
23369
 
23370
23370
  1. ONE OBSERVATION PER DISTINCT FACET. If the new memory is about "user's aunt's twins" and an existing memory is also about "user's aunt's twins", they are the same facet \u2192 update, never create.
23371
+ 1a. SAME SUBJECT REQUIRED \u2014 check this FIRST, before you look at the facet. The subject is WHO the fact is about: the user, or a specific person or thing in their life (a relative, their manager, their dog Biscuit). "update", "supersede" and "noop" all require the new memory and the existing one to be about the SAME subject. Two memories can share a facet and still have different subjects, and then they are simply two different facts: "User's daughter is allergic to peanuts" and "User is allergic to peanuts" are both about a peanut allergy, but one is about the DAUGHTER and one is about the USER \u2192 create, keep both. This holds for EVERY attribute, not just the one in this example \u2014 where someone lives, who they work for, what they own, how old they are. A possessive like "user's X" makes X the subject, not the user; strip it and ask "whose fact is this?" before comparing anything. Never retire the user's own value because a fact about somebody else resembles it \u2014 the user's fact is still true, and retiring it destroys it.
23371
23372
  2. MATCH BY FACET, NOT TOPIC. Two memories both mentioning "Zara" is not enough to merge \u2014 only merge if they describe the same property (e.g. both about "user's pending Zara boot exchange"). Two memories about Zara on different topics (e.g. "user shops at Zara" + "user returned a sweater to Zara last week") are separate facets.
23372
23373
  3. NO COMPUTATION. Do not sum counts, decrement quantities, or derive new facts from existing ones. If the new memory says "user spent $200 today" and an existing memory says "user spent $150 yesterday", these are TWO SEPARATE EVENTS \u2014 create.
23373
23374
  4. EVENTS vs STANDING STATE.
23374
23375
  - Distinct EVENTS (episodic \u2014 something that happened on a date) are separate memories, even if the same activity: "user went to gym Monday" vs "user went to gym Friday" \u2192 create; keep both.
23375
- - A STANDING ATTRIBUTE (an ongoing state: where the user lives/works, relationship status, current role) has ONE current value. When the new memory changes that value ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true).
23376
- 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory; then choose supersede. Over-merging loses information; a live contradiction is worse than either.
23376
+ - A STANDING ATTRIBUTE (an ongoing state: where someone lives/works, relationship status, current role) has ONE current value PER SUBJECT. When the new memory changes that value FOR THE SAME SUBJECT ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true). A different subject holding the same attribute is not a value change at all \u2192 create.
23377
+ 5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory ABOUT THE SAME SUBJECT; then choose supersede. Two facts about different subjects never contradict each other, however similarly they read. Over-merging loses information; a live contradiction is worse than either.
23378
+ 6. When in doubt between update and noop, ask whether the existing memory ALONE already answers everything the new one would. If it does, choose noop \u2014 a write that changes only the wording costs a re-embed and makes an old fact look newly observed.
23377
23379
 
23378
23380
  OUTPUT \u2014 strict JSON, no prose:
23379
23381
  {
@@ -29956,7 +29958,8 @@ function configMatches(a, b) {
29956
29958
  return a.redacted === b.redacted && a.facetsSignature === b.facetsSignature && (a.reviewedMemoryIdsSignature ?? "") === b.reviewedMemoryIdsSignature && a.scopes.length === b.scopes.length && a.scopes.every((s, i) => s === b.scopes[i]);
29957
29959
  }
29958
29960
  function reviewedMemoryIdsSignature(ids) {
29959
- if (ids === void 0 || ids.length === 0) return "";
29961
+ if (ids === void 0) return "";
29962
+ if (ids.length === 0) return "\0gated-empty";
29960
29963
  return [...new Set(ids)].sort().join("\n");
29961
29964
  }
29962
29965
  function facetsSignature(facets) {
@@ -30066,7 +30069,16 @@ async function synthesizeFacet(facet, ctx, options, prior) {
30066
30069
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
30067
30070
  const proofCountAlpha = options.proofCountAlpha ?? DEFAULT_PROFILE_PROOF_ALPHA;
30068
30071
  const reviewed = options.reviewedMemoryIds;
30069
- const hasReviewGate = reviewed !== void 0 && reviewed.length > 0;
30072
+ const hasReviewGate = reviewed !== void 0;
30073
+ if (hasReviewGate && reviewed.length === 0) {
30074
+ return {
30075
+ key: facet.key,
30076
+ label: facet.label,
30077
+ text: "",
30078
+ sourceMemoryIds: [],
30079
+ generatedAt: Date.now()
30080
+ };
30081
+ }
30070
30082
  const recalled = await recall(facet.query, ctx, {
30071
30083
  scopes,
30072
30084
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
@@ -8356,10 +8356,20 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
8356
8356
  */
8357
8357
  proofCountAlpha?: number;
8358
8358
  /**
8359
- * When non-empty, intersect each facet's recalled evidence with this id set
8360
- * before the LLM runs (publish-review gate). Empty intersection → empty
8361
- * section (legitimate no-evidence), not a stale fallback. Omit / empty →
8362
- * no gate.
8359
+ * Publish-review gate: when SUPPLIED, each facet's recalled evidence is
8360
+ * intersected with this id set before the LLM runs, so synthesis can only draw
8361
+ * on memories the user approved for publication. Empty intersection → empty
8362
+ * section (legitimate no-evidence), not a stale fallback.
8363
+ *
8364
+ * Pass the user's published set (e.g. `getAllVaultMemoriesOp(ctx, { visibility:
8365
+ * ["public"] })`) to keep a published profile derivable only from published
8366
+ * memories — People Nearby's two-tier model treats `private` memories as never
8367
+ * leaving the device, and a summary derived from them is a derivative that does.
8368
+ *
8369
+ * `[]` means "nothing approved" and gates everything OUT (no recall, no LLM
8370
+ * call, empty sections). **Omitting the field is the only way to run ungated**
8371
+ * — that asymmetry is deliberate, so a caller computing a published set can
8372
+ * never accidentally disable the gate by finding it empty.
8363
8373
  */
8364
8374
  reviewedMemoryIds?: readonly string[];
8365
8375
  }
@@ -8356,10 +8356,20 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
8356
8356
  */
8357
8357
  proofCountAlpha?: number;
8358
8358
  /**
8359
- * When non-empty, intersect each facet's recalled evidence with this id set
8360
- * before the LLM runs (publish-review gate). Empty intersection → empty
8361
- * section (legitimate no-evidence), not a stale fallback. Omit / empty →
8362
- * no gate.
8359
+ * Publish-review gate: when SUPPLIED, each facet's recalled evidence is
8360
+ * intersected with this id set before the LLM runs, so synthesis can only draw
8361
+ * on memories the user approved for publication. Empty intersection → empty
8362
+ * section (legitimate no-evidence), not a stale fallback.
8363
+ *
8364
+ * Pass the user's published set (e.g. `getAllVaultMemoriesOp(ctx, { visibility:
8365
+ * ["public"] })`) to keep a published profile derivable only from published
8366
+ * memories — People Nearby's two-tier model treats `private` memories as never
8367
+ * leaving the device, and a summary derived from them is a derivative that does.
8368
+ *
8369
+ * `[]` means "nothing approved" and gates everything OUT (no recall, no LLM
8370
+ * call, empty sections). **Omitting the field is the only way to run ungated**
8371
+ * — that asymmetry is deliberate, so a caller computing a published set can
8372
+ * never accidentally disable the gate by finding it empty.
8363
8373
  */
8364
8374
  reviewedMemoryIds?: readonly string[];
8365
8375
  }
@@ -5445,7 +5445,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5445
5445
  content: m.content,
5446
5446
  similarity: m.similarity
5447
5447
  }));
5448
- const { consolidateMemory: doConsolidate } = await import("./consolidate-4VEAS3YN.mjs");
5448
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-GRMEDWKK.mjs");
5449
5449
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
5450
5450
  if (decision.action === "create") return null;
5451
5451
  if (decision.action === "supersede" && decision.content) {
@@ -6991,7 +6991,8 @@ function configMatches(a, b) {
6991
6991
  return a.redacted === b.redacted && a.facetsSignature === b.facetsSignature && (a.reviewedMemoryIdsSignature ?? "") === b.reviewedMemoryIdsSignature && a.scopes.length === b.scopes.length && a.scopes.every((s, i) => s === b.scopes[i]);
6992
6992
  }
6993
6993
  function reviewedMemoryIdsSignature(ids) {
6994
- if (ids === void 0 || ids.length === 0) return "";
6994
+ if (ids === void 0) return "";
6995
+ if (ids.length === 0) return "\0gated-empty";
6995
6996
  return [...new Set(ids)].sort().join("\n");
6996
6997
  }
6997
6998
  function facetsSignature(facets) {
@@ -7101,7 +7102,16 @@ async function synthesizeFacet(facet, ctx, options, prior) {
7101
7102
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
7102
7103
  const proofCountAlpha = options.proofCountAlpha ?? DEFAULT_PROFILE_PROOF_ALPHA;
7103
7104
  const reviewed = options.reviewedMemoryIds;
7104
- const hasReviewGate = reviewed !== void 0 && reviewed.length > 0;
7105
+ const hasReviewGate = reviewed !== void 0;
7106
+ if (hasReviewGate && reviewed.length === 0) {
7107
+ return {
7108
+ key: facet.key,
7109
+ label: facet.label,
7110
+ text: "",
7111
+ sourceMemoryIds: [],
7112
+ generatedAt: Date.now()
7113
+ };
7114
+ }
7105
7115
  const recalled = await recall(facet.query, ctx, {
7106
7116
  scopes,
7107
7117
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "3.13.2",
3
+ "version": "4.0.1",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -159,7 +159,7 @@
159
159
  "homepage": "https://github.com/anuma-ai/sdk#readme",
160
160
  "packageManager": "pnpm@10.23.0",
161
161
  "dependencies": {
162
- "@anuma/portal": "1.0.0-next.20260727223242",
162
+ "@anuma/portal": "1.0.0-next.20260728181242",
163
163
  "@babel/parser": "^7.29.2",
164
164
  "@babel/types": "^7.29.0",
165
165
  "ai": "6.0.202",