@anuma/sdk 6.8.2 → 6.9.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/{chunk-LUGYCFVI.mjs → chunk-4M56MKBV.mjs} +66 -44
- package/dist/expo/index.cjs +71 -45
- package/dist/expo/index.d.mts +24 -0
- package/dist/expo/index.d.ts +24 -0
- package/dist/expo/index.mjs +7 -3
- package/dist/expo/{recall-77QK4RT6.mjs → recall-WIB7RQQZ.mjs} +1 -1
- package/dist/react/{chunk-NPJSO5ES.mjs → chunk-EJ65X4GI.mjs} +74 -46
- package/dist/react/index.cjs +79 -47
- package/dist/react/index.d.mts +24 -0
- package/dist/react/index.d.ts +24 -0
- package/dist/react/index.mjs +7 -3
- package/dist/react/{recall-3I5GDJZE.mjs → recall-FWILZSFH.mjs} +1 -1
- package/dist/server/{chunk-IP6PGI7H.mjs → chunk-I56324FR.mjs} +66 -44
- package/dist/server/index.cjs +71 -45
- package/dist/server/index.d.mts +24 -0
- package/dist/server/index.d.ts +24 -0
- package/dist/server/index.mjs +7 -3
- package/dist/server/{recall-RTW7T7KR.mjs → recall-T7CNXTQ3.mjs} +1 -1
- package/package.json +1 -1
|
@@ -2321,13 +2321,13 @@ async function upsertEntitiesInWrite(ctx, entities) {
|
|
|
2321
2321
|
}
|
|
2322
2322
|
const unique = Array.from(kindByName.keys());
|
|
2323
2323
|
const out = /* @__PURE__ */ new Map();
|
|
2324
|
-
if (unique.length === 0) return { entities: out, operations: [] };
|
|
2324
|
+
if (unique.length === 0) return { entities: out, operations: [], kindBackfills: [] };
|
|
2325
2325
|
const existing = await ctx.entityCollection.query(Q2.where("canonical_name", Q2.oneOf(unique))).fetch();
|
|
2326
2326
|
const existingNames = new Set(existing.map((e) => e.canonicalName));
|
|
2327
|
-
const
|
|
2327
|
+
const kindBackfills = existing.filter((e) => {
|
|
2328
2328
|
const incoming = kindByName.get(e.canonicalName);
|
|
2329
2329
|
return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
|
|
2330
|
-
});
|
|
2330
|
+
}).map((record) => ({ record, kind: kindByName.get(record.canonicalName) }));
|
|
2331
2331
|
const missing = unique.filter((n) => !existingNames.has(n));
|
|
2332
2332
|
const created = missing.map(
|
|
2333
2333
|
(name) => ctx.entityCollection.prepareCreate((record) => {
|
|
@@ -2336,14 +2336,19 @@ async function upsertEntitiesInWrite(ctx, entities) {
|
|
|
2336
2336
|
if (kind !== void 0) record._setRaw("kind", kind);
|
|
2337
2337
|
})
|
|
2338
2338
|
);
|
|
2339
|
-
const updated = updates.map(
|
|
2340
|
-
(e) => e.prepareUpdate((record) => {
|
|
2341
|
-
record._setRaw("kind", kindByName.get(e.canonicalName));
|
|
2342
|
-
})
|
|
2343
|
-
);
|
|
2344
2339
|
for (const e of existing) out.set(e.canonicalName, entityToStored(e));
|
|
2345
2340
|
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
2346
|
-
|
|
2341
|
+
for (const { record, kind } of kindBackfills) {
|
|
2342
|
+
out.set(record.canonicalName, { ...entityToStored(record), kind });
|
|
2343
|
+
}
|
|
2344
|
+
return { entities: out, operations: created, kindBackfills };
|
|
2345
|
+
}
|
|
2346
|
+
function prepareKindBackfills(backfills) {
|
|
2347
|
+
return backfills.map(
|
|
2348
|
+
({ record, kind }) => record.prepareUpdate((r) => {
|
|
2349
|
+
r._setRaw("kind", kind);
|
|
2350
|
+
})
|
|
2351
|
+
);
|
|
2347
2352
|
}
|
|
2348
2353
|
async function findVaultRowInWrite(ctx, memoryId) {
|
|
2349
2354
|
try {
|
|
@@ -2393,10 +2398,12 @@ function topicsForEntities(entities, displayNames, source) {
|
|
|
2393
2398
|
function toEntityObjects(entityInputs) {
|
|
2394
2399
|
return entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
2395
2400
|
}
|
|
2396
|
-
async function
|
|
2401
|
+
async function resolveMemoryTopicsWrite(ctx, memoryId, linked, inputs, source) {
|
|
2397
2402
|
const row = await findVaultRowInWrite(ctx, memoryId);
|
|
2398
|
-
|
|
2399
|
-
|
|
2403
|
+
return resolveTopicsWrite(row, linked, displayNamesOf(toEntityObjects(inputs)), source);
|
|
2404
|
+
}
|
|
2405
|
+
function prepareMemoryTopicsUpdate(write) {
|
|
2406
|
+
return prepareTopicsUpdate(write.row, write.topics, Date.now());
|
|
2400
2407
|
}
|
|
2401
2408
|
async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
2402
2409
|
if (entityInputs.length === 0) return [];
|
|
@@ -2405,15 +2412,16 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
2405
2412
|
let skipped = false;
|
|
2406
2413
|
let entities = [];
|
|
2407
2414
|
await ctx.database.write(async () => {
|
|
2408
|
-
const {
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2415
|
+
const {
|
|
2416
|
+
entities: byName,
|
|
2417
|
+
operations: entityOps,
|
|
2418
|
+
kindBackfills
|
|
2419
|
+
} = await upsertEntitiesInWrite(ctx, normalized);
|
|
2412
2420
|
entities = Array.from(byName.values());
|
|
2413
2421
|
const vaultRow = await findVaultRowInWrite(ctx, memoryId);
|
|
2414
2422
|
if (options?.unlessTopicsUserManaged && autoLinkBlocked(vaultRow)) {
|
|
2415
|
-
if (entityOps.length > 0) {
|
|
2416
|
-
await ctx.database.batch(...entityOps);
|
|
2423
|
+
if (entityOps.length > 0 || kindBackfills.length > 0) {
|
|
2424
|
+
await ctx.database.batch(...entityOps, ...prepareKindBackfills(kindBackfills));
|
|
2417
2425
|
}
|
|
2418
2426
|
skipped = true;
|
|
2419
2427
|
return;
|
|
@@ -2433,7 +2441,9 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
2433
2441
|
displayNamesOf(normalized),
|
|
2434
2442
|
options?.topicsSource ?? "auto"
|
|
2435
2443
|
);
|
|
2436
|
-
if (entityOps.length === 0 && toCreate.length === 0 && topicsWrite === null)
|
|
2444
|
+
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && topicsWrite === null) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2437
2447
|
const linkOps = toCreate.map(
|
|
2438
2448
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
2439
2449
|
record._setRaw("memory_id", memoryId);
|
|
@@ -2443,6 +2453,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
2443
2453
|
);
|
|
2444
2454
|
await ctx.database.batch(
|
|
2445
2455
|
...entityOps,
|
|
2456
|
+
...prepareKindBackfills(kindBackfills),
|
|
2446
2457
|
...linkOps,
|
|
2447
2458
|
...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
|
|
2448
2459
|
);
|
|
@@ -2480,10 +2491,11 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
|
2480
2491
|
skipped = true;
|
|
2481
2492
|
return;
|
|
2482
2493
|
}
|
|
2483
|
-
const {
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2494
|
+
const {
|
|
2495
|
+
entities: byName,
|
|
2496
|
+
operations: entityOps,
|
|
2497
|
+
kindBackfills
|
|
2498
|
+
} = await upsertEntitiesInWrite(ctx, normalized);
|
|
2487
2499
|
entities = Array.from(byName.values());
|
|
2488
2500
|
const existingLinks = await ctx.memoryEntityCollection.query(Q2.where("memory_id", memoryId)).fetch();
|
|
2489
2501
|
const keep = new Set(entities.map((e) => e.uniqueId));
|
|
@@ -2496,12 +2508,13 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
|
2496
2508
|
displayNamesOf(normalized),
|
|
2497
2509
|
options.topicsSource
|
|
2498
2510
|
);
|
|
2499
|
-
if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
2511
|
+
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
2500
2512
|
return;
|
|
2501
2513
|
}
|
|
2502
2514
|
const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
|
|
2503
2515
|
await ctx.database.batch(
|
|
2504
2516
|
...entityOps,
|
|
2517
|
+
...prepareKindBackfills(kindBackfills),
|
|
2505
2518
|
...toCreate.map(
|
|
2506
2519
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
2507
2520
|
record._setRaw("memory_id", memoryId);
|
|
@@ -3314,10 +3327,16 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
|
|
|
3314
3327
|
const existing = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", memoryId)).fetch();
|
|
3315
3328
|
const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
|
|
3316
3329
|
if (staleLinks.length === 0 && entities.length > 0) return;
|
|
3317
|
-
const
|
|
3330
|
+
const topicsWrite = await resolveMemoryTopicsWrite(
|
|
3331
|
+
entityCtx,
|
|
3332
|
+
memoryId,
|
|
3333
|
+
linked,
|
|
3334
|
+
entities,
|
|
3335
|
+
"user"
|
|
3336
|
+
);
|
|
3318
3337
|
await ctx.database.batch(
|
|
3319
3338
|
...staleLinks.map((l) => l.prepareDestroyPermanently()),
|
|
3320
|
-
...
|
|
3339
|
+
...topicsWrite ? [prepareMemoryTopicsUpdate(topicsWrite)] : []
|
|
3321
3340
|
);
|
|
3322
3341
|
});
|
|
3323
3342
|
if (stale) return null;
|
|
@@ -3636,27 +3655,36 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
|
|
|
3636
3655
|
if (!entityCtx) {
|
|
3637
3656
|
throw new Error("backfillMemoryTopicsOp requires ctx.entityCtx");
|
|
3638
3657
|
}
|
|
3658
|
+
const uniqueIds = Array.from(new Set(memoryIds));
|
|
3659
|
+
if (uniqueIds.length === 0) return [];
|
|
3639
3660
|
const filled = [];
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
record = await ctx.vaultMemoryCollection.find(id);
|
|
3644
|
-
} catch {
|
|
3645
|
-
continue;
|
|
3646
|
-
}
|
|
3647
|
-
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
|
|
3648
|
-
if (parseTopics(record.topics) !== null) continue;
|
|
3649
|
-
const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", id)).unsafeFetchRaw();
|
|
3650
|
-
const entityIds = links.map((l) => String(l.entity_id));
|
|
3651
|
-
if (entityIds.length === 0) continue;
|
|
3652
|
-
const entities = await entityCtx.entityCollection.query(Q3.where("id", Q3.oneOf(entityIds))).fetch();
|
|
3653
|
-
if (entities.length === 0) continue;
|
|
3654
|
-
const source = record.topicsUserManaged ? "user" : "auto";
|
|
3661
|
+
const CHUNK = 500;
|
|
3662
|
+
for (let i = 0; i < uniqueIds.length; i += CHUNK) {
|
|
3663
|
+
const chunkIds = uniqueIds.slice(i, i + CHUNK);
|
|
3655
3664
|
await ctx.database.write(async () => {
|
|
3656
|
-
const
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3665
|
+
const writes = [];
|
|
3666
|
+
for (const id of chunkIds) {
|
|
3667
|
+
let record;
|
|
3668
|
+
try {
|
|
3669
|
+
record = await ctx.vaultMemoryCollection.find(id);
|
|
3670
|
+
} catch {
|
|
3671
|
+
continue;
|
|
3672
|
+
}
|
|
3673
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
|
|
3674
|
+
if (parseTopics(record.topics) !== null) continue;
|
|
3675
|
+
const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", id)).unsafeFetchRaw();
|
|
3676
|
+
const entityIds = links.map((l) => String(l.entity_id));
|
|
3677
|
+
if (entityIds.length === 0) continue;
|
|
3678
|
+
const entities = await entityCtx.entityCollection.query(Q3.where("id", Q3.oneOf(entityIds))).fetch();
|
|
3679
|
+
if (entities.length === 0) continue;
|
|
3680
|
+
const source = record.topicsUserManaged ? "user" : "auto";
|
|
3681
|
+
const write = await resolveMemoryTopicsWrite(entityCtx, id, entities, [], source);
|
|
3682
|
+
if (write) writes.push(write);
|
|
3683
|
+
}
|
|
3684
|
+
if (writes.length === 0) return;
|
|
3685
|
+
const prepared = writes.map(prepareMemoryTopicsUpdate);
|
|
3686
|
+
await ctx.database.batch(...prepared);
|
|
3687
|
+
for (const write of writes) filled.push(write.row.id);
|
|
3660
3688
|
});
|
|
3661
3689
|
}
|
|
3662
3690
|
return filled;
|
|
@@ -7096,7 +7124,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
7096
7124
|
subQueries = decomp.subQueries;
|
|
7097
7125
|
}
|
|
7098
7126
|
}
|
|
7099
|
-
const { recall: recall2 } = await import("./recall-
|
|
7127
|
+
const { recall: recall2 } = await import("./recall-FWILZSFH.mjs");
|
|
7100
7128
|
let recallDegraded = [];
|
|
7101
7129
|
const result = await recall2(
|
|
7102
7130
|
query,
|
package/dist/react/index.cjs
CHANGED
|
@@ -4888,13 +4888,13 @@ async function upsertEntitiesInWrite(ctx, entities) {
|
|
|
4888
4888
|
}
|
|
4889
4889
|
const unique = Array.from(kindByName.keys());
|
|
4890
4890
|
const out = /* @__PURE__ */ new Map();
|
|
4891
|
-
if (unique.length === 0) return { entities: out, operations: [] };
|
|
4891
|
+
if (unique.length === 0) return { entities: out, operations: [], kindBackfills: [] };
|
|
4892
4892
|
const existing = await ctx.entityCollection.query(import_watermelondb9.Q.where("canonical_name", import_watermelondb9.Q.oneOf(unique))).fetch();
|
|
4893
4893
|
const existingNames = new Set(existing.map((e) => e.canonicalName));
|
|
4894
|
-
const
|
|
4894
|
+
const kindBackfills = existing.filter((e) => {
|
|
4895
4895
|
const incoming = kindByName.get(e.canonicalName);
|
|
4896
4896
|
return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
|
|
4897
|
-
});
|
|
4897
|
+
}).map((record) => ({ record, kind: kindByName.get(record.canonicalName) }));
|
|
4898
4898
|
const missing = unique.filter((n) => !existingNames.has(n));
|
|
4899
4899
|
const created = missing.map(
|
|
4900
4900
|
(name) => ctx.entityCollection.prepareCreate((record) => {
|
|
@@ -4903,14 +4903,19 @@ async function upsertEntitiesInWrite(ctx, entities) {
|
|
|
4903
4903
|
if (kind !== void 0) record._setRaw("kind", kind);
|
|
4904
4904
|
})
|
|
4905
4905
|
);
|
|
4906
|
-
const updated = updates.map(
|
|
4907
|
-
(e) => e.prepareUpdate((record) => {
|
|
4908
|
-
record._setRaw("kind", kindByName.get(e.canonicalName));
|
|
4909
|
-
})
|
|
4910
|
-
);
|
|
4911
4906
|
for (const e of existing) out.set(e.canonicalName, entityToStored(e));
|
|
4912
4907
|
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
4913
|
-
|
|
4908
|
+
for (const { record, kind } of kindBackfills) {
|
|
4909
|
+
out.set(record.canonicalName, { ...entityToStored(record), kind });
|
|
4910
|
+
}
|
|
4911
|
+
return { entities: out, operations: created, kindBackfills };
|
|
4912
|
+
}
|
|
4913
|
+
function prepareKindBackfills(backfills) {
|
|
4914
|
+
return backfills.map(
|
|
4915
|
+
({ record, kind }) => record.prepareUpdate((r) => {
|
|
4916
|
+
r._setRaw("kind", kind);
|
|
4917
|
+
})
|
|
4918
|
+
);
|
|
4914
4919
|
}
|
|
4915
4920
|
async function findVaultRowInWrite(ctx, memoryId) {
|
|
4916
4921
|
try {
|
|
@@ -4960,10 +4965,12 @@ function topicsForEntities(entities, displayNames, source) {
|
|
|
4960
4965
|
function toEntityObjects(entityInputs) {
|
|
4961
4966
|
return entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
4962
4967
|
}
|
|
4963
|
-
async function
|
|
4968
|
+
async function resolveMemoryTopicsWrite(ctx, memoryId, linked, inputs, source) {
|
|
4964
4969
|
const row = await findVaultRowInWrite(ctx, memoryId);
|
|
4965
|
-
|
|
4966
|
-
|
|
4970
|
+
return resolveTopicsWrite(row, linked, displayNamesOf(toEntityObjects(inputs)), source);
|
|
4971
|
+
}
|
|
4972
|
+
function prepareMemoryTopicsUpdate(write) {
|
|
4973
|
+
return prepareTopicsUpdate(write.row, write.topics, Date.now());
|
|
4967
4974
|
}
|
|
4968
4975
|
async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
4969
4976
|
if (entityInputs.length === 0) return [];
|
|
@@ -4972,15 +4979,16 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
4972
4979
|
let skipped = false;
|
|
4973
4980
|
let entities = [];
|
|
4974
4981
|
await ctx.database.write(async () => {
|
|
4975
|
-
const {
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4982
|
+
const {
|
|
4983
|
+
entities: byName,
|
|
4984
|
+
operations: entityOps,
|
|
4985
|
+
kindBackfills
|
|
4986
|
+
} = await upsertEntitiesInWrite(ctx, normalized);
|
|
4979
4987
|
entities = Array.from(byName.values());
|
|
4980
4988
|
const vaultRow = await findVaultRowInWrite(ctx, memoryId);
|
|
4981
4989
|
if (options?.unlessTopicsUserManaged && autoLinkBlocked(vaultRow)) {
|
|
4982
|
-
if (entityOps.length > 0) {
|
|
4983
|
-
await ctx.database.batch(...entityOps);
|
|
4990
|
+
if (entityOps.length > 0 || kindBackfills.length > 0) {
|
|
4991
|
+
await ctx.database.batch(...entityOps, ...prepareKindBackfills(kindBackfills));
|
|
4984
4992
|
}
|
|
4985
4993
|
skipped = true;
|
|
4986
4994
|
return;
|
|
@@ -5000,7 +5008,9 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
5000
5008
|
displayNamesOf(normalized),
|
|
5001
5009
|
options?.topicsSource ?? "auto"
|
|
5002
5010
|
);
|
|
5003
|
-
if (entityOps.length === 0 && toCreate.length === 0 && topicsWrite === null)
|
|
5011
|
+
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && topicsWrite === null) {
|
|
5012
|
+
return;
|
|
5013
|
+
}
|
|
5004
5014
|
const linkOps = toCreate.map(
|
|
5005
5015
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
5006
5016
|
record._setRaw("memory_id", memoryId);
|
|
@@ -5010,6 +5020,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
5010
5020
|
);
|
|
5011
5021
|
await ctx.database.batch(
|
|
5012
5022
|
...entityOps,
|
|
5023
|
+
...prepareKindBackfills(kindBackfills),
|
|
5013
5024
|
...linkOps,
|
|
5014
5025
|
...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
|
|
5015
5026
|
);
|
|
@@ -5047,10 +5058,11 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
|
5047
5058
|
skipped = true;
|
|
5048
5059
|
return;
|
|
5049
5060
|
}
|
|
5050
|
-
const {
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5061
|
+
const {
|
|
5062
|
+
entities: byName,
|
|
5063
|
+
operations: entityOps,
|
|
5064
|
+
kindBackfills
|
|
5065
|
+
} = await upsertEntitiesInWrite(ctx, normalized);
|
|
5054
5066
|
entities = Array.from(byName.values());
|
|
5055
5067
|
const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", memoryId)).fetch();
|
|
5056
5068
|
const keep = new Set(entities.map((e) => e.uniqueId));
|
|
@@ -5063,12 +5075,13 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
|
5063
5075
|
displayNamesOf(normalized),
|
|
5064
5076
|
options.topicsSource
|
|
5065
5077
|
);
|
|
5066
|
-
if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
5078
|
+
if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
5067
5079
|
return;
|
|
5068
5080
|
}
|
|
5069
5081
|
const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
|
|
5070
5082
|
await ctx.database.batch(
|
|
5071
5083
|
...entityOps,
|
|
5084
|
+
...prepareKindBackfills(kindBackfills),
|
|
5072
5085
|
...toCreate.map(
|
|
5073
5086
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
5074
5087
|
record._setRaw("memory_id", memoryId);
|
|
@@ -5897,10 +5910,16 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
|
|
|
5897
5910
|
const existing = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", memoryId)).fetch();
|
|
5898
5911
|
const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
|
|
5899
5912
|
if (staleLinks.length === 0 && entities.length > 0) return;
|
|
5900
|
-
const
|
|
5913
|
+
const topicsWrite = await resolveMemoryTopicsWrite(
|
|
5914
|
+
entityCtx,
|
|
5915
|
+
memoryId,
|
|
5916
|
+
linked,
|
|
5917
|
+
entities,
|
|
5918
|
+
"user"
|
|
5919
|
+
);
|
|
5901
5920
|
await ctx.database.batch(
|
|
5902
5921
|
...staleLinks.map((l) => l.prepareDestroyPermanently()),
|
|
5903
|
-
...
|
|
5922
|
+
...topicsWrite ? [prepareMemoryTopicsUpdate(topicsWrite)] : []
|
|
5904
5923
|
);
|
|
5905
5924
|
});
|
|
5906
5925
|
if (stale) return null;
|
|
@@ -6218,27 +6237,36 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
|
|
|
6218
6237
|
if (!entityCtx) {
|
|
6219
6238
|
throw new Error("backfillMemoryTopicsOp requires ctx.entityCtx");
|
|
6220
6239
|
}
|
|
6240
|
+
const uniqueIds = Array.from(new Set(memoryIds));
|
|
6241
|
+
if (uniqueIds.length === 0) return [];
|
|
6221
6242
|
const filled = [];
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
record = await ctx.vaultMemoryCollection.find(id);
|
|
6226
|
-
} catch {
|
|
6227
|
-
continue;
|
|
6228
|
-
}
|
|
6229
|
-
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
|
|
6230
|
-
if (parseTopics(record.topics) !== null) continue;
|
|
6231
|
-
const links = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", id)).unsafeFetchRaw();
|
|
6232
|
-
const entityIds = links.map((l) => String(l.entity_id));
|
|
6233
|
-
if (entityIds.length === 0) continue;
|
|
6234
|
-
const entities = await entityCtx.entityCollection.query(import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(entityIds))).fetch();
|
|
6235
|
-
if (entities.length === 0) continue;
|
|
6236
|
-
const source = record.topicsUserManaged ? "user" : "auto";
|
|
6243
|
+
const CHUNK = 500;
|
|
6244
|
+
for (let i = 0; i < uniqueIds.length; i += CHUNK) {
|
|
6245
|
+
const chunkIds = uniqueIds.slice(i, i + CHUNK);
|
|
6237
6246
|
await ctx.database.write(async () => {
|
|
6238
|
-
const
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6247
|
+
const writes = [];
|
|
6248
|
+
for (const id of chunkIds) {
|
|
6249
|
+
let record;
|
|
6250
|
+
try {
|
|
6251
|
+
record = await ctx.vaultMemoryCollection.find(id);
|
|
6252
|
+
} catch {
|
|
6253
|
+
continue;
|
|
6254
|
+
}
|
|
6255
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
|
|
6256
|
+
if (parseTopics(record.topics) !== null) continue;
|
|
6257
|
+
const links = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", id)).unsafeFetchRaw();
|
|
6258
|
+
const entityIds = links.map((l) => String(l.entity_id));
|
|
6259
|
+
if (entityIds.length === 0) continue;
|
|
6260
|
+
const entities = await entityCtx.entityCollection.query(import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(entityIds))).fetch();
|
|
6261
|
+
if (entities.length === 0) continue;
|
|
6262
|
+
const source = record.topicsUserManaged ? "user" : "auto";
|
|
6263
|
+
const write = await resolveMemoryTopicsWrite(entityCtx, id, entities, [], source);
|
|
6264
|
+
if (write) writes.push(write);
|
|
6265
|
+
}
|
|
6266
|
+
if (writes.length === 0) return;
|
|
6267
|
+
const prepared = writes.map(prepareMemoryTopicsUpdate);
|
|
6268
|
+
await ctx.database.batch(...prepared);
|
|
6269
|
+
for (const write of writes) filled.push(write.row.id);
|
|
6242
6270
|
});
|
|
6243
6271
|
}
|
|
6244
6272
|
return filled;
|
|
@@ -18387,7 +18415,7 @@ function createAutoExtractor(options) {
|
|
|
18387
18415
|
const t0 = Date.now();
|
|
18388
18416
|
void (async () => {
|
|
18389
18417
|
try {
|
|
18390
|
-
const { candidates, results, failedCount, outcome } = await extractAndRetain(
|
|
18418
|
+
const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
|
|
18391
18419
|
window2,
|
|
18392
18420
|
retainCtx,
|
|
18393
18421
|
{
|
|
@@ -18427,7 +18455,11 @@ function createAutoExtractor(options) {
|
|
|
18427
18455
|
failedCount,
|
|
18428
18456
|
durationMs: Date.now() - t0,
|
|
18429
18457
|
conversationId,
|
|
18430
|
-
outcome
|
|
18458
|
+
outcome,
|
|
18459
|
+
// Only set alongside `outcome: "empty-after-retry"`. Spread so the key
|
|
18460
|
+
// is absent rather than explicitly undefined on a healthy turn —
|
|
18461
|
+
// analytics backends store an explicit undefined as a real value.
|
|
18462
|
+
...failure !== void 0 && { failure }
|
|
18431
18463
|
});
|
|
18432
18464
|
} catch (err) {
|
|
18433
18465
|
options.onError?.(err instanceof Error ? err : new Error(String(err)), conversationId);
|
package/dist/react/index.d.mts
CHANGED
|
@@ -8114,6 +8114,14 @@ declare function relinkMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryI
|
|
|
8114
8114
|
* row has: a curated memory's topics are recorded as `user`, everything else as
|
|
8115
8115
|
* `auto`. Skips deleted, foreign-user, unlinked rows, and rows that already have
|
|
8116
8116
|
* a record. Returns the ids filled.
|
|
8117
|
+
*
|
|
8118
|
+
* Runs as ONE writer over the whole (caller-bounded) list, in two phases:
|
|
8119
|
+
* resolve every row's write — all the awaits — and only then prepare and batch,
|
|
8120
|
+
* synchronously. Resolving and preparing per row inside its own writer is what
|
|
8121
|
+
* fired WatermelonDB's "wasn't sent to batch() synchronously" diagnostic once
|
|
8122
|
+
* per memory (~107 on a single dev launch, sdk#891); it also cost N writes.
|
|
8123
|
+
* Same treatment as {@link stampTopicsExtractedAtOp}, including its
|
|
8124
|
+
* transpilation hazard — the prepare pass MUST stay a `.map()`.
|
|
8117
8125
|
*/
|
|
8118
8126
|
declare function backfillMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[]): Promise<string[]>;
|
|
8119
8127
|
declare function deleteAllVaultMemoriesForUserOp(ctx: VaultMemoryOperationsContext, userId: string): Promise<number>;
|
|
@@ -8398,6 +8406,22 @@ interface TurnCompleteEvent {
|
|
|
8398
8406
|
* previously indistinguishable (both surfaced as zero candidates).
|
|
8399
8407
|
*/
|
|
8400
8408
|
outcome: ExtractOutcome;
|
|
8409
|
+
/**
|
|
8410
|
+
* Present only alongside `outcome: "empty-after-retry"` — WHICH failure ended
|
|
8411
|
+
* the turn (#888).
|
|
8412
|
+
*
|
|
8413
|
+
* `outcome` says extraction gave up; this says why, from a stable enum. The
|
|
8414
|
+
* distinction is the whole point: a 2026-08-11 audit measured ~63% of
|
|
8415
|
+
* production extraction turns ending in `empty-after-retry` and could not tell
|
|
8416
|
+
* from telemetry whether the cause was the freeloader 403 everyone assumed or
|
|
8417
|
+
* something else. It took a Prometheus cross-check to find the real one — the
|
|
8418
|
+
* portal returning HTTP 200 with an empty body, which it counts as a success.
|
|
8419
|
+
*
|
|
8420
|
+
* Forward `failure.reason` into your extraction analytics event; all three
|
|
8421
|
+
* fields are bounded (an enum, an HTTP status, a small attempt count) and none
|
|
8422
|
+
* carries content.
|
|
8423
|
+
*/
|
|
8424
|
+
failure?: PortalLlmFailure;
|
|
8401
8425
|
}
|
|
8402
8426
|
/** @public */
|
|
8403
8427
|
/**
|
package/dist/react/index.d.ts
CHANGED
|
@@ -8114,6 +8114,14 @@ declare function relinkMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryI
|
|
|
8114
8114
|
* row has: a curated memory's topics are recorded as `user`, everything else as
|
|
8115
8115
|
* `auto`. Skips deleted, foreign-user, unlinked rows, and rows that already have
|
|
8116
8116
|
* a record. Returns the ids filled.
|
|
8117
|
+
*
|
|
8118
|
+
* Runs as ONE writer over the whole (caller-bounded) list, in two phases:
|
|
8119
|
+
* resolve every row's write — all the awaits — and only then prepare and batch,
|
|
8120
|
+
* synchronously. Resolving and preparing per row inside its own writer is what
|
|
8121
|
+
* fired WatermelonDB's "wasn't sent to batch() synchronously" diagnostic once
|
|
8122
|
+
* per memory (~107 on a single dev launch, sdk#891); it also cost N writes.
|
|
8123
|
+
* Same treatment as {@link stampTopicsExtractedAtOp}, including its
|
|
8124
|
+
* transpilation hazard — the prepare pass MUST stay a `.map()`.
|
|
8117
8125
|
*/
|
|
8118
8126
|
declare function backfillMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[]): Promise<string[]>;
|
|
8119
8127
|
declare function deleteAllVaultMemoriesForUserOp(ctx: VaultMemoryOperationsContext, userId: string): Promise<number>;
|
|
@@ -8398,6 +8406,22 @@ interface TurnCompleteEvent {
|
|
|
8398
8406
|
* previously indistinguishable (both surfaced as zero candidates).
|
|
8399
8407
|
*/
|
|
8400
8408
|
outcome: ExtractOutcome;
|
|
8409
|
+
/**
|
|
8410
|
+
* Present only alongside `outcome: "empty-after-retry"` — WHICH failure ended
|
|
8411
|
+
* the turn (#888).
|
|
8412
|
+
*
|
|
8413
|
+
* `outcome` says extraction gave up; this says why, from a stable enum. The
|
|
8414
|
+
* distinction is the whole point: a 2026-08-11 audit measured ~63% of
|
|
8415
|
+
* production extraction turns ending in `empty-after-retry` and could not tell
|
|
8416
|
+
* from telemetry whether the cause was the freeloader 403 everyone assumed or
|
|
8417
|
+
* something else. It took a Prometheus cross-check to find the real one — the
|
|
8418
|
+
* portal returning HTTP 200 with an empty body, which it counts as a success.
|
|
8419
|
+
*
|
|
8420
|
+
* Forward `failure.reason` into your extraction analytics event; all three
|
|
8421
|
+
* fields are bounded (an enum, an HTTP status, a small attempt count) and none
|
|
8422
|
+
* carries content.
|
|
8423
|
+
*/
|
|
8424
|
+
failure?: PortalLlmFailure;
|
|
8401
8425
|
}
|
|
8402
8426
|
/** @public */
|
|
8403
8427
|
/**
|
package/dist/react/index.mjs
CHANGED
|
@@ -178,7 +178,7 @@ import {
|
|
|
178
178
|
updateVaultMemoryOp,
|
|
179
179
|
useEncryption,
|
|
180
180
|
vaultMemoryToStored
|
|
181
|
-
} from "./chunk-
|
|
181
|
+
} from "./chunk-EJ65X4GI.mjs";
|
|
182
182
|
import {
|
|
183
183
|
PiiRedactor,
|
|
184
184
|
createStreamingDeAnonymizer,
|
|
@@ -8357,7 +8357,7 @@ function createAutoExtractor(options) {
|
|
|
8357
8357
|
const t0 = Date.now();
|
|
8358
8358
|
void (async () => {
|
|
8359
8359
|
try {
|
|
8360
|
-
const { candidates, results, failedCount, outcome } = await extractAndRetain(
|
|
8360
|
+
const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
|
|
8361
8361
|
window2,
|
|
8362
8362
|
retainCtx,
|
|
8363
8363
|
{
|
|
@@ -8397,7 +8397,11 @@ function createAutoExtractor(options) {
|
|
|
8397
8397
|
failedCount,
|
|
8398
8398
|
durationMs: Date.now() - t0,
|
|
8399
8399
|
conversationId,
|
|
8400
|
-
outcome
|
|
8400
|
+
outcome,
|
|
8401
|
+
// Only set alongside `outcome: "empty-after-retry"`. Spread so the key
|
|
8402
|
+
// is absent rather than explicitly undefined on a healthy turn —
|
|
8403
|
+
// analytics backends store an explicit undefined as a real value.
|
|
8404
|
+
...failure !== void 0 && { failure }
|
|
8401
8405
|
});
|
|
8402
8406
|
} catch (err) {
|
|
8403
8407
|
options.onError?.(err instanceof Error ? err : new Error(String(err)), conversationId);
|