@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
@@ -5641,7 +5641,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
5641
5641
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
5642
5642
  );
5643
5643
  }
5644
- const { recall: recall2 } = await import("./recall-JOLS3V4B.mjs");
5644
+ const { recall: recall2 } = await import("./recall-Z7BWJ67X.mjs");
5645
5645
  const result = await recall2(
5646
5646
  query,
5647
5647
  { 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
@@ -4175,6 +4175,29 @@ var init_redactor = __esm({
4175
4175
  }
4176
4176
  });
4177
4177
 
4178
+ // src/lib/chat/endpointOverride.ts
4179
+ function validateEndpointOverride(override) {
4180
+ const trimmed = override.trim();
4181
+ if (trimmed === "") {
4182
+ return {
4183
+ valid: false,
4184
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
4185
+ };
4186
+ }
4187
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
4188
+ return {
4189
+ valid: false,
4190
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
4191
+ };
4192
+ }
4193
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
4194
+ }
4195
+ var init_endpointOverride = __esm({
4196
+ "src/lib/chat/endpointOverride.ts"() {
4197
+ "use strict";
4198
+ }
4199
+ });
4200
+
4178
4201
  // src/lib/db/entities/types.ts
4179
4202
  function normalizeEntityName(name) {
4180
4203
  return name.trim().toLowerCase();
@@ -5304,6 +5327,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
5304
5327
  }
5305
5328
  async function callPortalJsonCompletion(req) {
5306
5329
  const log = getLogger();
5330
+ let endpoint = "/api/v1/chat/completions";
5331
+ if (req.endpointOverride !== void 0) {
5332
+ const overrideValidation = validateEndpointOverride(req.endpointOverride);
5333
+ if (!overrideValidation.valid) {
5334
+ throw new Error(overrideValidation.message);
5335
+ }
5336
+ endpoint = overrideValidation.endpoint;
5337
+ }
5307
5338
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
5308
5339
  const startedAt = Date.now();
5309
5340
  const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
@@ -5319,7 +5350,7 @@ async function callPortalJsonCompletion(req) {
5319
5350
  Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
5320
5351
  )
5321
5352
  } : req;
5322
- const outcome = await attemptPortalJson(attemptReq);
5353
+ const outcome = await attemptPortalJson(attemptReq, endpoint);
5323
5354
  if (outcome.kind === "ok") return outcome.value;
5324
5355
  if (outcome.kind === "terminal") {
5325
5356
  log.warn(`[${req.tag}] ${outcome.reason}`);
@@ -5342,7 +5373,7 @@ async function callPortalJsonCompletion(req) {
5342
5373
  }
5343
5374
  return null;
5344
5375
  }
