@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/react/index.cjs
CHANGED
|
@@ -4229,7 +4229,7 @@ function entityToStored(e) {
|
|
|
4229
4229
|
updatedAt: e.updatedAt
|
|
4230
4230
|
};
|
|
4231
4231
|
}
|
|
4232
|
-
async function
|
|
4232
|
+
async function upsertEntitiesInWrite(ctx, entities) {
|
|
4233
4233
|
const kindByName = /* @__PURE__ */ new Map();
|
|
4234
4234
|
for (const e of entities) {
|
|
4235
4235
|
const name = normalizeEntityName(e.name);
|
|
@@ -4243,60 +4243,62 @@ async function upsertEntitiesOp(ctx, entities) {
|
|
|
4243
4243
|
}
|
|
4244
4244
|
const unique = Array.from(kindByName.keys());
|
|
4245
4245
|
const out = /* @__PURE__ */ new Map();
|
|
4246
|
-
if (unique.length === 0) return out;
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
const
|
|
4251
|
-
|
|
4252
|
-
return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
|
|
4253
|
-
});
|
|
4254
|
-
const missing = unique.filter((n) => !existingNames.has(n));
|
|
4255
|
-
const created = missing.map(
|
|
4256
|
-
(name) => ctx.entityCollection.prepareCreate((record) => {
|
|
4257
|
-
record._setRaw("canonical_name", name);
|
|
4258
|
-
const kind = kindByName.get(name);
|
|
4259
|
-
if (kind !== void 0) record._setRaw("kind", kind);
|
|
4260
|
-
})
|
|
4261
|
-
);
|
|
4262
|
-
const updated = updates.map(
|
|
4263
|
-
(e) => e.prepareUpdate((record) => {
|
|
4264
|
-
record._setRaw("kind", kindByName.get(e.canonicalName));
|
|
4265
|
-
})
|
|
4266
|
-
);
|
|
4267
|
-
if (created.length > 0 || updated.length > 0) {
|
|
4268
|
-
await ctx.database.batch(...created, ...updated);
|
|
4269
|
-
}
|
|
4270
|
-
for (const e of existing) out.set(e.canonicalName, entityToStored(e));
|
|
4271
|
-
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
4272
|
-
return out;
|
|
4246
|
+
if (unique.length === 0) return { entities: out, operations: [] };
|
|
4247
|
+
const existing = await ctx.entityCollection.query(import_watermelondb9.Q.where("canonical_name", import_watermelondb9.Q.oneOf(unique))).fetch();
|
|
4248
|
+
const existingNames = new Set(existing.map((e) => e.canonicalName));
|
|
4249
|
+
const updates = existing.filter((e) => {
|
|
4250
|
+
const incoming = kindByName.get(e.canonicalName);
|
|
4251
|
+
return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
|
|
4273
4252
|
});
|
|
4253
|
+
const missing = unique.filter((n) => !existingNames.has(n));
|
|
4254
|
+
const created = missing.map(
|
|
4255
|
+
(name) => ctx.entityCollection.prepareCreate((record) => {
|
|
4256
|
+
record._setRaw("canonical_name", name);
|
|
4257
|
+
const kind = kindByName.get(name);
|
|
4258
|
+
if (kind !== void 0) record._setRaw("kind", kind);
|
|
4259
|
+
})
|
|
4260
|
+
);
|
|
4261
|
+
const updated = updates.map(
|
|
4262
|
+
(e) => e.prepareUpdate((record) => {
|
|
4263
|
+
record._setRaw("kind", kindByName.get(e.canonicalName));
|
|
4264
|
+
})
|
|
4265
|
+
);
|
|
4266
|
+
for (const e of existing) out.set(e.canonicalName, entityToStored(e));
|
|
4267
|
+
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
4268
|
+
return { entities: out, operations: [...created, ...updated] };
|
|
4274
4269
|
}
|
|
4275
4270
|
async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
4276
4271
|
if (entityInputs.length === 0) return [];
|
|
4277
4272
|
const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
4278
|
-
const byName = await upsertEntitiesOp(ctx, normalized);
|
|
4279
|
-
const entities = Array.from(byName.values());
|
|
4280
|
-
if (entities.length === 0) return [];
|
|
4281
4273
|
const userId = ctx.userId;
|
|
4282
4274
|
let skipped = false;
|
|
4275
|
+
let entities = [];
|
|
4283
4276
|
await ctx.database.write(async () => {
|
|
4277
|
+
const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
|
|
4278
|
+
ctx,
|
|
4279
|
+
normalized
|
|
4280
|
+
);
|
|
4281
|
+
entities = Array.from(byName.values());
|
|
4284
4282
|
if (options?.unlessTopicsUserManaged && await autoLinkBlocked(ctx, memoryId)) {
|
|
4283
|
+
if (entityOps.length > 0) {
|
|
4284
|
+
await ctx.database.batch(...entityOps);
|
|
4285
|
+
}
|
|
4285
4286
|
skipped = true;
|
|
4286
4287
|
return;
|
|
4287
4288
|
}
|
|
4289
|
+
if (entities.length === 0) return;
|
|
4288
4290
|
const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", memoryId)).fetch();
|
|
4289
4291
|
const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
|
|
4290
4292
|
const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
|
|
4291
|
-
if (toCreate.length === 0) return;
|
|
4292
|
-
const
|
|
4293
|
+
if (entityOps.length === 0 && toCreate.length === 0) return;
|
|
4294
|
+
const linkOps = toCreate.map(
|
|
4293
4295
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
4294
4296
|
record._setRaw("memory_id", memoryId);
|
|
4295
4297
|
record._setRaw("entity_id", e.uniqueId);
|
|
4296
4298
|
if (userId !== void 0) record._setRaw("user_id", userId);
|
|
4297
4299
|
})
|
|
4298
4300
|
);
|
|
4299
|
-
await ctx.database.batch(...
|
|
4301
|
+
await ctx.database.batch(...entityOps, ...linkOps);
|
|
4300
4302
|
});
|
|
4301
4303
|
return skipped ? [] : entities;
|
|
4302
4304
|
}
|
|
@@ -4311,22 +4313,28 @@ async function autoLinkBlocked(ctx, memoryId) {
|
|
|
4311
4313
|
}
|
|
4312
4314
|
async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
4313
4315
|
const normalized = entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
4314
|
-
const byName = await upsertEntitiesOp(ctx, normalized);
|
|
4315
|
-
const entities = Array.from(byName.values());
|
|
4316
4316
|
const userId = ctx.userId;
|
|
4317
4317
|
let skipped = false;
|
|
4318
|
+
let entities = [];
|
|
4318
4319
|
await ctx.database.write(async () => {
|
|
4319
4320
|
if (await autoLinkBlocked(ctx, memoryId)) {
|
|
4320
4321
|
skipped = true;
|
|
4321
4322
|
return;
|
|
4322
4323
|
}
|
|
4324
|
+
const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
|
|
4325
|
+
ctx,
|
|
4326
|
+
normalized
|
|
4327
|
+
);
|
|
4328
|
+
entities = Array.from(byName.values());
|
|
4323
4329
|
const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", memoryId)).fetch();
|
|
4324
4330
|
const keep = new Set(entities.map((e) => e.uniqueId));
|
|
4325
4331
|
const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
|
|
4326
4332
|
const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
|
|
4327
4333
|
const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
|
|
4328
|
-
if (toCreate.length === 0 && toDestroy.length === 0) return;
|
|
4334
|
+
if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0) return;
|
|
4335
|
+
const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
|
|
4329
4336
|
await ctx.database.batch(
|
|
4337
|
+
...entityOps,
|
|
4330
4338
|
...toCreate.map(
|
|
4331
4339
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
4332
4340
|
record._setRaw("memory_id", memoryId);
|
|
@@ -4334,11 +4342,23 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
|
4334
4342
|
if (userId !== void 0) record._setRaw("user_id", userId);
|
|
4335
4343
|
})
|
|
4336
4344
|
),
|
|
4337
|
-
...toDestroy.map((l) => l.prepareDestroyPermanently())
|
|
4345
|
+
...toDestroy.map((l) => l.prepareDestroyPermanently()),
|
|
4346
|
+
...orphans.map((e) => e.prepareDestroyPermanently())
|
|
4338
4347
|
);
|
|
4339
4348
|
});
|
|
4340
4349
|
return skipped ? null : entities;
|
|
4341
4350
|
}
|
|
4351
|
+
async function findOrphanedEntities(ctx, memoryId, toDestroy) {
|
|
4352
|
+
const candidateIds = [...new Set(toDestroy.map((l) => String(l.entityId)))];
|
|
4353
|
+
if (candidateIds.length === 0) return [];
|
|
4354
|
+
const links = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("entity_id", import_watermelondb9.Q.oneOf(candidateIds))).fetch();
|
|
4355
|
+
const stillLinked = new Set(
|
|
4356
|
+
links.filter((l) => String(l.memoryId) !== memoryId).map((l) => String(l.entityId))
|
|
4357
|
+
);
|
|
4358
|
+
const orphanIds = candidateIds.filter((id) => !stillLinked.has(id));
|
|
4359
|
+
if (orphanIds.length === 0) return [];
|
|
4360
|
+
return await ctx.entityCollection.query(import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(orphanIds))).fetch();
|
|
4361
|
+
}
|
|
4342
4362
|
async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
|
|
4343
4363
|
if (memoryIds.length === 0) return;
|
|
4344
4364
|
const links = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", import_watermelondb9.Q.oneOf(Array.from(memoryIds)))).fetch();
|
|
@@ -5275,7 +5295,7 @@ var init_operations3 = __esm({
|
|
|
5275
5295
|
init_operations2();
|
|
5276
5296
|
init_encryption2();
|
|
5277
5297
|
KNOWN_TRUST_TIERS = /* @__PURE__ */ new Set(["quarantined", "trusted"]);
|
|
5278
|
-
TOPICS_EXTRACTION_VERSION =
|
|
5298
|
+
TOPICS_EXTRACTION_VERSION = 3;
|
|
5279
5299
|
}
|
|
5280
5300
|
});
|
|
5281
5301
|
|
|
@@ -15034,6 +15054,162 @@ init_types3();
|
|
|
15034
15054
|
init_logger();
|
|
15035
15055
|
init_redactor();
|
|
15036
15056
|
|
|
15057
|
+
// src/lib/memory/entitySalience.ts
|
|
15058
|
+
var GENERIC_ENTITY_NAMES = /* @__PURE__ */ new Set([
|
|
15059
|
+
// Relative and absolute time
|
|
15060
|
+
"today",
|
|
15061
|
+
"tomorrow",
|
|
15062
|
+
"yesterday",
|
|
15063
|
+
"tonight",
|
|
15064
|
+
"morning",
|
|
15065
|
+
"afternoon",
|
|
15066
|
+
"evening",
|
|
15067
|
+
"night",
|
|
15068
|
+
"day",
|
|
15069
|
+
"date",
|
|
15070
|
+
"time",
|
|
15071
|
+
"week",
|
|
15072
|
+
"weekend",
|
|
15073
|
+
"weekday",
|
|
15074
|
+
"month",
|
|
15075
|
+
"year",
|
|
15076
|
+
"monday",
|
|
15077
|
+
"tuesday",
|
|
15078
|
+
"wednesday",
|
|
15079
|
+
"thursday",
|
|
15080
|
+
"friday",
|
|
15081
|
+
"saturday",
|
|
15082
|
+
"sunday",
|
|
15083
|
+
"january",
|
|
15084
|
+
"february",
|
|
15085
|
+
"march",
|
|
15086
|
+
"april",
|
|
15087
|
+
"may",
|
|
15088
|
+
"june",
|
|
15089
|
+
"july",
|
|
15090
|
+
"august",
|
|
15091
|
+
"september",
|
|
15092
|
+
"october",
|
|
15093
|
+
"november",
|
|
15094
|
+
"december",
|
|
15095
|
+
// Generic containers and venues — the named ones ("Blue Bottle on Valencia",
|
|
15096
|
+
// "Stanford") are unaffected because they are not bare tokens.
|
|
15097
|
+
"home",
|
|
15098
|
+
"house",
|
|
15099
|
+
"apartment",
|
|
15100
|
+
"room",
|
|
15101
|
+
"office",
|
|
15102
|
+
"work",
|
|
15103
|
+
"workplace",
|
|
15104
|
+
"school",
|
|
15105
|
+
"gym",
|
|
15106
|
+
"store",
|
|
15107
|
+
"shop",
|
|
15108
|
+
"hospital",
|
|
15109
|
+
"airport",
|
|
15110
|
+
"hotel",
|
|
15111
|
+
"city",
|
|
15112
|
+
"town",
|
|
15113
|
+
"country",
|
|
15114
|
+
"place",
|
|
15115
|
+
// Generic calendar entries — "Chicago Marathon" survives, "meeting" doesn't.
|
|
15116
|
+
"meeting",
|
|
15117
|
+
"call",
|
|
15118
|
+
"appointment",
|
|
15119
|
+
"reminder",
|
|
15120
|
+
"event",
|
|
15121
|
+
"birthday",
|
|
15122
|
+
"breakfast",
|
|
15123
|
+
"lunch",
|
|
15124
|
+
"dinner",
|
|
15125
|
+
"brunch",
|
|
15126
|
+
"vacation",
|
|
15127
|
+
"holiday",
|
|
15128
|
+
"trip",
|
|
15129
|
+
// Plurals are listed explicitly rather than stemmed: naive `s`-stripping
|
|
15130
|
+
// would fold real titles into generics ("The Times" → "time"). Only plurals
|
|
15131
|
+
// that are junk as topics in their own right belong here — note the absence
|
|
15132
|
+
// of "times", "places", and anything else that reads as a name.
|
|
15133
|
+
"days",
|
|
15134
|
+
"weeks",
|
|
15135
|
+
"weekends",
|
|
15136
|
+
"months",
|
|
15137
|
+
"years",
|
|
15138
|
+
"rooms",
|
|
15139
|
+
"offices",
|
|
15140
|
+
"houses",
|
|
15141
|
+
"apartments",
|
|
15142
|
+
"stores",
|
|
15143
|
+
"shops",
|
|
15144
|
+
"hotels",
|
|
15145
|
+
"meetings",
|
|
15146
|
+
"calls",
|
|
15147
|
+
"appointments",
|
|
15148
|
+
"reminders",
|
|
15149
|
+
"events",
|
|
15150
|
+
"birthdays",
|
|
15151
|
+
"trips",
|
|
15152
|
+
"vacations",
|
|
15153
|
+
"holidays",
|
|
15154
|
+
// Meta / filler the models occasionally echo back as an entity
|
|
15155
|
+
"user",
|
|
15156
|
+
"assistant",
|
|
15157
|
+
"memory",
|
|
15158
|
+
"memories",
|
|
15159
|
+
"note",
|
|
15160
|
+
"notes",
|
|
15161
|
+
"thing",
|
|
15162
|
+
"things",
|
|
15163
|
+
"stuff",
|
|
15164
|
+
"something",
|
|
15165
|
+
"someone",
|
|
15166
|
+
"anything",
|
|
15167
|
+
"everything",
|
|
15168
|
+
"none",
|
|
15169
|
+
"other",
|
|
15170
|
+
"misc",
|
|
15171
|
+
"general",
|
|
15172
|
+
"unknown"
|
|
15173
|
+
]);
|
|
15174
|
+
function isGenericEntityName(name) {
|
|
15175
|
+
const tokens = name.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
|
|
15176
|
+
if (tokens.length === 0) return true;
|
|
15177
|
+
const distinctive = tokens.filter(
|
|
15178
|
+
(token) => !GENERIC_ENTITY_NAMES.has(token) && !ARTICLES.has(token) && !WEAK_MODIFIERS.has(token)
|
|
15179
|
+
);
|
|
15180
|
+
if (distinctive.length > 0) return false;
|
|
15181
|
+
const generic = tokens.filter((token) => GENERIC_ENTITY_NAMES.has(token));
|
|
15182
|
+
if (generic.length === 0) return true;
|
|
15183
|
+
const titleShaped = generic.length === 1 && tokens.some((token) => ARTICLES.has(token)) && !tokens.some((token) => WEAK_MODIFIERS.has(token));
|
|
15184
|
+
return !titleShaped;
|
|
15185
|
+
}
|
|
15186
|
+
var ARTICLES = /* @__PURE__ */ new Set(["the", "a", "an"]);
|
|
15187
|
+
var WEAK_MODIFIERS = /* @__PURE__ */ new Set([
|
|
15188
|
+
"my",
|
|
15189
|
+
"our",
|
|
15190
|
+
"their",
|
|
15191
|
+
"his",
|
|
15192
|
+
"her",
|
|
15193
|
+
"its",
|
|
15194
|
+
"this",
|
|
15195
|
+
"that",
|
|
15196
|
+
"next",
|
|
15197
|
+
"last",
|
|
15198
|
+
"upcoming",
|
|
15199
|
+
"every",
|
|
15200
|
+
"at",
|
|
15201
|
+
"in",
|
|
15202
|
+
"on",
|
|
15203
|
+
"of",
|
|
15204
|
+
"for",
|
|
15205
|
+
"with",
|
|
15206
|
+
"from",
|
|
15207
|
+
"to",
|
|
15208
|
+
"by",
|
|
15209
|
+
"and",
|
|
15210
|
+
"or"
|
|
15211
|
+
]);
|
|
15212
|
+
|
|
15037
15213
|
// src/lib/memory/injectionClassifier.ts
|
|
15038
15214
|
init_logger();
|
|
15039
15215
|
init_redactor();
|
|
@@ -15974,13 +16150,13 @@ function parseEntities(raw) {
|
|
|
15974
16150
|
for (const e of raw) {
|
|
15975
16151
|
if (typeof e === "string") {
|
|
15976
16152
|
const name2 = e.trim();
|
|
15977
|
-
if (name2.length > 0) out.push({ name: name2 });
|
|
16153
|
+
if (name2.length > 0 && !isGenericEntityName(name2)) out.push({ name: name2 });
|
|
15978
16154
|
continue;
|
|
15979
16155
|
}
|
|
15980
16156
|
if (typeof e !== "object" || e === null) continue;
|
|
15981
16157
|
const obj = e;
|
|
15982
16158
|
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
15983
|
-
if (name.length === 0) continue;
|
|
16159
|
+
if (name.length === 0 || isGenericEntityName(name)) continue;
|
|
15984
16160
|
const kind = typeof obj.kind === "string" && ENTITY_KINDS.includes(obj.kind) ? obj.kind : void 0;
|
|
15985
16161
|
out.push(kind !== void 0 ? { name, kind } : { name });
|
|
15986
16162
|
}
|
|
@@ -16202,9 +16378,11 @@ function createAutoExtractor(options) {
|
|
|
16202
16378
|
}
|
|
16203
16379
|
}
|
|
16204
16380
|
);
|
|
16205
|
-
|
|
16206
|
-
|
|
16207
|
-
|
|
16381
|
+
if (outcome !== "empty-after-retry") {
|
|
16382
|
+
const advancedTo = window2[window2.length - 1].id;
|
|
16383
|
+
stateFor(conversationId).watermark = advancedTo;
|
|
16384
|
+
persistCursor(conversationId, messages, advancedTo, contiguous);
|
|
16385
|
+
}
|
|
16208
16386
|
for (let i = 0; i < results.length; i++) {
|
|
16209
16387
|
if (results[i].action === "suppressed") continue;
|
|
16210
16388
|
options.onMemoryExtracted?.({
|
|
@@ -17322,7 +17500,11 @@ var SYSTEM_PROMPT6 = `You assign topics to saved memories for a personal memory
|
|
|
17322
17500
|
|
|
17323
17501
|
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.
|
|
17324
17502
|
|
|
17325
|
-
Include only NAMED entities, skip generic/common nouns.
|
|
17503
|
+
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:
|
|
17504
|
+
- 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.
|
|
17505
|
+
- KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
|
|
17506
|
+
- Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
|
|
17507
|
+
${ENTITY_KIND_GUIDELINES}
|
|
17326
17508
|
|
|
17327
17509
|
Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
|
|
17328
17510
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
@@ -17334,7 +17516,7 @@ async function extractEntitiesForMemories(memories, options) {
|
|
|
17334
17516
|
const redactor = resolvePiiRedactor(options.piiRedaction);
|
|
17335
17517
|
const vocabulary = (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES).map((n) => redactor ? redactor.redactText(n).text : n);
|
|
17336
17518
|
const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
|
|
17337
|
-
When a memory refers to one of these, reuse the EXACT existing name (same spelling and
|
|
17519
|
+
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".
|
|
17338
17520
|
|
|
17339
17521
|
` : "";
|
|
17340
17522
|
for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
|
package/dist/react/index.d.mts
CHANGED
|
@@ -5771,7 +5771,7 @@ interface EntityOperationsContext {
|
|
|
5771
5771
|
* Link a memory to one or more entities. Accepts bare names (back-compat)
|
|
5772
5772
|
* or `{ name, kind }` objects. Names are normalized; missing entities are
|
|
5773
5773
|
* auto-created (with their kind), and an existing entity's null kind is
|
|
5774
|
-
* back-filled — see {@link
|
|
5774
|
+
* back-filled — see {@link upsertEntitiesInWrite}. Idempotent — duplicate
|
|
5775
5775
|
* (memory_id, entity_id) pairs are skipped.
|
|
5776
5776
|
*
|
|
5777
5777
|
* `options.unlessTopicsUserManaged` re-checks the memory's vault row INSIDE
|
|
@@ -5782,8 +5782,9 @@ interface EntityOperationsContext {
|
|
|
5782
5782
|
* mid-call (orphaning links the cascade already swept) — so only an in-write
|
|
5783
5783
|
* check guarantees a user's manual edit or delete can't be grafted over. The
|
|
5784
5784
|
* row read fails CLOSED (skip links) so a transient fault never attaches
|
|
5785
|
-
* topics to a memory we couldn't verify. Entity upserts
|
|
5786
|
-
*
|
|
5785
|
+
* topics to a memory we couldn't verify. Entity upserts and link creation
|
|
5786
|
+
* run in ONE writer to prevent orphan-prune races; returns [] when links
|
|
5787
|
+
* were skipped.
|
|
5787
5788
|
*/
|
|
5788
5789
|
declare function linkMemoryEntitiesOp(ctx: EntityOperationsContext, memoryId: string, entityInputs: ReadonlyArray<EntityInput>, options?: {
|
|
5789
5790
|
unlessTopicsUserManaged?: boolean;
|
|
@@ -5798,6 +5799,10 @@ declare function linkMemoryEntitiesOp(ctx: EntityOperationsContext, memoryId: st
|
|
|
5798
5799
|
* destroy-stale are batched in ONE writer, after the same in-write guard as
|
|
5799
5800
|
* the link op (user-managed / deleted / absent / read-fault ⇒ skip).
|
|
5800
5801
|
*
|
|
5802
|
+
* That batch also prunes `entity` rows left with no links at all (see
|
|
5803
|
+
* {@link findOrphanedEntities}) — otherwise a topic the extractor has disowned
|
|
5804
|
+
* keeps rendering as a chip that matches no memory.
|
|
5805
|
+
*
|
|
5801
5806
|
* Returns the linked entities ([] for an answered-empty set), or null when
|
|
5802
5807
|
* the guard skipped — callers must treat null as "not persisted" (e.g. don't
|
|
5803
5808
|
* stamp `topics_extracted_at`).
|
|
@@ -7208,7 +7213,7 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
7208
7213
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
7209
7214
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
7210
7215
|
*/
|
|
7211
|
-
declare const TOPICS_EXTRACTION_VERSION =
|
|
7216
|
+
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
7212
7217
|
/**
|
|
7213
7218
|
* Result of {@link getMemoriesNeedingTopicExtractionOp}: which memories the
|
|
7214
7219
|
* background topic worker should run LLM entity extraction on, and which it
|
|
@@ -7383,6 +7388,12 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
|
|
|
7383
7388
|
* during a busy window are still examined instead of scrolling out of a last-N
|
|
7384
7389
|
* window.
|
|
7385
7390
|
*
|
|
7391
|
+
* The watermark advances only when the extractor genuinely EXAMINED the window.
|
|
7392
|
+
* A pipeline throw and an `empty-after-retry` outcome (the extraction LLM
|
|
7393
|
+
* returned empty/malformed after exhausting its retries) both leave it in place,
|
|
7394
|
+
* so the next turn's window re-covers those messages instead of stranding them.
|
|
7395
|
+
* A quiet turn that legitimately yielded no facts *does* advance it.
|
|
7396
|
+
*
|
|
7386
7397
|
* Durability: the in-memory watermark alone does NOT survive process death, so
|
|
7387
7398
|
* a session killed after messages accumulate but before extraction would, on
|
|
7388
7399
|
* the next launch, fall back to a trailing-window guess and could skip the
|
package/dist/react/index.d.ts
CHANGED
|
@@ -5771,7 +5771,7 @@ interface EntityOperationsContext {
|
|
|
5771
5771
|
* Link a memory to one or more entities. Accepts bare names (back-compat)
|
|
5772
5772
|
* or `{ name, kind }` objects. Names are normalized; missing entities are
|
|
5773
5773
|
* auto-created (with their kind), and an existing entity's null kind is
|
|
5774
|
-
* back-filled — see {@link
|
|
5774
|
+
* back-filled — see {@link upsertEntitiesInWrite}. Idempotent — duplicate
|
|
5775
5775
|
* (memory_id, entity_id) pairs are skipped.
|
|
5776
5776
|
*
|
|
5777
5777
|
* `options.unlessTopicsUserManaged` re-checks the memory's vault row INSIDE
|
|
@@ -5782,8 +5782,9 @@ interface EntityOperationsContext {
|
|
|
5782
5782
|
* mid-call (orphaning links the cascade already swept) — so only an in-write
|
|
5783
5783
|
* check guarantees a user's manual edit or delete can't be grafted over. The
|
|
5784
5784
|
* row read fails CLOSED (skip links) so a transient fault never attaches
|
|
5785
|
-
* topics to a memory we couldn't verify. Entity upserts
|
|
5786
|
-
*
|
|
5785
|
+
* topics to a memory we couldn't verify. Entity upserts and link creation
|
|
5786
|
+
* run in ONE writer to prevent orphan-prune races; returns [] when links
|
|
5787
|
+
* were skipped.
|
|
5787
5788
|
*/
|
|
5788
5789
|
declare function linkMemoryEntitiesOp(ctx: EntityOperationsContext, memoryId: string, entityInputs: ReadonlyArray<EntityInput>, options?: {
|
|
5789
5790
|
unlessTopicsUserManaged?: boolean;
|
|
@@ -5798,6 +5799,10 @@ declare function linkMemoryEntitiesOp(ctx: EntityOperationsContext, memoryId: st
|
|
|
5798
5799
|
* destroy-stale are batched in ONE writer, after the same in-write guard as
|
|
5799
5800
|
* the link op (user-managed / deleted / absent / read-fault ⇒ skip).
|
|
5800
5801
|
*
|
|
5802
|
+
* That batch also prunes `entity` rows left with no links at all (see
|
|
5803
|
+
* {@link findOrphanedEntities}) — otherwise a topic the extractor has disowned
|
|
5804
|
+
* keeps rendering as a chip that matches no memory.
|
|
5805
|
+
*
|
|
5801
5806
|
* Returns the linked entities ([] for an answered-empty set), or null when
|
|
5802
5807
|
* the guard skipped — callers must treat null as "not persisted" (e.g. don't
|
|
5803
5808
|
* stamp `topics_extracted_at`).
|
|
@@ -7208,7 +7213,7 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
7208
7213
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
7209
7214
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
7210
7215
|
*/
|
|
7211
|
-
declare const TOPICS_EXTRACTION_VERSION =
|
|
7216
|
+
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
7212
7217
|
/**
|
|
7213
7218
|
* Result of {@link getMemoriesNeedingTopicExtractionOp}: which memories the
|
|
7214
7219
|
* background topic worker should run LLM entity extraction on, and which it
|
|
@@ -7383,6 +7388,12 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
|
|
|
7383
7388
|
* during a busy window are still examined instead of scrolling out of a last-N
|
|
7384
7389
|
* window.
|
|
7385
7390
|
*
|
|
7391
|
+
* The watermark advances only when the extractor genuinely EXAMINED the window.
|
|
7392
|
+
* A pipeline throw and an `empty-after-retry` outcome (the extraction LLM
|
|
7393
|
+
* returned empty/malformed after exhausting its retries) both leave it in place,
|
|
7394
|
+
* so the next turn's window re-covers those messages instead of stranding them.
|
|
7395
|
+
* A quiet turn that legitimately yielded no facts *does* advance it.
|
|
7396
|
+
*
|
|
7386
7397
|
* Durability: the in-memory watermark alone does NOT survive process death, so
|
|
7387
7398
|
* a session killed after messages accumulate but before extraction would, on
|
|
7388
7399
|
* the next launch, fall back to a trailing-window guess and could skip the
|