@anuma/sdk 3.7.0 → 3.8.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.
@@ -19,7 +19,7 @@ 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
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 targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
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
23
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
24
24
 
25
25
  RULES (carry these strictly):
@@ -35,14 +35,15 @@ RULES (carry these strictly):
35
35
  OUTPUT \u2014 strict JSON, no prose:
36
36
  {
37
37
  "action": "create" | "update" | "supersede" | "noop",
38
- "targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
38
+ "targetId": "<existing memory id \u2014 required for update/noop, omit for create/supersede>",
39
+ "targetIds": ["<id>", ...],
39
40
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
40
41
  }
41
42
 
42
- For "create": content is the new memory verbatim (or a slight refinement).
43
- For "update": content is the merged richest-version, \u226480 words.
44
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
45
- For "noop": no content (existing memory is already correct).`;
43
+ For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
44
+ For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
45
+ For "supersede": content is the NEW fact only (\u226480 words); "targetIds" lists EVERY stale memory being retired (all candidates describing the same now-changed attribute).
46
+ For "noop": no content (existing memory is already correct); targetId is that memory.`;
46
47
  async function consolidateMemory(newContent, candidates, options) {
47
48
  const fallback = {
48
49
  action: "create",
@@ -123,14 +124,27 @@ function validate(parsed, newContent, validIds) {
123
124
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
124
125
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
125
126
  }
126
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
127
- if (!targetId || !validIds.has(targetId)) return null;
128
127
  if (action === "noop") {
129
- return { action: "noop", targetId };
128
+ const targetId2 = typeof obj.targetId === "string" ? obj.targetId : null;
129
+ if (!targetId2 || !validIds.has(targetId2)) return null;
130
+ return { action: "noop", targetId: targetId2 };
130
131
  }
131
132
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
132
133
  if (c.length === 0) return null;
133
- return { action, targetId, content: c };
134
+ if (action === "supersede") {
135
+ const rawIds = [
136
+ ...Array.isArray(obj.targetIds) ? obj.targetIds : [],
137
+ ...typeof obj.targetId === "string" ? [obj.targetId] : []
138
+ ];
139
+ const targetIds = [
140
+ ...new Set(rawIds.filter((id) => typeof id === "string" && validIds.has(id)))
141
+ ];
142
+ if (targetIds.length === 0) return null;
143
+ return { action: "supersede", targetId: targetIds[0], targetIds, content: c };
144
+ }
145
+ const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
146
+ if (!targetId || !validIds.has(targetId)) return null;
147
+ return { action: "update", targetId, content: c };
134
148
  }
135
149
  export {
136
150
  consolidateMemory
@@ -7633,14 +7633,27 @@ function validate2(parsed, newContent, validIds) {
7633
7633
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
7634
7634
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
7635
7635
  }
7636
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
7637
- if (!targetId || !validIds.has(targetId)) return null;
7638
7636
  if (action === "noop") {
7639
- return { action: "noop", targetId };
7637
+ const targetId2 = typeof obj.targetId === "string" ? obj.targetId : null;
7638
+ if (!targetId2 || !validIds.has(targetId2)) return null;
7639
+ return { action: "noop", targetId: targetId2 };
7640
7640
  }
7641
7641
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
7642
7642
  if (c.length === 0) return null;
7643
- return { action, targetId, content: c };
7643
+ if (action === "supersede") {
7644
+ const rawIds = [
7645
+ ...Array.isArray(obj.targetIds) ? obj.targetIds : [],
7646
+ ...typeof obj.targetId === "string" ? [obj.targetId] : []
7647
+ ];
7648
+ const targetIds = [
7649
+ ...new Set(rawIds.filter((id) => typeof id === "string" && validIds.has(id)))
7650
+ ];
7651
+ if (targetIds.length === 0) return null;
7652
+ return { action: "supersede", targetId: targetIds[0], targetIds, content: c };
7653
+ }
7654
+ const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
7655
+ if (!targetId || !validIds.has(targetId)) return null;
7656
+ return { action: "update", targetId, content: c };
7644
7657
  }
7645
7658
  var DEFAULT_MODEL3, DEFAULT_CONSOLIDATE_ATTEMPTS, DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS, SYSTEM_PROMPT3;
7646
7659
  var init_consolidate = __esm({
@@ -7660,7 +7673,7 @@ Decide one of four actions:
7660
7673
 
7661
7674
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
7662
7675
  - "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).
7663
- - "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 targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
7676
+ - "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.
7664
7677
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
7665
7678
 
7666
7679
  RULES (carry these strictly):
@@ -7676,14 +7689,15 @@ RULES (carry these strictly):
7676
7689
  OUTPUT \u2014 strict JSON, no prose:
7677
7690
  {
7678
7691
  "action": "create" | "update" | "supersede" | "noop",
7679
- "targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
7692
+ "targetId": "<existing memory id \u2014 required for update/noop, omit for create/supersede>",
7693
+ "targetIds": ["<id>", ...],
7680
7694
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
7681
7695
  }
7682
7696
 
7683
- For "create": content is the new memory verbatim (or a slight refinement).
7684
- For "update": content is the merged richest-version, \u226480 words.
7685
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
7686
- For "noop": no content (existing memory is already correct).`;
7697
+ For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
7698
+ For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
7699
+ For "supersede": content is the NEW fact only (\u226480 words); "targetIds" lists EVERY stale memory being retired (all candidates describing the same now-changed attribute).
7700
+ For "noop": no content (existing memory is already correct); targetId is that memory.`;
7687
7701
  }
7688
7702
  });
7689
7703
 
@@ -14001,14 +14015,15 @@ init_portalLlm();
14001
14015
 
14002
14016
  // src/lib/memory/retain.ts
