@anuma/sdk 3.8.3 → 3.8.5
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-IFZZQPRO.mjs → chunk-42Y42CAZ.mjs} +60 -40
- package/dist/expo/index.cjs +228 -46
- package/dist/expo/index.d.mts +7 -1
- package/dist/expo/index.d.ts +7 -1
- package/dist/expo/index.mjs +170 -8
- package/dist/expo/{recall-MUFOZB6P.mjs → recall-JFZ3Z6EX.mjs} +1 -1
- package/dist/react/{chunk-35BHTCK4.mjs → chunk-NLVSQAYH.mjs} +60 -40
- package/dist/react/index.cjs +228 -46
- package/dist/react/index.d.mts +15 -4
- package/dist/react/index.d.ts +15 -4
- package/dist/react/index.mjs +170 -8
- package/dist/react/{recall-JUERXQA7.mjs → recall-BCEH7U2Z.mjs} +1 -1
- package/dist/server/{chunk-GKN5NJKZ.mjs → chunk-FS56IDOW.mjs} +60 -40
- package/dist/server/index.cjs +228 -46
- package/dist/server/index.d.mts +7 -1
- package/dist/server/index.d.ts +7 -1
- package/dist/server/index.mjs +170 -8
- package/dist/server/{recall-XZ6WLFPJ.mjs → recall-VDMDNQNB.mjs} +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -16797,7 +16797,7 @@ function entityToStored(e) {
|
|
|
16797
16797
|
updatedAt: e.updatedAt
|
|
16798
16798
|
};
|
|
16799
16799
|
}
|
|
16800
|
-
async function
|
|
16800
|
+
async function upsertEntitiesInWrite(ctx, entities) {
|
|
16801
16801
|
const kindByName = /* @__PURE__ */ new Map();
|
|
16802
16802
|
for (const e of entities) {
|
|
16803
16803
|
const name = normalizeEntityName(e.name);
|
|
@@ -16811,60 +16811,62 @@ async function upsertEntitiesOp(ctx, entities) {
|
|
|
16811
16811
|
}
|
|
16812
16812
|
const unique = Array.from(kindByName.keys());
|
|
16813
16813
|
const out = /* @__PURE__ */ new Map();
|
|
16814
|
-
if (unique.length === 0) return out;
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
const
|
|
16819
|
-
|
|
16820
|
-
return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
|
|
16821
|
-
});
|
|
16822
|
-
const missing = unique.filter((n) => !existingNames.has(n));
|
|
16823
|
-
const created = missing.map(
|
|
16824
|
-
(name) => ctx.entityCollection.prepareCreate((record) => {
|
|
16825
|
-
record._setRaw("canonical_name", name);
|
|
16826
|
-
const kind = kindByName.get(name);
|
|
16827
|
-
if (kind !== void 0) record._setRaw("kind", kind);
|
|
16828
|
-
})
|
|
16829
|
-
);
|
|
16830
|
-
const updated = updates.map(
|
|
16831
|
-
(e) => e.prepareUpdate((record) => {
|
|
16832
|
-
record._setRaw("kind", kindByName.get(e.canonicalName));
|
|
16833
|
-
})
|
|
16834
|
-
);
|
|
16835
|
-
if (created.length > 0 || updated.length > 0) {
|
|
16836
|
-
await ctx.database.batch(...created, ...updated);
|
|
16837
|
-
}
|
|
16838
|
-
for (const e of existing) out.set(e.canonicalName, entityToStored(e));
|
|
16839
|
-
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
16840
|
-
return out;
|
|
16814
|
+
if (unique.length === 0) return { entities: out, operations: [] };
|
|
16815
|
+
const existing = await ctx.entityCollection.query(import_watermelondb16.Q.where("canonical_name", import_watermelondb16.Q.oneOf(unique))).fetch();
|
|
16816
|
+
const existingNames = new Set(existing.map((e) => e.canonicalName));
|
|
16817
|
+
const updates = existing.filter((e) => {
|
|
16818
|
+
const incoming = kindByName.get(e.canonicalName);
|
|
16819
|
+
return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
|
|
16841
16820
|
});
|
|
16821
|
+
const missing = unique.filter((n) => !existingNames.has(n));
|
|
16822
|
+
const created = missing.map(
|
|
16823
|
+
(name) => ctx.entityCollection.prepareCreate((record) => {
|
|
16824
|
+
record._setRaw("canonical_name", name);
|
|
16825
|
+
const kind = kindByName.get(name);
|
|
16826
|
+
if (kind !== void 0) record._setRaw("kind", kind);
|
|
16827
|
+
})
|
|
16828
|
+
);
|
|
16829
|
+
const updated = updates.map(
|
|
16830
|
+
(e) => e.prepareUpdate((record) => {
|
|
16831
|
+
record._setRaw("kind", kindByName.get(e.canonicalName));
|
|
16832
|
+
})
|
|
16833
|
+
);
|
|
16834
|
+
for (const e of existing) out.set(e.canonicalName, entityToStored(e));
|
|
16835
|
+
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
16836
|
+
return { entities: out, operations: [...created, ...updated] };
|
|
16842
16837
|
}
|
|
16843
16838
|
async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
16844
16839
|
if (entityInputs.length === 0) return [];
|
|
16845
16840
|
const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
16846
|
-
const byName = await upsertEntitiesOp(ctx, normalized);
|
|
16847
|
-
const entities = Array.from(byName.values());
|
|
16848
|
-
if (entities.length === 0) return [];
|
|
16849
16841
|
const userId = ctx.userId;
|
|
16850
16842
|
let skipped = false;
|
|
16843
|
+
let entities = [];
|
|
16851
16844
|
await ctx.database.write(async () => {
|
|
16845
|
+
const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
|
|
16846
|
+
ctx,
|
|
16847
|
+
normalized
|
|
16848
|
+
);
|
|
16849
|
+
entities = Array.from(byName.values());
|
|
16852
16850
|
if (options?.unlessTopicsUserManaged && await autoLinkBlocked(ctx, memoryId)) {
|
|
16851
|
+
if (entityOps.length > 0) {
|
|
16852
|
+
await ctx.database.batch(...entityOps);
|
|
16853
|
+
}
|
|
16853
16854
|
skipped = true;
|
|
16854
16855
|
return;
|
|
16855
16856
|
}
|
|
16857
|
+
if (entities.length === 0) return;
|
|
16856
16858
|
const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb16.Q.where("memory_id", memoryId)).fetch();
|
|
16857
16859
|
const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
|
|
16858
16860
|
const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
|
|
16859
|
-
if (toCreate.length === 0) return;
|
|
16860
|
-
const
|
|
16861
|
+
if (entityOps.length === 0 && toCreate.length === 0) return;
|
|
16862
|
+
const linkOps = toCreate.map(
|
|
16861
16863
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
16862
16864
|
record._setRaw("memory_id", memoryId);
|
|
16863
16865
|
record._setRaw("entity_id", e.uniqueId);
|
|
16864
16866
|
if (userId !== void 0) record._setRaw("user_id", userId);
|
|
16865
16867
|
})
|
|
16866
16868
|
);
|
|
16867
|
-
await ctx.database.batch(...
|
|
16869
|
+
await ctx.database.batch(...entityOps, ...linkOps);
|
|
16868
16870
|
});
|
|
16869
16871
|
return skipped ? [] : entities;
|
|
16870
16872
|
}
|
|
@@ -16879,22 +16881,28 @@ async function autoLinkBlocked(ctx, memoryId) {
|
|
|
16879
16881
|
}
|
|
16880
16882
|
async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
16881
16883
|
const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
16882
|
-
const byName = await upsertEntitiesOp(ctx, normalized);
|
|
16883
|
-
const entities = Array.from(byName.values());
|
|
16884
16884
|
const userId = ctx.userId;
|
|
16885
16885
|
let skipped = false;
|
|
16886
|
+
let entities = [];
|
|
16886
16887
|
await ctx.database.write(async () => {
|
|
16887
16888
|
if (await autoLinkBlocked(ctx, memoryId)) {
|
|
16888
16889
|
skipped = true;
|
|
16889
16890
|
return;
|
|
16890
16891
|
}
|
|
16892
|
+
const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
|
|
16893
|
+
ctx,
|
|
16894
|
+
normalized
|
|
16895
|
+
);
|
|
16896
|
+
entities = Array.from(byName.values());
|
|
16891
16897
|
const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb16.Q.where("memory_id", memoryId)).fetch();
|
|
16892
16898
|
const keep = new Set(entities.map((e) => e.uniqueId));
|
|
16893
16899
|
const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
|
|
16894
16900
|
const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
|
|
16895
16901
|
const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
|
|
16896
|
-
if (toCreate.length === 0 && toDestroy.length === 0) return;
|
|
16902
|
+
if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0) return;
|
|
16903
|
+
const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
|
|
16897
16904
|
await ctx.database.batch(
|
|
16905
|
+
...entityOps,
|
|
16898
16906
|
...toCreate.map(
|
|
16899
16907
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
16900
16908
|
record._setRaw("memory_id", memoryId);
|
|
@@ -16902,11 +16910,23 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
|
16902
16910
|
if (userId !== void 0) record._setRaw("user_id", userId);
|
|
16903
16911
|
})
|
|
16904
16912
|
),
|
|
16905
|
-
...toDestroy.map((l) => l.prepareDestroyPermanently())
|
|
16913
|
+
...toDestroy.map((l) => l.prepareDestroyPermanently()),
|
|
16914
|
+
...orphans.map((e) => e.prepareDestroyPermanently())
|
|
16906
16915
|
);
|
|
16907
16916
|
});
|
|
16908
16917
|
return skipped ? null : entities;
|
|
16909
16918
|
}
|
|
16919
|
+
async function findOrphanedEntities(ctx, memoryId, toDestroy) {
|
|
16920
|
+
const candidateIds = [...new Set(toDestroy.map((l) => String(l.entityId)))];
|
|
16921
|
+
if (candidateIds.length === 0) return [];
|
|
16922
|
+
const links = await ctx.memoryEntityCollection.query(import_watermelondb16.Q.where("entity_id", import_watermelondb16.Q.oneOf(candidateIds))).fetch();
|
|
16923
|
+
const stillLinked = new Set(
|
|
16924
|
+
links.filter((l) => String(l.memoryId) !== memoryId).map((l) => String(l.entityId))
|
|
16925
|
+
);
|
|
16926
|
+
const orphanIds = candidateIds.filter((id) => !stillLinked.has(id));
|
|
16927
|
+
if (orphanIds.length === 0) return [];
|
|
16928
|
+
return await ctx.entityCollection.query(import_watermelondb16.Q.where("id", import_watermelondb16.Q.oneOf(orphanIds))).fetch();
|
|
16929
|
+
}
|
|
16910
16930
|
async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
|
|
16911
16931
|
if (memoryIds.length === 0) return;
|
|
16912
16932
|
const links = await ctx.memoryEntityCollection.query(import_watermelondb16.Q.where("memory_id", import_watermelondb16.Q.oneOf(Array.from(memoryIds)))).fetch();
|
|
@@ -17764,7 +17784,7 @@ var init_operations3 = __esm({
|
|
|
17764
17784
|
init_operations2();
|
|
17765
17785
|
init_encryption2();
|
|
17766
17786
|
KNOWN_TRUST_TIERS = /* @__PURE__ */ new Set(["quarantined", "trusted"]);
|
|
17767
|
-
TOPICS_EXTRACTION_VERSION =
|
|
17787
|
+
TOPICS_EXTRACTION_VERSION = 3;
|
|
17768
17788
|
}
|
|
17769
17789
|
});
|
|
17770
17790
|
|
|
@@ -26900,6 +26920,162 @@ init_types3();
|
|
|
26900
26920
|
init_logger();
|
|
26901
26921
|
init_redactor();
|
|
26902
26922
|
|
|
26923
|
+
// src/lib/memory/entitySalience.ts
|
|
26924
|
+
var GENERIC_ENTITY_NAMES = /* @__PURE__ */ new Set([
|
|
26925
|
+
// Relative and absolute time
|
|
26926
|
+
"today",
|
|
26927
|
+
"tomorrow",
|
|
26928
|
+
"yesterday",
|
|
26929
|
+
"tonight",
|
|
26930
|
+
"morning",
|
|
26931
|
+
"afternoon",
|
|
26932
|
+
"evening",
|
|
26933
|
+
"night",
|
|
26934
|
+
"day",
|
|
26935
|
+
"date",
|
|
26936
|
+
"time",
|
|
26937
|
+
"week",
|
|
26938
|
+
"weekend",
|
|
26939
|
+
"weekday",
|
|
26940
|
+
"month",
|
|
26941
|
+
"year",
|
|
26942
|
+
"monday",
|
|
26943
|
+
"tuesday",
|
|
26944
|
+
"wednesday",
|
|
26945
|
+
"thursday",
|
|
26946
|
+
"friday",
|
|
26947
|
+
"saturday",
|
|
26948
|
+
"sunday",
|
|
26949
|
+
"january",
|
|
26950
|
+
"february",
|
|
26951
|
+
"march",
|
|
26952
|
+
"april",
|
|
26953
|
+
"may",
|
|
26954
|
+
"june",
|
|
26955
|
+
"july",
|
|
26956
|
+
"august",
|
|
26957
|
+
"september",
|
|
26958
|
+
"october",
|
|
26959
|
+
"november",
|
|
26960
|
+
"december",
|
|
26961
|
+
// Generic containers and venues — the named ones ("Blue Bottle on Valencia",
|
|
26962
|
+
// "Stanford") are unaffected because they are not bare tokens.
|
|
26963
|
+
"home",
|
|
26964
|
+
"house",
|
|
26965
|
+
"apartment",
|
|
26966
|
+
"room",
|
|
26967
|
+
"office",
|
|
26968
|
+
"work",
|
|
26969
|
+
"workplace",
|
|
26970
|
+
"school",
|
|
26971
|
+
"gym",
|
|
26972
|
+
"store",
|
|
26973
|
+
"shop",
|
|
26974
|
+
"hospital",
|
|
26975
|
+
"airport",
|
|
26976
|
+
"hotel",
|
|
26977
|
+
"city",
|
|
26978
|
+
"town",
|
|
26979
|
+
"country",
|
|
26980
|
+
"place",
|
|
26981
|
+
// Generic calendar entries — "Chicago Marathon" survives, "meeting" doesn't.
|
|
26982
|
+
"meeting",
|
|
26983
|
+
"call",
|
|
26984
|
+
"appointment",
|
|
26985
|
+
"reminder",
|
|
26986
|
+
"event",
|
|
26987
|
+
"birthday",
|
|
26988
|
+
"breakfast",
|
|
26989
|
+
"lunch",
|
|
26990
|
+
"dinner",
|
|
26991
|
+
"brunch",
|
|
26992
|
+
"vacation",
|
|
26993
|
+
"holiday",
|
|
26994
|
+
"trip",
|
|
26995
|
+
// Plurals are listed explicitly rather than stemmed: naive `s`-stripping
|
|
26996
|
+
// would fold real titles into generics ("The Times" → "time"). Only plurals
|
|
26997
|
+
// that are junk as topics in their own right belong here — note the absence
|
|
26998
|
+
// of "times", "places", and anything else that reads as a name.
|
|
26999
|
+
"days",
|
|
27000
|
+
"weeks",
|
|
27001
|
+
"weekends",
|
|
27002
|
+
"months",
|
|
27003
|
+
"years",
|
|
27004
|
+
"rooms",
|
|
27005
|
+
"offices",
|
|
27006
|
+
"houses",
|
|
27007
|
+
"apartments",
|
|
27008
|
+
"stores",
|
|
27009
|
+
"shops",
|
|
27010
|
+
"hotels",
|
|
27011
|
+
"meetings",
|
|
27012
|
+
"calls",
|
|
27013
|
+
"appointments",
|
|
27014
|
+
"reminders",
|
|
27015
|
+
"events",
|
|
27016
|
+
"birthdays",
|
|
27017
|
+
"trips",
|
|
27018
|
+
"vacations",
|
|
27019
|
+
"holidays",
|
|
27020
|
+
// Meta / filler the models occasionally echo back as an entity
|
|
27021
|
+
"user",
|
|
27022
|
+
"assistant",
|
|
27023
|
+
"memory",
|
|
27024
|
+
"memories",
|
|
27025
|
+
"note",
|
|
27026
|
+
"notes",
|
|
27027
|
+
"thing",
|
|
27028
|
+
"things",
|
|
27029
|
+
"stuff",
|
|
27030
|
+
"something",
|
|
27031
|
+
"someone",
|
|
27032
|
+
"anything",
|
|
27033
|
+
"everything",
|
|
27034
|
+
"none",
|
|
27035
|
+
"other",
|
|
27036
|
+
"misc",
|
|
27037
|
+
"general",
|
|
27038
|
+
"unknown"
|
|
27039
|
+
]);
|
|
27040
|
+
function isGenericEntityName(name) {
|
|
27041
|
+
const tokens = name.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
|
|
27042
|
+
if (tokens.length === 0) return true;
|
|
27043
|
+
const distinctive = tokens.filter(
|
|
27044
|
+
(token) => !GENERIC_ENTITY_NAMES.has(token) && !ARTICLES.has(token) && !WEAK_MODIFIERS.has(token)
|
|
27045
|
+
);
|
|
27046
|
+
if (distinctive.length > 0) return false;
|
|
27047
|
+
const generic = tokens.filter((token) => GENERIC_ENTITY_NAMES.has(token));
|
|
27048
|
+
if (generic.length === 0) return true;
|
|
27049
|
+
const titleShaped = generic.length === 1 && tokens.some((token) => ARTICLES.has(token)) && !tokens.some((token) => WEAK_MODIFIERS.has(token));
|
|
27050
|
+
return !titleShaped;
|
|
27051
|
+
}
|
|
27052
|
+
var ARTICLES = /* @__PURE__ */ new Set(["the", "a", "an"]);
|
|
27053
|
+
var WEAK_MODIFIERS = /* @__PURE__ */ new Set([
|
|
27054
|
+
"my",
|
|
27055
|
+
"our",
|
|
27056
|
+
"their",
|
|
27057
|
+
"his",
|
|
27058
|
+
"her",
|
|
27059
|
+
"its",
|
|
27060
|
+
"this",
|
|
27061
|
+
"that",
|
|
27062
|
+
"next",
|
|
27063
|
+
"last",
|
|
27064
|
+
"upcoming",
|
|
27065
|
+
"every",
|
|
27066
|
+
"at",
|
|
27067
|
+
"in",
|
|
27068
|
+
"on",
|
|
27069
|
+
"of",
|
|
27070
|
+
"for",
|
|
27071
|
+
"with",
|
|
27072
|
+
"from",
|
|
27073
|
+
"to",
|
|
27074
|
+
"by",
|
|
27075
|
+
"and",
|
|
27076
|
+
"or"
|
|
27077
|
+
]);
|
|
27078
|
+
|
|
26903
27079
|
// src/lib/memory/injectionClassifier.ts
|
|
26904
27080
|
init_logger();
|
|
26905
27081
|
init_redactor();
|
|
@@ -27840,13 +28016,13 @@ function parseEntities(raw) {
|
|
|
27840
28016
|
for (const e of raw) {
|
|
27841
28017
|
if (typeof e === "string") {
|
|
27842
28018
|
const name2 = e.trim();
|
|
27843
|
-
if (name2.length > 0) out.push({ name: name2 });
|
|
28019
|
+
if (name2.length > 0 && !isGenericEntityName(name2)) out.push({ name: name2 });
|
|
27844
28020
|
continue;
|
|
27845
28021
|
}
|
|
27846
28022
|
if (typeof e !== "object" || e === null) continue;
|
|
27847
28023
|
const obj = e;
|
|
27848
28024
|
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
27849
|
-
if (name.length === 0) continue;
|
|
28025
|
+
if (name.length === 0 || isGenericEntityName(name)) continue;
|
|
27850
28026
|
const kind = typeof obj.kind === "string" && ENTITY_KINDS.includes(obj.kind) ? obj.kind : void 0;
|
|
27851
28027
|
out.push(kind !== void 0 ? { name, kind } : { name });
|
|
27852
28028
|
}
|
|
@@ -28068,9 +28244,11 @@ function createAutoExtractor(options) {
|
|
|
28068
28244
|
}
|
|
28069
28245
|
}
|
|
28070
28246
|
);
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
28247
|
+
if (outcome !== "empty-after-retry") {
|
|
28248
|
+
const advancedTo = window2[window2.length - 1].id;
|
|
28249
|
+
stateFor(conversationId).watermark = advancedTo;
|
|
28250
|
+
persistCursor(conversationId, messages, advancedTo, contiguous);
|
|
28251
|
+
}
|
|
28074
28252
|
for (let i = 0; i < results.length; i++) {
|
|
28075
28253
|
if (results[i].action === "suppressed") continue;
|
|
28076
28254
|
options.onMemoryExtracted?.({
|
|
@@ -29188,7 +29366,11 @@ var SYSTEM_PROMPT6 = `You assign topics to saved memories for a personal memory
|
|
|
29188
29366
|
|
|
29189
29367
|
Each memory is a short statement about the user. For each memory, list the NAMED entities it mentions \u2014 these become the memory's topics, used to connect related memories in a knowledge graph.
|
|
29190
29368
|
|
|
29191
|
-
Include only NAMED entities, skip generic/common nouns.
|
|
29369
|
+
Include only NAMED entities, skip generic/common nouns. A topic must identify a specific thing the user could recognize by name; if it would be equally true of thousands of unrelated memories, leave it out:
|
|
29370
|
+
- SKIP bare generic nouns even when the memory capitalizes them or a calendar entry is titled that way \u2014 "Home", "Work", "Meeting", "Lunch", "Birthday", "Appointment", "Trip", "Today", "Friday", "March" are NOT topics.
|
|
29371
|
+
- KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
|
|
29372
|
+
- Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
|
|
29373
|
+
${ENTITY_KIND_GUIDELINES}
|
|
29192
29374
|
|
|
29193
29375
|
Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
|
|
29194
29376
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
@@ -29200,7 +29382,7 @@ async function extractEntitiesForMemories(memories, options) {
|
|
|
29200
29382
|
const redactor = resolvePiiRedactor(options.piiRedaction);
|
|
29201
29383
|
const vocabulary = (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES).map((n) => redactor ? redactor.redactText(n).text : n);
|
|
29202
29384
|
const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
|
|
29203
|
-
When a memory refers to one of these, reuse the EXACT existing name (same spelling and
|
|
29385
|
+
When a memory refers to one of these, reuse the EXACT existing name (same spelling, casing, spacing and punctuation) instead of a variant \u2014 if "Foo.ai" is listed, never emit "Foo ai" or "Foo AI", and if "Jane Roe" is listed, emit that rather than a bare "Jane".
|
|
29204
29386
|
|
|
29205
29387
|
` : "";
|
|
29206
29388
|
for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
|
package/dist/server/index.d.mts
CHANGED
|
@@ -5805,7 +5805,7 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
5805
5805
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
5806
5806
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
5807
5807
|
*/
|
|
5808
|
-
declare const TOPICS_EXTRACTION_VERSION =
|
|
5808
|
+
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
5809
5809
|
/**
|
|
5810
5810
|
* Result of {@link getMemoriesNeedingTopicExtractionOp}: which memories the
|
|
5811
5811
|
* background topic worker should run LLM entity extraction on, and which it
|
|
@@ -6943,6 +6943,12 @@ declare function createMemoryVaultTool(vaultCtx: VaultMemoryOperationsContext, o
|
|
|
6943
6943
|
* during a busy window are still examined instead of scrolling out of a last-N
|
|
6944
6944
|
* window.
|
|
6945
6945
|
*
|
|
6946
|
+
* The watermark advances only when the extractor genuinely EXAMINED the window.
|
|
6947
|
+
* A pipeline throw and an `empty-after-retry` outcome (the extraction LLM
|
|
6948
|
+
* returned empty/malformed after exhausting its retries) both leave it in place,
|
|
6949
|
+
* so the next turn's window re-covers those messages instead of stranding them.
|
|
6950
|
+
* A quiet turn that legitimately yielded no facts *does* advance it.
|
|
6951
|
+
*
|
|
6946
6952
|
* Durability: the in-memory watermark alone does NOT survive process death, so
|
|
6947
6953
|
* a session killed after messages accumulate but before extraction would, on
|
|
6948
6954
|
* the next launch, fall back to a trailing-window guess and could skip the
|
package/dist/server/index.d.ts
CHANGED
|
@@ -5805,7 +5805,7 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
5805
5805
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
5806
5806
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
5807
5807
|
*/
|
|
5808
|
-
declare const TOPICS_EXTRACTION_VERSION =
|
|
5808
|
+
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
5809
5809
|
/**
|
|
5810
5810
|
* Result of {@link getMemoriesNeedingTopicExtractionOp}: which memories the
|
|
5811
5811
|
* background topic worker should run LLM entity extraction on, and which it
|
|
@@ -6943,6 +6943,12 @@ declare function createMemoryVaultTool(vaultCtx: VaultMemoryOperationsContext, o
|
|
|
6943
6943
|
* during a busy window are still examined instead of scrolling out of a last-N
|
|
6944
6944
|
* window.
|
|
6945
6945
|
*
|
|
6946
|
+
* The watermark advances only when the extractor genuinely EXAMINED the window.
|
|
6947
|
+
* A pipeline throw and an `empty-after-retry` outcome (the extraction LLM
|
|
6948
|
+
* returned empty/malformed after exhausting its retries) both leave it in place,
|
|
6949
|
+
* so the next turn's window re-covers those messages instead of stranding them.
|
|
6950
|
+
* A quiet turn that legitimately yielded no facts *does* advance it.
|
|
6951
|
+
*
|
|
6946
6952
|
* Durability: the in-memory watermark alone does NOT survive process death, so
|
|
6947
6953
|
* a session killed after messages accumulate but before extraction would, on
|
|
6948
6954
|
* the next launch, fall back to a trailing-window guess and could skip the
|
package/dist/server/index.mjs
CHANGED
|
@@ -125,7 +125,7 @@ import {
|
|
|
125
125
|
updateVaultMemoryOp,
|
|
126
126
|
upsertMessageOp,
|
|
127
127
|
vaultMemoryToStored
|
|
128
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-FS56IDOW.mjs";
|
|
129
129
|
import {
|
|
130
130
|
createStreamingDeAnonymizer,
|
|
131
131
|
resolvePiiRedactor
|
|
@@ -4831,6 +4831,162 @@ function createMemoryVaultTool(vaultCtx, options, embeddingOptions, cache) {
|
|
|
4831
4831
|
// src/lib/memory/autoExtract.ts
|
|
4832
4832
|
var import_watermelondb20 = __toESM(require_watermelondb());
|
|
4833
4833
|
|
|
4834
|
+
// src/lib/memory/entitySalience.ts
|
|
4835
|
+
var GENERIC_ENTITY_NAMES = /* @__PURE__ */ new Set([
|
|
4836
|
+
// Relative and absolute time
|
|
4837
|
+
"today",
|
|
4838
|
+
"tomorrow",
|
|
4839
|
+
"yesterday",
|
|
4840
|
+
"tonight",
|
|
4841
|
+
"morning",
|
|
4842
|
+
"afternoon",
|
|
4843
|
+
"evening",
|
|
4844
|
+
"night",
|
|
4845
|
+
"day",
|
|
4846
|
+
"date",
|
|
4847
|
+
"time",
|
|
4848
|
+
"week",
|
|
4849
|
+
"weekend",
|
|
4850
|
+
"weekday",
|
|
4851
|
+
"month",
|
|
4852
|
+
"year",
|
|
4853
|
+
"monday",
|
|
4854
|
+
"tuesday",
|
|
4855
|
+
"wednesday",
|
|
4856
|
+
"thursday",
|
|
4857
|
+
"friday",
|
|
4858
|
+
"saturday",
|
|
4859
|
+
"sunday",
|
|
4860
|
+
"january",
|
|
4861
|
+
"february",
|
|
4862
|
+
"march",
|
|
4863
|
+
"april",
|
|
4864
|
+
"may",
|
|
4865
|
+
"june",
|
|
4866
|
+
"july",
|
|
4867
|
+
"august",
|
|
4868
|
+
"september",
|
|
4869
|
+
"october",
|
|
4870
|
+
"november",
|
|
4871
|
+
"december",
|
|
4872
|
+
// Generic containers and venues — the named ones ("Blue Bottle on Valencia",
|
|
4873
|
+
// "Stanford") are unaffected because they are not bare tokens.
|
|
4874
|
+
"home",
|
|
4875
|
+
"house",
|
|
4876
|
+
"apartment",
|
|
4877
|
+
"room",
|
|
4878
|
+
"office",
|
|
4879
|
+
"work",
|
|
4880
|
+
"workplace",
|
|
4881
|
+
"school",
|
|
4882
|
+
"gym",
|
|
4883
|
+
"store",
|
|
4884
|
+
"shop",
|
|
4885
|
+
"hospital",
|
|
4886
|
+
"airport",
|
|
4887
|
+
"hotel",
|
|
4888
|
+
"city",
|
|
4889
|
+
"town",
|
|
4890
|
+
"country",
|
|
4891
|
+
"place",
|
|
4892
|
+
// Generic calendar entries — "Chicago Marathon" survives, "meeting" doesn't.
|
|
4893
|
+
"meeting",
|
|
4894
|
+
"call",
|
|
4895
|
+
"appointment",
|
|
4896
|
+
"reminder",
|
|
4897
|
+
"event",
|
|
4898
|
+
"birthday",
|
|
4899
|
+
"breakfast",
|
|
4900
|
+
"lunch",
|
|
4901
|
+
"dinner",
|
|
4902
|
+
"brunch",
|
|
4903
|
+
"vacation",
|
|
4904
|
+
"holiday",
|
|
4905
|
+
"trip",
|
|
4906
|
+
// Plurals are listed explicitly rather than stemmed: naive `s`-stripping
|
|
4907
|
+
// would fold real titles into generics ("The Times" → "time"). Only plurals
|
|
4908
|
+
// that are junk as topics in their own right belong here — note the absence
|
|
4909
|
+
// of "times", "places", and anything else that reads as a name.
|
|
4910
|
+
"days",
|
|
4911
|
+
"weeks",
|
|
4912
|
+
"weekends",
|
|
4913
|
+
"months",
|
|
4914
|
+
"years",
|
|
4915
|
+
"rooms",
|
|
4916
|
+
"offices",
|
|
4917
|
+
"houses",
|
|
4918
|
+
"apartments",
|
|
4919
|
+
"stores",
|
|
4920
|
+
"shops",
|
|
4921
|
+
"hotels",
|
|
4922
|
+
"meetings",
|
|
4923
|
+
"calls",
|
|
4924
|
+
"appointments",
|
|
4925
|
+
"reminders",
|
|
4926
|
+
"events",
|
|
4927
|
+
"birthdays",
|
|
4928
|
+
"trips",
|
|
4929
|
+
"vacations",
|
|
4930
|
+
"holidays",
|
|
4931
|
+
// Meta / filler the models occasionally echo back as an entity
|
|
4932
|
+
"user",
|
|
4933
|
+
"assistant",
|
|
4934
|
+
"memory",
|
|
4935
|
+
"memories",
|
|
4936
|
+
"note",
|
|
4937
|
+
"notes",
|
|
4938
|
+
"thing",
|
|
4939
|
+
"things",
|
|
4940
|
+
"stuff",
|
|
4941
|
+
"something",
|
|
4942
|
+
"someone",
|
|
4943
|
+
"anything",
|
|
4944
|
+
"everything",
|
|
4945
|
+
"none",
|
|
4946
|
+
"other",
|
|
4947
|
+
"misc",
|
|
4948
|
+
"general",
|
|
4949
|
+
"unknown"
|
|
4950
|
+
]);
|
|
4951
|
+
function isGenericEntityName(name) {
|
|
4952
|
+
const tokens = name.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
|
|
4953
|
+
if (tokens.length === 0) return true;
|
|
4954
|
+
const distinctive = tokens.filter(
|
|
4955
|
+
(token) => !GENERIC_ENTITY_NAMES.has(token) && !ARTICLES.has(token) && !WEAK_MODIFIERS.has(token)
|
|
4956
|
+
);
|
|
4957
|
+
if (distinctive.length > 0) return false;
|
|
4958
|
+
const generic = tokens.filter((token) => GENERIC_ENTITY_NAMES.has(token));
|
|
4959
|
+
if (generic.length === 0) return true;
|
|
4960
|
+
const titleShaped = generic.length === 1 && tokens.some((token) => ARTICLES.has(token)) && !tokens.some((token) => WEAK_MODIFIERS.has(token));
|
|
4961
|
+
return !titleShaped;
|
|
4962
|
+
}
|
|
4963
|
+
var ARTICLES = /* @__PURE__ */ new Set(["the", "a", "an"]);
|
|
4964
|
+
var WEAK_MODIFIERS = /* @__PURE__ */ new Set([
|
|
4965
|
+
"my",
|
|
4966
|
+
"our",
|
|
4967
|
+
"their",
|
|
4968
|
+
"his",
|
|
4969
|
+
"her",
|
|
4970
|
+
"its",
|
|
4971
|
+
"this",
|
|
4972
|
+
"that",
|
|
4973
|
+
"next",
|
|
4974
|
+
"last",
|
|
4975
|
+
"upcoming",
|
|
4976
|
+
"every",
|
|
4977
|
+
"at",
|
|
4978
|
+
"in",
|
|
4979
|
+
"on",
|
|
4980
|
+
"of",
|
|
4981
|
+
"for",
|
|
4982
|
+
"with",
|
|
4983
|
+
"from",
|
|
4984
|
+
"to",
|
|
4985
|
+
"by",
|
|
4986
|
+
"and",
|
|
4987
|
+
"or"
|
|
4988
|
+
]);
|
|
4989
|
+
|
|
4834
4990
|
// src/lib/memory/injectionClassifier.ts
|
|
4835
4991
|
var DEFAULT_MODEL = "inclusionai/ling-2.6-flash";
|
|
4836
4992
|
var DEFAULT_ATTEMPTS = 2;
|
|
@@ -5759,13 +5915,13 @@ function parseEntities(raw) {
|
|
|
5759
5915
|
for (const e of raw) {
|
|
5760
5916
|
if (typeof e === "string") {
|
|
5761
5917
|
const name2 = e.trim();
|
|
5762
|
-
if (name2.length > 0) out.push({ name: name2 });
|
|
5918
|
+
if (name2.length > 0 && !isGenericEntityName(name2)) out.push({ name: name2 });
|
|
5763
5919
|
continue;
|
|
5764
5920
|
}
|
|
5765
5921
|
if (typeof e !== "object" || e === null) continue;
|
|
5766
5922
|
const obj = e;
|
|
5767
5923
|
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
5768
|
-
if (name.length === 0) continue;
|
|
5924
|
+
if (name.length === 0 || isGenericEntityName(name)) continue;
|
|
5769
5925
|
const kind = typeof obj.kind === "string" && ENTITY_KINDS.includes(obj.kind) ? obj.kind : void 0;
|
|
5770
5926
|
out.push(kind !== void 0 ? { name, kind } : { name });
|
|
5771
5927
|
}
|
|
@@ -5985,9 +6141,11 @@ function createAutoExtractor(options) {
|
|
|
5985
6141
|
}
|
|
5986
6142
|
}
|
|
5987
6143
|
);
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
6144
|
+
if (outcome !== "empty-after-retry") {
|
|
6145
|
+
const advancedTo = window2[window2.length - 1].id;
|
|
6146
|
+
stateFor(conversationId).watermark = advancedTo;
|
|
6147
|
+
persistCursor(conversationId, messages, advancedTo, contiguous);
|
|
6148
|
+
}
|
|
5991
6149
|
for (let i = 0; i < results.length; i++) {
|
|
5992
6150
|
if (results[i].action === "suppressed") continue;
|
|
5993
6151
|
options.onMemoryExtracted?.({
|
|
@@ -7068,7 +7226,11 @@ var SYSTEM_PROMPT4 = `You assign topics to saved memories for a personal memory
|
|
|
7068
7226
|
|
|
7069
7227
|
Each memory is a short statement about the user. For each memory, list the NAMED entities it mentions \u2014 these become the memory's topics, used to connect related memories in a knowledge graph.
|
|
7070
7228
|
|
|
7071
|
-
Include only NAMED entities, skip generic/common nouns.
|
|
7229
|
+
Include only NAMED entities, skip generic/common nouns. A topic must identify a specific thing the user could recognize by name; if it would be equally true of thousands of unrelated memories, leave it out:
|
|
7230
|
+
- SKIP bare generic nouns even when the memory capitalizes them or a calendar entry is titled that way \u2014 "Home", "Work", "Meeting", "Lunch", "Birthday", "Appointment", "Trip", "Today", "Friday", "March" are NOT topics.
|
|
7231
|
+
- KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
|
|
7232
|
+
- Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
|
|
7233
|
+
${ENTITY_KIND_GUIDELINES}
|
|
7072
7234
|
|
|
7073
7235
|
Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
|
|
7074
7236
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
@@ -7080,7 +7242,7 @@ async function extractEntitiesForMemories(memories, options) {
|
|
|
7080
7242
|
const redactor = resolvePiiRedactor(options.piiRedaction);
|
|
7081
7243
|
const vocabulary = (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES).map((n) => redactor ? redactor.redactText(n).text : n);
|
|
7082
7244
|
const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
|
|
7083
|
-
When a memory refers to one of these, reuse the EXACT existing name (same spelling and
|
|
7245
|
+
When a memory refers to one of these, reuse the EXACT existing name (same spelling, casing, spacing and punctuation) instead of a variant \u2014 if "Foo.ai" is listed, never emit "Foo ai" or "Foo AI", and if "Jane Roe" is listed, emit that rather than a bare "Jane".
|
|
7084
7246
|
|
|
7085
7247
|
` : "";
|
|
7086
7248
|
for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
|