5345
- async function attemptPortalJson(req) {
5376
+ async function attemptPortalJson(req, endpoint) {
5346
5377
  const baseUrl = req.baseUrl ?? defaultBaseUrl();
5347
5378
  const fetchImpl = req.fetchFn ?? fetch;
5348
5379
  const timeoutMs = req.timeoutMs ?? 6e4;
@@ -5367,7 +5398,7 @@ async function attemptPortalJson(req) {
5367
5398
  if (!supportsJsonObjectFormat) delete requestBody.response_format;
5368
5399
  let response;
5369
5400
  try {
5370
- response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
5401
+ response = await fetchImpl(`${baseUrl}${endpoint}`, {
5371
5402
  method: "POST",
5372
5403
  headers: { ...authHeaders, "Content-Type": "application/json" },
5373
5404
  body: JSON.stringify(requestBody),
@@ -5464,6 +5495,7 @@ var init_portalLlm = __esm({
5464
5495
  "src/lib/memory/portalLlm.ts"() {
5465
5496
  "use strict";
5466
5497
  init_clientConfig();
5498
+ init_endpointOverride();
5467
5499
  init_logger();
5468
5500
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
5469
5501
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
@@ -7879,14 +7911,27 @@ function validate2(parsed, newContent, validIds) {
7879
7911
  const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
7880
7912
  return { action: "create", content: c2.length > 0 ? c2 : newContent };
7881
7913
  }
7882
- const targetId = typeof obj.targetId === "string" ? obj.targetId : null;
7883
- if (!targetId || !validIds.has(targetId)) return null;
7884
7914
  if (action === "noop") {
7885
- 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 };
7886
7918
  }
7887
7919
  const c = typeof obj.content === "string" ? obj.content.trim() : "";
7888
7920
  if (c.length === 0) return null;
7889
- 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 };
7890
7935
  }
7891
7936
  var DEFAULT_MODEL3, DEFAULT_CONSOLIDATE_ATTEMPTS, DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS, SYSTEM_PROMPT3;
7892
7937
  var init_consolidate = __esm({
@@ -7906,7 +7951,7 @@ Decide one of four actions:
7906
7951
 
7907
7952
  - "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
7908
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).
7909
- - "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.
7910
7955
  - "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
7911
7956
 
7912
7957
  RULES (carry these strictly):
@@ -7922,14 +7967,15 @@ RULES (carry these strictly):
7922
7967
  OUTPUT \u2014 strict JSON, no prose:
7923
7968
  {
7924
7969
  "action": "create" | "update" | "supersede" | "noop",
7925
- "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>", ...],
7926
7972
  "content": "<content \u2014 required for create/update/supersede, omit for noop>"
7927
7973
  }
7928
7974
 
7929
- For "create": content is the new memory verbatim (or a slight refinement).
7930
- For "update": content is the merged richest-version, \u226480 words.
7931
- For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
7932
- 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.`;
7933
7979
  }
7934
7980
  });
7935
7981
 
@@ -8378,6 +8424,7 @@ init_serverSentEvents_gen();
8378
8424
  init_clientConfig();
8379
8425
  init_embeddings();
8380
8426
  init_redactor();
8427
+ init_endpointOverride();
8381
8428
 
8382
8429
  // src/lib/chat/runHooks.ts
8383
8430
  function composeHooks(hooks) {
@@ -10557,22 +10604,6 @@ function linkAbortSignals(a, b) {
10557
10604
  }
10558
10605
  };
10559
10606
  }
10560
- function validateEndpointOverride(override) {
10561
- const trimmed = override.trim();
10562
- if (trimmed === "") {
10563
- return {
10564
- valid: false,
10565
- message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
10566
- };
10567
- }
10568
- if (trimmed.startsWith("//") || trimmed.includes("://")) {
10569
- return {
10570
- valid: false,
10571
- message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
10572
- };
10573
- }
10574
- return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
10575
- }
10576
10607
  async function runToolLoop(options) {
10577
10608
  const {
10578
10609
  model,
@@ -15274,14 +15305,15 @@ init_portalLlm();
15274
15305
 
15275
15306
  // src/lib/memory/retain.ts
15276
15307
  init_operations3();
15308
+ init_logger();
15277
15309
  init_constants();
15278
15310
  init_embeddings();
15279
15311
  init_vector();
15280
15312
  init_searchTool();
15281
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
15313
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
15282
15314
  var DEFAULT_SCOPE = "private";
15283
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
15284
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
15315
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
15316
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
15285
15317
  async function retain(content, ctx, options = {}) {
15286
15318
  const trimmed = content.trim();
15287
15319
  if (trimmed.length === 0) {
@@ -15290,18 +15322,18 @@ async function retain(content, ctx, options = {}) {
15290
15322
  const enableAutoMerge = options.enableAutoMerge ?? true;
15291
15323
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
15292
15324
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
15293
- let supersedeTargetId;
15325
+ let supersedeTargetIds = [];
15294
15326
  let supersedeContent;
15295
15327
  if (enableAutoMerge) {
15296
15328
  if (options.consolidateOptions) {
15297
15329
  const outcome = await tryConsolidate(trimmed, ctx, options);
15298
15330
  if (outcome) {
15299
15331
  if ("done" in outcome) return outcome.done;
15300
- supersedeTargetId = outcome.supersede;
15332
+ supersedeTargetIds = outcome.supersede;
15301
15333
  supersedeContent = outcome.content;
15302
15334
  }
15303
15335
  }
15304
- if (!supersedeTargetId) {
15336
+ if (supersedeTargetIds.length === 0) {
15305
15337
  const matches = await searchVaultMemories(
15306
15338
  trimmed,
15307
15339
  ctx.vaultCtx,
@@ -15405,18 +15437,42 @@ async function retain(content, ctx, options = {}) {
15405
15437
  // memory's tier. The DB op re-validates against the known set.
15406
15438
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
15407
15439
  };
15408
- if (supersedeTargetId) {
15440
+ if (supersedeTargetIds.length > 0) {
15441
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
15409
15442
  const { created: created2, retired } = await createSupersedingMemoryOp(
15410
15443
  ctx.vaultCtx,
15411
15444
  createOpts,
15412
- supersedeTargetId
15445
+ primaryTargetId
15413
15446
  );
15414
15447
  if (created2 && retired) {
15415
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
+ }
15416
15472
  return {
15417
15473
  action: "supersede",
15418
15474
  memoryId: created2.uniqueId,
15419
- targetId: supersedeTargetId,
15475
+ targetId: primaryTargetId,
15420
15476
  proofCount: 1
15421
15477
  };
15422
15478
  }
@@ -15502,10 +15558,16 @@ async function tryConsolidate(trimmed, ctx, options) {
15502
15558
  const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
15503
15559
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
15504
15560
  if (decision.action === "create") return null;
15505
- if (decision.action === "supersede" && decision.targetId && decision.content) {
15506
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
15507
- if (!existing || existing.supersededBy) return null;
15508
- 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 };
15509
15571
  }
15510
15572
  if (decision.action === "noop" && decision.targetId) {
15511
15573
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -17281,6 +17343,9 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
17281
17343
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
17282
17344
  ...options.getToken !== void 0 && { getToken: options.getToken },
17283
17345
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
17346
+ ...options.endpointOverride !== void 0 && {
17347
+ endpointOverride: options.endpointOverride
17348
+ },
17284
17349
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
17285
17350
  systemPrompt: SYSTEM_PROMPT6,
17286
17351
  userMessage: `${vocabularyNote}Memories:
@@ -8607,6 +8607,15 @@ interface TopicExtractionInput {
8607
8607
  */
8608
8608
  interface TopicExtractOptions extends PortalLlmAuth {
8609
8609
  baseUrl?: string;
8610
+ /**
8611
+ * Optional per-call request path override, forwarded to
8612
+ * {@link callPortalJsonCompletion}. When set, topic extraction POSTs to
8613
+ * `baseUrl + endpointOverride` instead of the default
8614
+ * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
8615
+ * this internal-utility pass to a dedicated endpoint. Invalid values throw at
8616
+ * call time (see {@link validateEndpointOverride}).
8617
+ */
8618
+ endpointOverride?: string;
8610
8619
  /** Defaults to {@link DEFAULT_EXTRACTION_MODEL} — the sanctioned extraction
8611
8620
  * model. Don't point this at a second model without an eval. */
8612
8621
  model?: string;
@@ -8607,6 +8607,15 @@ interface TopicExtractionInput {
8607
8607
  */
8608
8608
  interface TopicExtractOptions extends PortalLlmAuth {
8609
8609
  baseUrl?: string;
8610
+ /**
8611
+ * Optional per-call request path override, forwarded to
8612
+ * {@link callPortalJsonCompletion}. When set, topic extraction POSTs to
8613
+ * `baseUrl + endpointOverride` instead of the default
8614
+ * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
8615
+ * this internal-utility pass to a dedicated endpoint. Invalid values throw at
8616
+ * call time (see {@link validateEndpointOverride}).
8617
+ */
8618
+ endpointOverride?: string;
8610
8619
  /** Defaults to {@link DEFAULT_EXTRACTION_MODEL} — the sanctioned extraction
8611
8620
  * model. Don't point this at a second model without an eval. */
8612
8621
  model?: string;
@@ -149,7 +149,7 @@ import {
149
149
  updateVaultMemoryOp,
150
150
  useEncryption,
151
151
  vaultMemoryToStored
152
- } from "./chunk-ZGMHRWOR.mjs";
152
+ } from "./chunk-3VPEZDYL.mjs";
153
153
  import {
154
154
  PiiRedactor,
155
155
  createStreamingDeAnonymizer,
@@ -164,8 +164,9 @@ import {
164
164
  noopLogger,
165
165
  resolvePortalAuthHeaders,
166
166
  setLogger,
167
- supportsResponseFormat
168
- } from "./chunk-DFVCS3QQ.mjs";
167
+ supportsResponseFormat,
168
+ validateEndpointOverride
169
+ } from "./chunk-BOY27A2C.mjs";
169
170
  import {
170
171
  BASE_URL,
171
172
  MCP_R2_DOMAIN,
@@ -2346,22 +2347,6 @@ function linkAbortSignals(a, b) {
2346
2347
  }
2347
2348
  };
2348
2349
  }
2349
- function validateEndpointOverride(override) {
2350
- const trimmed = override.trim();
2351
- if (trimmed === "") {
2352
- return {
2353
- valid: false,
2354
- message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
2355
- };
2356
- }
2357
- if (trimmed.startsWith("//") || trimmed.includes("://")) {
2358
- return {
2359
- valid: false,
2360
- message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
2361
- };
2362
- }
2363
- return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
2364
- }
2365
2350
  async function runToolLoop(options) {
2366
2351
  const {
2367
2352
  model,
@@ -7022,10 +7007,10 @@ function injectionSignatureCatalog() {
7022
7007
  }
7023
7008
 
7024
7009
  // src/lib/memory/retain.ts
7025
- var DEFAULT_AUTO_MERGE_THRESHOLD = 0.85;
7010
+ var DEFAULT_AUTO_MERGE_THRESHOLD = 0.8;
7026
7011
  var DEFAULT_SCOPE = "private";
7027
- var DEFAULT_CONSOLIDATE_THRESHOLD = 0.65;
7028
- var DEFAULT_CONSOLIDATE_TOP_K = 5;
7012
+ var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
7013
+ var DEFAULT_CONSOLIDATE_TOP_K = 20;
7029
7014
  async function retain(content, ctx, options = {}) {
7030
7015
  const trimmed = content.trim();
7031
7016
  if (trimmed.length === 0) {
@@ -7034,18 +7019,18 @@ async function retain(content, ctx, options = {}) {
7034
7019
  const enableAutoMerge = options.enableAutoMerge ?? true;
7035
7020
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
7036
7021
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
7037
- let supersedeTargetId;
7022
+ let supersedeTargetIds = [];
7038
7023
  let supersedeContent;
7039
7024
  if (enableAutoMerge) {
7040
7025
  if (options.consolidateOptions) {
7041
7026
  const outcome = await tryConsolidate(trimmed, ctx, options);
7042
7027
  if (outcome) {
7043
7028
  if ("done" in outcome) return outcome.done;
7044
- supersedeTargetId = outcome.supersede;
7029
+ supersedeTargetIds = outcome.supersede;
7045
7030
  supersedeContent = outcome.content;
7046
7031
  }
7047
7032
  }
7048
- if (!supersedeTargetId) {
7033
+ if (supersedeTargetIds.length === 0) {
7049
7034
  const matches = await searchVaultMemories(
7050
7035
  trimmed,
7051
7036
  ctx.vaultCtx,
@@ -7149,18 +7134,42 @@ async function retain(content, ctx, options = {}) {
7149
7134
  // memory's tier. The DB op re-validates against the known set.
7150
7135
  ...options.trustTier !== void 0 && { trustTier: options.trustTier }
7151
7136
  };
7152
- if (supersedeTargetId) {
7137
+ if (supersedeTargetIds.length > 0) {
7138
+ const [primaryTargetId, ...restTargetIds] = supersedeTargetIds;
7153
7139
  const { created: created2, retired } = await createSupersedingMemoryOp(
7154
7140
  ctx.vaultCtx,
7155
7141
  createOpts,
7156
- supersedeTargetId
7142
+ primaryTargetId
7157
7143
  );
7158
7144
  if (created2 && retired) {
7159
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
+ }
7160
7169
  return {
7161
7170
  action: "supersede",
7162
7171
  memoryId: created2.uniqueId,
7163
- targetId: supersedeTargetId,
7172
+ targetId: primaryTargetId,
7164
7173
  proofCount: 1
7165
7174
  };
7166
7175
  }
@@ -7243,13 +7252,19 @@ async function tryConsolidate(trimmed, ctx, options) {
7243
7252
  content: m.content,
7244
7253
  similarity: m.similarity
7245
7254
  }));
7246
- const { consolidateMemory: doConsolidate } = await import("./consolidate-3MXZV3G7.mjs");
7255
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-7UUX56GM.mjs");
7247
7256
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
7248
7257
  if (decision.action === "create") return null;
7249
- if (decision.action === "supersede" && decision.targetId && decision.content) {
7250
- const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
7251
- if (!existing || existing.supersededBy) return null;
7252
- 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 };
7253
7268
  }
7254
7269
  if (decision.action === "noop" && decision.targetId) {
7255
7270
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
@@ -8986,6 +9001,9 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
8986
9001
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
8987
9002
  ...options.getToken !== void 0 && { getToken: options.getToken },
8988
9003
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
9004
+ ...options.endpointOverride !== void 0 && {
9005
+ endpointOverride: options.endpointOverride
9006
+ },
8989
9007
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
8990
9008
  systemPrompt: SYSTEM_PROMPT4,
8991
9009
  userMessage: `${vocabularyNote}Memories:
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-FKACNV6X.mjs";
4
- import "./chunk-DFVCS3QQ.mjs";
3
+ } from "./chunk-3VPEZDYL.mjs";
4
+ import "./chunk-BOY27A2C.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
7
7
  recall