14003
14017
  init_operations3();
14018
+ init_logger();
14004
14019
  init_constants();
14005
14020
  init_embeddings();
14006
14021
  init_vector();
14007
14022
  init_searchTool();
14008
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
14023
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
14009
14024
  var DEFAULT_SCOPE = "private";
14010
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
14011
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
14025
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
14026
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
14012
14027
  async function retain(content, ctx, options = {}) {
14013
14028
  const trimmed = content.trim();
14014
14029
  if (trimmed.length === 0) {
@@ -14017,18 +14032,18 @@ async function retain(content, ctx, options = {}) {
14017
14032
  const enableAutoMerge = options.enableAutoMerge ?? true;
14018
14033
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
14019
14034
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
14020
- let supersedeTargetId;
14035
+ let supersedeTargetIds = [];
14021
14036
  let supersedeContent;
14022
14037
  if (enableAutoMerge) {
14023
14038
  if (options.consolidateOptions) {
14024
14039
  const outcome = await tryConsolidate(trimmed, ctx, options);
14025
14040
  if (outcome) {
14026
14041
  if ("done" in outcome) return outcome.done;
14027
- supersedeTargetId = outcome.supersede;
14042
+ supersedeTargetIds = outcome.supersede;
14028
14043
  supersedeContent = outcome.content;
14029
14044
  }
14030
14045
  }
14031
- if (!supersedeTargetId) {
14046
+ if (supersedeTargetIds.length === 0) {
14032
14047
  const matches = await searchVaultMemories(
14033
14048
  trimmed,
14034
14049
  ctx.vaultCtx,
@@ -14132,18 +14147,42 @@ async function retain(content, ctx, options = {}) {
14132
14147
  // memory's tier. The DB op re-validates against the known set.
14133
14148
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
14134
14149
  };
14135
- if (supersedeTargetId) {
14150
+ if (supersedeTargetIds.length > 0) {
14151
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
14136
14152
  const { created: created2, retired } = await createSupersedingMemoryOp(
14137
14153
  ctx.vaultCtx,
14138
14154
  createOpts,
14139
- supersedeTargetId
14155
+ primaryTargetId
14140
14156
  );
14141
14157
  if (created2 && retired) {
14142
14158
  ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
14159
+ const liveLeftovers = [];
14160
+ for (const staleId of restTargetIds) {
14161
+ let ok = false;
14162
+ try {
14163
+ ok = await supersedeVaultMemoryOp(ctx.vaultCtx, staleId, created2.uniqueId) === true;
14164
+ } catch {
14165
+ }
14166
+ if (ok) continue;
14167
+ const stillLive = await getVaultMemoryOp(ctx.vaultCtx, staleId).catch(() => null);
14168
+ if (stillLive && !stillLive.supersededBy) liveLeftovers.push(staleId);
14169
+ }
14170
+ if (liveLeftovers.length > 0) {
14171
+ getLogger().warn(
14172
+ "[memory/retain] supersede left duplicate row(s) live \u2014 will reconcile at next consolidation",
14173
+ {
14174
+ newMemoryId: created2.uniqueId,
14175
+ leftover: liveLeftovers.length,
14176
+ // Log the specific ids (opaque record ids, not content) so an
14177
+ // operator can identify exactly which rows are still active.
14178
+ leftoverIds: liveLeftovers
14179
+ }
14180
+ );
14181
+ }
14143
14182
  return {
14144
14183
  action: "supersede",
14145
14184
  memoryId: created2.uniqueId,
14146
- targetId: supersedeTargetId,
14185
+ targetId: primaryTargetId,
14147
14186
  proofCount: 1
14148
14187
  };
14149
14188
  }
@@ -14229,10 +14268,16 @@ async function tryConsolidate(trimmed, ctx, options) {
14229
14268
  const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
14230
14269
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
14231
14270
  if (decision.action === "create") return null;
14232
- if (decision.action === "supersede" && decision.targetId && decision.content) {
14233
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
14234
- if (!existing || existing.supersededBy) return null;
14235
- return { supersede: decision.targetId, content: decision.content };
14271
+ if (decision.action === "supersede" && decision.content) {
14272
+ const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
14273
+ if (requestedIds.length === 0) return null;
14274
+ const valid = [];
14275
+ for (const id of requestedIds) {
14276
+ const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
14277
+ if (existing && !existing.supersededBy) valid.push(id);
14278
+ }
14279
+ if (valid.length === 0) return null;
14280
+ return { supersede: valid, content: decision.content };
14236
14281
  }
14237
14282
  if (decision.action === "noop" && decision.targetId) {
14238
14283
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -6194,10 +6194,10 @@ function injectionSignatureCatalog() {
6194
6194
  }
6195
6195
 
6196
6196
  // src/lib/memory/retain.ts
6197
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
6197
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
6198
6198
  var DEFAULT_SCOPE = "private";
6199
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
6200
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
6199
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
6200
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
6201
6201
  async function retain(content, ctx, options = {}) {
6202
6202
  const trimmed = content.trim();
6203
6203
  if (trimmed.length === 0) {
@@ -6206,18 +6206,18 @@ async function retain(content, ctx, options = {}) {
6206
6206
  const enableAutoMerge = options.enableAutoMerge ?? true;
6207
6207
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
6208
6208
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
6209
- let supersedeTargetId;
6209
+ let supersedeTargetIds = [];
6210
6210
  let supersedeContent;
6211
6211
  if (enableAutoMerge) {
6212
6212
  if (options.consolidateOptions) {
6213
6213
  const outcome = await tryConsolidate(trimmed, ctx, options);
6214
6214
  if (outcome) {
6215
6215
  if ("done" in outcome) return outcome.done;
6216
- supersedeTargetId = outcome.supersede;
6216
+ supersedeTargetIds = outcome.supersede;
6217
6217
  supersedeContent = outcome.content;
6218
6218
  }
6219
6219
  }
6220
- if (!supersedeTargetId) {
6220
+ if (supersedeTargetIds.length === 0) {
6221
6221
  const matches = await searchVaultMemories(
6222
6222
  trimmed,
6223
6223
  ctx.vaultCtx,
@@ -6321,18 +6321,42 @@ async function retain(content, ctx, options = {}) {
6321
6321
  // memory's tier. The DB op re-validates against the known set.
6322
6322
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
6323
6323
  };
6324
- if (supersedeTargetId) {
6324
+ if (supersedeTargetIds.length > 0) {
6325
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
6325
6326
  const { created: created2, retired } = await createSupersedingMemoryOp(
6326
6327
  ctx.vaultCtx,
6327
6328
  createOpts,
6328
- supersedeTargetId
6329
+ primaryTargetId
6329
6330
  );
6330
6331
  if (created2 && retired) {
6331
6332
  ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
6333
+ const liveLeftovers = [];
6334
+ for (const staleId of restTargetIds) {
6335
+ let ok = false;
6336
+ try {
6337
+ ok = await supersedeVaultMemoryOp(ctx.vaultCtx, staleId, created2.uniqueId) === true;
6338
+ } catch {
6339
+ }
6340
+ if (ok) continue;
6341
+ const stillLive = await getVaultMemoryOp(ctx.vaultCtx, staleId).catch(() => null);
6342
+ if (stillLive && !stillLive.supersededBy) liveLeftovers.push(staleId);
6343
+ }
6344
+ if (liveLeftovers.length > 0) {
6345
+ getLogger().warn(
6346
+ "[memory/retain] supersede left duplicate row(s) live \u2014 will reconcile at next consolidation",
6347
+ {
6348
+ newMemoryId: created2.uniqueId,
6349
+ leftover: liveLeftovers.length,
6350
+ // Log the specific ids (opaque record ids, not content) so an
6351
+ // operator can identify exactly which rows are still active.
6352
+ leftoverIds: liveLeftovers
6353
+ }
6354
+ );
6355
+ }
6332
6356
  return {
6333
6357
  action: "supersede",
6334
6358
  memoryId: created2.uniqueId,
6335
- targetId: supersedeTargetId,
6359
+ targetId: primaryTargetId,
6336
6360
  proofCount: 1
6337
6361
  };
6338
6362
  }
@@ -6415,13 +6439,19 @@ async function tryConsolidate(trimmed, ctx, options) {
6415
6439
  content: m.content,
6416
6440
  similarity: m.similarity
6417
6441
  }));
6418
- const { consolidateMemory: doConsolidate } = await import("./consolidate-J5PHQKKY.mjs");
6442
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-HKN32P5W.mjs");
6419
6443
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
6420
6444
  if (decision.action === "create") return null;
6421
- if (decision.action === "supersede" && decision.targetId && decision.content) {
6422
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
6423
- if (!existing || existing.supersededBy) return null;
6424
- return { supersede: decision.targetId, content: decision.content };
6445
+ if (decision.action === "supersede" && decision.content) {
6446
+ const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
6447
+ if (requestedIds.length === 0) return null;
6448
+ const valid = [];
6449
+ for (const id of requestedIds) {
6450
+ const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
6451
+ if (existing && !existing.supersededBy) valid.push(id);
6452
+ }
6453
+ if (valid.length === 0) return null;
6454
+ return { supersede: valid, content: decision.content };
6425
6455
  }
6426
6456
  if (decision.action === "noop" && decision.targetId) {
6427
6457
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -19,7 +19,7 @@ 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
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 targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
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
23
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
24
24
 
25
25
  RULES (carry these strictly):
@@ -35,14 +35,15 @@ RULES (carry these strictly):
35
35
  OUTPUT \u2014 strict JSON, no prose:
36
36
  {
37
37
  "action": "create" | "update" | "supersede" | "noop",
38
- "targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
38
+ "targetId": "<existing memory id \u2014 required for update/noop, omit for create/supersede>",
39
+ "targetIds": ["<id>", ...],
39
40
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
40
41
  }
41
42
 
42
- For "create": content is the new memory verbatim (or a slight refinement).
43
- For "update": content is the merged richest-version, \u226480 words.
44
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
45
- For "noop": no content (existing memory is already correct).`;
43
+ For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
44
+ For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
45
+ For "supersede": content is the NEW fact only (\u226480 words); "targetIds" lists EVERY stale memory being retired (all candidates describing the same now-changed attribute).
46
+ For "noop": no content (existing memory is already correct); targetId is that memory.`;
46
47
  async function consolidateMemory(newContent, candidates, options) {
47
48
  const fallback = {
48
49
  action: "create",
@@ -123,14 +124,27 @@ function validate(parsed, newContent, validIds) {
123
124
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
124
125
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
125
126
  }
126
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
127
- if (!targetId || !validIds.has(targetId)) return null;
128
127
  if (action === "noop") {
129
- return { action: "noop", targetId };
128
+ const targetId2 = typeof obj.targetId === "string" ? obj.targetId : null;
129
+ if (!targetId2 || !validIds.has(targetId2)) return null;
130
+ return { action: "noop", targetId: targetId2 };
130
131
  }
131
132
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
132
133
  if (c.length === 0) return null;
133
- return { action, targetId, content: c };
134
+ if (action === "supersede") {
135
+ const rawIds = [
136
+ ...Array.isArray(obj.targetIds) ? obj.targetIds : [],
137
+ ...typeof obj.targetId === "string" ? [obj.targetId] : []
138
+ ];
139
+ const targetIds = [
140
+ ...new Set(rawIds.filter((id) => typeof id === "string" && validIds.has(id)))
141
+ ];
142
+ if (targetIds.length === 0) return null;
143
+ return { action: "supersede", targetId: targetIds[0], targetIds, content: c };
144
+ }
145
+ const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
146
+ if (!targetId || !validIds.has(targetId)) return null;
147
+ return { action: "update", targetId, content: c };
134
148
  }
135
149
  export {
136
150
  consolidateMemory
@@ -7911,14 +7911,27 @@ function validate2(parsed, newContent, validIds) {
7911
7911
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
7912
7912
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
7913
7913
  }
7914
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
7915
- if (!targetId || !validIds.has(targetId)) return null;
7916
7914
  if (action === "noop") {
7917
- return { action: "noop", targetId };
7915
+ const targetId2 = typeof obj.targetId === "string" ? obj.targetId : null;
7916
+ if (!targetId2 || !validIds.has(targetId2)) return null;
7917
+ return { action: "noop", targetId: targetId2 };
7918
7918
  }
7919
7919
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
7920
7920
  if (c.length === 0) return null;
7921
- return { action, targetId, content: c };
7921
+ if (action === "supersede") {
7922
+ const rawIds = [
7923
+ ...Array.isArray(obj.targetIds) ? obj.targetIds : [],
7924
+ ...typeof obj.targetId === "string" ? [obj.targetId] : []
7925
+ ];
7926
+ const targetIds = [
7927
+ ...new Set(rawIds.filter((id) => typeof id === "string" && validIds.has(id)))
7928
+ ];
7929
+ if (targetIds.length === 0) return null;
7930
+ return { action: "supersede", targetId: targetIds[0], targetIds, content: c };
7931
+ }
7932
+ const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
7933
+ if (!targetId || !validIds.has(targetId)) return null;
7934
+ return { action: "update", targetId, content: c };
7922
7935
  }
7923
7936
  var DEFAULT_MODEL3, DEFAULT_CONSOLIDATE_ATTEMPTS, DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS, SYSTEM_PROMPT3;
7924
7937
  var init_consolidate = __esm({
@@ -7938,7 +7951,7 @@ Decide one of four actions:
7938
7951
 
7939
7952
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
7940
7953
  - "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).
7941
- - "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 targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
7954
+ - "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.
7942
7955
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
7943
7956
 
7944
7957
  RULES (carry these strictly):
@@ -7954,14 +7967,15 @@ RULES (carry these strictly):
7954
7967
  OUTPUT \u2014 strict JSON, no prose:
7955
7968
  {
7956
7969
  "action": "create" | "update" | "supersede" | "noop",
7957
- "targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
7970
+ "targetId": "<existing memory id \u2014 required for update/noop, omit for create/supersede>",
7971
+ "targetIds": ["<id>", ...],
7958
7972
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
7959
7973
  }
7960
7974
 
7961
- For "create": content is the new memory verbatim (or a slight refinement).
7962
- For "update": content is the merged richest-version, \u226480 words.
7963
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
7964
- For "noop": no content (existing memory is already correct).`;
7975
+ For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
7976
+ For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
7977
+ For "supersede": content is the NEW fact only (\u226480 words); "targetIds" lists EVERY stale memory being retired (all candidates describing the same now-changed attribute).
7978
+ For "noop": no content (existing memory is already correct); targetId is that memory.`;
7965
7979
  }
7966
7980
  });
7967
7981
 
@@ -15291,14 +15305,15 @@ init_portalLlm();
15291
15305
 
15292
15306
  // src/lib/memory/retain.ts
15293
15307
  init_operations3();
15308
+ init_logger();
15294
15309
  init_constants();
15295
15310
  init_embeddings();
15296
15311
  init_vector();
15297
15312
  init_searchTool();
15298
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
15313
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
15299
15314
  var DEFAULT_SCOPE = "private";
15300
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
15301
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
15315
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
15316
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
15302
15317
  async function retain(content, ctx, options = {}) {
15303
15318
  const trimmed = content.trim();
15304
15319
  if (trimmed.length === 0) {
@@ -15307,18 +15322,18 @@ async function retain(content, ctx, options = {}) {
15307
15322
  const enableAutoMerge = options.enableAutoMerge ?? true;
15308
15323
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
15309
15324
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
15310
- let supersedeTargetId;
15325
+ let supersedeTargetIds = [];
15311
15326
  let supersedeContent;
15312
15327
  if (enableAutoMerge) {
15313
15328
  if (options.consolidateOptions) {
15314
15329
  const outcome = await tryConsolidate(trimmed, ctx, options);
15315
15330
  if (outcome) {
15316
15331
  if ("done" in outcome) return outcome.done;
15317
- supersedeTargetId = outcome.supersede;
15332
+ supersedeTargetIds = outcome.supersede;
15318
15333
  supersedeContent = outcome.content;
15319
15334
  }
15320
15335
  }
15321
- if (!supersedeTargetId) {
15336
+ if (supersedeTargetIds.length === 0) {
15322
15337
  const matches = await searchVaultMemories(
15323
15338
  trimmed,
15324
15339
  ctx.vaultCtx,
@@ -15422,18 +15437,42 @@ async function retain(content, ctx, options = {}) {
15422
15437
  // memory's tier. The DB op re-validates against the known set.
15423
15438
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
15424
15439
  };
15425
- if (supersedeTargetId) {
15440
+ if (supersedeTargetIds.length > 0) {
15441
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
15426
15442
  const { created: created2, retired } = await createSupersedingMemoryOp(
15427
15443
  ctx.vaultCtx,
15428
15444
  createOpts,
15429
- supersedeTargetId
15445
+ primaryTargetId
15430
15446
  );
15431
15447
  if (created2 && retired) {
15432
15448
  ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
15449
+ const liveLeftovers = [];
15450
+ for (const staleId of restTargetIds) {
15451
+ let ok = false;
15452
+ try {
15453
+ ok = await supersedeVaultMemoryOp(ctx.vaultCtx, staleId, created2.uniqueId) === true;
15454
+ } catch {
15455
+ }
15456
+ if (ok) continue;
15457
+ const stillLive = await getVaultMemoryOp(ctx.vaultCtx, staleId).catch(() => null);
15458
+ if (stillLive && !stillLive.supersededBy) liveLeftovers.push(staleId);
15459
+ }
15460
+ if (liveLeftovers.length > 0) {
15461
+ getLogger().warn(
15462
+ "[memory/retain] supersede left duplicate row(s) live \u2014 will reconcile at next consolidation",
15463
+ {
15464
+ newMemoryId: created2.uniqueId,
15465
+ leftover: liveLeftovers.length,
15466
+ // Log the specific ids (opaque record ids, not content) so an
15467
+ // operator can identify exactly which rows are still active.
15468
+ leftoverIds: liveLeftovers
15469
+ }
15470
+ );
15471
+ }
15433
15472
  return {
15434
15473
  action: "supersede",
15435
15474
  memoryId: created2.uniqueId,
15436
- targetId: supersedeTargetId,
15475
+ targetId: primaryTargetId,
15437
15476
  proofCount: 1
15438
15477
  };
15439
15478
  }
@@ -15519,10 +15558,16 @@ async function tryConsolidate(trimmed, ctx, options) {
15519
15558
  const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
15520
15559
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
15521
15560
  if (decision.action === "create") return null;
15522
- if (decision.action === "supersede" && decision.targetId && decision.content) {
15523
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
15524
- if (!existing || existing.supersededBy) return null;
15525
- return { supersede: decision.targetId, content: decision.content };
15561
+ if (decision.action === "supersede" && decision.content) {
15562
+ const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
15563
+ if (requestedIds.length === 0) return null;
15564
+ const valid = [];
15565
+ for (const id of requestedIds) {
15566
+ const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
15567
+ if (existing && !existing.supersededBy) valid.push(id);
15568
+ }
15569
+ if (valid.length === 0) return null;
15570
+ return { supersede: valid, content: decision.content };
15526
15571
  }
15527
15572
  if (decision.action === "noop" && decision.targetId) {
15528
15573
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -7007,10 +7007,10 @@ function injectionSignatureCatalog() {
7007
7007
  }
7008
7008
 
7009
7009
  // src/lib/memory/retain.ts
7010
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
7010
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
7011
7011
  var DEFAULT_SCOPE = "private";
7012
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
7013
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
7012
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
7013
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
7014
7014
  async function retain(content, ctx, options = {}) {
7015
7015
  const trimmed = content.trim();
7016
7016
  if (trimmed.length === 0) {
@@ -7019,18 +7019,18 @@ async function retain(content, ctx, options = {}) {
7019
7019
  const enableAutoMerge = options.enableAutoMerge ?? true;
7020
7020
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
7021
7021
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
7022
- let supersedeTargetId;
7022
+ let supersedeTargetIds = [];
7023
7023
  let supersedeContent;
7024
7024
  if (enableAutoMerge) {
7025
7025
  if (options.consolidateOptions) {
7026
7026
  const outcome = await tryConsolidate(trimmed, ctx, options);
7027
7027
  if (outcome) {
7028
7028
  if ("done" in outcome) return outcome.done;
7029
- supersedeTargetId = outcome.supersede;
7029
+ supersedeTargetIds = outcome.supersede;
7030
7030
  supersedeContent = outcome.content;
7031
7031
  }
7032
7032
  }
7033
- if (!supersedeTargetId) {
7033
+ if (supersedeTargetIds.length === 0) {
7034
7034
  const matches = await searchVaultMemories(
7035
7035
  trimmed,
7036
7036
  ctx.vaultCtx,
@@ -7134,18 +7134,42 @@ async function retain(content, ctx, options = {}) {
7134
7134
  // memory's tier. The DB op re-validates against the known set.
7135
7135
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
7136
7136
  };
7137
- if (supersedeTargetId) {
7137
+ if (supersedeTargetIds.length > 0) {
7138
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
7138
7139
  const { created: created2, retired } = await createSupersedingMemoryOp(
7139
7140
  ctx.vaultCtx,
7140
7141
  createOpts,
7141
- supersedeTargetId
7142
+ primaryTargetId
7142
7143
  );
7143
7144
  if (created2 && retired) {
7144
7145
  ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
7146
+ const liveLeftovers = [];
7147
+ for (const staleId of restTargetIds) {
7148
+ let ok = false;
7149
+ try {
7150
+ ok = await supersedeVaultMemoryOp(ctx.vaultCtx, staleId, created2.uniqueId) === true;
7151
+ } catch {
7152
+ }
7153
+ if (ok) continue;
7154
+ const stillLive = await getVaultMemoryOp(ctx.vaultCtx, staleId).catch(() => null);
7155
+ if (stillLive && !stillLive.supersededBy) liveLeftovers.push(staleId);
7156
+ }
7157
+ if (liveLeftovers.length > 0) {
7158
+ getLogger().warn(
7159
+ "[memory/retain] supersede left duplicate row(s) live \u2014 will reconcile at next consolidation",
7160
+ {
7161
+ newMemoryId: created2.uniqueId,
7162
+ leftover: liveLeftovers.length,
7163
+ // Log the specific ids (opaque record ids, not content) so an
7164
+ // operator can identify exactly which rows are still active.
7165
+ leftoverIds: liveLeftovers
7166
+ }
7167
+ );
7168
+ }
7145
7169
  return {
7146
7170
  action: "supersede",
7147
7171
  memoryId: created2.uniqueId,
7148
- targetId: supersedeTargetId,
7172
+ targetId: primaryTargetId,
7149
7173
  proofCount: 1
7150
7174
  };
7151
7175
  }
@@ -7228,13 +7252,19 @@ async function tryConsolidate(trimmed, ctx, options) {
7228
7252
  content: m.content,
7229
7253
  similarity: m.similarity
7230
7254
  }));
7231
- const { consolidateMemory: doConsolidate } = await import("./consolidate-PWJCAPZA.mjs");
7255
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-7UUX56GM.mjs");
7232
7256
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
7233
7257
  if (decision.action === "create") return null;
7234
- if (decision.action === "supersede" && decision.targetId && decision.content) {
7235
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
7236
- if (!existing || existing.supersededBy) return null;
7237
- return { supersede: decision.targetId, content: decision.content };
7258
+ if (decision.action === "supersede" && decision.content) {
7259
+ const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
7260
+ if (requestedIds.length === 0) return null;
7261
+ const valid = [];
7262
+ for (const id of requestedIds) {
7263
+ const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
7264
+ if (existing && !existing.supersededBy) valid.push(id);
7265
+ }
7266
+ if (valid.length === 0) return null;
7267
+ return { supersede: valid, content: decision.content };
7238
7268
  }
7239
7269
  if (decision.action === "noop" && decision.targetId) {
7240
7270
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -19,7 +19,7 @@ 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
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 targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
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
23
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
24
24
 
25
25
  RULES (carry these strictly):
@@ -35,14 +35,15 @@ RULES (carry these strictly):
35
35
  OUTPUT \u2014 strict JSON, no prose:
36
36
  {
37
37
  "action": "create" | "update" | "supersede" | "noop",
38
- "targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
38
+ "targetId": "<existing memory id \u2014 required for update/noop, omit for create/supersede>",
39
+ "targetIds": ["<id>", ...],
39
40
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
40
41
  }
41
42
 
42
- For "create": content is the new memory verbatim (or a slight refinement).
43
- For "update": content is the merged richest-version, \u226480 words.
44
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
45
- For "noop": no content (existing memory is already correct).`;
43
+ For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
44
+ For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
45
+ For "supersede": content is the NEW fact only (\u226480 words); "targetIds" lists EVERY stale memory being retired (all candidates describing the same now-changed attribute).
46
+ For "noop": no content (existing memory is already correct); targetId is that memory.`;
46
47
  async function consolidateMemory(newContent, candidates, options) {
47
48
  const fallback = {
48
49
  action: "create",
@@ -123,14 +124,27 @@ function validate(parsed, newContent, validIds) {
123
124
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
124
125
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
125
126
  }
126
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
127
- if (!targetId || !validIds.has(targetId)) return null;
128
127
  if (action === "noop") {
129
- return { action: "noop", targetId };
128
+ const targetId2 = typeof obj.targetId === "string" ? obj.targetId : null;
129
+ if (!targetId2 || !validIds.has(targetId2)) return null;
130
+ return { action: "noop", targetId: targetId2 };
130
131
  }
131
132
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
132
133
  if (c.length === 0) return null;
133
- return { action, targetId, content: c };
134
+ if (action === "supersede") {
135
+ const rawIds = [
136
+ ...Array.isArray(obj.targetIds) ? obj.targetIds : [],
137
+ ...typeof obj.targetId === "string" ? [obj.targetId] : []
138
+ ];
139
+ const targetIds = [
140
+ ...new Set(rawIds.filter((id) => typeof id === "string" && validIds.has(id)))
141
+ ];
142
+ if (targetIds.length === 0) return null;
143
+ return { action: "supersede", targetId: targetIds[0], targetIds, content: c };
144
+ }
145
+ const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
146
+ if (!targetId || !validIds.has(targetId)) return null;
147
+ return { action: "update", targetId, content: c };
134
148
  }
135
149
  export {
136
150
  consolidateMemory
@@ -22458,14 +22458,27 @@ function validate2(parsed, newContent, validIds) {
22458
22458
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
22459
22459
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
22460
22460
  }
22461
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
22462
- if (!targetId || !validIds.has(targetId)) return null;
22463
22461
  if (action === "noop") {
22464
- return { action: "noop", targetId };
22462
+ const targetId2 = typeof obj.targetId === "string" ? obj.targetId : null;
22463
+ if (!targetId2 || !validIds.has(targetId2)) return null;
22464
+ return { action: "noop", targetId: targetId2 };
22465
22465
  }
22466
22466
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
22467
22467
  if (c.length === 0) return null;
22468
- return { action, targetId, content: c };
22468
+ if (action === "supersede") {
22469
+ const rawIds = [
22470
+ ...Array.isArray(obj.targetIds) ? obj.targetIds : [],
22471
+ ...typeof obj.targetId === "string" ? [obj.targetId] : []
22472
+ ];
22473
+ const targetIds = [
22474
+ ...new Set(rawIds.filter((id) => typeof id === "string" && validIds.has(id)))
22475
+ ];
22476
+ if (targetIds.length === 0) return null;
22477
+ return { action: "supersede", targetId: targetIds[0], targetIds, content: c };
22478
+ }
22479
+ const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
22480
+ if (!targetId || !validIds.has(targetId)) return null;
22481
+ return { action: "update", targetId, content: c };
22469
22482
  }
22470
22483
  var DEFAULT_MODEL3, DEFAULT_CONSOLIDATE_ATTEMPTS, DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS, SYSTEM_PROMPT3;
22471
22484
  var init_consolidate = __esm({
@@ -22485,7 +22498,7 @@ Decide one of four actions:
22485
22498
 
22486
22499
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
22487
22500
  - "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).
22488
- - "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 targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
22501
+ - "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.
22489
22502
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
22490
22503
 
22491
22504
  RULES (carry these strictly):
@@ -22501,14 +22514,15 @@ RULES (carry these strictly):
22501
22514
  OUTPUT \u2014 strict JSON, no prose:
22502
22515
  {
22503
22516
  "action": "create" | "update" | "supersede" | "noop",
22504
- "targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
22517
+ "targetId": "<existing memory id \u2014 required for update/noop, omit for create/supersede>",
22518
+ "targetIds": ["<id>", ...],
22505
22519
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
22506
22520
  }
22507
22521
 
22508
- For "create": content is the new memory verbatim (or a slight refinement).
22509
- For "update": content is the merged richest-version, \u226480 words.
22510
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
22511
- For "noop": no content (existing memory is already correct).`;
22522
+ For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
22523
+ For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
22524
+ For "supersede": content is the NEW fact only (\u226480 words); "targetIds" lists EVERY stale memory being retired (all candidates describing the same now-changed attribute).
22525
+ For "noop": no content (existing memory is already correct); targetId is that memory.`;
22512
22526
  }
22513
22527
  });
22514
22528
 
@@ -27157,14 +27171,15 @@ init_portalLlm();
27157
27171
 
27158
27172
  // src/lib/memory/retain.ts
27159
27173
  init_operations3();
27174
+ init_logger();
27160
27175
  init_constants();
27161
27176
  init_embeddings();
27162
27177
  init_vector();
27163
27178
  init_searchTool();
27164
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
27179
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
27165
27180
  var DEFAULT_SCOPE = "private";
27166
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
27167
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
27181
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
27182
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
27168
27183
  async function retain(content, ctx, options = {}) {
27169
27184
  const trimmed = content.trim();
27170
27185
  if (trimmed.length === 0) {
@@ -27173,18 +27188,18 @@ async function retain(content, ctx, options = {}) {
27173
27188
  const enableAutoMerge = options.enableAutoMerge ?? true;
27174
27189
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
27175
27190
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
27176
- let supersedeTargetId;
27191
+ let supersedeTargetIds = [];
27177
27192
  let supersedeContent;
27178
27193
  if (enableAutoMerge) {
27179
27194
  if (options.consolidateOptions) {
27180
27195
  const outcome = await tryConsolidate(trimmed, ctx, options);
27181
27196
  if (outcome) {
27182
27197
  if ("done" in outcome) return outcome.done;
27183
- supersedeTargetId = outcome.supersede;
27198
+ supersedeTargetIds = outcome.supersede;
27184
27199
  supersedeContent = outcome.content;
27185
27200
  }
27186
27201
  }
27187
- if (!supersedeTargetId) {
27202
+ if (supersedeTargetIds.length === 0) {
27188
27203
  const matches = await searchVaultMemories(
27189
27204
  trimmed,
27190
27205
  ctx.vaultCtx,
@@ -27288,18 +27303,42 @@ async function retain(content, ctx, options = {}) {
27288
27303
  // memory's tier. The DB op re-validates against the known set.
27289
27304
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
27290
27305
  };
27291
- if (supersedeTargetId) {
27306
+ if (supersedeTargetIds.length > 0) {
27307
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
27292
27308
  const { created: created2, retired } = await createSupersedingMemoryOp(
27293
27309
  ctx.vaultCtx,
27294
27310
  createOpts,
27295
- supersedeTargetId
27311
+ primaryTargetId
27296
27312
  );
27297
27313
  if (created2 && retired) {
27298
27314
  ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
27315
+ const liveLeftovers = [];
27316
+ for (const staleId of restTargetIds) {
27317
+ let ok = false;
27318
+ try {
27319
+ ok = await supersedeVaultMemoryOp(ctx.vaultCtx, staleId, created2.uniqueId) === true;
27320
+ } catch {
27321
+ }
27322
+ if (ok) continue;
27323
+ const stillLive = await getVaultMemoryOp(ctx.vaultCtx, staleId).catch(() => null);
27324
+ if (stillLive && !stillLive.supersededBy) liveLeftovers.push(staleId);
27325
+ }
27326
+ if (liveLeftovers.length > 0) {
27327
+ getLogger().warn(
27328
+ "[memory/retain] supersede left duplicate row(s) live \u2014 will reconcile at next consolidation",
27329
+ {
27330
+ newMemoryId: created2.uniqueId,
27331
+ leftover: liveLeftovers.length,
27332
+ // Log the specific ids (opaque record ids, not content) so an
27333
+ // operator can identify exactly which rows are still active.
27334
+ leftoverIds: liveLeftovers
27335
+ }
27336
+ );
27337
+ }
27299
27338
  return {
27300
27339
  action: "supersede",
27301
27340
  memoryId: created2.uniqueId,
27302
- targetId: supersedeTargetId,
27341
+ targetId: primaryTargetId,
27303
27342
  proofCount: 1
27304
27343
  };
27305
27344
  }
@@ -27385,10 +27424,16 @@ async function tryConsolidate(trimmed, ctx, options) {
27385
27424
  const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
27386
27425
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
27387
27426
  if (decision.action === "create") return null;
27388
- if (decision.action === "supersede" && decision.targetId && decision.content) {
27389
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
27390
- if (!existing || existing.supersededBy) return null;
27391
- return { supersede: decision.targetId, content: decision.content };
27427
+ if (decision.action === "supersede" && decision.content) {
27428
+ const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
27429
+ if (requestedIds.length === 0) return null;
27430
+ const valid = [];
27431
+ for (const id of requestedIds) {
27432
+ const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
27433
+ if (existing && !existing.supersededBy) valid.push(id);
27434
+ }
27435
+ if (valid.length === 0) return null;
27436
+ return { supersede: valid, content: decision.content };
27392
27437
  }
27393
27438
  if (decision.action === "noop" && decision.targetId) {
27394
27439
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -5095,10 +5095,10 @@ function injectionSignatureCatalog() {
5095
5095
  }
5096
5096
 
5097
5097
  // src/lib/memory/retain.ts
5098
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
5098
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
5099
5099
  var DEFAULT_SCOPE = "private";
5100
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
5101
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
5100
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
5101
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
5102
5102
  async function retain(content, ctx, options = {}) {
5103
5103
  const trimmed = content.trim();
5104
5104
  if (trimmed.length === 0) {
@@ -5107,18 +5107,18 @@ async function retain(content, ctx, options = {}) {
5107
5107
  const enableAutoMerge = options.enableAutoMerge ?? true;
5108
5108
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
5109
5109
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
5110
- let supersedeTargetId;
5110
+ let supersedeTargetIds = [];
5111
5111
  let supersedeContent;
5112
5112
  if (enableAutoMerge) {
5113
5113
  if (options.consolidateOptions) {
5114
5114
  const outcome = await tryConsolidate(trimmed, ctx, options);
5115
5115
  if (outcome) {
5116
5116
  if ("done" in outcome) return outcome.done;
5117
- supersedeTargetId = outcome.supersede;
5117
+ supersedeTargetIds = outcome.supersede;
5118
5118
  supersedeContent = outcome.content;
5119
5119
  }
5120
5120
  }
5121
- if (!supersedeTargetId) {
5121
+ if (supersedeTargetIds.length === 0) {
5122
5122
  const matches = await searchVaultMemories(
5123
5123
  trimmed,
5124
5124
  ctx.vaultCtx,
@@ -5222,18 +5222,42 @@ async function retain(content, ctx, options = {}) {
5222
5222
  // memory's tier. The DB op re-validates against the known set.
5223
5223
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
5224
5224
  };
5225
- if (supersedeTargetId) {
5225
+ if (supersedeTargetIds.length > 0) {
5226
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
5226
5227
  const { created: created2, retired } = await createSupersedingMemoryOp(
5227
5228
  ctx.vaultCtx,
5228
5229
  createOpts,
5229
- supersedeTargetId
5230
+ primaryTargetId
5230
5231
  );
5231
5232
  if (created2 && retired) {
5232
5233
  ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
5234
+ const liveLeftovers = [];
5235
+ for (const staleId of restTargetIds) {
5236
+ let ok = false;
5237
+ try {
5238
+ ok = await supersedeVaultMemoryOp(ctx.vaultCtx, staleId, created2.uniqueId) === true;
5239
+ } catch {
5240
+ }
5241
+ if (ok) continue;
5242
+ const stillLive = await getVaultMemoryOp(ctx.vaultCtx, staleId).catch(() => null);
5243
+ if (stillLive && !stillLive.supersededBy) liveLeftovers.push(staleId);
5244
+ }
5245
+ if (liveLeftovers.length > 0) {
5246
+ getLogger().warn(
5247
+ "[memory/retain] supersede left duplicate row(s) live \u2014 will reconcile at next consolidation",
5248
+ {
5249
+ newMemoryId: created2.uniqueId,
5250
+ leftover: liveLeftovers.length,
5251
+ // Log the specific ids (opaque record ids, not content) so an
5252
+ // operator can identify exactly which rows are still active.
5253
+ leftoverIds: liveLeftovers
5254
+ }
5255
+ );
5256
+ }
5233
5257
  return {
5234
5258
  action: "supersede",
5235
5259
  memoryId: created2.uniqueId,
5236
- targetId: supersedeTargetId,
5260
+ targetId: primaryTargetId,
5237
5261
  proofCount: 1
5238
5262
  };
5239
5263
  }
@@ -5316,13 +5340,19 @@ async function tryConsolidate(trimmed, ctx, options) {
5316
5340
  content: m.content,
5317
5341
  similarity: m.similarity
5318
5342
  }));
5319
- const { consolidateMemory: doConsolidate } = await import("./consolidate-PCHO3D3G.mjs");
5343
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-BKLNVH5Y.mjs");
5320
5344
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
5321
5345
  if (decision.action === "create") return null;
5322
- if (decision.action === "supersede" && decision.targetId && decision.content) {
5323
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
5324
- if (!existing || existing.supersededBy) return null;
5325
- return { supersede: decision.targetId, content: decision.content };
5346
+ if (decision.action === "supersede" && decision.content) {
5347
+ const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
5348
+ if (requestedIds.length === 0) return null;
5349
+ const valid = [];
5350
+ for (const id of requestedIds) {
5351
+ const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
5352
+ if (existing && !existing.supersededBy) valid.push(id);
5353
+ }
5354
+ if (valid.length === 0) return null;
5355
+ return { supersede: valid, content: decision.content };
5326
5356
  }
5327
5357
  if (decision.action === "noop" && decision.targetId) {
5328
5358
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -139,7 +139,8 @@
139
139
  "knip": "knip",
140
140
  "check-exports": "publint && attw --pack --ignore-rules no-resolution false-cjs && node scripts/assert-no-client-self-import.mjs",
141
141
  "check": "pnpm run lint && pnpm run knip && pnpm run format && pnpm run generate",
142
- "eval:extraction": "tsx test/memory/src/extraction/benchmark.test.ts"
142
+ "eval:extraction": "tsx test/memory/src/extraction/benchmark.test.ts",
143
+ "eval:consolidation": "tsx test/memory/src/consolidation/benchmark.test.ts"
143
144
  },
144
145
  "repository": {
145
146
  "type": "git",