@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
package/dist/react/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
|
});
|
|
@@ -1837,7 +1859,7 @@ function onKeyAvailable(address, callback) {
|
|
|
1837
1859
|
callbacks.add(callback);
|
|
1838
1860
|
return () => {
|
|
1839
1861
|
callbacks.delete(callback);
|
|
1840
|
-
if (callbacks.size === 0) {
|
|
1862
|
+
if (callbacks.size === 0 && keyAvailableCallbacks.get(address) === callbacks) {
|
|
1841
1863
|
keyAvailableCallbacks.delete(address);
|
|
1842
1864
|
}
|
|
1843
1865
|
};
|
|
@@ -20732,7 +20754,12 @@ function createAutoExtractor(options) {
|
|
|
20732
20754
|
...baseUrl !== void 0 && { baseUrl }
|
|
20733
20755
|
};
|
|
20734
20756
|
};
|
|
20735
|
-
const
|
|
20757
|
+
const consolidateEndpoint = options.consolidate?.endpointOverride ?? options.extract.endpointOverride;
|
|
20758
|
+
const consolidateOptions = options.consolidate ? {
|
|
20759
|
+
...options.consolidate,
|
|
20760
|
+
...subPassAuth(options.consolidate.baseUrl),
|
|
20761
|
+
...consolidateEndpoint !== void 0 && { endpointOverride: consolidateEndpoint }
|
|
20762
|
+
} : void 0;
|
|
20736
20763
|
const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
|
|
20737
20764
|
if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
|
|
20738
20765
|
getLogger().warn(
|
|
@@ -26119,10 +26146,20 @@ function batchError(result) {
|
|
|
26119
26146
|
var AccountFailureError = class extends Error {
|
|
26120
26147
|
};
|
|
26121
26148
|
var LockedSourcesError = class extends Error {
|
|
26122
|
-
constructor(persistentIds) {
|
|
26123
|
-
|
|
26149
|
+
constructor(persistentIds, locked) {
|
|
26150
|
+
const counts = /* @__PURE__ */ new Map();
|
|
26151
|
+
for (const message of locked) {
|
|
26152
|
+
const version = /^enc:(v\d+):/.exec(message.content)?.[1] ?? "unknown";
|
|
26153
|
+
const key = `${message.decryptionStatus ?? "no_status"}:${version}`;
|
|
26154
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
26155
|
+
}
|
|
26156
|
+
const summary = [...counts].map(([key, count2]) => `${key}=${count2}`).join(", ");
|
|
26157
|
+
super(`Source messages are locked; retained for retry (${summary})`);
|
|
26124
26158
|
this.persistentIds = persistentIds;
|
|
26125
26159
|
}
|
|
26160
|
+
get awaitingKey() {
|
|
26161
|
+
return this.persistentIds.length === 0;
|
|
26162
|
+
}
|
|
26126
26163
|
};
|
|
26127
26164
|
function splitTurns(messages) {
|
|
26128
26165
|
const turns = [];
|
|
@@ -26176,6 +26213,8 @@ function createDurableAutoExtractor(options) {
|
|
|
26176
26213
|
const attempts = /* @__PURE__ */ new Map();
|
|
26177
26214
|
const sessionFailures = /* @__PURE__ */ new Map();
|
|
26178
26215
|
const skippedThisSession = /* @__PURE__ */ new Set();
|
|
26216
|
+
const awaitingKey = /* @__PURE__ */ new Set();
|
|
26217
|
+
let keyEpoch = 0;
|
|
26179
26218
|
const clock = options.now ?? Date.now;
|
|
26180
26219
|
const reportError = (error, conversationId) => {
|
|
26181
26220
|
try {
|
|
@@ -26369,13 +26408,15 @@ function createDurableAutoExtractor(options) {
|
|
|
26369
26408
|
for (const job of pending) {
|
|
26370
26409
|
if (disposed) break;
|
|
26371
26410
|
const attempted = attempts.get(job.id) ?? 0;
|
|
26372
|
-
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id))
|
|
26411
|
+
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id) || awaitingKey.has(job.id))
|
|
26412
|
+
continue;
|
|
26373
26413
|
if (processed >= MAX_JOBS_PER_PASS) {
|
|
26374
26414
|
scheduleAfterSuccess = true;
|
|
26375
26415
|
break;
|
|
26376
26416
|
}
|
|
26377
26417
|
processed++;
|
|
26378
26418
|
const conversationId = String(job._getRaw("conversation_id"));
|
|
26419
|
+
const epochAtRead = keyEpoch;
|
|
26379
26420
|
let ids = [];
|
|
26380
26421
|
let loaded = [];
|
|
26381
26422
|
try {
|
|
@@ -26429,12 +26470,15 @@ function createDurableAutoExtractor(options) {
|
|
|
26429
26470
|
);
|
|
26430
26471
|
continue;
|
|
26431
26472
|
}
|
|
26432
|
-
const locked = loaded.filter(
|
|
26473
|
+
const locked = loaded.filter(
|
|
26474
|
+
(message) => !!message && isEncrypted(message.content)
|
|
26475
|
+
);
|
|
26433
26476
|
if (locked.length) {
|
|
26434
26477
|
throw new LockedSourcesError(
|
|
26435
26478
|
locked.flatMap(
|
|
26436
26479
|
(message) => message.decryptionStatus === "auth_mismatch" || message.decryptionStatus === "invalid_payload" ? [message.uniqueId] : []
|
|
26437
|
-
)
|
|
26480
|
+
),
|
|
26481
|
+
locked
|
|
26438
26482
|
);
|
|
26439
26483
|
}
|
|
26440
26484
|
const messages = loaded.flatMap(
|
|
@@ -26488,6 +26532,12 @@ function createDurableAutoExtractor(options) {
|
|
|
26488
26532
|
scheduleAfterSuccess = true;
|
|
26489
26533
|
} catch (error) {
|
|
26490
26534
|
if (disposed) break;
|
|
26535
|
+
if (error instanceof LockedSourcesError && error.awaitingKey) {
|
|
26536
|
+
reportError(error, conversationId);
|
|
26537
|
+
if (keyEpoch === epochAtRead) awaitingKey.add(job.id);
|
|
26538
|
+
else scheduleAfterSuccess = true;
|
|
26539
|
+
continue;
|
|
26540
|
+
}
|
|
26491
26541
|
attempts.set(job.id, attempted + 1);
|
|
26492
26542
|
retry = true;
|
|
26493
26543
|
reportError(error, conversationId);
|
|
@@ -26515,7 +26565,18 @@ function createDurableAutoExtractor(options) {
|
|
|
26515
26565
|
activeBatch?.abort(options.signal?.reason);
|
|
26516
26566
|
if (timer !== void 0) clearTimeout(timer);
|
|
26517
26567
|
options.signal?.removeEventListener("abort", dispose);
|
|
26568
|
+
unsubscribeKey?.();
|
|
26518
26569
|
}
|
|
26570
|
+
const unsubscribeKey = vault.walletAddress ? onKeyAvailable(vault.walletAddress, () => {
|
|
26571
|
+
keyEpoch++;
|
|
26572
|
+
if (!awaitingKey.size) return;
|
|
26573
|
+
awaitingKey.clear();
|
|
26574
|
+
if (timer !== void 0) {
|
|
26575
|
+
clearTimeout(timer);
|
|
26576
|
+
timer = void 0;
|
|
26577
|
+
}
|
|
26578
|
+
schedule(0);
|
|
26579
|
+
}) : void 0;
|
|
26519
26580
|
if (options.signal?.aborted) dispose();
|
|
26520
26581
|
else options.signal?.addEventListener("abort", dispose, { once: true });
|
|
26521
26582
|
schedule(0);
|
|
@@ -26605,6 +26666,7 @@ function createDurableAutoExtractor(options) {
|
|
|
26605
26666
|
(r) => r._setRaw("message_ids", JSON.stringify([.../* @__PURE__ */ new Set([...idsOf(job), ...fresh])]))
|
|
26606
26667
|
);
|
|
26607
26668
|
attempts.delete(job.id);
|
|
26669
|
+
awaitingKey.delete(job.id);
|
|
26608
26670
|
} else {
|
|
26609
26671
|
await jobs.create((r) => {
|
|
26610
26672
|
r._setRaw("owner_key", owner);
|
package/dist/react/index.d.mts
CHANGED
|
@@ -5337,7 +5337,7 @@ type RetainSource = "manual" | "auto-extracted" | "capsule";
|
|
|
5337
5337
|
* rate of exactly zero is as likely to mean "the fields are being omitted" as
|
|
5338
5338
|
* "the mistake never happens". Check compliance before reading zero as health.
|
|
5339
5339
|
*/
|
|
5340
|
-
type ConsolidationFallbackReason = "llm_error" | "invalid_response" | "target_vanished"
|
|
5340
|
+
type ConsolidationFallbackReason = "llm_error" | "llm_timeout" | "content_flagged" | "invalid_response" | "target_vanished"
|
|
5341
5341
|
/**
|
|
5342
5342
|
* A `supersede` was downgraded to `create` because the model's own stated
|
|
5343
5343
|
* subjects disagreed: the new fact is about someone other than the fact it
|
|
@@ -5376,6 +5376,7 @@ interface RetainOptions {
|
|
|
5376
5376
|
consolidateOptions?: PortalLlmAuth & {
|
|
5377
5377
|
baseUrl?: string;
|
|
5378
5378
|
model?: string;
|
|
5379
|
+
endpointOverride?: string;
|
|
5379
5380
|
/**
|
|
5380
5381
|
* Invoked when the consolidator degrades to its "create" fallback
|
|
5381
5382
|
* instead of returning a real decision — `llm_error` for network /
|
|
@@ -10700,6 +10701,7 @@ interface CreateAutoExtractorOptions {
|
|
|
10700
10701
|
baseUrl?: string;
|
|
10701
10702
|
/** Override the consolidation model. Default: see `consolidate.ts`. */
|
|
10702
10703
|
model?: string;
|
|
10704
|
+
endpointOverride?: string;
|
|
10703
10705
|
/**
|
|
10704
10706
|
* Notified on each degraded create-fallback (LLM failure or
|
|
10705
10707
|
* schema-violating response). See
|
package/dist/react/index.d.ts
CHANGED
|
@@ -5337,7 +5337,7 @@ type RetainSource = "manual" | "auto-extracted" | "capsule";
|
|
|
5337
5337
|
* rate of exactly zero is as likely to mean "the fields are being omitted" as
|
|
5338
5338
|
* "the mistake never happens". Check compliance before reading zero as health.
|
|
5339
5339
|
*/
|
|
5340
|
-
type ConsolidationFallbackReason = "llm_error" | "invalid_response" | "target_vanished"
|
|
5340
|
+
type ConsolidationFallbackReason = "llm_error" | "llm_timeout" | "content_flagged" | "invalid_response" | "target_vanished"
|
|
5341
5341
|
/**
|
|
5342
5342
|
* A `supersede` was downgraded to `create` because the model's own stated
|
|
5343
5343
|
* subjects disagreed: the new fact is about someone other than the fact it
|
|
@@ -5376,6 +5376,7 @@ interface RetainOptions {
|
|
|
5376
5376
|
consolidateOptions?: PortalLlmAuth & {
|
|
5377
5377
|
baseUrl?: string;
|
|
5378
5378
|
model?: string;
|
|
5379
|
+
endpointOverride?: string;
|
|
5379
5380
|
/**
|
|
5380
5381
|
* Invoked when the consolidator degrades to its "create" fallback
|
|
5381
5382
|
* instead of returning a real decision — `llm_error` for network /
|
|
@@ -10700,6 +10701,7 @@ interface CreateAutoExtractorOptions {
|
|
|
10700
10701
|
baseUrl?: string;
|
|
10701
10702
|
/** Override the consolidation model. Default: see `consolidate.ts`. */
|
|
10702
10703
|
model?: string;
|
|
10704
|
+
endpointOverride?: string;
|
|
10703
10705
|
/**
|
|
10704
10706
|
* Notified on each degraded create-fallback (LLM failure or
|
|
10705
10707
|
* schema-violating response). See
|
package/dist/react/index.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
taskTypeHeader,
|
|
20
20
|
validateEndpointOverride,
|
|
21
21
|
withInternalFlowMarker
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-7QX4J7KV.mjs";
|
|
23
23
|
import {
|
|
24
24
|
BASE_URL,
|
|
25
25
|
MCP_R2_DOMAIN,
|
|
@@ -69,7 +69,7 @@ function onKeyAvailable(address, callback) {
|
|
|
69
69
|
callbacks.add(callback);
|
|
70
70
|
return () => {
|
|
71
71
|
callbacks.delete(callback);
|
|
72
|
-
if (callbacks.size === 0) {
|
|
72
|
+
if (callbacks.size === 0 && keyAvailableCallbacks.get(address) === callbacks) {
|
|
73
73
|
keyAvailableCallbacks.delete(address);
|
|
74
74
|
}
|
|
75
75
|
};
|
|
@@ -18190,7 +18190,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
18190
18190
|
content: m.content,
|
|
18191
18191
|
similarity: m.similarity
|
|
18192
18192
|
}));
|
|
18193
|
-
const { consolidateMemory: doConsolidate } = await import("./consolidate-
|
|
18193
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-WLDYJF6J.mjs");
|
|
18194
18194
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
18195
18195
|
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
18196
18196
|
if (decision.action === "supersede" && decision.content) {
|
|
@@ -18906,7 +18906,12 @@ function createAutoExtractor(options) {
|
|
|
18906
18906
|
...baseUrl !== void 0 && { baseUrl }
|
|
18907
18907
|
};
|
|
18908
18908
|
};
|
|
18909
|
-
const
|
|
18909
|
+
const consolidateEndpoint = options.consolidate?.endpointOverride ?? options.extract.endpointOverride;
|
|
18910
|
+
const consolidateOptions = options.consolidate ? {
|
|
18911
|
+
...options.consolidate,
|
|
18912
|
+
...subPassAuth(options.consolidate.baseUrl),
|
|
18913
|
+
...consolidateEndpoint !== void 0 && { endpointOverride: consolidateEndpoint }
|
|
18914
|
+
} : void 0;
|
|
18910
18915
|
const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
|
|
18911
18916
|
if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
|
|
18912
18917
|
getLogger().warn(
|
|
@@ -24266,10 +24271,20 @@ function batchError(result) {
|
|
|
24266
24271
|
var AccountFailureError = class extends Error {
|
|
24267
24272
|
};
|
|
24268
24273
|
var LockedSourcesError = class extends Error {
|
|
24269
|
-
constructor(persistentIds) {
|
|
24270
|
-
|
|
24274
|
+
constructor(persistentIds, locked) {
|
|
24275
|
+
const counts = /* @__PURE__ */ new Map();
|
|
24276
|
+
for (const message of locked) {
|
|
24277
|
+
const version = /^enc:(v\d+):/.exec(message.content)?.[1] ?? "unknown";
|
|
24278
|
+
const key = `${message.decryptionStatus ?? "no_status"}:${version}`;
|
|
24279
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
24280
|
+
}
|
|
24281
|
+
const summary = [...counts].map(([key, count2]) => `${key}=${count2}`).join(", ");
|
|
24282
|
+
super(`Source messages are locked; retained for retry (${summary})`);
|
|
24271
24283
|
this.persistentIds = persistentIds;
|
|
24272
24284
|
}
|
|
24285
|
+
get awaitingKey() {
|
|
24286
|
+
return this.persistentIds.length === 0;
|
|
24287
|
+
}
|
|
24273
24288
|
};
|
|
24274
24289
|
function splitTurns(messages) {
|
|
24275
24290
|
const turns = [];
|
|
@@ -24323,6 +24338,8 @@ function createDurableAutoExtractor(options) {
|
|
|
24323
24338
|
const attempts = /* @__PURE__ */ new Map();
|
|
24324
24339
|
const sessionFailures = /* @__PURE__ */ new Map();
|
|
24325
24340
|
const skippedThisSession = /* @__PURE__ */ new Set();
|
|
24341
|
+
const awaitingKey = /* @__PURE__ */ new Set();
|
|
24342
|
+
let keyEpoch = 0;
|
|
24326
24343
|
const clock = options.now ?? Date.now;
|
|
24327
24344
|
const reportError = (error, conversationId) => {
|
|
24328
24345
|
try {
|
|
@@ -24516,13 +24533,15 @@ function createDurableAutoExtractor(options) {
|
|
|
24516
24533
|
for (const job of pending) {
|
|
24517
24534
|
if (disposed) break;
|
|
24518
24535
|
const attempted = attempts.get(job.id) ?? 0;
|
|
24519
|
-
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id))
|
|
24536
|
+
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id) || awaitingKey.has(job.id))
|
|
24537
|
+
continue;
|
|
24520
24538
|
if (processed >= MAX_JOBS_PER_PASS) {
|
|
24521
24539
|
scheduleAfterSuccess = true;
|
|
24522
24540
|
break;
|
|
24523
24541
|
}
|
|
24524
24542
|
processed++;
|
|
24525
24543
|
const conversationId = String(job._getRaw("conversation_id"));
|
|
24544
|
+
const epochAtRead = keyEpoch;
|
|
24526
24545
|
let ids = [];
|
|
24527
24546
|
let loaded = [];
|
|
24528
24547
|
try {
|
|
@@ -24576,12 +24595,15 @@ function createDurableAutoExtractor(options) {
|
|
|
24576
24595
|
);
|
|
24577
24596
|
continue;
|
|
24578
24597
|
}
|
|
24579
|
-
const locked = loaded.filter(
|
|
24598
|
+
const locked = loaded.filter(
|
|
24599
|
+
(message) => !!message && isEncrypted(message.content)
|
|
24600
|
+
);
|
|
24580
24601
|
if (locked.length) {
|
|
24581
24602
|
throw new LockedSourcesError(
|
|
24582
24603
|
locked.flatMap(
|
|
24583
24604
|
(message) => message.decryptionStatus === "auth_mismatch" || message.decryptionStatus === "invalid_payload" ? [message.uniqueId] : []
|
|
24584
|
-
)
|
|
24605
|
+
),
|
|
24606
|
+
locked
|
|
24585
24607
|
);
|
|
24586
24608
|
}
|
|
24587
24609
|
const messages = loaded.flatMap(
|
|
@@ -24635,6 +24657,12 @@ function createDurableAutoExtractor(options) {
|
|
|
24635
24657
|
scheduleAfterSuccess = true;
|
|
24636
24658
|
} catch (error) {
|
|
24637
24659
|
if (disposed) break;
|
|
24660
|
+
if (error instanceof LockedSourcesError && error.awaitingKey) {
|
|
24661
|
+
reportError(error, conversationId);
|
|
24662
|
+
if (keyEpoch === epochAtRead) awaitingKey.add(job.id);
|
|
24663
|
+
else scheduleAfterSuccess = true;
|
|
24664
|
+
continue;
|
|
24665
|
+
}
|
|
24638
24666
|
attempts.set(job.id, attempted + 1);
|
|
24639
24667
|
retry = true;
|
|
24640
24668
|
reportError(error, conversationId);
|
|
@@ -24662,7 +24690,18 @@ function createDurableAutoExtractor(options) {
|
|
|
24662
24690
|
activeBatch?.abort(options.signal?.reason);
|
|
24663
24691
|
if (timer !== void 0) clearTimeout(timer);
|
|
24664
24692
|
options.signal?.removeEventListener("abort", dispose);
|
|
24693
|
+
unsubscribeKey?.();
|
|
24665
24694
|
}
|
|
24695
|
+
const unsubscribeKey = vault.walletAddress ? onKeyAvailable(vault.walletAddress, () => {
|
|
24696
|
+
keyEpoch++;
|
|
24697
|
+
if (!awaitingKey.size) return;
|
|
24698
|
+
awaitingKey.clear();
|
|
24699
|
+
if (timer !== void 0) {
|
|
24700
|
+
clearTimeout(timer);
|
|
24701
|
+
timer = void 0;
|
|
24702
|
+
}
|
|
24703
|
+
schedule(0);
|
|
24704
|
+
}) : void 0;
|
|
24666
24705
|
if (options.signal?.aborted) dispose();
|
|
24667
24706
|
else options.signal?.addEventListener("abort", dispose, { once: true });
|
|
24668
24707
|
schedule(0);
|
|
@@ -24752,6 +24791,7 @@ function createDurableAutoExtractor(options) {
|
|
|
24752
24791
|
(r) => r._setRaw("message_ids", JSON.stringify([.../* @__PURE__ */ new Set([...idsOf(job), ...fresh])]))
|
|
24753
24792
|
);
|
|
24754
24793
|
attempts.delete(job.id);
|
|
24794
|
+
awaitingKey.delete(job.id);
|
|
24755
24795
|
} else {
|
|
24756
24796
|
await jobs.create((r) => {
|
|
24757
24797
|
r._setRaw("owner_key", owner);
|
|
@@ -696,6 +696,13 @@ var FOREIGN_ENDPOINT_SUFFIX = {
|
|
|
696
696
|
chat: "/responses",
|
|
697
697
|
responses: "/chat/completions"
|
|
698
698
|
};
|
|
699
|
+
function endpointForTransport(endpoint, transport) {
|
|
700
|
+
const { path, rest } = splitEndpoint(endpoint);
|
|
701
|
+
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
702
|
+
if (!path.endsWith(foreign)) return endpoint;
|
|
703
|
+
const own = FOREIGN_ENDPOINT_SUFFIX[transport === "chat" ? "responses" : "chat"];
|
|
704
|
+
return `${path.slice(0, -foreign.length)}${own}${rest}`;
|
|
705
|
+
}
|
|
699
706
|
function assertTransportMatchesEndpoint(transport, endpoint) {
|
|
700
707
|
const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
|
|
701
708
|
if (!splitEndpoint(endpoint).path.endsWith(foreign)) return;
|
|
@@ -753,10 +760,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
753
760
|
throw new Error(overrideValidation.message);
|
|
754
761
|
}
|
|
755
762
|
endpoint = overrideValidation.endpoint;
|
|
756
|
-
|
|
757
|
-
if (autoUpgraded && path.endsWith("/chat/completions")) {
|
|
758
|
-
endpoint = `${path.slice(0, -"/chat/completions".length)}/responses${rest}`;
|
|
759
|
-
}
|
|
763
|
+
if (autoUpgraded) endpoint = endpointForTransport(endpoint, transport);
|
|
760
764
|
assertTransportMatchesEndpoint(transport, endpoint);
|
|
761
765
|
}
|
|
762
766
|
const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
|
|
@@ -1055,6 +1059,7 @@ export {
|
|
|
1055
1059
|
withInternalFlowMarker,
|
|
1056
1060
|
supportsResponseFormat,
|
|
1057
1061
|
requiresResponsesTransport,
|
|
1062
|
+
endpointForTransport,
|
|
1058
1063
|
resolvePortalAuthHeaders,
|
|
1059
1064
|
callPortalJsonCompletion,
|
|
1060
1065
|
extractJsonCandidate,
|
|
@@ -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-AU3Z7NBC.mjs";
|
|
6
8
|
import "./chunk-3RPGXXPZ.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);
|