@anuma/sdk 3.6.3 → 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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  callPortalJsonCompletion,
3
3
  getLogger
4
- } from "./chunk-DFVCS3QQ.mjs";
4
+ } from "./chunk-BOY27A2C.mjs";
5
5
  import {
6
6
  BASE_URL,
7
7
  createClientConfig
@@ -5363,7 +5363,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
5363
5363
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
5364
5364
  );
5365
5365
  }
5366
- const { recall: recall2 } = await import("./recall-VZHV7ILE.mjs");
5366
+ const { recall: recall2 } = await import("./recall-YVU7OSSJ.mjs");
5367
5367
  const result = await recall2(
5368
5368
  query,
5369
5369
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -27,6 +27,24 @@ function getLogger() {
27
27
  return currentLogger;
28
28
  }
29
29
 
30
+ // src/lib/chat/endpointOverride.ts
31
+ function validateEndpointOverride(override) {
32
+ const trimmed = override.trim();
33
+ if (trimmed === "") {
34
+ return {
35
+ valid: false,
36
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
37
+ };
38
+ }
39
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
40
+ return {
41
+ valid: false,
42
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
43
+ };
44
+ }
45
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
46
+ }
47
+
30
48
  // src/lib/memory/portalLlm.ts
31
49
  function defaultBaseUrl() {
32
50
  return typeof process !== "undefined" && process.env?.ANUMA_PORTAL_BASE_URL || BASE_URL;
@@ -78,6 +96,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
78
96
  }
79
97
  async function callPortalJsonCompletion(req) {
80
98
  const log = getLogger();
99
+ let endpoint = "/api/v1/chat/completions";
100
+ if (req.endpointOverride !== void 0) {
101
+ const overrideValidation = validateEndpointOverride(req.endpointOverride);
102
+ if (!overrideValidation.valid) {
103
+ throw new Error(overrideValidation.message);
104
+ }
105
+ endpoint = overrideValidation.endpoint;
106
+ }
81
107
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
82
108
  const startedAt = Date.now();
83
109
  const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
@@ -93,7 +119,7 @@ async function callPortalJsonCompletion(req) {
93
119
  Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
94
120
  )
95
121
  } : req;
96
- const outcome = await attemptPortalJson(attemptReq);
122
+ const outcome = await attemptPortalJson(attemptReq, endpoint);
97
123
  if (outcome.kind === "ok") return outcome.value;
98
124
  if (outcome.kind === "terminal") {
99
125
  log.warn(`[${req.tag}] ${outcome.reason}`);
@@ -116,7 +142,7 @@ async function callPortalJsonCompletion(req) {
116
142
  }
117
143
  return null;
118
144
  }
