@anuma/sdk 6.39.3 → 6.41.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.
- package/dist/expo/index.cjs +360 -92
- package/dist/expo/index.d.mts +49 -2
- package/dist/expo/index.d.ts +49 -2
- package/dist/expo/index.mjs +357 -92
- package/dist/memory/context.d.mts +2 -0
- package/dist/memory/context.d.ts +2 -0
- package/dist/react/index.cjs +322 -52
- package/dist/react/index.d.mts +49 -2
- package/dist/react/index.d.ts +49 -2
- package/dist/react/index.mjs +319 -52
- package/dist/server/index.cjs +90 -40
- package/dist/server/index.d.mts +28 -2
- package/dist/server/index.d.ts +28 -2
- package/dist/server/index.mjs +90 -40
- package/package.json +1 -1
package/dist/expo/index.cjs
CHANGED
|
@@ -1415,6 +1415,7 @@ __export(index_exports, {
|
|
|
1415
1415
|
TOOL_RESULT_ORIGIN: () => TOOL_RESULT_ORIGIN,
|
|
1416
1416
|
TOPICS_EXTRACTION_VERSION: () => TOPICS_EXTRACTION_VERSION,
|
|
1417
1417
|
TOPIC_EXTRACTION_BATCH_SIZE: () => TOPIC_EXTRACTION_BATCH_SIZE,
|
|
1418
|
+
TOPIC_SALIENCE_WEIGHTS: () => TOPIC_SALIENCE_WEIGHTS,
|
|
1418
1419
|
TREND_RECENT_WINDOW_DAYS: () => TREND_RECENT_WINDOW_DAYS,
|
|
1419
1420
|
TREND_STALE_WINDOW_DAYS: () => TREND_STALE_WINDOW_DAYS,
|
|
1420
1421
|
VAULT_SIZE_HOP_CAP: () => VAULT_SIZE_HOP_CAP,
|
|
@@ -1539,6 +1540,7 @@ __export(index_exports, {
|
|
|
1539
1540
|
onKeyAvailable: () => onKeyAvailable,
|
|
1540
1541
|
parseMedia: () => parseMedia,
|
|
1541
1542
|
preEmbedVaultMemories: () => preEmbedVaultMemories,
|
|
1543
|
+
primaryTopic: () => primaryTopic,
|
|
1542
1544
|
quantizeEmbedding: () => quantizeEmbedding,
|
|
1543
1545
|
queueManager: () => queueManager,
|
|
1544
1546
|
rankProfileCandidates: () => rankProfileCandidates,
|
|
@@ -1572,6 +1574,7 @@ __export(index_exports, {
|
|
|
1572
1574
|
summarizeObservationTrends: () => summarizeObservationTrends,
|
|
1573
1575
|
supersedeVaultMemoryOp: () => supersedeVaultMemoryOp,
|
|
1574
1576
|
synthesizeProfile: () => synthesizeProfile,
|
|
1577
|
+
topicWeight: () => topicWeight,
|
|
1575
1578
|
traverseGraphLane: () => traverseGraphLane,
|
|
1576
1579
|
ttlForType: () => ttlForType,
|
|
1577
1580
|
updateVaultFolderContextOp: () => updateVaultFolderContextOp,
|
|
@@ -10921,6 +10924,62 @@ async function clearConversationMemoriesOp(ctx, conversationId) {
|
|
|
10921
10924
|
});
|
|
10922
10925
|
}
|
|
10923
10926
|
|
|
10927
|
+
// src/lib/db/entities/types.ts
|
|
10928
|
+
var ENTITY_KINDS = [
|
|
10929
|
+
"person",
|
|
10930
|
+
"organization",
|
|
10931
|
+
"place",
|
|
10932
|
+
"event",
|
|
10933
|
+
"product",
|
|
10934
|
+
"thing",
|
|
10935
|
+
"concept",
|
|
10936
|
+
"other"
|
|
10937
|
+
];
|
|
10938
|
+
function normalizeEntityName(name) {
|
|
10939
|
+
return name.trim().toLowerCase();
|
|
10940
|
+
}
|
|
10941
|
+
var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
|
|
10942
|
+
function topicWeight(topic) {
|
|
10943
|
+
return topic.source === "user" ? 1 : topic.weight ?? 1;
|
|
10944
|
+
}
|
|
10945
|
+
function primaryTopic(topics) {
|
|
10946
|
+
let best = null;
|
|
10947
|
+
for (const topic of topics ?? []) {
|
|
10948
|
+
if (best === null || topicWeight(topic) > topicWeight(best)) best = topic;
|
|
10949
|
+
}
|
|
10950
|
+
return best;
|
|
10951
|
+
}
|
|
10952
|
+
function parseTopicWeight(value) {
|
|
10953
|
+
return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
|
|
10954
|
+
}
|
|
10955
|
+
function serializeTopics(topics) {
|
|
10956
|
+
return JSON.stringify(topics);
|
|
10957
|
+
}
|
|
10958
|
+
function parseTopics(value) {
|
|
10959
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
10960
|
+
let parsed;
|
|
10961
|
+
try {
|
|
10962
|
+
parsed = JSON.parse(value);
|
|
10963
|
+
} catch {
|
|
10964
|
+
return null;
|
|
10965
|
+
}
|
|
10966
|
+
if (!Array.isArray(parsed)) return null;
|
|
10967
|
+
const out = [];
|
|
10968
|
+
for (const raw of parsed) {
|
|
10969
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
10970
|
+
const { name, kind, source, weight } = raw;
|
|
10971
|
+
if (typeof name !== "string" || name.trim().length === 0) continue;
|
|
10972
|
+
const parsedWeight = parseTopicWeight(weight);
|
|
10973
|
+
out.push({
|
|
10974
|
+
name,
|
|
10975
|
+
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
10976
|
+
source: source === "user" ? "user" : "auto",
|
|
10977
|
+
...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
|
|
10978
|
+
});
|
|
10979
|
+
}
|
|
10980
|
+
return out;
|
|
10981
|
+
}
|
|
10982
|
+
|
|
10924
10983
|
// src/lib/db/manager.ts
|
|
10925
10984
|
var import_watermelondb18 = require("@nozbe/watermelondb");
|
|
10926
10985
|
|
|
@@ -12273,48 +12332,6 @@ init_logger();
|
|
|
12273
12332
|
|
|
12274
12333
|
// src/lib/db/entities/operations.ts
|
|
12275
12334
|
var import_watermelondb19 = require("@nozbe/watermelondb");
|
|
12276
|
-
|
|
12277
|
-
// src/lib/db/entities/types.ts
|
|
12278
|
-
var ENTITY_KINDS = [
|
|
12279
|
-
"person",
|
|
12280
|
-
"organization",
|
|
12281
|
-
"place",
|
|
12282
|
-
"event",
|
|
12283
|
-
"product",
|
|
12284
|
-
"thing",
|
|
12285
|
-
"concept",
|
|
12286
|
-
"other"
|
|
12287
|
-
];
|
|
12288
|
-
function normalizeEntityName(name) {
|
|
12289
|
-
return name.trim().toLowerCase();
|
|
12290
|
-
}
|
|
12291
|
-
function serializeTopics(topics) {
|
|
12292
|
-
return JSON.stringify(topics);
|
|
12293
|
-
}
|
|
12294
|
-
function parseTopics(value) {
|
|
12295
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
12296
|
-
let parsed;
|
|
12297
|
-
try {
|
|
12298
|
-
parsed = JSON.parse(value);
|
|
12299
|
-
} catch {
|
|
12300
|
-
return null;
|
|
12301
|
-
}
|
|
12302
|
-
if (!Array.isArray(parsed)) return null;
|
|
12303
|
-
const out = [];
|
|
12304
|
-
for (const raw of parsed) {
|
|
12305
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
12306
|
-
const { name, kind, source } = raw;
|
|
12307
|
-
if (typeof name !== "string" || name.trim().length === 0) continue;
|
|
12308
|
-
out.push({
|
|
12309
|
-
name,
|
|
12310
|
-
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
12311
|
-
source: source === "user" ? "user" : "auto"
|
|
12312
|
-
});
|
|
12313
|
-
}
|
|
12314
|
-
return out;
|
|
12315
|
-
}
|
|
12316
|
-
|
|
12317
|
-
// src/lib/db/entities/operations.ts
|
|
12318
12335
|
function entityToStored(e) {
|
|
12319
12336
|
return {
|
|
12320
12337
|
uniqueId: e.id,
|
|
@@ -12395,21 +12412,47 @@ function displayNamesOf(entities) {
|
|
|
12395
12412
|
}
|
|
12396
12413
|
function topicsEqual(stored2, computed) {
|
|
12397
12414
|
if (stored2 === null || stored2.length !== computed.length) return false;
|
|
12398
|
-
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source]);
|
|
12415
|
+
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source, t.weight ?? null]);
|
|
12399
12416
|
const storedKeys = stored2.map(key).sort();
|
|
12400
12417
|
const computedKeys = computed.map(key).sort();
|
|
12401
12418
|
return storedKeys.every((k, i) => k === computedKeys[i]);
|
|
12402
12419
|
}
|
|
12403
|
-
function resolveTopicsWrite(row, linked,
|
|
12420
|
+
function resolveTopicsWrite(row, linked, inputs, source, mode) {
|
|
12404
12421
|
if (row === null || source === null) return null;
|
|
12405
|
-
const
|
|
12406
|
-
|
|
12422
|
+
const stored2 = parseTopics(row.topics);
|
|
12423
|
+
const carried = mode === "merge" ? stored2 : null;
|
|
12424
|
+
const weights = source === "auto" ? weightsOf(carried, inputs) : null;
|
|
12425
|
+
const displayNames = displayNamesOf([...inputs, ...carried ?? []]);
|
|
12426
|
+
const topics = topicsForEntities(linked, displayNames, source, weights);
|
|
12427
|
+
if (topicsEqual(stored2, topics)) return null;
|
|
12407
12428
|
return { row, topics };
|
|
12408
12429
|
}
|
|
12409
|
-
function
|
|
12430
|
+
function weightsOf(stored2, inputs) {
|
|
12431
|
+
const out = /* @__PURE__ */ new Map();
|
|
12432
|
+
for (const topic of stored2 ?? []) out.set(normalizeEntityName(topic.name), topic.weight);
|
|
12433
|
+
for (const input of inputs) {
|
|
12434
|
+
const weight = parseTopicWeight(input.weight);
|
|
12435
|
+
if (weight === void 0) continue;
|
|
12436
|
+
const name = normalizeEntityName(input.name);
|
|
12437
|
+
if (!out.has(name)) {
|
|
12438
|
+
out.set(name, weight);
|
|
12439
|
+
continue;
|
|
12440
|
+
}
|
|
12441
|
+
const current = out.get(name);
|
|
12442
|
+
if (current !== void 0 && weight > current) out.set(name, weight);
|
|
12443
|
+
}
|
|
12444
|
+
return out;
|
|
12445
|
+
}
|
|
12446
|
+
function topicsForEntities(entities, displayNames, source, weights) {
|
|
12410
12447
|
return entities.map((e) => {
|
|
12411
12448
|
const name = displayNames.get(e.canonicalName) ?? e.canonicalName;
|
|
12412
|
-
|
|
12449
|
+
const weight = weights?.get(e.canonicalName);
|
|
12450
|
+
return {
|
|
12451
|
+
name,
|
|
12452
|
+
...e.kind ? { kind: e.kind } : {},
|
|
12453
|
+
source,
|
|
12454
|
+
...weight !== void 0 ? { weight } : {}
|
|
12455
|
+
};
|
|
12413
12456
|
});
|
|
12414
12457
|
}
|
|
12415
12458
|
function toEntityObjects(entityInputs) {
|
|
@@ -12417,7 +12460,7 @@ function toEntityObjects(entityInputs) {
|
|
|
12417
12460
|
}
|
|
12418
12461
|
async function resolveMemoryTopicsWrite(ctx, memoryId, linked, inputs, source) {
|
|
12419
12462
|
const row = await findVaultRowInWrite(ctx, memoryId);
|
|
12420
|
-
return resolveTopicsWrite(row, linked,
|
|
12463
|
+
return resolveTopicsWrite(row, linked, toEntityObjects(inputs), source, "replace");
|
|
12421
12464
|
}
|
|
12422
12465
|
function prepareMemoryTopicsUpdate(write) {
|
|
12423
12466
|
return prepareTopicsUpdate(write.row, write.topics, Date.now());
|
|
@@ -12455,8 +12498,9 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
12455
12498
|
const topicsWrite = resolveTopicsWrite(
|
|
12456
12499
|
vaultRow,
|
|
12457
12500
|
[...carriedOver, ...entities],
|
|
12458
|
-
|
|
12459
|
-
options?.topicsSource ?? "auto"
|
|
12501
|
+
normalized,
|
|
12502
|
+
options?.topicsSource ?? "auto",
|
|
12503
|
+
"merge"
|
|
12460
12504
|
);
|
|
12461
12505
|
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && topicsWrite === null) {
|
|
12462
12506
|
return;
|
|
@@ -12522,8 +12566,9 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
|
12522
12566
|
const topicsWrite = resolveTopicsWrite(
|
|
12523
12567
|
vaultRow,
|
|
12524
12568
|
entities,
|
|
12525
|
-
|
|
12526
|
-
options.topicsSource
|
|
12569
|
+
normalized,
|
|
12570
|
+
options.topicsSource,
|
|
12571
|
+
"replace"
|
|
12527
12572
|
);
|
|
12528
12573
|
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
12529
12574
|
return;
|
|
@@ -17348,9 +17393,9 @@ For each durable fact, output:
|
|
|
17348
17393
|
- type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
|
|
17349
17394
|
- confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
|
|
17350
17395
|
- sourceMessageIds: which message IDs from the conversation contained the evidence
|
|
17351
|
-
- entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. ${ENTITY_KIND_GUIDELINES} Examples:
|
|
17352
|
-
- "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
|
|
17353
|
-
- "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
|
|
17396
|
+
- entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other", "salience": one of "primary" | "secondary" | "mention" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. ${ENTITY_KIND_GUIDELINES} Salience is how central the entity is to the fact: "primary" = the fact is ABOUT it (usually exactly one per fact), "secondary" = a meaningful part of it, "mention" = named in passing. Examples:
|
|
17397
|
+
- "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization", "salience": "primary" }, { "name": "Haskell", "kind": "concept", "salience": "secondary" }]
|
|
17398
|
+
- "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event", "salience": "primary" }, { "name": "Whoop", "kind": "product", "salience": "mention" }]
|
|
17354
17399
|
- eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
|
|
17355
17400
|
- { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
|
|
17356
17401
|
- { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
|
|
@@ -17431,9 +17476,10 @@ function restoreCandidates(candidates, redactor, ownNames) {
|
|
|
17431
17476
|
candidate: {
|
|
17432
17477
|
...c,
|
|
17433
17478
|
content: content.text,
|
|
17434
|
-
entities: c.entities.
|
|
17435
|
-
|
|
17436
|
-
|
|
17479
|
+
entities: c.entities.flatMap((e) => {
|
|
17480
|
+
const restored = redactor.restoreForStorage(e.name);
|
|
17481
|
+
return restored.unresolved ? [] : [{ ...e, name: restored.text }];
|
|
17482
|
+
})
|
|
17437
17483
|
},
|
|
17438
17484
|
unresolved: content.unresolved
|
|
17439
17485
|
};
|
|
@@ -17662,7 +17708,12 @@ function parseEntities(raw) {
|
|
|
17662
17708
|
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
17663
17709
|
if (name.length === 0 || isGenericEntityName(name)) continue;
|
|
17664
17710
|
const kind = typeof obj.kind === "string" && ENTITY_KINDS.includes(obj.kind) ? obj.kind : void 0;
|
|
17665
|
-
|
|
17711
|
+
const weight = typeof obj.salience === "string" && Object.keys(TOPIC_SALIENCE_WEIGHTS).includes(obj.salience) ? TOPIC_SALIENCE_WEIGHTS[obj.salience] : void 0;
|
|
17712
|
+
out.push({
|
|
17713
|
+
name,
|
|
17714
|
+
...kind !== void 0 ? { kind } : {},
|
|
17715
|
+
...weight !== void 0 ? { weight } : {}
|
|
17716
|
+
});
|
|
17666
17717
|
}
|
|
17667
17718
|
return out;
|
|
17668
17719
|
}
|
|
@@ -19642,7 +19693,13 @@ Include only NAMED entities, skip generic/common nouns. A topic must identify a
|
|
|
19642
19693
|
- Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
|
|
19643
19694
|
${ENTITY_KIND_GUIDELINES}
|
|
19644
19695
|
|
|
19645
|
-
|
|
19696
|
+
Give each entity a "salience" \u2014 how central it is to the memory:
|
|
19697
|
+
- "primary": the memory is ABOUT it. A memory with entities usually has exactly one primary; never more than two.
|
|
19698
|
+
- "secondary": a meaningful part of the memory, but not its subject.
|
|
19699
|
+
- "mention": named in passing; the memory would say much the same without it.
|
|
19700
|
+
Example: "Ran the Chicago Marathon with my sister Ana in 3:41" \u2192 Chicago Marathon primary, Ana secondary.
|
|
19701
|
+
|
|
19702
|
+
Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string, "salience": "primary" | "secondary" | "mention"}]}]}
|
|
19646
19703
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
19647
19704
|
- "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
|
|
19648
19705
|
No prose.`;
|
|
@@ -19713,9 +19770,10 @@ List each memory's named entities.`,
|
|
|
19713
19770
|
return out;
|
|
19714
19771
|
}
|
|
19715
19772
|
function restoreEntities(entities, redactor) {
|
|
19716
|
-
return entities.
|
|
19717
|
-
|
|
19718
|
-
|
|
19773
|
+
return entities.flatMap((e) => {
|
|
19774
|
+
const restored = redactor.restoreForStorage(e.name);
|
|
19775
|
+
return restored.unresolved ? [] : [{ ...e, name: restored.text }];
|
|
19776
|
+
});
|
|
19719
19777
|
}
|
|
19720
19778
|
function parseTopicResponse(parsed, validIds) {
|
|
19721
19779
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
@@ -19876,6 +19934,14 @@ function createLocalMemoryStore(options) {
|
|
|
19876
19934
|
getLogger().warn("[memory/store] Failed to embed memory:", err);
|
|
19877
19935
|
});
|
|
19878
19936
|
};
|
|
19937
|
+
const maintenance = {
|
|
19938
|
+
createDecaySweeper: (sweeperOptions) => createDecaySweeper({ ...sweeperOptions, vaultCtx }),
|
|
19939
|
+
getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
|
|
19940
|
+
extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
|
|
19941
|
+
stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
|
|
19942
|
+
relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
|
|
19943
|
+
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
19944
|
+
};
|
|
19879
19945
|
return {
|
|
19880
19946
|
masksEmbeddingInput: options.embeddingOptions.maskInput !== void 0,
|
|
19881
19947
|
list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
|
|
@@ -19889,21 +19955,22 @@ function createLocalMemoryStore(options) {
|
|
|
19889
19955
|
memoriesByTopics: (names) => getMemoriesByEntityNamesOp(entityCtx, names),
|
|
19890
19956
|
topicsByMemories: (memoryIds) => getEntitiesByMemoryIdsOp(entityCtx, memoryIds),
|
|
19891
19957
|
listTopics: async () => {
|
|
19892
|
-
const links = await entityCtx.memoryEntityCollection.query(...linksOwnedBy).
|
|
19958
|
+
const links = await entityCtx.memoryEntityCollection.query(...linksOwnedBy).unsafeFetchRaw();
|
|
19893
19959
|
const members = /* @__PURE__ */ new Map();
|
|
19894
19960
|
for (const link of links) {
|
|
19895
|
-
const entityId = String(link.
|
|
19961
|
+
const entityId = String(link.entity_id);
|
|
19896
19962
|
const set = members.get(entityId) ?? /* @__PURE__ */ new Set();
|
|
19897
|
-
set.add(String(link.
|
|
19963
|
+
set.add(String(link.memory_id));
|
|
19898
19964
|
members.set(entityId, set);
|
|
19899
19965
|
}
|
|
19900
|
-
const entities = userId === void 0 ? await entityCtx.entityCollection.query().
|
|
19966
|
+
const entities = userId === void 0 ? await entityCtx.entityCollection.query().unsafeFetchRaw() : members.size ? await entityCtx.entityCollection.query(import_watermelondb23.Q.where("id", import_watermelondb23.Q.oneOf([...members.keys()]))).unsafeFetchRaw() : [];
|
|
19901
19967
|
const byName = /* @__PURE__ */ new Map();
|
|
19902
19968
|
for (const entity of entities) {
|
|
19903
|
-
const
|
|
19904
|
-
const
|
|
19905
|
-
byName.
|
|
19906
|
-
|
|
19969
|
+
const name = String(entity.canonical_name);
|
|
19970
|
+
const count2 = members.get(String(entity.id))?.size ?? 0;
|
|
19971
|
+
const existing = byName.get(name);
|
|
19972
|
+
byName.set(name, {
|
|
19973
|
+
name,
|
|
19907
19974
|
kind: existing?.kind ?? entity.kind ?? null,
|
|
19908
19975
|
memoryCount: (existing?.memoryCount ?? 0) + count2
|
|
19909
19976
|
});
|
|
@@ -19994,14 +20061,8 @@ function createLocalMemoryStore(options) {
|
|
|
19994
20061
|
}
|
|
19995
20062
|
return () => subscriptions.forEach((s) => s.unsubscribe());
|
|
19996
20063
|
},
|
|
19997
|
-
maintenance
|
|
19998
|
-
|
|
19999
|
-
getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
|
|
20000
|
-
extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
|
|
20001
|
-
stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
|
|
20002
|
-
relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
|
|
20003
|
-
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
20004
|
-
}
|
|
20064
|
+
maintenance,
|
|
20065
|
+
topicMaintenance: maintenance
|
|
20005
20066
|
};
|
|
20006
20067
|
}
|
|
20007
20068
|
|
|
@@ -20786,7 +20847,7 @@ function createRemoteMemoryPipeline(options) {
|
|
|
20786
20847
|
const memory = {
|
|
20787
20848
|
...snapshot.memory,
|
|
20788
20849
|
content: patch.content,
|
|
20789
|
-
updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Date.now()
|
|
20850
|
+
updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
|
|
20790
20851
|
};
|
|
20791
20852
|
if (patch.scope !== void 0) memory.scope = patch.scope;
|
|
20792
20853
|
if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
|
|
@@ -20920,6 +20981,7 @@ function projection({ memory }) {
|
|
|
20920
20981
|
};
|
|
20921
20982
|
}
|
|
20922
20983
|
function relevant(change, options) {
|
|
20984
|
+
if (change === "metadata") return false;
|
|
20923
20985
|
return options.includeDeleted ? change === "membership" : change !== "embedding" || !!options.embeddings;
|
|
20924
20986
|
}
|
|
20925
20987
|
function safely(onChange) {
|
|
@@ -21000,17 +21062,32 @@ function topicInputs(topics, source) {
|
|
|
21000
21062
|
return topics.flatMap((topic) => {
|
|
21001
21063
|
const name = (typeof topic === "string" ? topic : topic.name).trim();
|
|
21002
21064
|
const kind = typeof topic === "string" ? void 0 : topic.kind;
|
|
21003
|
-
|
|
21065
|
+
const weight = typeof topic === "string" || source === "user" ? void 0 : parseTopicWeight(topic.weight);
|
|
21066
|
+
return name ? [{ name, ...kind && { kind }, source, ...weight && { weight } }] : [];
|
|
21004
21067
|
});
|
|
21005
21068
|
}
|
|
21006
21069
|
function mergeTopics(existing, added) {
|
|
21007
21070
|
const byName = /* @__PURE__ */ new Map();
|
|
21008
21071
|
for (const topic of [...existing, ...added]) {
|
|
21009
21072
|
const key = normalizeEntityName(topic.name);
|
|
21010
|
-
|
|
21073
|
+
const kept = byName.get(key);
|
|
21074
|
+
if (!kept) byName.set(key, topic);
|
|
21075
|
+
else if (kept.weight !== void 0 && topic.weight !== void 0 && topic.weight > kept.weight)
|
|
21076
|
+
byName.set(key, { ...kept, weight: topic.weight });
|
|
21011
21077
|
}
|
|
21012
21078
|
return [...byName.values()];
|
|
21013
21079
|
}
|
|
21080
|
+
function sameTopics(stored2, next) {
|
|
21081
|
+
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source, t.weight ?? null]);
|
|
21082
|
+
if (stored2 === null || stored2.length !== next.length) return false;
|
|
21083
|
+
const keys = new Set(stored2.map(key));
|
|
21084
|
+
return next.every((t) => keys.has(key(t)));
|
|
21085
|
+
}
|
|
21086
|
+
function extractionDue(memory) {
|
|
21087
|
+
const stamp = memory.topics_extracted_at;
|
|
21088
|
+
if (stamp === void 0 || stamp === null) return "fresh";
|
|
21089
|
+
return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
|
|
21090
|
+
}
|
|
21014
21091
|
function canonicalTopics(row) {
|
|
21015
21092
|
return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
|
|
21016
21093
|
}
|
|
@@ -21023,7 +21100,12 @@ function createRemoteMemoryStore(options) {
|
|
|
21023
21100
|
const seen = /* @__PURE__ */ new Map();
|
|
21024
21101
|
let timer;
|
|
21025
21102
|
const notify = (change, records = []) => {
|
|
21026
|
-
if (timer)
|
|
21103
|
+
if (timer)
|
|
21104
|
+
for (const record2 of records) {
|
|
21105
|
+
const previous = seen.get(record2.memory.memory_id);
|
|
21106
|
+
if ((previous?.version ?? 0) + 1 === record2.version)
|
|
21107
|
+
seen.set(record2.memory.memory_id, record2);
|
|
21108
|
+
}
|
|
21027
21109
|
for (const subscriber of [...subscribers])
|
|
21028
21110
|
if (relevant(change, subscriber.options)) safely(subscriber.onChange);
|
|
21029
21111
|
};
|
|
@@ -21084,15 +21166,72 @@ function createRemoteMemoryStore(options) {
|
|
|
21084
21166
|
const record2 = await persistence.get(id);
|
|
21085
21167
|
return record2 && !record2.memory.is_deleted ? record2 : null;
|
|
21086
21168
|
};
|
|
21087
|
-
const
|
|
21169
|
+
const mutateLocked = async (id, change, edit) => {
|
|
21088
21170
|
const record2 = await live(id);
|
|
21089
21171
|
if (!record2) return null;
|
|
21090
21172
|
const next = edit(structuredClone(record2.memory));
|
|
21091
21173
|
if (!next) return null;
|
|
21092
21174
|
const saved = await persistence.put(next, record2);
|
|
21093
|
-
notify(change, [saved]);
|
|
21175
|
+
notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
|
|
21094
21176
|
return saved;
|
|
21095
|
-
}
|
|
21177
|
+
};
|
|
21178
|
+
const mutate = (id, change, edit) => serialized(id, () => mutateLocked(id, change, edit));
|
|
21179
|
+
const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
|
|
21180
|
+
const mutateMany = async (ids, change, edit) => {
|
|
21181
|
+
const saved = /* @__PURE__ */ new Map();
|
|
21182
|
+
const failed = /* @__PURE__ */ new Map();
|
|
21183
|
+
for (const slice of chunks([...new Set(ids)], MAX_BATCH)) {
|
|
21184
|
+
await serializedAll(slice, async () => {
|
|
21185
|
+
const writes = [];
|
|
21186
|
+
let read;
|
|
21187
|
+
try {
|
|
21188
|
+
read = await readEveryState(slice);
|
|
21189
|
+
} catch (error) {
|
|
21190
|
+
for (const id of slice) failed.set(id, error);
|
|
21191
|
+
return;
|
|
21192
|
+
}
|
|
21193
|
+
const { records, unreadable } = read;
|
|
21194
|
+
for (const id of slice) {
|
|
21195
|
+
const record2 = records.get(id);
|
|
21196
|
+
if (unreadable.has(id)) failed.set(id, new Error(`Memory ${id} could not be decrypted`));
|
|
21197
|
+
else if (!record2) failed.set(id, new Error(`Memory ${id} was not found`));
|
|
21198
|
+
else if (!record2.memory.is_deleted) {
|
|
21199
|
+
const memory = edit(structuredClone(record2.memory));
|
|
21200
|
+
if (memory) writes.push({ before: record2, memory });
|
|
21201
|
+
}
|
|
21202
|
+
}
|
|
21203
|
+
if (writes.length === 0) return;
|
|
21204
|
+
try {
|
|
21205
|
+
const written = await persistence.putMany(
|
|
21206
|
+
writes.map(({ before, memory }) => ({ memory, expectedVersion: before }))
|
|
21207
|
+
);
|
|
21208
|
+
const byChange = /* @__PURE__ */ new Map();
|
|
21209
|
+
written.forEach((record2, i) => {
|
|
21210
|
+
saved.set(record2.memory.memory_id, record2);
|
|
21211
|
+
const kind = change(writes[i].before.memory);
|
|
21212
|
+
byChange.set(kind, [...byChange.get(kind) ?? [], record2]);
|
|
21213
|
+
});
|
|
21214
|
+
for (const [kind, changed] of byChange) notify(kind, changed);
|
|
21215
|
+
} catch (error) {
|
|
21216
|
+
const perRow = error instanceof RemoteMemoryError && (error.code === "version_conflict" || error.status === 429);
|
|
21217
|
+
for (const { before } of writes) {
|
|
21218
|
+
const id = before.memory.memory_id;
|
|
21219
|
+
if (!perRow) {
|
|
21220
|
+
failed.set(id, error);
|
|
21221
|
+
continue;
|
|
21222
|
+
}
|
|
21223
|
+
try {
|
|
21224
|
+
const record2 = await mutateLocked(id, change, edit);
|
|
21225
|
+
if (record2) saved.set(id, record2);
|
|
21226
|
+
} catch (rowError) {
|
|
21227
|
+
failed.set(id, rowError);
|
|
21228
|
+
}
|
|
21229
|
+
}
|
|
21230
|
+
}
|
|
21231
|
+
});
|
|
21232
|
+
}
|
|
21233
|
+
return { saved, failed };
|
|
21234
|
+
};
|
|
21096
21235
|
const embedInBackground = ({ memory: { memory_id: id, content } }) => {
|
|
21097
21236
|
generateEmbedding(content, embeddingOptions).then(
|
|
21098
21237
|
(vector) => serialized(id, async () => {
|
|
@@ -21148,11 +21287,10 @@ function createRemoteMemoryStore(options) {
|
|
|
21148
21287
|
return records.map((record2) => stored(record2.memory));
|
|
21149
21288
|
};
|
|
21150
21289
|
const asStored = (record2) => record2 ? stored(record2.memory) : null;
|
|
21151
|
-
const
|
|
21290
|
+
const knownKinds = async (topics) => {
|
|
21152
21291
|
const unkinded = [
|
|
21153
21292
|
...new Set(topics.filter((t) => !t.kind).map((t) => normalizeEntityName(t.name)))
|
|
21154
21293
|
];
|
|
21155
|
-
if (unkinded.length === 0) return topics;
|
|
21156
21294
|
const kinds = /* @__PURE__ */ new Map();
|
|
21157
21295
|
for (const names of chunks(unkinded, MAX_QUERY_TOPICS)) {
|
|
21158
21296
|
for (const { memory } of await pages(metadata, {
|
|
@@ -21167,10 +21305,136 @@ function createRemoteMemoryStore(options) {
|
|
|
21167
21305
|
}
|
|
21168
21306
|
}
|
|
21169
21307
|
}
|
|
21170
|
-
return
|
|
21308
|
+
return (topic) => {
|
|
21171
21309
|
const kind = topic.kind ?? kinds.get(normalizeEntityName(topic.name));
|
|
21172
21310
|
return kind ? { ...topic, kind } : topic;
|
|
21173
|
-
}
|
|
21311
|
+
};
|
|
21312
|
+
};
|
|
21313
|
+
const withKnownKinds = async (topics) => topics.map(await knownKinds(topics));
|
|
21314
|
+
const readEveryState = async (ids) => {
|
|
21315
|
+
const records = /* @__PURE__ */ new Map();
|
|
21316
|
+
const unreadable = /* @__PURE__ */ new Set();
|
|
21317
|
+
for (const memory_ids of chunks(ids, MAX_QUERY_IDS)) {
|
|
21318
|
+
let cursor2;
|
|
21319
|
+
do {
|
|
21320
|
+
const page = await persistence.query({
|
|
21321
|
+
memory_ids,
|
|
21322
|
+
include_deleted: true,
|
|
21323
|
+
include_archived: true,
|
|
21324
|
+
include_quarantined: true,
|
|
21325
|
+
include_superseded: true,
|
|
21326
|
+
limit: 200,
|
|
21327
|
+
...cursor2 && { cursor: cursor2 }
|
|
21328
|
+
});
|
|
21329
|
+
warnFailed(page.failed);
|
|
21330
|
+
for (const record2 of page.items) records.set(record2.memory.memory_id, record2);
|
|
21331
|
+
for (const failure of page.failed) unreadable.add(failure.memory_id);
|
|
21332
|
+
cursor2 = page.next_cursor;
|
|
21333
|
+
} while (cursor2);
|
|
21334
|
+
}
|
|
21335
|
+
return { records, unreadable };
|
|
21336
|
+
};
|
|
21337
|
+
const topicMaintenance = {
|
|
21338
|
+
getTopicBacklog: async (backlogOptions = {}) => {
|
|
21339
|
+
const stale = [];
|
|
21340
|
+
const fresh = [];
|
|
21341
|
+
for (const { memory } of await pages(metadata, { order: "created" })) {
|
|
21342
|
+
if (memory.topics_user_managed) continue;
|
|
21343
|
+
const due = extractionDue(memory);
|
|
21344
|
+
if (due === "stale") stale.push(memory.memory_id);
|
|
21345
|
+
else if (due === "fresh") fresh.push(memory.memory_id);
|
|
21346
|
+
}
|
|
21347
|
+
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
21348
|
+
return {
|
|
21349
|
+
pending: [...stale, ...fresh].slice(0, limit).map((uniqueId) => ({ uniqueId })),
|
|
21350
|
+
linkedUnstamped: [],
|
|
21351
|
+
topicsToRelink: [],
|
|
21352
|
+
topicsBackfill: []
|
|
21353
|
+
};
|
|
21354
|
+
},
|
|
21355
|
+
extractTopics: async (memoryIds, extractOptions) => {
|
|
21356
|
+
const extractedAt = extractOptions.now ?? Date.now();
|
|
21357
|
+
const skippedIds = [];
|
|
21358
|
+
const skippedReasons = /* @__PURE__ */ new Map();
|
|
21359
|
+
const skip = (id, reason) => {
|
|
21360
|
+
if (skippedReasons.has(id)) return;
|
|
21361
|
+
skippedIds.push(id);
|
|
21362
|
+
skippedReasons.set(id, reason);
|
|
21363
|
+
};
|
|
21364
|
+
const ids = [...new Set(memoryIds)];
|
|
21365
|
+
const { records, unreadable } = await readEveryState(ids);
|
|
21366
|
+
const inputs = [];
|
|
21367
|
+
for (const id of ids) {
|
|
21368
|
+
const memory = records.get(id)?.memory;
|
|
21369
|
+
if (unreadable.has(id)) skip(id, "unreadable");
|
|
21370
|
+
else if (!memory) skip(id, "not-found");
|
|
21371
|
+
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
21372
|
+
else inputs.push({ id, content: memory.content });
|
|
21373
|
+
}
|
|
21374
|
+
const entitiesByMemory = await extractEntitiesForMemories(inputs, extractOptions);
|
|
21375
|
+
const answered = /* @__PURE__ */ new Map();
|
|
21376
|
+
for (const input of inputs) {
|
|
21377
|
+
const entities = entitiesByMemory.get(input.id);
|
|
21378
|
+
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
21379
|
+
else
|
|
21380
|
+
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
21381
|
+
}
|
|
21382
|
+
const stampedIds = [];
|
|
21383
|
+
const fail = (id, reason) => {
|
|
21384
|
+
skip(id, reason);
|
|
21385
|
+
entitiesByMemory.delete(id);
|
|
21386
|
+
};
|
|
21387
|
+
try {
|
|
21388
|
+
const kinded = await knownKinds([...answered.values()].flatMap((a) => a.topics));
|
|
21389
|
+
const topics = new Map(
|
|
21390
|
+
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
21391
|
+
);
|
|
21392
|
+
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
21393
|
+
const { saved, failed } = await mutateMany(
|
|
21394
|
+
[...answered.keys()],
|
|
21395
|
+
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
21396
|
+
(memory) => {
|
|
21397
|
+
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
21398
|
+
return null;
|
|
21399
|
+
return {
|
|
21400
|
+
...memory,
|
|
21401
|
+
...!unchanged(memory) && {
|
|
21402
|
+
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
21403
|
+
topics_updated_at: Date.now()
|
|
21404
|
+
},
|
|
21405
|
+
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
21406
|
+
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
21407
|
+
};
|
|
21408
|
+
}
|
|
21409
|
+
);
|
|
21410
|
+
for (const id of answered.keys()) {
|
|
21411
|
+
if (saved.has(id)) stampedIds.push(id);
|
|
21412
|
+
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
21413
|
+
}
|
|
21414
|
+
if (failed.size)
|
|
21415
|
+
getLogger().warn(
|
|
21416
|
+
"[memory/remote-store] Failed to write extracted topics:",
|
|
21417
|
+
failed.values().next().value
|
|
21418
|
+
);
|
|
21419
|
+
} catch (error) {
|
|
21420
|
+
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
21421
|
+
for (const id of answered.keys()) fail(id, "link-failed");
|
|
21422
|
+
}
|
|
21423
|
+
return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
|
|
21424
|
+
},
|
|
21425
|
+
stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
|
|
21426
|
+
const ids = [...new Set(memoryIds)];
|
|
21427
|
+
const { saved, failed } = await mutateMany(
|
|
21428
|
+
ids,
|
|
21429
|
+
() => "metadata",
|
|
21430
|
+
(memory) => memory.topics_user_managed ? null : { ...memory, topics_extracted_at: extractedAt, topics_extracted_version: version }
|
|
21431
|
+
);
|
|
21432
|
+
if (failed.size)
|
|
21433
|
+
getLogger().warn("[memory/remote-store] Failed to stamp topics:", [...failed.values()]);
|
|
21434
|
+
return ids.filter((id) => saved.has(id));
|
|
21435
|
+
},
|
|
21436
|
+
relinkTopics: () => Promise.resolve([]),
|
|
21437
|
+
backfillTopics: () => Promise.resolve([])
|
|
21174
21438
|
};
|
|
21175
21439
|
const everyState = {
|
|
21176
21440
|
include_deleted: true,
|
|
@@ -21249,6 +21513,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21249
21513
|
};
|
|
21250
21514
|
return {
|
|
21251
21515
|
masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
|
|
21516
|
+
topicMaintenance,
|
|
21252
21517
|
list: async (listOptions = {}) => (await listRows(decrypted, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
|
|
21253
21518
|
(r) => stored(r.memory)
|
|
21254
21519
|
),
|
|
@@ -21333,7 +21598,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21333
21598
|
const kind = patch.kind !== void 0 ? patch.kind : memory.kind;
|
|
21334
21599
|
assertValidKindLevel(kind, level);
|
|
21335
21600
|
memory.content = patch.content;
|
|
21336
|
-
memory.updated_at = Date.now();
|
|
21601
|
+
memory.updated_at = Math.max(Date.now(), (memory.topics_extracted_at ?? 0) + 1);
|
|
21337
21602
|
if (patch.level !== void 0) {
|
|
21338
21603
|
memory.level = level;
|
|
21339
21604
|
memory.scope = scopeForLevel(level);
|
|
@@ -27751,6 +28016,7 @@ function useChatStorage(options) {
|
|
|
27751
28016
|
TOOL_RESULT_ORIGIN,
|
|
27752
28017
|
TOPICS_EXTRACTION_VERSION,
|
|
27753
28018
|
TOPIC_EXTRACTION_BATCH_SIZE,
|
|
28019
|
+
TOPIC_SALIENCE_WEIGHTS,
|
|
27754
28020
|
TREND_RECENT_WINDOW_DAYS,
|
|
27755
28021
|
TREND_STALE_WINDOW_DAYS,
|
|
27756
28022
|
VAULT_SIZE_HOP_CAP,
|
|
@@ -27875,6 +28141,7 @@ function useChatStorage(options) {
|
|
|
27875
28141
|
onKeyAvailable,
|
|
27876
28142
|
parseMedia,
|
|
27877
28143
|
preEmbedVaultMemories,
|
|
28144
|
+
primaryTopic,
|
|
27878
28145
|
quantizeEmbedding,
|
|
27879
28146
|
queueManager,
|
|
27880
28147
|
rankProfileCandidates,
|
|
@@ -27908,6 +28175,7 @@ function useChatStorage(options) {
|
|
|
27908
28175
|
summarizeObservationTrends,
|
|
27909
28176
|
supersedeVaultMemoryOp,
|
|
27910
28177
|
synthesizeProfile,
|
|
28178
|
+
topicWeight,
|
|
27911
28179
|
traverseGraphLane,
|
|
27912
28180
|
ttlForType,
|
|
27913
28181
|
updateVaultFolderContextOp,
|