@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.mjs
CHANGED
|
@@ -9333,6 +9333,62 @@ async function clearConversationMemoriesOp(ctx, conversationId) {
|
|
|
9333
9333
|
});
|
|
9334
9334
|
}
|
|
9335
9335
|
|
|
9336
|
+
// src/lib/db/entities/types.ts
|
|
9337
|
+
var ENTITY_KINDS = [
|
|
9338
|
+
"person",
|
|
9339
|
+
"organization",
|
|
9340
|
+
"place",
|
|
9341
|
+
"event",
|
|
9342
|
+
"product",
|
|
9343
|
+
"thing",
|
|
9344
|
+
"concept",
|
|
9345
|
+
"other"
|
|
9346
|
+
];
|
|
9347
|
+
function normalizeEntityName(name) {
|
|
9348
|
+
return name.trim().toLowerCase();
|
|
9349
|
+
}
|
|
9350
|
+
var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
|
|
9351
|
+
function topicWeight(topic) {
|
|
9352
|
+
return topic.source === "user" ? 1 : topic.weight ?? 1;
|
|
9353
|
+
}
|
|
9354
|
+
function primaryTopic(topics) {
|
|
9355
|
+
let best = null;
|
|
9356
|
+
for (const topic of topics ?? []) {
|
|
9357
|
+
if (best === null || topicWeight(topic) > topicWeight(best)) best = topic;
|
|
9358
|
+
}
|
|
9359
|
+
return best;
|
|
9360
|
+
}
|
|
9361
|
+
function parseTopicWeight(value) {
|
|
9362
|
+
return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
|
|
9363
|
+
}
|
|
9364
|
+
function serializeTopics(topics) {
|
|
9365
|
+
return JSON.stringify(topics);
|
|
9366
|
+
}
|
|
9367
|
+
function parseTopics(value) {
|
|
9368
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
9369
|
+
let parsed;
|
|
9370
|
+
try {
|
|
9371
|
+
parsed = JSON.parse(value);
|
|
9372
|
+
} catch {
|
|
9373
|
+
return null;
|
|
9374
|
+
}
|
|
9375
|
+
if (!Array.isArray(parsed)) return null;
|
|
9376
|
+
const out = [];
|
|
9377
|
+
for (const raw of parsed) {
|
|
9378
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
9379
|
+
const { name, kind, source, weight } = raw;
|
|
9380
|
+
if (typeof name !== "string" || name.trim().length === 0) continue;
|
|
9381
|
+
const parsedWeight = parseTopicWeight(weight);
|
|
9382
|
+
out.push({
|
|
9383
|
+
name,
|
|
9384
|
+
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
9385
|
+
source: source === "user" ? "user" : "auto",
|
|
9386
|
+
...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
|
|
9387
|
+
});
|
|
9388
|
+
}
|
|
9389
|
+
return out;
|
|
9390
|
+
}
|
|
9391
|
+
|
|
9336
9392
|
// src/lib/db/manager.ts
|
|
9337
9393
|
import { Database } from "@nozbe/watermelondb";
|
|
9338
9394
|
|
|
@@ -10689,48 +10745,6 @@ import { Q as Q5 } from "@nozbe/watermelondb";
|
|
|
10689
10745
|
|
|
10690
10746
|
// src/lib/db/entities/operations.ts
|
|
10691
10747
|
import { Q as Q4 } from "@nozbe/watermelondb";
|
|
10692
|
-
|
|
10693
|
-
// src/lib/db/entities/types.ts
|
|
10694
|
-
var ENTITY_KINDS = [
|
|
10695
|
-
"person",
|
|
10696
|
-
"organization",
|
|
10697
|
-
"place",
|
|
10698
|
-
"event",
|
|
10699
|
-
"product",
|
|
10700
|
-
"thing",
|
|
10701
|
-
"concept",
|
|
10702
|
-
"other"
|
|
10703
|
-
];
|
|
10704
|
-
function normalizeEntityName(name) {
|
|
10705
|
-
return name.trim().toLowerCase();
|
|
10706
|
-
}
|
|
10707
|
-
function serializeTopics(topics) {
|
|
10708
|
-
return JSON.stringify(topics);
|
|
10709
|
-
}
|
|
10710
|
-
function parseTopics(value) {
|
|
10711
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
10712
|
-
let parsed;
|
|
10713
|
-
try {
|
|
10714
|
-
parsed = JSON.parse(value);
|
|
10715
|
-
} catch {
|
|
10716
|
-
return null;
|
|
10717
|
-
}
|
|
10718
|
-
if (!Array.isArray(parsed)) return null;
|
|
10719
|
-
const out = [];
|
|
10720
|
-
for (const raw of parsed) {
|
|
10721
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
10722
|
-
const { name, kind, source } = raw;
|
|
10723
|
-
if (typeof name !== "string" || name.trim().length === 0) continue;
|
|
10724
|
-
out.push({
|
|
10725
|
-
name,
|
|
10726
|
-
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
10727
|
-
source: source === "user" ? "user" : "auto"
|
|
10728
|
-
});
|
|
10729
|
-
}
|
|
10730
|
-
return out;
|
|
10731
|
-
}
|
|
10732
|
-
|
|
10733
|
-
// src/lib/db/entities/operations.ts
|
|
10734
10748
|
function entityToStored(e) {
|
|
10735
10749
|
return {
|
|
10736
10750
|
uniqueId: e.id,
|
|
@@ -10811,21 +10825,47 @@ function displayNamesOf(entities) {
|
|
|
10811
10825
|
}
|
|
10812
10826
|
function topicsEqual(stored2, computed) {
|
|
10813
10827
|
if (stored2 === null || stored2.length !== computed.length) return false;
|
|
10814
|
-
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source]);
|
|
10828
|
+
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source, t.weight ?? null]);
|
|
10815
10829
|
const storedKeys = stored2.map(key).sort();
|
|
10816
10830
|
const computedKeys = computed.map(key).sort();
|
|
10817
10831
|
return storedKeys.every((k, i) => k === computedKeys[i]);
|
|
10818
10832
|
}
|
|
10819
|
-
function resolveTopicsWrite(row, linked,
|
|
10833
|
+
function resolveTopicsWrite(row, linked, inputs, source, mode) {
|
|
10820
10834
|
if (row === null || source === null) return null;
|
|
10821
|
-
const
|
|
10822
|
-
|
|
10835
|
+
const stored2 = parseTopics(row.topics);
|
|
10836
|
+
const carried = mode === "merge" ? stored2 : null;
|
|
10837
|
+
const weights = source === "auto" ? weightsOf(carried, inputs) : null;
|
|
10838
|
+
const displayNames = displayNamesOf([...inputs, ...carried ?? []]);
|
|
10839
|
+
const topics = topicsForEntities(linked, displayNames, source, weights);
|
|
10840
|
+
if (topicsEqual(stored2, topics)) return null;
|
|
10823
10841
|
return { row, topics };
|
|
10824
10842
|
}
|
|
10825
|
-
function
|
|
10843
|
+
function weightsOf(stored2, inputs) {
|
|
10844
|
+
const out = /* @__PURE__ */ new Map();
|
|
10845
|
+
for (const topic of stored2 ?? []) out.set(normalizeEntityName(topic.name), topic.weight);
|
|
10846
|
+
for (const input of inputs) {
|
|
10847
|
+
const weight = parseTopicWeight(input.weight);
|
|
10848
|
+
if (weight === void 0) continue;
|
|
10849
|
+
const name = normalizeEntityName(input.name);
|
|
10850
|
+
if (!out.has(name)) {
|
|
10851
|
+
out.set(name, weight);
|
|
10852
|
+
continue;
|
|
10853
|
+
}
|
|
10854
|
+
const current = out.get(name);
|
|
10855
|
+
if (current !== void 0 && weight > current) out.set(name, weight);
|
|
10856
|
+
}
|
|
10857
|
+
return out;
|
|
10858
|
+
}
|
|
10859
|
+
function topicsForEntities(entities, displayNames, source, weights) {
|
|
10826
10860
|
return entities.map((e) => {
|
|
10827
10861
|
const name = displayNames.get(e.canonicalName) ?? e.canonicalName;
|
|
10828
|
-
|
|
10862
|
+
const weight = weights?.get(e.canonicalName);
|
|
10863
|
+
return {
|
|
10864
|
+
name,
|
|
10865
|
+
...e.kind ? { kind: e.kind } : {},
|
|
10866
|
+
source,
|
|
10867
|
+
...weight !== void 0 ? { weight } : {}
|
|
10868
|
+
};
|
|
10829
10869
|
});
|
|
10830
10870
|
}
|
|
10831
10871
|
function toEntityObjects(entityInputs) {
|
|
@@ -10833,7 +10873,7 @@ function toEntityObjects(entityInputs) {
|
|
|
10833
10873
|
}
|
|
10834
10874
|
async function resolveMemoryTopicsWrite(ctx, memoryId, linked, inputs, source) {
|
|
10835
10875
|
const row = await findVaultRowInWrite(ctx, memoryId);
|
|
10836
|
-
return resolveTopicsWrite(row, linked,
|
|
10876
|
+
return resolveTopicsWrite(row, linked, toEntityObjects(inputs), source, "replace");
|
|
10837
10877
|
}
|
|
10838
10878
|
function prepareMemoryTopicsUpdate(write) {
|
|
10839
10879
|
return prepareTopicsUpdate(write.row, write.topics, Date.now());
|
|
@@ -10871,8 +10911,9 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
10871
10911
|
const topicsWrite = resolveTopicsWrite(
|
|
10872
10912
|
vaultRow,
|
|
10873
10913
|
[...carriedOver, ...entities],
|
|
10874
|
-
|
|
10875
|
-
options?.topicsSource ?? "auto"
|
|
10914
|
+
normalized,
|
|
10915
|
+
options?.topicsSource ?? "auto",
|
|
10916
|
+
"merge"
|
|
10876
10917
|
);
|
|
10877
10918
|
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && topicsWrite === null) {
|
|
10878
10919
|
return;
|
|
@@ -10938,8 +10979,9 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
|
10938
10979
|
const topicsWrite = resolveTopicsWrite(
|
|
10939
10980
|
vaultRow,
|
|
10940
10981
|
entities,
|
|
10941
|
-
|
|
10942
|
-
options.topicsSource
|
|
10982
|
+
normalized,
|
|
10983
|
+
options.topicsSource,
|
|
10984
|
+
"replace"
|
|
10943
10985
|
);
|
|
10944
10986
|
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
10945
10987
|
return;
|
|
@@ -15739,9 +15781,9 @@ For each durable fact, output:
|
|
|
15739
15781
|
- type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
|
|
15740
15782
|
- confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
|
|
15741
15783
|
- sourceMessageIds: which message IDs from the conversation contained the evidence
|
|
15742
|
-
- 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:
|
|
15743
|
-
- "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" }]
|
|
15744
|
-
- "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
|
|
15784
|
+
- 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:
|
|
15785
|
+
- "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" }]
|
|
15786
|
+
- "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" }]
|
|
15745
15787
|
- 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:
|
|
15746
15788
|
- { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
|
|
15747
15789
|
- { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
|
|
@@ -15822,9 +15864,10 @@ function restoreCandidates(candidates, redactor, ownNames) {
|
|
|
15822
15864
|
candidate: {
|
|
15823
15865
|
...c,
|
|
15824
15866
|
content: content.text,
|
|
15825
|
-
entities: c.entities.
|
|
15826
|
-
|
|
15827
|
-
|
|
15867
|
+
entities: c.entities.flatMap((e) => {
|
|
15868
|
+
const restored = redactor.restoreForStorage(e.name);
|
|
15869
|
+
return restored.unresolved ? [] : [{ ...e, name: restored.text }];
|
|
15870
|
+
})
|
|
15828
15871
|
},
|
|
15829
15872
|
unresolved: content.unresolved
|
|
15830
15873
|
};
|
|
@@ -16053,7 +16096,12 @@ function parseEntities(raw) {
|
|
|
16053
16096
|
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
16054
16097
|
if (name.length === 0 || isGenericEntityName(name)) continue;
|
|
16055
16098
|
const kind = typeof obj.kind === "string" && ENTITY_KINDS.includes(obj.kind) ? obj.kind : void 0;
|
|
16056
|
-
|
|
16099
|
+
const weight = typeof obj.salience === "string" && Object.keys(TOPIC_SALIENCE_WEIGHTS).includes(obj.salience) ? TOPIC_SALIENCE_WEIGHTS[obj.salience] : void 0;
|
|
16100
|
+
out.push({
|
|
16101
|
+
name,
|
|
16102
|
+
...kind !== void 0 ? { kind } : {},
|
|
16103
|
+
...weight !== void 0 ? { weight } : {}
|
|
16104
|
+
});
|
|
16057
16105
|
}
|
|
16058
16106
|
return out;
|
|
16059
16107
|
}
|
|
@@ -18012,7 +18060,13 @@ Include only NAMED entities, skip generic/common nouns. A topic must identify a
|
|
|
18012
18060
|
- Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
|
|
18013
18061
|
${ENTITY_KIND_GUIDELINES}
|
|
18014
18062
|
|
|
18015
|
-
|
|
18063
|
+
Give each entity a "salience" \u2014 how central it is to the memory:
|
|
18064
|
+
- "primary": the memory is ABOUT it. A memory with entities usually has exactly one primary; never more than two.
|
|
18065
|
+
- "secondary": a meaningful part of the memory, but not its subject.
|
|
18066
|
+
- "mention": named in passing; the memory would say much the same without it.
|
|
18067
|
+
Example: "Ran the Chicago Marathon with my sister Ana in 3:41" \u2192 Chicago Marathon primary, Ana secondary.
|
|
18068
|
+
|
|
18069
|
+
Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string, "salience": "primary" | "secondary" | "mention"}]}]}
|
|
18016
18070
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
18017
18071
|
- "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
|
|
18018
18072
|
No prose.`;
|
|
@@ -18083,9 +18137,10 @@ List each memory's named entities.`,
|
|
|
18083
18137
|
return out;
|
|
18084
18138
|
}
|
|
18085
18139
|
function restoreEntities(entities, redactor) {
|
|
18086
|
-
return entities.
|
|
18087
|
-
|
|
18088
|
-
|
|
18140
|
+
return entities.flatMap((e) => {
|
|
18141
|
+
const restored = redactor.restoreForStorage(e.name);
|
|
18142
|
+
return restored.unresolved ? [] : [{ ...e, name: restored.text }];
|
|
18143
|
+
});
|
|
18089
18144
|
}
|
|
18090
18145
|
function parseTopicResponse(parsed, validIds) {
|
|
18091
18146
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
@@ -18246,6 +18301,14 @@ function createLocalMemoryStore(options) {
|
|
|
18246
18301
|
getLogger().warn("[memory/store] Failed to embed memory:", err);
|
|
18247
18302
|
});
|
|
18248
18303
|
};
|
|
18304
|
+
const maintenance = {
|
|
18305
|
+
createDecaySweeper: (sweeperOptions) => createDecaySweeper({ ...sweeperOptions, vaultCtx }),
|
|
18306
|
+
getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
|
|
18307
|
+
extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
|
|
18308
|
+
stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
|
|
18309
|
+
relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
|
|
18310
|
+
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
18311
|
+
};
|
|
18249
18312
|
return {
|
|
18250
18313
|
masksEmbeddingInput: options.embeddingOptions.maskInput !== void 0,
|
|
18251
18314
|
list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
|
|
@@ -18259,21 +18322,22 @@ function createLocalMemoryStore(options) {
|
|
|
18259
18322
|
memoriesByTopics: (names) => getMemoriesByEntityNamesOp(entityCtx, names),
|
|
18260
18323
|
topicsByMemories: (memoryIds) => getEntitiesByMemoryIdsOp(entityCtx, memoryIds),
|
|
18261
18324
|
listTopics: async () => {
|
|
18262
|
-
const links = await entityCtx.memoryEntityCollection.query(...linksOwnedBy).
|
|
18325
|
+
const links = await entityCtx.memoryEntityCollection.query(...linksOwnedBy).unsafeFetchRaw();
|
|
18263
18326
|
const members = /* @__PURE__ */ new Map();
|
|
18264
18327
|
for (const link of links) {
|
|
18265
|
-
const entityId = String(link.
|
|
18328
|
+
const entityId = String(link.entity_id);
|
|
18266
18329
|
const set = members.get(entityId) ?? /* @__PURE__ */ new Set();
|
|
18267
|
-
set.add(String(link.
|
|
18330
|
+
set.add(String(link.memory_id));
|
|
18268
18331
|
members.set(entityId, set);
|
|
18269
18332
|
}
|
|
18270
|
-
const entities = userId === void 0 ? await entityCtx.entityCollection.query().
|
|
18333
|
+
const entities = userId === void 0 ? await entityCtx.entityCollection.query().unsafeFetchRaw() : members.size ? await entityCtx.entityCollection.query(Q8.where("id", Q8.oneOf([...members.keys()]))).unsafeFetchRaw() : [];
|
|
18271
18334
|
const byName = /* @__PURE__ */ new Map();
|
|
18272
18335
|
for (const entity of entities) {
|
|
18273
|
-
const
|
|
18274
|
-
const
|
|
18275
|
-
byName.
|
|
18276
|
-
|
|
18336
|
+
const name = String(entity.canonical_name);
|
|
18337
|
+
const count2 = members.get(String(entity.id))?.size ?? 0;
|
|
18338
|
+
const existing = byName.get(name);
|
|
18339
|
+
byName.set(name, {
|
|
18340
|
+
name,
|
|
18277
18341
|
kind: existing?.kind ?? entity.kind ?? null,
|
|
18278
18342
|
memoryCount: (existing?.memoryCount ?? 0) + count2
|
|
18279
18343
|
});
|
|
@@ -18364,14 +18428,8 @@ function createLocalMemoryStore(options) {
|
|
|
18364
18428
|
}
|
|
18365
18429
|
return () => subscriptions.forEach((s) => s.unsubscribe());
|
|
18366
18430
|
},
|
|
18367
|
-
maintenance
|
|
18368
|
-
|
|
18369
|
-
getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
|
|
18370
|
-
extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
|
|
18371
|
-
stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
|
|
18372
|
-
relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
|
|
18373
|
-
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
18374
|
-
}
|
|
18431
|
+
maintenance,
|
|
18432
|
+
topicMaintenance: maintenance
|
|
18375
18433
|
};
|
|
18376
18434
|
}
|
|
18377
18435
|
|
|
@@ -19155,7 +19213,7 @@ function createRemoteMemoryPipeline(options) {
|
|
|
19155
19213
|
const memory = {
|
|
19156
19214
|
...snapshot.memory,
|
|
19157
19215
|
content: patch.content,
|
|
19158
|
-
updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Date.now()
|
|
19216
|
+
updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
|
|
19159
19217
|
};
|
|
19160
19218
|
if (patch.scope !== void 0) memory.scope = patch.scope;
|
|
19161
19219
|
if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
|
|
@@ -19288,6 +19346,7 @@ function projection({ memory }) {
|
|
|
19288
19346
|
};
|
|
19289
19347
|
}
|
|
19290
19348
|
function relevant(change, options) {
|
|
19349
|
+
if (change === "metadata") return false;
|
|
19291
19350
|
return options.includeDeleted ? change === "membership" : change !== "embedding" || !!options.embeddings;
|
|
19292
19351
|
}
|
|
19293
19352
|
function safely(onChange) {
|
|
@@ -19368,17 +19427,32 @@ function topicInputs(topics, source) {
|
|
|
19368
19427
|
return topics.flatMap((topic) => {
|
|
19369
19428
|
const name = (typeof topic === "string" ? topic : topic.name).trim();
|
|
19370
19429
|
const kind = typeof topic === "string" ? void 0 : topic.kind;
|
|
19371
|
-
|
|
19430
|
+
const weight = typeof topic === "string" || source === "user" ? void 0 : parseTopicWeight(topic.weight);
|
|
19431
|
+
return name ? [{ name, ...kind && { kind }, source, ...weight && { weight } }] : [];
|
|
19372
19432
|
});
|
|
19373
19433
|
}
|
|
19374
19434
|
function mergeTopics(existing, added) {
|
|
19375
19435
|
const byName = /* @__PURE__ */ new Map();
|
|
19376
19436
|
for (const topic of [...existing, ...added]) {
|
|
19377
19437
|
const key = normalizeEntityName(topic.name);
|
|
19378
|
-
|
|
19438
|
+
const kept = byName.get(key);
|
|
19439
|
+
if (!kept) byName.set(key, topic);
|
|
19440
|
+
else if (kept.weight !== void 0 && topic.weight !== void 0 && topic.weight > kept.weight)
|
|
19441
|
+
byName.set(key, { ...kept, weight: topic.weight });
|
|
19379
19442
|
}
|
|
19380
19443
|
return [...byName.values()];
|
|
19381
19444
|
}
|
|
19445
|
+
function sameTopics(stored2, next) {
|
|
19446
|
+
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source, t.weight ?? null]);
|
|
19447
|
+
if (stored2 === null || stored2.length !== next.length) return false;
|
|
19448
|
+
const keys = new Set(stored2.map(key));
|
|
19449
|
+
return next.every((t) => keys.has(key(t)));
|
|
19450
|
+
}
|
|
19451
|
+
function extractionDue(memory) {
|
|
19452
|
+
const stamp = memory.topics_extracted_at;
|
|
19453
|
+
if (stamp === void 0 || stamp === null) return "fresh";
|
|
19454
|
+
return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
|
|
19455
|
+
}
|
|
19382
19456
|
function canonicalTopics(row) {
|
|
19383
19457
|
return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
|
|
19384
19458
|
}
|
|
@@ -19391,7 +19465,12 @@ function createRemoteMemoryStore(options) {
|
|
|
19391
19465
|
const seen = /* @__PURE__ */ new Map();
|
|
19392
19466
|
let timer;
|
|
19393
19467
|
const notify = (change, records = []) => {
|
|
19394
|
-
if (timer)
|
|
19468
|
+
if (timer)
|
|
19469
|
+
for (const record2 of records) {
|
|
19470
|
+
const previous = seen.get(record2.memory.memory_id);
|
|
19471
|
+
if ((previous?.version ?? 0) + 1 === record2.version)
|
|
19472
|
+
seen.set(record2.memory.memory_id, record2);
|
|
19473
|
+
}
|
|
19395
19474
|
for (const subscriber of [...subscribers])
|
|
19396
19475
|
if (relevant(change, subscriber.options)) safely(subscriber.onChange);
|
|
19397
19476
|
};
|
|
@@ -19452,15 +19531,72 @@ function createRemoteMemoryStore(options) {
|
|
|
19452
19531
|
const record2 = await persistence.get(id);
|
|
19453
19532
|
return record2 && !record2.memory.is_deleted ? record2 : null;
|
|
19454
19533
|
};
|
|
19455
|
-
const
|
|
19534
|
+
const mutateLocked = async (id, change, edit) => {
|
|
19456
19535
|
const record2 = await live(id);
|
|
19457
19536
|
if (!record2) return null;
|
|
19458
19537
|
const next = edit(structuredClone(record2.memory));
|
|
19459
19538
|
if (!next) return null;
|
|
19460
19539
|
const saved = await persistence.put(next, record2);
|
|
19461
|
-
notify(change, [saved]);
|
|
19540
|
+
notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
|
|
19462
19541
|
return saved;
|
|
19463
|
-
}
|
|
19542
|
+
};
|
|
19543
|
+
const mutate = (id, change, edit) => serialized(id, () => mutateLocked(id, change, edit));
|
|
19544
|
+
const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
|
|
19545
|
+
const mutateMany = async (ids, change, edit) => {
|
|
19546
|
+
const saved = /* @__PURE__ */ new Map();
|
|
19547
|
+
const failed = /* @__PURE__ */ new Map();
|
|
19548
|
+
for (const slice of chunks([...new Set(ids)], MAX_BATCH)) {
|
|
19549
|
+
await serializedAll(slice, async () => {
|
|
19550
|
+
const writes = [];
|
|
19551
|
+
let read;
|
|
19552
|
+
try {
|
|
19553
|
+
read = await readEveryState(slice);
|
|
19554
|
+
} catch (error) {
|
|
19555
|
+
for (const id of slice) failed.set(id, error);
|
|
19556
|
+
return;
|
|
19557
|
+
}
|
|
19558
|
+
const { records, unreadable } = read;
|
|
19559
|
+
for (const id of slice) {
|
|
19560
|
+
const record2 = records.get(id);
|
|
19561
|
+
if (unreadable.has(id)) failed.set(id, new Error(`Memory ${id} could not be decrypted`));
|
|
19562
|
+
else if (!record2) failed.set(id, new Error(`Memory ${id} was not found`));
|
|
19563
|
+
else if (!record2.memory.is_deleted) {
|
|
19564
|
+
const memory = edit(structuredClone(record2.memory));
|
|
19565
|
+
if (memory) writes.push({ before: record2, memory });
|
|
19566
|
+
}
|
|
19567
|
+
}
|
|
19568
|
+
if (writes.length === 0) return;
|
|
19569
|
+
try {
|
|
19570
|
+
const written = await persistence.putMany(
|
|
19571
|
+
writes.map(({ before, memory }) => ({ memory, expectedVersion: before }))
|
|
19572
|
+
);
|
|
19573
|
+
const byChange = /* @__PURE__ */ new Map();
|
|
19574
|
+
written.forEach((record2, i) => {
|
|
19575
|
+
saved.set(record2.memory.memory_id, record2);
|
|
19576
|
+
const kind = change(writes[i].before.memory);
|
|
19577
|
+
byChange.set(kind, [...byChange.get(kind) ?? [], record2]);
|
|
19578
|
+
});
|
|
19579
|
+
for (const [kind, changed] of byChange) notify(kind, changed);
|
|
19580
|
+
} catch (error) {
|
|
19581
|
+
const perRow = error instanceof RemoteMemoryError && (error.code === "version_conflict" || error.status === 429);
|
|
19582
|
+
for (const { before } of writes) {
|
|
19583
|
+
const id = before.memory.memory_id;
|
|
19584
|
+
if (!perRow) {
|
|
19585
|
+
failed.set(id, error);
|
|
19586
|
+
continue;
|
|
19587
|
+
}
|
|
19588
|
+
try {
|
|
19589
|
+
const record2 = await mutateLocked(id, change, edit);
|
|
19590
|
+
if (record2) saved.set(id, record2);
|
|
19591
|
+
} catch (rowError) {
|
|
19592
|
+
failed.set(id, rowError);
|
|
19593
|
+
}
|
|
19594
|
+
}
|
|
19595
|
+
}
|
|
19596
|
+
});
|
|
19597
|
+
}
|
|
19598
|
+
return { saved, failed };
|
|
19599
|
+
};
|
|
19464
19600
|
const embedInBackground = ({ memory: { memory_id: id, content } }) => {
|
|
19465
19601
|
generateEmbedding(content, embeddingOptions).then(
|
|
19466
19602
|
(vector) => serialized(id, async () => {
|
|
@@ -19516,11 +19652,10 @@ function createRemoteMemoryStore(options) {
|
|
|
19516
19652
|
return records.map((record2) => stored(record2.memory));
|
|
19517
19653
|
};
|
|
19518
19654
|
const asStored = (record2) => record2 ? stored(record2.memory) : null;
|
|
19519
|
-
const
|
|
19655
|
+
const knownKinds = async (topics) => {
|
|
19520
19656
|
const unkinded = [
|
|
19521
19657
|
...new Set(topics.filter((t) => !t.kind).map((t) => normalizeEntityName(t.name)))
|
|
19522
19658
|
];
|
|
19523
|
-
if (unkinded.length === 0) return topics;
|
|
19524
19659
|
const kinds = /* @__PURE__ */ new Map();
|
|
19525
19660
|
for (const names of chunks(unkinded, MAX_QUERY_TOPICS)) {
|
|
19526
19661
|
for (const { memory } of await pages(metadata, {
|
|
@@ -19535,10 +19670,136 @@ function createRemoteMemoryStore(options) {
|
|
|
19535
19670
|
}
|
|
19536
19671
|
}
|
|
19537
19672
|
}
|
|
19538
|
-
return
|
|
19673
|
+
return (topic) => {
|
|
19539
19674
|
const kind = topic.kind ?? kinds.get(normalizeEntityName(topic.name));
|
|
19540
19675
|
return kind ? { ...topic, kind } : topic;
|
|
19541
|
-
}
|
|
19676
|
+
};
|
|
19677
|
+
};
|
|
19678
|
+
const withKnownKinds = async (topics) => topics.map(await knownKinds(topics));
|
|
19679
|
+
const readEveryState = async (ids) => {
|
|
19680
|
+
const records = /* @__PURE__ */ new Map();
|
|
19681
|
+
const unreadable = /* @__PURE__ */ new Set();
|
|
19682
|
+
for (const memory_ids of chunks(ids, MAX_QUERY_IDS)) {
|
|
19683
|
+
let cursor2;
|
|
19684
|
+
do {
|
|
19685
|
+
const page = await persistence.query({
|
|
19686
|
+
memory_ids,
|
|
19687
|
+
include_deleted: true,
|
|
19688
|
+
include_archived: true,
|
|
19689
|
+
include_quarantined: true,
|
|
19690
|
+
include_superseded: true,
|
|
19691
|
+
limit: 200,
|
|
19692
|
+
...cursor2 && { cursor: cursor2 }
|
|
19693
|
+
});
|
|
19694
|
+
warnFailed(page.failed);
|
|
19695
|
+
for (const record2 of page.items) records.set(record2.memory.memory_id, record2);
|
|
19696
|
+
for (const failure of page.failed) unreadable.add(failure.memory_id);
|
|
19697
|
+
cursor2 = page.next_cursor;
|
|
19698
|
+
} while (cursor2);
|
|
19699
|
+
}
|
|
19700
|
+
return { records, unreadable };
|
|
19701
|
+
};
|
|
19702
|
+
const topicMaintenance = {
|
|
19703
|
+
getTopicBacklog: async (backlogOptions = {}) => {
|
|
19704
|
+
const stale = [];
|
|
19705
|
+
const fresh = [];
|
|
19706
|
+
for (const { memory } of await pages(metadata, { order: "created" })) {
|
|
19707
|
+
if (memory.topics_user_managed) continue;
|
|
19708
|
+
const due = extractionDue(memory);
|
|
19709
|
+
if (due === "stale") stale.push(memory.memory_id);
|
|
19710
|
+
else if (due === "fresh") fresh.push(memory.memory_id);
|
|
19711
|
+
}
|
|
19712
|
+
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
19713
|
+
return {
|
|
19714
|
+
pending: [...stale, ...fresh].slice(0, limit).map((uniqueId) => ({ uniqueId })),
|
|
19715
|
+
linkedUnstamped: [],
|
|
19716
|
+
topicsToRelink: [],
|
|
19717
|
+
topicsBackfill: []
|
|
19718
|
+
};
|
|
19719
|
+
},
|
|
19720
|
+
extractTopics: async (memoryIds, extractOptions) => {
|
|
19721
|
+
const extractedAt = extractOptions.now ?? Date.now();
|
|
19722
|
+
const skippedIds = [];
|
|
19723
|
+
const skippedReasons = /* @__PURE__ */ new Map();
|
|
19724
|
+
const skip = (id, reason) => {
|
|
19725
|
+
if (skippedReasons.has(id)) return;
|
|
19726
|
+
skippedIds.push(id);
|
|
19727
|
+
skippedReasons.set(id, reason);
|
|
19728
|
+
};
|
|
19729
|
+
const ids = [...new Set(memoryIds)];
|
|
19730
|
+
const { records, unreadable } = await readEveryState(ids);
|
|
19731
|
+
const inputs = [];
|
|
19732
|
+
for (const id of ids) {
|
|
19733
|
+
const memory = records.get(id)?.memory;
|
|
19734
|
+
if (unreadable.has(id)) skip(id, "unreadable");
|
|
19735
|
+
else if (!memory) skip(id, "not-found");
|
|
19736
|
+
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
19737
|
+
else inputs.push({ id, content: memory.content });
|
|
19738
|
+
}
|
|
19739
|
+
const entitiesByMemory = await extractEntitiesForMemories(inputs, extractOptions);
|
|
19740
|
+
const answered = /* @__PURE__ */ new Map();
|
|
19741
|
+
for (const input of inputs) {
|
|
19742
|
+
const entities = entitiesByMemory.get(input.id);
|
|
19743
|
+
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
19744
|
+
else
|
|
19745
|
+
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
19746
|
+
}
|
|
19747
|
+
const stampedIds = [];
|
|
19748
|
+
const fail = (id, reason) => {
|
|
19749
|
+
skip(id, reason);
|
|
19750
|
+
entitiesByMemory.delete(id);
|
|
19751
|
+
};
|
|
19752
|
+
try {
|
|
19753
|
+
const kinded = await knownKinds([...answered.values()].flatMap((a) => a.topics));
|
|
19754
|
+
const topics = new Map(
|
|
19755
|
+
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
19756
|
+
);
|
|
19757
|
+
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
19758
|
+
const { saved, failed } = await mutateMany(
|
|
19759
|
+
[...answered.keys()],
|
|
19760
|
+
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
19761
|
+
(memory) => {
|
|
19762
|
+
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
19763
|
+
return null;
|
|
19764
|
+
return {
|
|
19765
|
+
...memory,
|
|
19766
|
+
...!unchanged(memory) && {
|
|
19767
|
+
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
19768
|
+
topics_updated_at: Date.now()
|
|
19769
|
+
},
|
|
19770
|
+
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
19771
|
+
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
19772
|
+
};
|
|
19773
|
+
}
|
|
19774
|
+
);
|
|
19775
|
+
for (const id of answered.keys()) {
|
|
19776
|
+
if (saved.has(id)) stampedIds.push(id);
|
|
19777
|
+
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
19778
|
+
}
|
|
19779
|
+
if (failed.size)
|
|
19780
|
+
getLogger().warn(
|
|
19781
|
+
"[memory/remote-store] Failed to write extracted topics:",
|
|
19782
|
+
failed.values().next().value
|
|
19783
|
+
);
|
|
19784
|
+
} catch (error) {
|
|
19785
|
+
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
19786
|
+
for (const id of answered.keys()) fail(id, "link-failed");
|
|
19787
|
+
}
|
|
19788
|
+
return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
|
|
19789
|
+
},
|
|
19790
|
+
stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
|
|
19791
|
+
const ids = [...new Set(memoryIds)];
|
|
19792
|
+
const { saved, failed } = await mutateMany(
|
|
19793
|
+
ids,
|
|
19794
|
+
() => "metadata",
|
|
19795
|
+
(memory) => memory.topics_user_managed ? null : { ...memory, topics_extracted_at: extractedAt, topics_extracted_version: version }
|
|
19796
|
+
);
|
|
19797
|
+
if (failed.size)
|
|
19798
|
+
getLogger().warn("[memory/remote-store] Failed to stamp topics:", [...failed.values()]);
|
|
19799
|
+
return ids.filter((id) => saved.has(id));
|
|
19800
|
+
},
|
|
19801
|
+
relinkTopics: () => Promise.resolve([]),
|
|
19802
|
+
backfillTopics: () => Promise.resolve([])
|
|
19542
19803
|
};
|
|
19543
19804
|
const everyState = {
|
|
19544
19805
|
include_deleted: true,
|
|
@@ -19617,6 +19878,7 @@ function createRemoteMemoryStore(options) {
|
|
|
19617
19878
|
};
|
|
19618
19879
|
return {
|
|
19619
19880
|
masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
|
|
19881
|
+
topicMaintenance,
|
|
19620
19882
|
list: async (listOptions = {}) => (await listRows(decrypted, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
|
|
19621
19883
|
(r) => stored(r.memory)
|
|
19622
19884
|
),
|
|
@@ -19701,7 +19963,7 @@ function createRemoteMemoryStore(options) {
|
|
|
19701
19963
|
const kind = patch.kind !== void 0 ? patch.kind : memory.kind;
|
|
19702
19964
|
assertValidKindLevel(kind, level);
|
|
19703
19965
|
memory.content = patch.content;
|
|
19704
|
-
memory.updated_at = Date.now();
|
|
19966
|
+
memory.updated_at = Math.max(Date.now(), (memory.topics_extracted_at ?? 0) + 1);
|
|
19705
19967
|
if (patch.level !== void 0) {
|
|
19706
19968
|
memory.level = level;
|
|
19707
19969
|
memory.scope = scopeForLevel(level);
|
|
@@ -26093,6 +26355,7 @@ export {
|
|
|
26093
26355
|
TOOL_RESULT_ORIGIN,
|
|
26094
26356
|
TOPICS_EXTRACTION_VERSION,
|
|
26095
26357
|
TOPIC_EXTRACTION_BATCH_SIZE,
|
|
26358
|
+
TOPIC_SALIENCE_WEIGHTS,
|
|
26096
26359
|
TREND_RECENT_WINDOW_DAYS,
|
|
26097
26360
|
TREND_STALE_WINDOW_DAYS,
|
|
26098
26361
|
VAULT_SIZE_HOP_CAP,
|
|
@@ -26217,6 +26480,7 @@ export {
|
|
|
26217
26480
|
onKeyAvailable,
|
|
26218
26481
|
parseMedia,
|
|
26219
26482
|
preEmbedVaultMemories,
|
|
26483
|
+
primaryTopic,
|
|
26220
26484
|
quantizeEmbedding,
|
|
26221
26485
|
queueManager,
|
|
26222
26486
|
rankProfileCandidates,
|
|
@@ -26250,6 +26514,7 @@ export {
|
|
|
26250
26514
|
summarizeObservationTrends,
|
|
26251
26515
|
supersedeVaultMemoryOp,
|
|
26252
26516
|
synthesizeProfile,
|
|
26517
|
+
topicWeight,
|
|
26253
26518
|
traverseGraphLane,
|
|
26254
26519
|
ttlForType,
|
|
26255
26520
|
updateVaultFolderContextOp,
|
|
@@ -157,6 +157,8 @@ interface StoredTopic {
|
|
|
157
157
|
name: string;
|
|
158
158
|
kind?: string;
|
|
159
159
|
source: TopicSource;
|
|
160
|
+
/** How central the topic is to the memory, in (0, 1]; read it with {@link topicWeight}. */
|
|
161
|
+
weight?: number;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
interface EntityOperationsContext {
|