@anuma/sdk 7.3.1 → 7.3.3
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.
- package/dist/expo/{chunk-AF37N5Z6.mjs → chunk-XW3LYSDA.mjs} +9 -4
- package/dist/expo/{consolidate-TOITBD6W.mjs → consolidate-4CQJYKRK.mjs} +25 -5
- package/dist/expo/index.cjs +77 -15
- package/dist/expo/index.d.mts +3 -1
- package/dist/expo/index.d.ts +3 -1
- package/dist/expo/index.mjs +49 -9
- package/dist/react/{chunk-TGLAOFVA.mjs → chunk-7QX4J7KV.mjs} +9 -4
- package/dist/react/{consolidate-ZEINV2LX.mjs → consolidate-WLDYJF6J.mjs} +25 -5
- package/dist/react/index.cjs +77 -15
- package/dist/react/index.d.mts +3 -1
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.mjs +49 -9
- package/dist/server/{chunk-WCG5BFI6.mjs → chunk-AU3Z7NBC.mjs} +9 -4
- package/dist/server/{consolidate-WY6BHODK.mjs → consolidate-B6OSCZU4.mjs} +25 -5
- package/dist/server/index.cjs +77 -15
- package/dist/server/index.d.mts +3 -1
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.mjs +49 -9
- package/package.json +1 -1
|
@@ -709,6 +709,13 @@ var FOREIGN_ENDPOINT_SUFFIX = {
|
|
|
709
709
|
chat: "/responses",
|
|
710
710
|
responses: "/chat/completions"
|
|
711
711
|
};
|
|
712
|
+
function endpointForTransport(endpoint, transport) {
|
|
713
|
+
const { path, rest } = splitEndpoint(endpoint);
|
|
714
|
+
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
715
|
+
if (!path.endsWith(foreign)) return endpoint;
|
|
716
|
+
const own = FOREIGN_ENDPOINT_SUFFIX[transport === "chat" ? "responses" : "chat"];
|
|
717
|
+
return `${path.slice(0, -foreign.length)}${own}${rest}`;
|
|
718
|
+
}
|
|
712
719
|
function assertTransportMatchesEndpoint(transport, endpoint) {
|
|
713
720
|
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
714
721
|
if (!splitEndpoint(endpoint).path.endsWith(foreign)) return;
|
|
@@ -766,10 +773,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
766
773
|
throw new Error(overrideValidation.message);
|
|
767
774
|
}
|
|
768
775
|
endpoint = overrideValidation.endpoint;
|
|
769
|
-
|
|
770
|
-
if (autoUpgraded && path.endsWith("/chat/completions")) {
|
|
771
|
-
endpoint = `${path.slice(0, -"/chat/completions".length)}/responses${rest}`;
|
|
772
|
-
}
|
|
776
|
+
if (autoUpgraded) endpoint = endpointForTransport(endpoint, transport);
|
|
773
777
|
assertTransportMatchesEndpoint(transport, endpoint);
|
|
774
778
|
}
|
|
775
779
|
const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
|
|
@@ -1074,6 +1078,7 @@ export {
|
|
|
1074
1078
|
withInternalFlowMarker,
|
|
1075
1079
|
supportsResponseFormat,
|
|
1076
1080
|
requiresResponsesTransport,
|
|
1081
|
+
endpointForTransport,
|
|
1077
1082
|
parseRetryAfterMs,
|
|
1078
1083
|
resolvePortalAuthHeaders,
|
|
1079
1084
|
callPortalJsonCompletion,
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
callPortalJsonCompletion,
|
|
3
|
+
endpointForTransport,
|
|
3
4
|
notifyConsolidationFallback,
|
|
5
|
+
requiresResponsesTransport,
|
|
4
6
|
resolvePiiRedactor
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XW3LYSDA.mjs";
|
|
6
8
|
import "./chunk-GYKJOWEO.mjs";
|
|
7
9
|
|
|
8
10
|
// src/lib/memory/consolidate.ts
|
|
9
|
-
var DEFAULT_CONSOLIDATION_MODEL = "
|
|
11
|
+
var DEFAULT_CONSOLIDATION_MODEL = "openai/gpt-6-luna";
|
|
10
12
|
var DEFAULT_CONSOLIDATE_ATTEMPTS = 3;
|
|
11
13
|
var DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS = 2e4;
|
|
12
14
|
var SYSTEM_PROMPT = `You consolidate a new memory against existing memories from the same user.
|
|
@@ -46,7 +48,11 @@ OUTPUT \u2014 strict JSON, no prose:
|
|
|
46
48
|
For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
|
|
47
49
|
For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
|
|
48
50
|
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). You MUST also name the subjects: "newSubject" for the new memory, and one entry in "targetSubjects" for EACH memory you are retiring (use "targetSubject" as well when you retire a single memory). List a subject ONLY for the memories you are actually retiring, not for every candidate you were shown. Write the plain subject, not a sentence: "the user", "the user's sister", "the user's manager", "Biscuit the dog". Rule 1a means a supersede is only valid when every one of those subjects is the SAME as "newSubject"; name them and check them against each other before you commit to the action. If ANY of them differs, the answer is "create" \u2014 do not retire the ones that match and keep the rest, because a group that mixes subjects was grouped wrongly in the first place. A subjectless fact ("Lives in Denver", "Works at Riverbend") is about "the user" \u2014 the extractor omits the subject when it is the user, so treat the absence of a subject as the user, never as unknown.
|
|
49
|
-
For "noop": no content (existing memory is already correct); targetId is that memory
|
|
51
|
+
For "noop": no content (existing memory is already correct); targetId is that memory.
|
|
52
|
+
|
|
53
|
+
ADDING TO AN INTRODUCED ENTITY (check this before choosing "create"): when an existing memory only introduces or names someone or something in the user's life ("User has a cat named Mochi", "User's sister is called Ana") and the new memory gives an attribute of that SAME entity that the existing memory does not state yet (its age, breed, job, home city), choose "update": targetId is that memory and content folds both into one sentence ("User has a cat named Mochi, who is four years old."). The "dog's name vs dog's age" example above means two memories that each already state a different attribute stay separate; a memory that only introduces the entity absorbs the next attribute about it. If the existing memory already states a value and the new memory changes it, that is "supersede" (rule 4), not this.
|
|
54
|
+
|
|
55
|
+
CARRY FORWARD UNCHANGED DETAILS (every "update" and "supersede"): a memory can hold several details at once, such as one folded from an introduction and an attribute ("User's sister Ana lives in Paris"). content replaces ONLY the value that changed and keeps every other detail of the memory it replaces, the subject's relation to the user and name above all: "Ana lives in London" against "User's sister Ana lives in Paris" is "supersede" with content "User's sister Ana lives in London", never just "Ana lives in London". Name the subject the same way on both sides: here "newSubject" and "targetSubjects" are both "the user's sister Ana".`;
|
|
50
56
|
async function consolidateMemory(newContent, candidates, options) {
|
|
51
57
|
const fallback = {
|
|
52
58
|
action: "create",
|
|
@@ -69,14 +75,23 @@ async function consolidateMemory(newContent, candidates, options) {
|
|
|
69
75
|
|
|
70
76
|
Existing memories (top ${candidates.length} by cosine):
|
|
71
77
|
${candidateText}`;
|
|
78
|
+
const model = options.model ?? DEFAULT_CONSOLIDATION_MODEL;
|
|
79
|
+
const viaResponses = requiresResponsesTransport(model);
|
|
80
|
+
const endpointOverride = options.endpointOverride === void 0 ? void 0 : endpointForTransport(options.endpointOverride, viaResponses ? "responses" : "chat");
|
|
72
81
|
let parsed;
|
|
82
|
+
let failure;
|
|
73
83
|
try {
|
|
74
84
|
parsed = await callPortalJsonCompletion({
|
|
85
|
+
onFailure: (f) => {
|
|
86
|
+
failure = f;
|
|
87
|
+
},
|
|
75
88
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
76
89
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
77
90
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
91
|
+
...endpointOverride !== void 0 && { endpointOverride },
|
|
78
92
|
taskType: "memory_consolidate",
|
|
79
|
-
model
|
|
93
|
+
model,
|
|
94
|
+
...viaResponses ? { transport: "responses", reasoning: { effort: "low" } } : { transport: "chat" },
|
|
80
95
|
systemPrompt: SYSTEM_PROMPT,
|
|
81
96
|
userMessage,
|
|
82
97
|
tag: "memory/consolidate",
|
|
@@ -88,7 +103,7 @@ ${candidateText}`;
|
|
|
88
103
|
} catch (err) {
|
|
89
104
|
return degrade("llm_error", fallback, options, err);
|
|
90
105
|
}
|
|
91
|
-
if (parsed === null) return degrade(
|
|
106
|
+
if (parsed === null) return degrade(fallbackReasonFor(failure), fallback, options, failure);
|
|
92
107
|
const validIds = new Set(candidates.map((c) => c.id));
|
|
93
108
|
const result = validate(parsed, trimmed, validIds);
|
|
94
109
|
if (!result) return degrade("invalid_response", fallback, options);
|
|
@@ -101,6 +116,11 @@ ${candidateText}`;
|
|
|
101
116
|
}
|
|
102
117
|
return reportRefusal(result, options);
|
|
103
118
|
}
|
|
119
|
+
function fallbackReasonFor(failure) {
|
|
120
|
+
if (failure?.reason === "time-budget-exhausted") return "llm_timeout";
|
|
121
|
+
if (failure?.reason === "content-flagged") return "content_flagged";
|
|
122
|
+
return "llm_error";
|
|
123
|
+
}
|
|
104
124
|
function reportRefusal(result, options) {
|
|
105
125
|
if (result.fallbackReason) {
|
|
106
126
|
notifyConsolidationFallback(result.fallbackReason, options.onFallback);
|
package/dist/expo/index.cjs
CHANGED
|
@@ -807,6 +807,13 @@ function splitEndpoint(endpoint) {
|
|
|
807
807
|
rest: cut === -1 ? "" : endpoint.slice(cut)
|
|
808
808
|
};
|
|
809
809
|
}
|
|
810
|
+
function endpointForTransport(endpoint, transport) {
|
|
811
|
+
const { path, rest } = splitEndpoint(endpoint);
|
|
812
|
+
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
813
|
+
if (!path.endsWith(foreign)) return endpoint;
|
|
814
|
+
const own = FOREIGN_ENDPOINT_SUFFIX[transport === "chat" ? "responses" : "chat"];
|
|
815
|
+
return `${path.slice(0, -foreign.length)}${own}${rest}`;
|
|
816
|
+
}
|
|
810
817
|
function assertTransportMatchesEndpoint(transport, endpoint) {
|
|
811
818
|
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
812
819
|
if (!splitEndpoint(endpoint).path.endsWith(foreign)) return;
|
|
@@ -863,10 +870,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
863
870
|
throw new Error(overrideValidation.message);
|
|
864
871
|
}
|
|
865
872
|
endpoint = overrideValidation.endpoint;
|
|
866
|
-
|
|
867
|
-
if (autoUpgraded && path.endsWith("/chat/completions")) {
|
|
868
|
-
endpoint = `${path.slice(0, -"/chat/completions".length)}/responses${rest}`;
|
|
869
|
-
}
|
|
873
|
+
if (autoUpgraded) endpoint = endpointForTransport(endpoint, transport);
|
|
870
874
|
assertTransportMatchesEndpoint(transport, endpoint);
|
|
871
875
|
}
|
|
872
876
|
const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
|
|
@@ -1212,14 +1216,23 @@ async function consolidateMemory(newContent, candidates, options) {
|
|
|
1212
1216
|
|
|
1213
1217
|
Existing memories (top ${candidates.length} by cosine):
|
|
1214
1218
|
${candidateText}`;
|
|
1219
|
+
const model = options.model ?? DEFAULT_CONSOLIDATION_MODEL;
|
|
1220
|
+
const viaResponses = requiresResponsesTransport(model);
|
|
1221
|
+
const endpointOverride = options.endpointOverride === void 0 ? void 0 : endpointForTransport(options.endpointOverride, viaResponses ? "responses" : "chat");
|
|
1215
1222
|
let parsed;
|
|
1223
|
+
let failure;
|
|
1216
1224
|
try {
|
|
1217
1225
|
parsed = await callPortalJsonCompletion({
|
|
1226
|
+
onFailure: (f) => {
|
|
1227
|
+
failure = f;
|
|
1228
|
+
},
|
|
1218
1229
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
1219
1230
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
1220
1231
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
1232
|
+
...endpointOverride !== void 0 && { endpointOverride },
|
|
1221
1233
|
taskType: "memory_consolidate",
|
|
1222
|
-
model
|
|
1234
|
+
model,
|
|
1235
|
+
...viaResponses ? { transport: "responses", reasoning: { effort: "low" } } : { transport: "chat" },
|
|
1223
1236
|
systemPrompt: SYSTEM_PROMPT3,
|
|
1224
1237
|
userMessage,
|
|
1225
1238
|
tag: "memory/consolidate",
|
|
@@ -1231,7 +1244,7 @@ ${candidateText}`;
|
|
|
1231
1244
|
} catch (err) {
|
|
1232
1245
|
return degrade("llm_error", fallback, options, err);
|
|
1233
1246
|
}
|
|
1234
|
-
if (parsed === null) return degrade(
|
|
1247
|
+
if (parsed === null) return degrade(fallbackReasonFor(failure), fallback, options, failure);
|
|
1235
1248
|
const validIds = new Set(candidates.map((c) => c.id));
|
|
1236
1249
|
const result = validate2(parsed, trimmed, validIds);
|
|
1237
1250
|
if (!result) return degrade("invalid_response", fallback, options);
|
|
@@ -1244,6 +1257,11 @@ ${candidateText}`;
|
|
|
1244
1257
|
}
|
|
1245
1258
|
return reportRefusal(result, options);
|
|
1246
1259
|
}
|
|
1260
|
+
function fallbackReasonFor(failure) {
|
|
1261
|
+
if (failure?.reason === "time-budget-exhausted") return "llm_timeout";
|
|
1262
|
+
if (failure?.reason === "content-flagged") return "content_flagged";
|
|
1263
|
+
return "llm_error";
|
|
1264
|
+
}
|
|
1247
1265
|
function reportRefusal(result, options) {
|
|
1248
1266
|
if (result.fallbackReason) {
|
|
1249
1267
|
notifyConsolidationFallback(result.fallbackReason, options.onFallback);
|
|
@@ -1317,7 +1335,7 @@ var init_consolidate = __esm({
|
|
|
1317
1335
|
init_redactor();
|
|
1318
1336
|
init_consolidationFallback();
|
|
1319
1337
|
init_portalLlm();
|
|
1320
|
-
DEFAULT_CONSOLIDATION_MODEL = "
|
|
1338
|
+
DEFAULT_CONSOLIDATION_MODEL = "openai/gpt-6-luna";
|
|
1321
1339
|
DEFAULT_CONSOLIDATE_ATTEMPTS = 3;
|
|
1322
1340
|
DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS = 2e4;
|
|
1323
1341
|
SYSTEM_PROMPT3 = `You consolidate a new memory against existing memories from the same user.
|
|
@@ -1357,7 +1375,11 @@ OUTPUT \u2014 strict JSON, no prose:
|
|
|
1357
1375
|
For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
|
|
1358
1376
|
For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
|
|
1359
1377
|
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). You MUST also name the subjects: "newSubject" for the new memory, and one entry in "targetSubjects" for EACH memory you are retiring (use "targetSubject" as well when you retire a single memory). List a subject ONLY for the memories you are actually retiring, not for every candidate you were shown. Write the plain subject, not a sentence: "the user", "the user's sister", "the user's manager", "Biscuit the dog". Rule 1a means a supersede is only valid when every one of those subjects is the SAME as "newSubject"; name them and check them against each other before you commit to the action. If ANY of them differs, the answer is "create" \u2014 do not retire the ones that match and keep the rest, because a group that mixes subjects was grouped wrongly in the first place. A subjectless fact ("Lives in Denver", "Works at Riverbend") is about "the user" \u2014 the extractor omits the subject when it is the user, so treat the absence of a subject as the user, never as unknown.
|
|
1360
|
-
For "noop": no content (existing memory is already correct); targetId is that memory
|
|
1378
|
+
For "noop": no content (existing memory is already correct); targetId is that memory.
|
|
1379
|
+
|
|
1380
|
+
ADDING TO AN INTRODUCED ENTITY (check this before choosing "create"): when an existing memory only introduces or names someone or something in the user's life ("User has a cat named Mochi", "User's sister is called Ana") and the new memory gives an attribute of that SAME entity that the existing memory does not state yet (its age, breed, job, home city), choose "update": targetId is that memory and content folds both into one sentence ("User has a cat named Mochi, who is four years old."). The "dog's name vs dog's age" example above means two memories that each already state a different attribute stay separate; a memory that only introduces the entity absorbs the next attribute about it. If the existing memory already states a value and the new memory changes it, that is "supersede" (rule 4), not this.
|
|
1381
|
+
|
|
1382
|
+
CARRY FORWARD UNCHANGED DETAILS (every "update" and "supersede"): a memory can hold several details at once, such as one folded from an introduction and an attribute ("User's sister Ana lives in Paris"). content replaces ONLY the value that changed and keeps every other detail of the memory it replaces, the subject's relation to the user and name above all: "Ana lives in London" against "User's sister Ana lives in Paris" is "supersede" with content "User's sister Ana lives in London", never just "Ana lives in London". Name the subject the same way on both sides: here "newSubject" and "targetSubjects" are both "the user's sister Ana".`;
|
|
1361
1383
|
USER_SUBJECT_ALIASES = /* @__PURE__ */ new Set(["user", "themselves", "themself", "they", "me", "self"]);
|
|
1362
1384
|
}
|
|
1363
1385
|
});
|
|
@@ -2070,7 +2092,7 @@ function onKeyAvailable(address, callback) {
|
|
|
2070
2092
|
callbacks.add(callback);
|
|
2071
2093
|
return () => {
|
|
2072
2094
|
callbacks.delete(callback);
|
|
2073
|
-
if (callbacks.size === 0) {
|
|
2095
|
+
if (callbacks.size === 0 && keyAvailableCallbacks.get(address) === callbacks) {
|
|
2074
2096
|
keyAvailableCallbacks.delete(address);
|
|
2075
2097
|
}
|
|
2076
2098
|
};
|
|
@@ -18455,7 +18477,12 @@ function createAutoExtractor(options) {
|
|
|
18455
18477
|
...baseUrl !== void 0 && { baseUrl }
|
|
18456
18478
|
};
|
|
18457
18479
|
};
|
|
18458
|
-
const
|
|
18480
|
+
const consolidateEndpoint = options.consolidate?.endpointOverride ?? options.extract.endpointOverride;
|
|
18481
|
+
const consolidateOptions = options.consolidate ? {
|
|
18482
|
+
...options.consolidate,
|
|
18483
|
+
...subPassAuth(options.consolidate.baseUrl),
|
|
18484
|
+
...consolidateEndpoint !== void 0 && { endpointOverride: consolidateEndpoint }
|
|
18485
|
+
} : void 0;
|
|
18459
18486
|
const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
|
|
18460
18487
|
if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
|
|
18461
18488
|
getLogger().warn(
|
|
@@ -23816,10 +23843,20 @@ function batchError(result) {
|
|
|
23816
23843
|
var AccountFailureError = class extends Error {
|
|
23817
23844
|
};
|
|
23818
23845
|
var LockedSourcesError = class extends Error {
|
|
23819
|
-
constructor(persistentIds) {
|
|
23820
|
-
|
|
23846
|
+
constructor(persistentIds, locked) {
|
|
23847
|
+
const counts = /* @__PURE__ */ new Map();
|
|
23848
|
+
for (const message of locked) {
|
|
23849
|
+
const version = /^enc:(v\d+):/.exec(message.content)?.[1] ?? "unknown";
|
|
23850
|
+
const key = `${message.decryptionStatus ?? "no_status"}:${version}`;
|
|
23851
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
23852
|
+
}
|
|
23853
|
+
const summary = [...counts].map(([key, count2]) => `${key}=${count2}`).join(", ");
|
|
23854
|
+
super(`Source messages are locked; retained for retry (${summary})`);
|
|
23821
23855
|
this.persistentIds = persistentIds;
|
|
23822
23856
|
}
|
|
23857
|
+
get awaitingKey() {
|
|
23858
|
+
return this.persistentIds.length === 0;
|
|
23859
|
+
}
|
|
23823
23860
|
};
|
|
23824
23861
|
function splitTurns(messages) {
|
|
23825
23862
|
const turns = [];
|
|
@@ -23873,6 +23910,8 @@ function createDurableAutoExtractor(options) {
|
|
|
23873
23910
|
const attempts = /* @__PURE__ */ new Map();
|
|
23874
23911
|
const sessionFailures = /* @__PURE__ */ new Map();
|
|
23875
23912
|
const skippedThisSession = /* @__PURE__ */ new Set();
|
|
23913
|
+
const awaitingKey = /* @__PURE__ */ new Set();
|
|
23914
|
+
let keyEpoch = 0;
|
|
23876
23915
|
const clock = options.now ?? Date.now;
|
|
23877
23916
|
const reportError = (error, conversationId) => {
|
|
23878
23917
|
try {
|
|
@@ -24066,13 +24105,15 @@ function createDurableAutoExtractor(options) {
|
|
|
24066
24105
|
for (const job of pending) {
|
|
24067
24106
|
if (disposed) break;
|
|
24068
24107
|
const attempted = attempts.get(job.id) ?? 0;
|
|
24069
|
-
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id))
|
|
24108
|
+
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id) || awaitingKey.has(job.id))
|
|
24109
|
+
continue;
|
|
24070
24110
|
if (processed >= MAX_JOBS_PER_PASS) {
|
|
24071
24111
|
scheduleAfterSuccess = true;
|
|
24072
24112
|
break;
|
|
24073
24113
|
}
|
|
24074
24114
|
processed++;
|
|
24075
24115
|
const conversationId = String(job._getRaw("conversation_id"));
|
|
24116
|
+
const epochAtRead = keyEpoch;
|
|
24076
24117
|
let ids = [];
|
|
24077
24118
|
let loaded = [];
|
|
24078
24119
|
try {
|
|
@@ -24126,12 +24167,15 @@ function createDurableAutoExtractor(options) {
|
|
|
24126
24167
|
);
|
|
24127
24168
|
continue;
|
|
24128
24169
|
}
|
|
24129
|
-
const locked = loaded.filter(
|
|
24170
|
+
const locked = loaded.filter(
|
|
24171
|
+
(message) => !!message && isEncrypted(message.content)
|
|
24172
|
+
);
|
|
24130
24173
|
if (locked.length) {
|
|
24131
24174
|
throw new LockedSourcesError(
|
|
24132
24175
|
locked.flatMap(
|
|
24133
24176
|
(message) => message.decryptionStatus === "auth_mismatch" || message.decryptionStatus === "invalid_payload" ? [message.uniqueId] : []
|
|
24134
|
-
)
|
|
24177
|
+
),
|
|
24178
|
+
locked
|
|
24135
24179
|
);
|
|
24136
24180
|
}
|
|
24137
24181
|
const messages = loaded.flatMap(
|
|
@@ -24185,6 +24229,12 @@ function createDurableAutoExtractor(options) {
|
|
|
24185
24229
|
scheduleAfterSuccess = true;
|
|
24186
24230
|
} catch (error) {
|
|
24187
24231
|
if (disposed) break;
|
|
24232
|
+
if (error instanceof LockedSourcesError && error.awaitingKey) {
|
|
24233
|
+
reportError(error, conversationId);
|
|
24234
|
+
if (keyEpoch === epochAtRead) awaitingKey.add(job.id);
|
|
24235
|
+
else scheduleAfterSuccess = true;
|
|
24236
|
+
continue;
|
|
24237
|
+
}
|
|
24188
24238
|
attempts.set(job.id, attempted + 1);
|
|
24189
24239
|
retry = true;
|
|
24190
24240
|
reportError(error, conversationId);
|
|
@@ -24212,7 +24262,18 @@ function createDurableAutoExtractor(options) {
|
|
|
24212
24262
|
activeBatch?.abort(options.signal?.reason);
|
|
24213
24263
|
if (timer !== void 0) clearTimeout(timer);
|
|
24214
24264
|
options.signal?.removeEventListener("abort", dispose);
|
|
24265
|
+
unsubscribeKey?.();
|
|
24215
24266
|
}
|
|
24267
|
+
const unsubscribeKey = vault.walletAddress ? onKeyAvailable(vault.walletAddress, () => {
|
|
24268
|
+
keyEpoch++;
|
|
24269
|
+
if (!awaitingKey.size) return;
|
|
24270
|
+
awaitingKey.clear();
|
|
24271
|
+
if (timer !== void 0) {
|
|
24272
|
+
clearTimeout(timer);
|
|
24273
|
+
timer = void 0;
|
|
24274
|
+
}
|
|
24275
|
+
schedule(0);
|
|
24276
|
+
}) : void 0;
|
|
24216
24277
|
if (options.signal?.aborted) dispose();
|
|
24217
24278
|
else options.signal?.addEventListener("abort", dispose, { once: true });
|
|
24218
24279
|
schedule(0);
|
|
@@ -24302,6 +24363,7 @@ function createDurableAutoExtractor(options) {
|
|
|
24302
24363
|
(r) => r._setRaw("message_ids", JSON.stringify([.../* @__PURE__ */ new Set([...idsOf(job), ...fresh])]))
|
|
24303
24364
|
);
|
|
24304
24365
|
attempts.delete(job.id);
|
|
24366
|
+
awaitingKey.delete(job.id);
|
|
24305
24367
|
} else {
|
|
24306
24368
|
await jobs.create((r) => {
|
|
24307
24369
|
r._setRaw("owner_key", owner);
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -4821,7 +4821,7 @@ type RetainSource = "manual" | "auto-extracted" | "capsule";
|
|
|
4821
4821
|
* rate of exactly zero is as likely to mean "the fields are being omitted" as
|
|
4822
4822
|
* "the mistake never happens". Check compliance before reading zero as health.
|
|
4823
4823
|
*/
|
|
4824
|
-
type ConsolidationFallbackReason = "llm_error" | "invalid_response" | "target_vanished"
|
|
4824
|
+
type ConsolidationFallbackReason = "llm_error" | "llm_timeout" | "content_flagged" | "invalid_response" | "target_vanished"
|
|
4825
4825
|
/**
|
|
4826
4826
|
* A `supersede` was downgraded to `create` because the model's own stated
|
|
4827
4827
|
* subjects disagreed: the new fact is about someone other than the fact it
|
|
@@ -4860,6 +4860,7 @@ interface RetainOptions {
|
|
|
4860
4860
|
consolidateOptions?: PortalLlmAuth & {
|
|
4861
4861
|
baseUrl?: string;
|
|
4862
4862
|
model?: string;
|
|
4863
|
+
endpointOverride?: string;
|
|
4863
4864
|
/**
|
|
4864
4865
|
* Invoked when the consolidator degrades to its "create" fallback
|
|
4865
4866
|
* instead of returning a real decision — `llm_error` for network /
|
|
@@ -8967,6 +8968,7 @@ interface CreateAutoExtractorOptions {
|
|
|
8967
8968
|
baseUrl?: string;
|
|
8968
8969
|
/** Override the consolidation model. Default: see `consolidate.ts`. */
|
|
8969
8970
|
model?: string;
|
|
8971
|
+
endpointOverride?: string;
|
|
8970
8972
|
/**
|
|
8971
8973
|
* Notified on each degraded create-fallback (LLM failure or
|
|
8972
8974
|
* schema-violating response). See
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -4821,7 +4821,7 @@ type RetainSource = "manual" | "auto-extracted" | "capsule";
|
|
|
4821
4821
|
* rate of exactly zero is as likely to mean "the fields are being omitted" as
|
|
4822
4822
|
* "the mistake never happens". Check compliance before reading zero as health.
|
|
4823
4823
|
*/
|
|
4824
|
-
type ConsolidationFallbackReason = "llm_error" | "invalid_response" | "target_vanished"
|
|
4824
|
+
type ConsolidationFallbackReason = "llm_error" | "llm_timeout" | "content_flagged" | "invalid_response" | "target_vanished"
|
|
4825
4825
|
/**
|
|
4826
4826
|
* A `supersede` was downgraded to `create` because the model's own stated
|
|
4827
4827
|
* subjects disagreed: the new fact is about someone other than the fact it
|
|
@@ -4860,6 +4860,7 @@ interface RetainOptions {
|
|
|
4860
4860
|
consolidateOptions?: PortalLlmAuth & {
|
|
4861
4861
|
baseUrl?: string;
|
|
4862
4862
|
model?: string;
|
|
4863
|
+
endpointOverride?: string;
|
|
4863
4864
|
/**
|
|
4864
4865
|
* Invoked when the consolidator degrades to its "create" fallback
|
|
4865
4866
|
* instead of returning a real decision — `llm_error` for network /
|
|
@@ -8967,6 +8968,7 @@ interface CreateAutoExtractorOptions {
|
|
|
8967
8968
|
baseUrl?: string;
|
|
8968
8969
|
/** Override the consolidation model. Default: see `consolidate.ts`. */
|
|
8969
8970
|
model?: string;
|
|
8971
|
+
endpointOverride?: string;
|
|
8970
8972
|
/**
|
|
8971
8973
|
* Notified on each degraded create-fallback (LLM failure or
|
|
8972
8974
|
* schema-violating response). See
|
package/dist/expo/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
taskTypeHeader,
|
|
21
21
|
validateEndpointOverride,
|
|
22
22
|
withInternalFlowMarker
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XW3LYSDA.mjs";
|
|
24
24
|
import {
|
|
25
25
|
BASE_URL,
|
|
26
26
|
MCP_R2_DOMAIN,
|
|
@@ -503,7 +503,7 @@ function onKeyAvailable(address, callback) {
|
|
|
503
503
|
callbacks.add(callback);
|
|
504
504
|
return () => {
|
|
505
505
|
callbacks.delete(callback);
|
|
506
|
-
if (callbacks.size === 0) {
|
|
506
|
+
if (callbacks.size === 0 && keyAvailableCallbacks.get(address) === callbacks) {
|
|
507
507
|
keyAvailableCallbacks.delete(address);
|
|
508
508
|
}
|
|
509
509
|
};
|
|
@@ -16121,7 +16121,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
16121
16121
|
content: m.content,
|
|
16122
16122
|
similarity: m.similarity
|
|
16123
16123
|
}));
|
|
16124
|
-
const { consolidateMemory: doConsolidate } = await import("./consolidate-
|
|
16124
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-4CQJYKRK.mjs");
|
|
16125
16125
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
16126
16126
|
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
16127
16127
|
if (decision.action === "supersede" && decision.content) {
|
|
@@ -16837,7 +16837,12 @@ function createAutoExtractor(options) {
|
|
|
16837
16837
|
...baseUrl !== void 0 && { baseUrl }
|
|
16838
16838
|
};
|
|
16839
16839
|
};
|
|
16840
|
-
const
|
|
16840
|
+
const consolidateEndpoint = options.consolidate?.endpointOverride ?? options.extract.endpointOverride;
|
|
16841
|
+
const consolidateOptions = options.consolidate ? {
|
|
16842
|
+
...options.consolidate,
|
|
16843
|
+
...subPassAuth(options.consolidate.baseUrl),
|
|
16844
|
+
...consolidateEndpoint !== void 0 && { endpointOverride: consolidateEndpoint }
|
|
16845
|
+
} : void 0;
|
|
16841
16846
|
const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
|
|
16842
16847
|
if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
|
|
16843
16848
|
getLogger().warn(
|
|
@@ -22171,10 +22176,20 @@ function batchError(result) {
|
|
|
22171
22176
|
var AccountFailureError = class extends Error {
|
|
22172
22177
|
};
|
|
22173
22178
|
var LockedSourcesError = class extends Error {
|
|
22174
|
-
constructor(persistentIds) {
|
|
22175
|
-
|
|
22179
|
+
constructor(persistentIds, locked) {
|
|
22180
|
+
const counts = /* @__PURE__ */ new Map();
|
|
22181
|
+
for (const message of locked) {
|
|
22182
|
+
const version = /^enc:(v\d+):/.exec(message.content)?.[1] ?? "unknown";
|
|
22183
|
+
const key = `${message.decryptionStatus ?? "no_status"}:${version}`;
|
|
22184
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
22185
|
+
}
|
|
22186
|
+
const summary = [...counts].map(([key, count2]) => `${key}=${count2}`).join(", ");
|
|
22187
|
+
super(`Source messages are locked; retained for retry (${summary})`);
|
|
22176
22188
|
this.persistentIds = persistentIds;
|
|
22177
22189
|
}
|
|
22190
|
+
get awaitingKey() {
|
|
22191
|
+
return this.persistentIds.length === 0;
|
|
22192
|
+
}
|
|
22178
22193
|
};
|
|
22179
22194
|
function splitTurns(messages) {
|
|
22180
22195
|
const turns = [];
|
|
@@ -22228,6 +22243,8 @@ function createDurableAutoExtractor(options) {
|
|
|
22228
22243
|
const attempts = /* @__PURE__ */ new Map();
|
|
22229
22244
|
const sessionFailures = /* @__PURE__ */ new Map();
|
|
22230
22245
|
const skippedThisSession = /* @__PURE__ */ new Set();
|
|
22246
|
+
const awaitingKey = /* @__PURE__ */ new Set();
|
|
22247
|
+
let keyEpoch = 0;
|
|
22231
22248
|
const clock = options.now ?? Date.now;
|
|
22232
22249
|
const reportError = (error, conversationId) => {
|
|
22233
22250
|
try {
|
|
@@ -22421,13 +22438,15 @@ function createDurableAutoExtractor(options) {
|
|
|
22421
22438
|
for (const job of pending) {
|
|
22422
22439
|
if (disposed) break;
|
|
22423
22440
|
const attempted = attempts.get(job.id) ?? 0;
|
|
22424
|
-
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id))
|
|
22441
|
+
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id) || awaitingKey.has(job.id))
|
|
22442
|
+
continue;
|
|
22425
22443
|
if (processed >= MAX_JOBS_PER_PASS) {
|
|
22426
22444
|
scheduleAfterSuccess = true;
|
|
22427
22445
|
break;
|
|
22428
22446
|
}
|
|
22429
22447
|
processed++;
|
|
22430
22448
|
const conversationId = String(job._getRaw("conversation_id"));
|
|
22449
|
+
const epochAtRead = keyEpoch;
|
|
22431
22450
|
let ids = [];
|
|
22432
22451
|
let loaded = [];
|
|
22433
22452
|
try {
|
|
@@ -22481,12 +22500,15 @@ function createDurableAutoExtractor(options) {
|
|
|
22481
22500
|
);
|
|
22482
22501
|
continue;
|
|
22483
22502
|
}
|
|
22484
|
-
const locked = loaded.filter(
|
|
22503
|
+
const locked = loaded.filter(
|
|
22504
|
+
(message) => !!message && isEncrypted(message.content)
|
|
22505
|
+
);
|
|
22485
22506
|
if (locked.length) {
|
|
22486
22507
|
throw new LockedSourcesError(
|
|
22487
22508
|
locked.flatMap(
|
|
22488
22509
|
(message) => message.decryptionStatus === "auth_mismatch" || message.decryptionStatus === "invalid_payload" ? [message.uniqueId] : []
|
|
22489
|
-
)
|
|
22510
|
+
),
|
|
22511
|
+
locked
|
|
22490
22512
|
);
|
|
22491
22513
|
}
|
|
22492
22514
|
const messages = loaded.flatMap(
|
|
@@ -22540,6 +22562,12 @@ function createDurableAutoExtractor(options) {
|
|
|
22540
22562
|
scheduleAfterSuccess = true;
|
|
22541
22563
|
} catch (error) {
|
|
22542
22564
|
if (disposed) break;
|
|
22565
|
+
if (error instanceof LockedSourcesError && error.awaitingKey) {
|
|
22566
|
+
reportError(error, conversationId);
|
|
22567
|
+
if (keyEpoch === epochAtRead) awaitingKey.add(job.id);
|
|
22568
|
+
else scheduleAfterSuccess = true;
|
|
22569
|
+
continue;
|
|
22570
|
+
}
|
|
22543
22571
|
attempts.set(job.id, attempted + 1);
|
|
22544
22572
|
retry = true;
|
|
22545
22573
|
reportError(error, conversationId);
|
|
@@ -22567,7 +22595,18 @@ function createDurableAutoExtractor(options) {
|
|
|
22567
22595
|
activeBatch?.abort(options.signal?.reason);
|
|
22568
22596
|
if (timer !== void 0) clearTimeout(timer);
|
|
22569
22597
|
options.signal?.removeEventListener("abort", dispose);
|
|
22598
|
+
unsubscribeKey?.();
|
|
22570
22599
|
}
|
|
22600
|
+
const unsubscribeKey = vault.walletAddress ? onKeyAvailable(vault.walletAddress, () => {
|
|
22601
|
+
keyEpoch++;
|
|
22602
|
+
if (!awaitingKey.size) return;
|
|
22603
|
+
awaitingKey.clear();
|
|
22604
|
+
if (timer !== void 0) {
|
|
22605
|
+
clearTimeout(timer);
|
|
22606
|
+
timer = void 0;
|
|
22607
|
+
}
|
|
22608
|
+
schedule(0);
|
|
22609
|
+
}) : void 0;
|
|
22571
22610
|
if (options.signal?.aborted) dispose();
|
|
22572
22611
|
else options.signal?.addEventListener("abort", dispose, { once: true });
|
|
22573
22612
|
schedule(0);
|
|
@@ -22657,6 +22696,7 @@ function createDurableAutoExtractor(options) {
|
|
|
22657
22696
|
(r) => r._setRaw("message_ids", JSON.stringify([.../* @__PURE__ */ new Set([...idsOf(job), ...fresh])]))
|
|
22658
22697
|
);
|
|
22659
22698
|
attempts.delete(job.id);
|
|
22699
|
+
awaitingKey.delete(job.id);
|
|
22660
22700
|
} else {
|
|
22661
22701
|
await jobs.create((r) => {
|
|
22662
22702
|
r._setRaw("owner_key", owner);
|
|
@@ -709,6 +709,13 @@ var FOREIGN_ENDPOINT_SUFFIX = {
|
|
|
709
709
|
chat: "/responses",
|
|
710
710
|
responses: "/chat/completions"
|
|
711
711
|
};
|
|
712
|
+
function endpointForTransport(endpoint, transport) {
|
|
713
|
+
const { path, rest } = splitEndpoint(endpoint);
|
|
714
|
+
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
715
|
+
if (!path.endsWith(foreign)) return endpoint;
|
|
716
|
+
const own = FOREIGN_ENDPOINT_SUFFIX[transport === "chat" ? "responses" : "chat"];
|
|
717
|
+
return `${path.slice(0, -foreign.length)}${own}${rest}`;
|
|
718
|
+
}
|
|
712
719
|
function assertTransportMatchesEndpoint(transport, endpoint) {
|
|
713
720
|
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
714
721
|
if (!splitEndpoint(endpoint).path.endsWith(foreign)) return;
|
|
@@ -766,10 +773,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
766
773
|
throw new Error(overrideValidation.message);
|
|
767
774
|
}
|
|
768
775
|
endpoint = overrideValidation.endpoint;
|
|
769
|
-
|
|
770
|
-
if (autoUpgraded && path.endsWith("/chat/completions")) {
|
|
771
|
-
endpoint = `${path.slice(0, -"/chat/completions".length)}/responses${rest}`;
|
|
772
|
-
}
|
|
776
|
+
if (autoUpgraded) endpoint = endpointForTransport(endpoint, transport);
|
|
773
777
|
assertTransportMatchesEndpoint(transport, endpoint);
|
|
774
778
|
}
|
|
775
779
|
const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
|
|
@@ -1073,6 +1077,7 @@ export {
|
|
|
1073
1077
|
withInternalFlowMarker,
|
|
1074
1078
|
supportsResponseFormat,
|
|
1075
1079
|
requiresResponsesTransport,
|
|
1080
|
+
endpointForTransport,
|
|
1076
1081
|
parseRetryAfterMs,
|
|
1077
1082
|
resolvePortalAuthHeaders,
|
|
1078
1083
|
callPortalJsonCompletion,
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
callPortalJsonCompletion,
|
|
3
|
+
endpointForTransport,
|
|
3
4
|
notifyConsolidationFallback,
|
|
5
|
+
requiresResponsesTransport,
|
|
4
6
|
resolvePiiRedactor
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7QX4J7KV.mjs";
|
|
6
8
|
import "./chunk-GYKJOWEO.mjs";
|
|
7
9
|
|
|
8
10
|
// src/lib/memory/consolidate.ts
|
|
9
|
-
var DEFAULT_CONSOLIDATION_MODEL = "
|
|
11
|
+
var DEFAULT_CONSOLIDATION_MODEL = "openai/gpt-6-luna";
|
|
10
12
|
var DEFAULT_CONSOLIDATE_ATTEMPTS = 3;
|
|
11
13
|
var DEFAULT_CONSOLIDATE_TOTAL_TIMEOUT_MS = 2e4;
|
|
12
14
|
var SYSTEM_PROMPT = `You consolidate a new memory against existing memories from the same user.
|
|
@@ -46,7 +48,11 @@ OUTPUT \u2014 strict JSON, no prose:
|
|
|
46
48
|
For "create": content is the new memory verbatim (or a slight refinement); omit targetId/targetIds.
|
|
47
49
|
For "update": content is the merged richest-version, \u226480 words; targetId is the single memory to update.
|
|
48
50
|
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). You MUST also name the subjects: "newSubject" for the new memory, and one entry in "targetSubjects" for EACH memory you are retiring (use "targetSubject" as well when you retire a single memory). List a subject ONLY for the memories you are actually retiring, not for every candidate you were shown. Write the plain subject, not a sentence: "the user", "the user's sister", "the user's manager", "Biscuit the dog". Rule 1a means a supersede is only valid when every one of those subjects is the SAME as "newSubject"; name them and check them against each other before you commit to the action. If ANY of them differs, the answer is "create" \u2014 do not retire the ones that match and keep the rest, because a group that mixes subjects was grouped wrongly in the first place. A subjectless fact ("Lives in Denver", "Works at Riverbend") is about "the user" \u2014 the extractor omits the subject when it is the user, so treat the absence of a subject as the user, never as unknown.
|
|
49
|
-
For "noop": no content (existing memory is already correct); targetId is that memory
|
|
51
|
+
For "noop": no content (existing memory is already correct); targetId is that memory.
|
|
52
|
+
|
|
53
|
+
ADDING TO AN INTRODUCED ENTITY (check this before choosing "create"): when an existing memory only introduces or names someone or something in the user's life ("User has a cat named Mochi", "User's sister is called Ana") and the new memory gives an attribute of that SAME entity that the existing memory does not state yet (its age, breed, job, home city), choose "update": targetId is that memory and content folds both into one sentence ("User has a cat named Mochi, who is four years old."). The "dog's name vs dog's age" example above means two memories that each already state a different attribute stay separate; a memory that only introduces the entity absorbs the next attribute about it. If the existing memory already states a value and the new memory changes it, that is "supersede" (rule 4), not this.
|
|
54
|
+
|
|
55
|
+
CARRY FORWARD UNCHANGED DETAILS (every "update" and "supersede"): a memory can hold several details at once, such as one folded from an introduction and an attribute ("User's sister Ana lives in Paris"). content replaces ONLY the value that changed and keeps every other detail of the memory it replaces, the subject's relation to the user and name above all: "Ana lives in London" against "User's sister Ana lives in Paris" is "supersede" with content "User's sister Ana lives in London", never just "Ana lives in London". Name the subject the same way on both sides: here "newSubject" and "targetSubjects" are both "the user's sister Ana".`;
|
|
50
56
|
async function consolidateMemory(newContent, candidates, options) {
|
|
51
57
|
const fallback = {
|
|
52
58
|
action: "create",
|
|
@@ -69,14 +75,23 @@ async function consolidateMemory(newContent, candidates, options) {
|
|
|
69
75
|
|
|
70
76
|
Existing memories (top ${candidates.length} by cosine):
|
|
71
77
|
${candidateText}`;
|
|
78
|
+
const model = options.model ?? DEFAULT_CONSOLIDATION_MODEL;
|
|
79
|
+
const viaResponses = requiresResponsesTransport(model);
|
|
80
|
+
const endpointOverride = options.endpointOverride === void 0 ? void 0 : endpointForTransport(options.endpointOverride, viaResponses ? "responses" : "chat");
|
|
72
81
|
let parsed;
|
|
82
|
+
let failure;
|
|
73
83
|
try {
|
|
74
84
|
parsed = await callPortalJsonCompletion({
|
|
85
|
+
onFailure: (f) => {
|
|
86
|
+
failure = f;
|
|
87
|
+
},
|
|
75
88
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
76
89
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
77
90
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
91
|
+
...endpointOverride !== void 0 && { endpointOverride },
|
|
78
92
|
taskType: "memory_consolidate",
|
|
79
|
-
model
|
|
93
|
+
model,
|
|
94
|
+
...viaResponses ? { transport: "responses", reasoning: { effort: "low" } } : { transport: "chat" },
|
|
80
95
|
systemPrompt: SYSTEM_PROMPT,
|
|
81
96
|
userMessage,
|
|
82
97
|
tag: "memory/consolidate",
|
|
@@ -88,7 +103,7 @@ ${candidateText}`;
|
|
|
88
103
|
} catch (err) {
|
|
89
104
|
return degrade("llm_error", fallback, options, err);
|
|
90
105
|
}
|
|
91
|
-
if (parsed === null) return degrade(
|
|
106
|
+
if (parsed === null) return degrade(fallbackReasonFor(failure), fallback, options, failure);
|
|
92
107
|
const validIds = new Set(candidates.map((c) => c.id));
|
|
93
108
|
const result = validate(parsed, trimmed, validIds);
|
|
94
109
|
if (!result) return degrade("invalid_response", fallback, options);
|
|
@@ -101,6 +116,11 @@ ${candidateText}`;
|
|
|
101
116
|
}
|
|
102
117
|
return reportRefusal(result, options);
|
|
103
118
|
}
|
|
119
|
+
function fallbackReasonFor(failure) {
|
|
120
|
+
if (failure?.reason === "time-budget-exhausted") return "llm_timeout";
|
|
121
|
+
if (failure?.reason === "content-flagged") return "content_flagged";
|
|
122
|
+
return "llm_error";
|
|
123
|
+
}
|
|
104
124
|
function reportRefusal(result, options) {
|
|
105
125
|
if (result.fallbackReason) {
|
|
106
126
|
notifyConsolidationFallback(result.fallbackReason, options.onFallback);
|