119
- async function attemptPortalJson(req) {
145
+ async function attemptPortalJson(req, endpoint) {
120
146
  const baseUrl = req.baseUrl ?? defaultBaseUrl();
121
147
  const fetchImpl = req.fetchFn ?? fetch;
122
148
  const timeoutMs = req.timeoutMs ?? 6e4;
@@ -141,7 +167,7 @@ async function attemptPortalJson(req) {
141
167
  if (!supportsJsonObjectFormat) delete requestBody.response_format;
142
168
  let response;
143
169
  try {
144
- response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
170
+ response = await fetchImpl(`${baseUrl}${endpoint}`, {
145
171
  method: "POST",
146
172
  headers: { ...authHeaders, "Content-Type": "application/json" },
147
173
  body: JSON.stringify(requestBody),
@@ -239,6 +265,7 @@ export {
239
265
  noopLogger,
240
266
  setLogger,
241
267
  getLogger,
268
+ validateEndpointOverride,
242
269
  supportsResponseFormat,
243
270
  resolvePortalAuthHeaders,
244
271
  callPortalJsonCompletion,
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  callPortalJsonCompletion,
6
6
  getLogger
7
- } from "./chunk-DFVCS3QQ.mjs";
7
+ } from "./chunk-BOY27A2C.mjs";
8
8
  import "./chunk-GYKJOWEO.mjs";
9
9
 
10
10
  // src/lib/memory/consolidate.ts
@@ -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
@@ -3976,6 +3976,29 @@ var init_redactor = __esm({
3976
3976
  }
3977
3977
  });
3978
3978
 
3979
+ // src/lib/chat/endpointOverride.ts
3980
+ function validateEndpointOverride(override) {
3981
+ const trimmed = override.trim();
3982
+ if (trimmed === "") {
3983
+ return {
3984
+ valid: false,
3985
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
3986
+ };
3987
+ }
3988
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
3989
+ return {
3990
+ valid: false,
3991
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
3992
+ };
3993
+ }
3994
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
3995
+ }
3996
+ var init_endpointOverride = __esm({
3997
+ "src/lib/chat/endpointOverride.ts"() {
3998
+ "use strict";
3999
+ }
4000
+ });
4001
+
3979
4002
  // src/lib/db/entities/types.ts
3980
4003
  function normalizeEntityName(name) {
3981
4004
  return name.trim().toLowerCase();
@@ -5026,6 +5049,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
5026
5049
  }
5027
5050
  async function callPortalJsonCompletion(req) {
5028
5051
  const log = getLogger();
5052
+ let endpoint = "/api/v1/chat/completions";
5053
+ if (req.endpointOverride !== void 0) {
5054
+ const overrideValidation = validateEndpointOverride(req.endpointOverride);
5055
+ if (!overrideValidation.valid) {
5056
+ throw new Error(overrideValidation.message);
5057
+ }
5058
+ endpoint = overrideValidation.endpoint;
5059
+ }
5029
5060
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
5030
5061
  const startedAt = Date.now();
5031
5062
  const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
@@ -5041,7 +5072,7 @@ async function callPortalJsonCompletion(req) {
5041
5072
  Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
5042
5073
  )
5043
5074
  } : req;
5044
- const outcome = await attemptPortalJson(attemptReq);
5075
+ const outcome = await attemptPortalJson(attemptReq, endpoint);
5045
5076
  if (outcome.kind === "ok") return outcome.value;
5046
5077
  if (outcome.kind === "terminal") {
5047
5078
  log.warn(`[${req.tag}] ${outcome.reason}`);
@@ -5064,7 +5095,7 @@ async function callPortalJsonCompletion(req) {
5064
5095
  }
5065
5096
  return null;
5066
5097
  }
5067
- async function attemptPortalJson(req) {
5098
+ async function attemptPortalJson(req, endpoint) {
5068
5099
  const baseUrl = req.baseUrl ?? defaultBaseUrl();
5069
5100
  const fetchImpl = req.fetchFn ?? fetch;
5070
5101
  const timeoutMs = req.timeoutMs ?? 6e4;
@@ -5089,7 +5120,7 @@ async function attemptPortalJson(req) {
5089
5120
  if (!supportsJsonObjectFormat) delete requestBody.response_format;
5090
5121
  let response;
5091
5122
  try {
5092
- response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
5123
+ response = await fetchImpl(`${baseUrl}${endpoint}`, {
5093
5124
  method: "POST",
5094
5125
  headers: { ...authHeaders, "Content-Type": "application/json" },
5095
5126
  body: JSON.stringify(requestBody),
@@ -5186,6 +5217,7 @@ var init_portalLlm = __esm({
5186
5217
  "src/lib/memory/portalLlm.ts"() {
5187
5218
  "use strict";
5188
5219
  init_clientConfig();
5220
+ init_endpointOverride();
5189
5221
  init_logger();
5190
5222
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
5191
5223
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
@@ -7601,14 +7633,27 @@ function validate2(parsed, newContent, validIds) {
7601
7633
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
7602
7634
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
7603
7635
  }
7604
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
7605
- if (!targetId || !validIds.has(targetId)) return null;
7606
7636
  if (action === "noop") {
7607
- 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 };
7608
7640
  }
7609
7641
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
7610
7642
  if (c.length === 0) return null;
7611
- 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 };
7612
7657
  }
7613
7658
  var DEFAULT_MODEL3, DEFAULT_CONSOLIDATE_ATTEMPTS, DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS, SYSTEM_PROMPT3;
7614
7659
  var init_consolidate = __esm({
@@ -7628,7 +7673,7 @@ Decide one of four actions:
7628
7673
 
7629
7674
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
7630
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).
7631
- - "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.
7632
7677
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
7633
7678
 
7634
7679
  RULES (carry these strictly):
@@ -7644,14 +7689,15 @@ RULES (carry these strictly):
7644
7689
  OUTPUT \u2014 strict JSON, no prose:
7645
7690
  {
7646
7691
  "action": "create" | "update" | "supersede" | "noop",
7647
- "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>", ...],
7648
7694
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
7649
7695
  }
7650
7696
 
7651
- For "create": content is the new memory verbatim (or a slight refinement).
7652
- For "update": content is the merged richest-version, \u226480 words.
7653
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
7654
- 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.`;
7655
7701
  }
7656
7702
  });
7657
7703
 
@@ -8027,6 +8073,7 @@ init_serverSentEvents_gen();
8027
8073
  init_clientConfig();
8028
8074
  init_embeddings();
8029
8075
  init_redactor();
8076
+ init_endpointOverride();
8030
8077
 
8031
8078
  // src/lib/chat/runHooks.ts
8032
8079
  function composeHooks(hooks) {
@@ -10206,22 +10253,6 @@ function linkAbortSignals(a, b) {
10206
10253
  }
10207
10254
  };
10208
10255
  }
10209
- function validateEndpointOverride(override) {
10210
- const trimmed = override.trim();
10211
- if (trimmed === "") {
10212
- return {
10213
- valid: false,
10214
- message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
10215
- };
10216
- }
10217
- if (trimmed.startsWith("//") || trimmed.includes("://")) {
10218
- return {
10219
- valid: false,
10220
- message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
10221
- };
10222
- }
10223
- return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
10224
- }
10225
10256
  async function runToolLoop(options) {
10226
10257
  const {
10227
10258
  model,
@@ -13984,14 +14015,15 @@ init_portalLlm();
13984
14015
 
13985
14016
  // src/lib/memory/retain.ts
13986
14017
  init_operations3();
14018
+ init_logger();
13987
14019
  init_constants();
13988
14020
  init_embeddings();
13989
14021
  init_vector();
13990
14022
  init_searchTool();
13991
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
14023
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
13992
14024
  var DEFAULT_SCOPE = "private";
13993
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
13994
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
14025
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
14026
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
13995
14027
  async function retain(content, ctx, options = {}) {
13996
14028
  const trimmed = content.trim();
13997
14029
  if (trimmed.length === 0) {
@@ -14000,18 +14032,18 @@ async function retain(content, ctx, options = {}) {
14000
14032
  const enableAutoMerge = options.enableAutoMerge ?? true;
14001
14033
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
14002
14034
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
14003
- let supersedeTargetId;
14035
+ let supersedeTargetIds = [];
14004
14036
  let supersedeContent;
14005
14037
  if (enableAutoMerge) {
14006
14038
  if (options.consolidateOptions) {
14007
14039
  const outcome = await tryConsolidate(trimmed, ctx, options);
14008
14040
  if (outcome) {
14009
14041
  if ("done" in outcome) return outcome.done;
14010
- supersedeTargetId = outcome.supersede;
14042
+ supersedeTargetIds = outcome.supersede;
14011
14043
  supersedeContent = outcome.content;
14012
14044
  }
14013
14045
  }
14014
- if (!supersedeTargetId) {
14046
+ if (supersedeTargetIds.length === 0) {
14015
14047
  const matches = await searchVaultMemories(
14016
14048
  trimmed,
14017
14049
  ctx.vaultCtx,
@@ -14115,18 +14147,42 @@ async function retain(content, ctx, options = {}) {
14115
14147
  // memory's tier. The DB op re-validates against the known set.
14116
14148
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
14117
14149
  };
14118
- if (supersedeTargetId) {
14150
+ if (supersedeTargetIds.length > 0) {
14151
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
14119
14152
  const { created: created2, retired } = await createSupersedingMemoryOp(
14120
14153
  ctx.vaultCtx,
14121
14154
  createOpts,
14122
- supersedeTargetId
14155
+ primaryTargetId
14123
14156
  );
14124
14157
  if (created2 && retired) {
14125
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
+ }
14126
14182
  return {
14127
14183
  action: "supersede",
14128
14184
  memoryId: created2.uniqueId,
14129
- targetId: supersedeTargetId,
14185
+ targetId: primaryTargetId,
14130
14186
  proofCount: 1
14131
14187
  };
14132
14188
  }
@@ -14212,10 +14268,16 @@ async function tryConsolidate(trimmed, ctx, options) {
14212
14268
  const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
14213
14269
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
14214
14270
  if (decision.action === "create") return null;
14215
- if (decision.action === "supersede" && decision.targetId && decision.content) {
14216
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
14217
- if (!existing || existing.supersededBy) return null;
14218
- 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 };
14219
14281
  }
14220
14282
  if (decision.action === "noop" && decision.targetId) {
14221
14283
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -15991,6 +16053,9 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
15991
16053
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
15992
16054
  ...options.getToken !== void 0 && { getToken: options.getToken },
15993
16055
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
16056
+ ...options.endpointOverride !== void 0 && {
16057
+ endpointOverride: options.endpointOverride
16058
+ },
15994
16059
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
15995
16060
  systemPrompt: SYSTEM_PROMPT6,
15996
16061
  userMessage: `${vocabularyNote}Memories:
@@ -7456,6 +7456,15 @@ interface TopicExtractionInput {
7456
7456
  */
7457
7457
  interface TopicExtractOptions extends PortalLlmAuth {
7458
7458
  baseUrl?: string;
7459
+ /**
7460
+ * Optional per-call request path override, forwarded to
7461
+ * {@link callPortalJsonCompletion}. When set, topic extraction POSTs to
7462
+ * `baseUrl + endpointOverride` instead of the default
7463
+ * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
7464
+ * this internal-utility pass to a dedicated endpoint. Invalid values throw at
7465
+ * call time (see {@link validateEndpointOverride}).
7466
+ */
7467
+ endpointOverride?: string;
7459
7468
  /** Defaults to {@link DEFAULT_EXTRACTION_MODEL} — the sanctioned extraction
7460
7469
  * model. Don't point this at a second model without an eval. */
7461
7470
  model?: string;
@@ -7456,6 +7456,15 @@ interface TopicExtractionInput {
7456
7456
  */
7457
7457
  interface TopicExtractOptions extends PortalLlmAuth {
7458
7458
  baseUrl?: string;
7459
+ /**
7460
+ * Optional per-call request path override, forwarded to
7461
+ * {@link callPortalJsonCompletion}. When set, topic extraction POSTs to
7462
+ * `baseUrl + endpointOverride` instead of the default
7463
+ * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
7464
+ * this internal-utility pass to a dedicated endpoint. Invalid values throw at
7465
+ * call time (see {@link validateEndpointOverride}).
7466
+ */
7467
+ endpointOverride?: string;
7459
7468
  /** Defaults to {@link DEFAULT_EXTRACTION_MODEL} — the sanctioned extraction
7460
7469
  * model. Don't point this at a second model without an eval. */
7461
7470
  model?: string;
@@ -108,7 +108,7 @@ import {
108
108
  upsertMessageOp,
109
109
  useEncryption,
110
110
  vaultMemoryToStored
111
- } from "./chunk-FKACNV6X.mjs";
111
+ } from "./chunk-AAB7OG7Q.mjs";
112
112
  import {
113
113
  PII_PATTERNS,
114
114
  PiiRedactor,
@@ -124,8 +124,9 @@ import {
124
124
  noopLogger,
125
125
  resolvePortalAuthHeaders,
126
126
  setLogger,
127
- supportsResponseFormat
128
- } from "./chunk-DFVCS3QQ.mjs";
127
+ supportsResponseFormat,
128
+ validateEndpointOverride
129
+ } from "./chunk-BOY27A2C.mjs";
129
130
  import {
130
131
  BASE_URL,
131
132
  MCP_R2_DOMAIN,
@@ -2473,22 +2474,6 @@ function linkAbortSignals(a, b) {
2473
2474
  }
2474
2475
  };
2475
2476
  }
2476
- function validateEndpointOverride(override) {
2477
- const trimmed = override.trim();
2478
- if (trimmed === "") {
2479
- return {
2480
- valid: false,
2481
- message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
2482
- };
2483
- }
2484
- if (trimmed.startsWith("//") || trimmed.includes("://")) {
2485
- return {
2486
- valid: false,
2487
- message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
2488
- };
2489
- }
2490
- return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
2491
- }
2492
2477
  async function runToolLoop(options) {
2493
2478
  const {
2494
2479
  model,
@@ -6209,10 +6194,10 @@ function injectionSignatureCatalog() {
6209
6194
  }
6210
6195
 
6211
6196
  // src/lib/memory/retain.ts
6212
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
6197
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
6213
6198
  var DEFAULT_SCOPE = "private";
6214
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
6215
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
6199
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
6200
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
6216
6201
  async function retain(content, ctx, options = {}) {
6217
6202
  const trimmed = content.trim();
6218
6203
  if (trimmed.length === 0) {
@@ -6221,18 +6206,18 @@ async function retain(content, ctx, options = {}) {
6221
6206
  const enableAutoMerge = options.enableAutoMerge ?? true;
6222
6207
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
6223
6208
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
6224
- let supersedeTargetId;
6209
+ let supersedeTargetIds = [];
6225
6210
  let supersedeContent;
6226
6211
  if (enableAutoMerge) {
6227
6212
  if (options.consolidateOptions) {
6228
6213
  const outcome = await tryConsolidate(trimmed, ctx, options);
6229
6214
  if (outcome) {
6230
6215
  if ("done" in outcome) return outcome.done;
6231
- supersedeTargetId = outcome.supersede;
6216
+ supersedeTargetIds = outcome.supersede;
6232
6217
  supersedeContent = outcome.content;
6233
6218
  }
6234
6219
  }
6235
- if (!supersedeTargetId) {
6220
+ if (supersedeTargetIds.length === 0) {
6236
6221
  const matches = await searchVaultMemories(
6237
6222
  trimmed,
6238
6223
  ctx.vaultCtx,
@@ -6336,18 +6321,42 @@ async function retain(content, ctx, options = {}) {
6336
6321
  // memory's tier. The DB op re-validates against the known set.
6337
6322
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
6338
6323
  };
6339
- if (supersedeTargetId) {
6324
+ if (supersedeTargetIds.length > 0) {
6325
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
6340
6326
  const { created: created2, retired } = await createSupersedingMemoryOp(
6341
6327
  ctx.vaultCtx,
6342
6328
  createOpts,
6343
- supersedeTargetId
6329
+ primaryTargetId
6344
6330
  );
6345
6331
  if (created2 && retired) {
6346
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
+ }
6347
6356
  return {
6348
6357
  action: "supersede",
6349
6358
  memoryId: created2.uniqueId,
6350
- targetId: supersedeTargetId,
6359
+ targetId: primaryTargetId,
6351
6360
  proofCount: 1
6352
6361
  };
6353
6362
  }
@@ -6430,13 +6439,19 @@ async function tryConsolidate(trimmed, ctx, options) {
6430
6439
  content: m.content,
6431
6440
  similarity: m.similarity
6432
6441
  }));
6433
- const { consolidateMemory: doConsolidate } = await import("./consolidate-3QQPUWD6.mjs");
6442
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-HKN32P5W.mjs");
6434
6443
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
6435
6444
  if (decision.action === "create") return null;
6436
- if (decision.action === "supersede" && decision.targetId && decision.content) {
6437
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
6438
- if (!existing || existing.supersededBy) return null;
6439
- 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 };
6440
6455
  }
6441
6456
  if (decision.action === "noop" && decision.targetId) {
6442
6457
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -8173,6 +8188,9 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
8173
8188
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
8174
8189
  ...options.getToken !== void 0 && { getToken: options.getToken },
8175
8190
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
8191
+ ...options.endpointOverride !== void 0 && {
8192
+ endpointOverride: options.endpointOverride
8193
+ },
8176
8194
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
8177
8195
  systemPrompt: SYSTEM_PROMPT4,
8178
8196
  userMessage: `${vocabularyNote}Memories:
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-ZGMHRWOR.mjs";
4
- import "./chunk-DFVCS3QQ.mjs";
3
+ } from "./chunk-AAB7OG7Q.mjs";
4
+ import "./chunk-BOY27A2C.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
7
7
  recall