@anuma/sdk 3.12.2 → 3.13.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-ORVYPS3R.mjs → chunk-BOAKGY4R.mjs} +277 -46
- package/dist/expo/index.cjs +330 -46
- package/dist/expo/index.d.mts +133 -13
- package/dist/expo/index.d.ts +133 -13
- package/dist/expo/index.mjs +56 -2
- package/dist/expo/{recall-A4WZELH5.mjs → recall-O65PGPIQ.mjs} +1 -1
- package/dist/react/{chunk-QURYPMCE.mjs → chunk-XXCZVG4Z.mjs} +290 -49
- package/dist/react/index.cjs +343 -49
- package/dist/react/index.d.mts +146 -14
- package/dist/react/index.d.ts +146 -14
- package/dist/react/index.mjs +56 -2
- package/dist/react/{recall-NQNZDPNU.mjs → recall-KTJCUAXN.mjs} +1 -1
- package/dist/server/{chunk-FXGG7JA5.mjs → chunk-ME6ZQN52.mjs} +277 -46
- package/dist/server/index.cjs +330 -46
- package/dist/server/index.d.mts +133 -13
- package/dist/server/index.d.ts +133 -13
- package/dist/server/index.mjs +56 -2
- package/dist/server/{recall-SLMMIFHM.mjs → recall-RMCSM6L6.mjs} +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -16770,6 +16770,31 @@ var init_clientConfig = __esm({
|
|
|
16770
16770
|
function normalizeEntityName(name) {
|
|
16771
16771
|
return name.trim().toLowerCase();
|
|
16772
16772
|
}
|
|
16773
|
+
function serializeTopics(topics) {
|
|
16774
|
+
return JSON.stringify(topics);
|
|
16775
|
+
}
|
|
16776
|
+
function parseTopics(value) {
|
|
16777
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
16778
|
+
let parsed;
|
|
16779
|
+
try {
|
|
16780
|
+
parsed = JSON.parse(value);
|
|
16781
|
+
} catch {
|
|
16782
|
+
return null;
|
|
16783
|
+
}
|
|
16784
|
+
if (!Array.isArray(parsed)) return null;
|
|
16785
|
+
const out = [];
|
|
16786
|
+
for (const raw of parsed) {
|
|
16787
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
16788
|
+
const { name, kind, source } = raw;
|
|
16789
|
+
if (typeof name !== "string" || name.trim().length === 0) continue;
|
|
16790
|
+
out.push({
|
|
16791
|
+
name,
|
|
16792
|
+
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
16793
|
+
source: source === "user" ? "user" : "auto"
|
|
16794
|
+
});
|
|
16795
|
+
}
|
|
16796
|
+
return out;
|
|
16797
|
+
}
|
|
16773
16798
|
var ENTITY_KINDS;
|
|
16774
16799
|
var init_types3 = __esm({
|
|
16775
16800
|
"src/lib/db/entities/types.ts"() {
|
|
@@ -16835,9 +16860,62 @@ async function upsertEntitiesInWrite(ctx, entities) {
|
|
|
16835
16860
|
for (const record of created) out.set(record.canonicalName, entityToStored(record));
|
|
16836
16861
|
return { entities: out, operations: [...created, ...updated] };
|
|
16837
16862
|
}
|
|
16863
|
+
async function findVaultRowInWrite(ctx, memoryId) {
|
|
16864
|
+
try {
|
|
16865
|
+
const rows = await ctx.database.get("memory_vault").query(import_watermelondb16.Q.where("id", memoryId)).fetch();
|
|
16866
|
+
return rows[0] ?? null;
|
|
16867
|
+
} catch {
|
|
16868
|
+
return null;
|
|
16869
|
+
}
|
|
16870
|
+
}
|
|
16871
|
+
function prepareTopicsUpdate(row, topics, now) {
|
|
16872
|
+
const originalUpdatedAt = row.updatedAt.getTime();
|
|
16873
|
+
return row.prepareUpdate((r) => {
|
|
16874
|
+
r._setRaw("topics", serializeTopics(topics));
|
|
16875
|
+
r._setRaw("topics_updated_at", now);
|
|
16876
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
16877
|
+
});
|
|
16878
|
+
}
|
|
16879
|
+
function displayNamesOf(entities) {
|
|
16880
|
+
const out = /* @__PURE__ */ new Map();
|
|
16881
|
+
for (const e of entities) {
|
|
16882
|
+
const name = e.name.trim();
|
|
16883
|
+
const normalized = normalizeEntityName(name);
|
|
16884
|
+
if (normalized.length === 0 || out.has(normalized)) continue;
|
|
16885
|
+
out.set(normalized, name);
|
|
16886
|
+
}
|
|
16887
|
+
return out;
|
|
16888
|
+
}
|
|
16889
|
+
function topicsEqual(stored, computed) {
|
|
16890
|
+
if (stored === null || stored.length !== computed.length) return false;
|
|
16891
|
+
const key = (t) => JSON.stringify([t.name, t.kind ?? null, t.source]);
|
|
16892
|
+
const storedKeys = stored.map(key).sort();
|
|
16893
|
+
const computedKeys = computed.map(key).sort();
|
|
16894
|
+
return storedKeys.every((k, i) => k === computedKeys[i]);
|
|
16895
|
+
}
|
|
16896
|
+
function resolveTopicsWrite(row, linked, displayNames, source) {
|
|
16897
|
+
if (row === null || source === null) return null;
|
|
16898
|
+
const topics = topicsForEntities(linked, displayNames, source);
|
|
16899
|
+
if (topicsEqual(parseTopics(row.topics), topics)) return null;
|
|
16900
|
+
return { row, topics };
|
|
16901
|
+
}
|
|
16902
|
+
function topicsForEntities(entities, displayNames, source) {
|
|
16903
|
+
return entities.map((e) => {
|
|
16904
|
+
const name = displayNames.get(e.canonicalName) ?? e.canonicalName;
|
|
16905
|
+
return { name, ...e.kind ? { kind: e.kind } : {}, source };
|
|
16906
|
+
});
|
|
16907
|
+
}
|
|
16908
|
+
function toEntityObjects(entityInputs) {
|
|
16909
|
+
return entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
|
|
16910
|
+
}
|
|
16911
|
+
async function prepareMemoryTopicsUpdate(ctx, memoryId, linked, inputs, source) {
|
|
16912
|
+
const row = await findVaultRowInWrite(ctx, memoryId);
|
|
16913
|
+
const write = resolveTopicsWrite(row, linked, displayNamesOf(toEntityObjects(inputs)), source);
|
|
16914
|
+
return write === null ? null : prepareTopicsUpdate(write.row, write.topics, Date.now());
|
|
16915
|
+
}
|
|
16838
16916
|
async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
16839
16917
|
if (entityInputs.length === 0) return [];
|
|
16840
|
-
const normalized = entityInputs
|
|
16918
|
+
const normalized = toEntityObjects(entityInputs);
|
|
16841
16919
|
const userId = ctx.userId;
|
|
16842
16920
|
let skipped = false;
|
|
16843
16921
|
let entities = [];
|
|
@@ -16847,7 +16925,8 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
16847
16925
|
normalized
|
|
16848
16926
|
);
|
|
16849
16927
|
entities = Array.from(byName.values());
|
|
16850
|
-
|
|
16928
|
+
const vaultRow = await findVaultRowInWrite(ctx, memoryId);
|
|
16929
|
+
if (options?.unlessTopicsUserManaged && autoLinkBlocked(vaultRow)) {
|
|
16851
16930
|
if (entityOps.length > 0) {
|
|
16852
16931
|
await ctx.database.batch(...entityOps);
|
|
16853
16932
|
}
|
|
@@ -16858,7 +16937,18 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
16858
16937
|
const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb16.Q.where("memory_id", memoryId)).fetch();
|
|
16859
16938
|
const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
|
|
16860
16939
|
const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
|
|
16861
|
-
|
|
16940
|
+
const keptIds = entities.map((e) => e.uniqueId);
|
|
16941
|
+
const carriedOver = await resolveEntitiesByIds(
|
|
16942
|
+
ctx,
|
|
16943
|
+
[...existingEntityIds].filter((id) => !keptIds.includes(id))
|
|
16944
|
+
);
|
|
16945
|
+
const topicsWrite = resolveTopicsWrite(
|
|
16946
|
+
vaultRow,
|
|
16947
|
+
[...carriedOver, ...entities],
|
|
16948
|
+
displayNamesOf(normalized),
|
|
16949
|
+
options?.topicsSource ?? "auto"
|
|
16950
|
+
);
|
|
16951
|
+
if (entityOps.length === 0 && toCreate.length === 0 && topicsWrite === null) return;
|
|
16862
16952
|
const linkOps = toCreate.map(
|
|
16863
16953
|
(e) => ctx.memoryEntityCollection.prepareCreate((record) => {
|
|
16864
16954
|
record._setRaw("memory_id", memoryId);
|
|
@@ -16866,26 +16956,42 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
|
|
|
16866
16956
|
if (userId !== void 0) record._setRaw("user_id", userId);
|
|
16867
16957
|
})
|
|
16868
16958
|
);
|
|
16869
|
-
await ctx.database.batch(
|
|
16959
|
+
await ctx.database.batch(
|
|
16960
|
+
...entityOps,
|
|
16961
|
+
...linkOps,
|
|
16962
|
+
...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
|
|
16963
|
+
);
|
|
16870
16964
|
});
|
|
16871
16965
|
return skipped ? [] : entities;
|
|
16872
16966
|
}
|
|
16873
|
-
async function
|
|
16874
|
-
|
|
16875
|
-
|
|
16876
|
-
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
}
|
|
16967
|
+
async function resolveEntitiesByIds(ctx, entityIds) {
|
|
16968
|
+
if (entityIds.length === 0) return [];
|
|
16969
|
+
const rows = await ctx.entityCollection.query(import_watermelondb16.Q.where("id", import_watermelondb16.Q.oneOf(Array.from(entityIds)))).fetch();
|
|
16970
|
+
return rows.map(entityToStored);
|
|
16971
|
+
}
|
|
16972
|
+
function autoLinkBlocked(row) {
|
|
16973
|
+
return !row || !!row.isDeleted || !!row.topicsUserManaged;
|
|
16881
16974
|
}
|
|
16882
16975
|
async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
16883
|
-
|
|
16976
|
+
return replaceMemoryEntities(ctx, memoryId, entityInputs, {
|
|
16977
|
+
guarded: true,
|
|
16978
|
+
topicsSource: "auto"
|
|
16979
|
+
});
|
|
16980
|
+
}
|
|
16981
|
+
async function relinkMemoryEntitiesFromTopicsOp(ctx, memoryId, topics) {
|
|
16982
|
+
return replaceMemoryEntities(ctx, memoryId, topics, {
|
|
16983
|
+
guarded: false,
|
|
16984
|
+
topicsSource: null
|
|
16985
|
+
});
|
|
16986
|
+
}
|
|
16987
|
+
async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
|
|
16988
|
+
const normalized = toEntityObjects(entityInputs);
|
|
16884
16989
|
const userId = ctx.userId;
|
|
16885
16990
|
let skipped = false;
|
|
16886
16991
|
let entities = [];
|
|
16887
16992
|
await ctx.database.write(async () => {
|
|
16888
|
-
|
|
16993
|
+
const vaultRow = await findVaultRowInWrite(ctx, memoryId);
|
|
16994
|
+
if (options.guarded && autoLinkBlocked(vaultRow)) {
|
|
16889
16995
|
skipped = true;
|
|
16890
16996
|
return;
|
|
16891
16997
|
}
|
|
@@ -16899,7 +17005,15 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
|
16899
17005
|
const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
|
|
16900
17006
|
const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
|
|
16901
17007
|
const toDestroy = existingLinks.filter((l) => !keep.has(String(l.entityId)));
|
|
16902
|
-
|
|
17008
|
+
const topicsWrite = resolveTopicsWrite(
|
|
17009
|
+
vaultRow,
|
|
17010
|
+
entities,
|
|
17011
|
+
displayNamesOf(normalized),
|
|
17012
|
+
options.topicsSource
|
|
17013
|
+
);
|
|
17014
|
+
if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
|
|
17015
|
+
return;
|
|
17016
|
+
}
|
|
16903
17017
|
const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
|
|
16904
17018
|
await ctx.database.batch(
|
|
16905
17019
|
...entityOps,
|
|
@@ -16911,7 +17025,8 @@ async function replaceMemoryEntitiesGuardedOp(ctx, memoryId, entityInputs) {
|
|
|
16911
17025
|
})
|
|
16912
17026
|
),
|
|
16913
17027
|
...toDestroy.map((l) => l.prepareDestroyPermanently()),
|
|
16914
|
-
...orphans.map((e) => e.prepareDestroyPermanently())
|
|
17028
|
+
...orphans.map((e) => e.prepareDestroyPermanently()),
|
|
17029
|
+
...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
|
|
16915
17030
|
);
|
|
16916
17031
|
});
|
|
16917
17032
|
return skipped ? null : entities;
|
|
@@ -17110,6 +17225,8 @@ function vaultMemoryToStoredRaw(memory) {
|
|
|
17110
17225
|
eventTimeEnd: memory.eventTimeEnd ?? null,
|
|
17111
17226
|
eventTimeKind: memory.eventTimeKind ?? null,
|
|
17112
17227
|
topicsUserManaged: memory.topicsUserManaged ?? false,
|
|
17228
|
+
topics: parseTopics(memory.topics),
|
|
17229
|
+
topicsUpdatedAt: memory.topicsUpdatedAt ?? null,
|
|
17113
17230
|
topicsExtractedAt: memory.topicsExtractedAt ?? null,
|
|
17114
17231
|
topicsExtractedVersion: memory.topicsExtractedVersion ?? null,
|
|
17115
17232
|
supersededBy: memory.supersededBy ?? null,
|
|
@@ -17386,6 +17503,8 @@ function vaultMemoryRawToStoredRaw(raw) {
|
|
|
17386
17503
|
eventTimeKind: raw.event_time_kind ?? null,
|
|
17387
17504
|
// SQLite stores booleans as 0/1, LokiJS as true/false — coerce both.
|
|
17388
17505
|
topicsUserManaged: raw.topics_user_managed === true || raw.topics_user_managed === 1,
|
|
17506
|
+
topics: parseTopics(raw.topics),
|
|
17507
|
+
topicsUpdatedAt: raw.topics_updated_at ?? null,
|
|
17389
17508
|
topicsExtractedAt: raw.topics_extracted_at ?? null,
|
|
17390
17509
|
topicsExtractedVersion: raw.topics_extracted_version ?? null,
|
|
17391
17510
|
supersededBy: raw.superseded_by ?? null,
|
|
@@ -17732,56 +17851,111 @@ async function getUnfiledVaultMemoriesOp(ctx) {
|
|
|
17732
17851
|
(record) => vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
|
|
17733
17852
|
);
|
|
17734
17853
|
}
|
|
17854
|
+
async function linkedEntityNamesByMemory(entityCtx, memoryIds) {
|
|
17855
|
+
const CHUNK = 500;
|
|
17856
|
+
const entityIdByMemory = /* @__PURE__ */ new Map();
|
|
17857
|
+
const allEntityIds = /* @__PURE__ */ new Set();
|
|
17858
|
+
for (let i = 0; i < memoryIds.length; i += CHUNK) {
|
|
17859
|
+
const links = await entityCtx.memoryEntityCollection.query(import_watermelondb17.Q.where("memory_id", import_watermelondb17.Q.oneOf(memoryIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
|
|
17860
|
+
for (const link of links) {
|
|
17861
|
+
const memoryId = String(link.memory_id);
|
|
17862
|
+
const entityId = String(link.entity_id);
|
|
17863
|
+
allEntityIds.add(entityId);
|
|
17864
|
+
let bucket = entityIdByMemory.get(memoryId);
|
|
17865
|
+
if (!bucket) {
|
|
17866
|
+
bucket = /* @__PURE__ */ new Set();
|
|
17867
|
+
entityIdByMemory.set(memoryId, bucket);
|
|
17868
|
+
}
|
|
17869
|
+
bucket.add(entityId);
|
|
17870
|
+
}
|
|
17871
|
+
}
|
|
17872
|
+
const entityIds = Array.from(allEntityIds);
|
|
17873
|
+
const nameById = /* @__PURE__ */ new Map();
|
|
17874
|
+
for (let i = 0; i < entityIds.length; i += CHUNK) {
|
|
17875
|
+
const entities = await entityCtx.entityCollection.query(import_watermelondb17.Q.where("id", import_watermelondb17.Q.oneOf(entityIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
|
|
17876
|
+
for (const e of entities) {
|
|
17877
|
+
if (typeof e.canonical_name === "string") nameById.set(String(e.id), e.canonical_name);
|
|
17878
|
+
}
|
|
17879
|
+
}
|
|
17880
|
+
const out = /* @__PURE__ */ new Map();
|
|
17881
|
+
for (const [memoryId, ids] of entityIdByMemory) {
|
|
17882
|
+
const names = /* @__PURE__ */ new Set();
|
|
17883
|
+
for (const id of ids) {
|
|
17884
|
+
const name = nameById.get(id);
|
|
17885
|
+
if (name !== void 0) names.add(name);
|
|
17886
|
+
}
|
|
17887
|
+
if (names.size > 0) out.set(memoryId, names);
|
|
17888
|
+
}
|
|
17889
|
+
return out;
|
|
17890
|
+
}
|
|
17891
|
+
function linksDivergeFromTopics(topics, linked) {
|
|
17892
|
+
const wanted = new Set(
|
|
17893
|
+
topics.map((t) => normalizeEntityName(t.name)).filter((n) => n.length > 0)
|
|
17894
|
+
);
|
|
17895
|
+
if (wanted.size !== linked.size) return true;
|
|
17896
|
+
for (const name of wanted) if (!linked.has(name)) return true;
|
|
17897
|
+
return false;
|
|
17898
|
+
}
|
|
17735
17899
|
async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
17736
17900
|
const entityCtx = ctx.entityCtx;
|
|
17737
17901
|
if (!entityCtx) {
|
|
17738
17902
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
17739
17903
|
}
|
|
17740
|
-
const conditions = [
|
|
17741
|
-
// Boolean optional column: null (legacy) and false both mean auto-managed.
|
|
17742
|
-
// Q.notEq(true) is a NULL trap on SQLite (NULL <> TRUE is NULL) — enumerate.
|
|
17743
|
-
import_watermelondb17.Q.or(import_watermelondb17.Q.where("topics_user_managed", null), import_watermelondb17.Q.where("topics_user_managed", false)),
|
|
17744
|
-
...baseVaultConditions(ctx),
|
|
17745
|
-
import_watermelondb17.Q.sortBy("created_at", import_watermelondb17.Q.desc)
|
|
17746
|
-
];
|
|
17904
|
+
const conditions = [...baseVaultConditions(ctx), import_watermelondb17.Q.sortBy("created_at", import_watermelondb17.Q.desc)];
|
|
17747
17905
|
const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
|
|
17748
|
-
const
|
|
17749
|
-
|
|
17906
|
+
const linkedNames = await linkedEntityNamesByMemory(
|
|
17907
|
+
entityCtx,
|
|
17908
|
+
rows.map((r) => r.id)
|
|
17909
|
+
);
|
|
17910
|
+
const pendingRaw = [];
|
|
17911
|
+
const stampedPendingRaw = [];
|
|
17912
|
+
const linkedUnstampedAll = [];
|
|
17913
|
+
const topicsToRelinkAll = [];
|
|
17914
|
+
const topicsBackfillAll = [];
|
|
17750
17915
|
for (const raw of rows) {
|
|
17916
|
+
const id = raw.id;
|
|
17917
|
+
const topics = parseTopics(raw.topics);
|
|
17918
|
+
const linked = linkedNames.get(id);
|
|
17919
|
+
if (topics !== null && topics.length > 0 && linksDivergeFromTopics(topics, linked ?? /* @__PURE__ */ new Set())) {
|
|
17920
|
+
topicsToRelinkAll.push(id);
|
|
17921
|
+
continue;
|
|
17922
|
+
}
|
|
17923
|
+
if (raw.topics_user_managed) {
|
|
17924
|
+
if (topics === null && linked !== void 0) topicsBackfillAll.push(id);
|
|
17925
|
+
continue;
|
|
17926
|
+
}
|
|
17751
17927
|
const stamp = raw.topics_extracted_at ?? null;
|
|
17928
|
+
let isPending = false;
|
|
17752
17929
|
if (stamp !== null) {
|
|
17753
17930
|
const version = raw.topics_extracted_version ?? 0;
|
|
17754
17931
|
if (raw.updated_at > stamp || version < TOPICS_EXTRACTION_VERSION) {
|
|
17755
|
-
|
|
17932
|
+
stampedPendingRaw.push(raw);
|
|
17933
|
+
isPending = true;
|
|
17756
17934
|
}
|
|
17757
|
-
} else {
|
|
17758
|
-
|
|
17759
|
-
}
|
|
17760
|
-
}
|
|
17761
|
-
const linkedIds = /* @__PURE__ */ new Set();
|
|
17762
|
-
const CHUNK = 500;
|
|
17763
|
-
for (let i = 0; i < unstamped.length; i += CHUNK) {
|
|
17764
|
-
const ids = unstamped.slice(i, i + CHUNK).map((r) => r.id);
|
|
17765
|
-
const links = await entityCtx.memoryEntityCollection.query(import_watermelondb17.Q.where("memory_id", import_watermelondb17.Q.oneOf(ids))).unsafeFetchRaw();
|
|
17766
|
-
for (const link of links) linkedIds.add(String(link.memory_id));
|
|
17767
|
-
}
|
|
17768
|
-
const linkedUnstampedAll = [];
|
|
17769
|
-
const pendingRaw = [...stampedPending];
|
|
17770
|
-
for (const raw of unstamped) {
|
|
17771
|
-
if (linkedIds.has(raw.id)) {
|
|
17772
|
-
linkedUnstampedAll.push(raw.id);
|
|
17935
|
+
} else if (linked !== void 0) {
|
|
17936
|
+
linkedUnstampedAll.push(id);
|
|
17773
17937
|
} else {
|
|
17774
17938
|
pendingRaw.push(raw);
|
|
17939
|
+
isPending = true;
|
|
17940
|
+
}
|
|
17941
|
+
if (!isPending && topics === null && linked !== void 0) {
|
|
17942
|
+
topicsBackfillAll.push(id);
|
|
17775
17943
|
}
|
|
17776
17944
|
}
|
|
17777
17945
|
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
17778
|
-
const
|
|
17779
|
-
const
|
|
17946
|
+
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
17947
|
+
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
17780
17948
|
const pending = await mapInBatches(
|
|
17781
17949
|
limitedPendingRaw,
|
|
17782
17950
|
(raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
|
|
17783
17951
|
);
|
|
17784
|
-
|
|
17952
|
+
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
17953
|
+
return {
|
|
17954
|
+
pending,
|
|
17955
|
+
linkedUnstamped: applyCap(linkedUnstampedAll),
|
|
17956
|
+
topicsToRelink: applyCap(topicsToRelinkAll),
|
|
17957
|
+
topicsBackfill: applyCap(topicsBackfillAll)
|
|
17958
|
+
};
|
|
17785
17959
|
}
|
|
17786
17960
|
async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) {
|
|
17787
17961
|
if (memoryIds.length === 0) return [];
|
|
@@ -17815,6 +17989,61 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = T
|
|
|
17815
17989
|
}
|
|
17816
17990
|
return stamped;
|
|
17817
17991
|
}
|
|
17992
|
+
async function relinkMemoryTopicsOp(ctx, memoryIds) {
|
|
17993
|
+
const entityCtx = ctx.entityCtx;
|
|
17994
|
+
if (!entityCtx) {
|
|
17995
|
+
throw new Error("relinkMemoryTopicsOp requires ctx.entityCtx");
|
|
17996
|
+
}
|
|
17997
|
+
const relinked = [];
|
|
17998
|
+
for (const id of Array.from(new Set(memoryIds))) {
|
|
17999
|
+
let record;
|
|
18000
|
+
try {
|
|
18001
|
+
record = await ctx.vaultMemoryCollection.find(id);
|
|
18002
|
+
} catch {
|
|
18003
|
+
continue;
|
|
18004
|
+
}
|
|
18005
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
|
|
18006
|
+
const topics = parseTopics(record.topics);
|
|
18007
|
+
if (topics === null || topics.length === 0) continue;
|
|
18008
|
+
try {
|
|
18009
|
+
await relinkMemoryEntitiesFromTopicsOp(entityCtx, id, topics);
|
|
18010
|
+
relinked.push(id);
|
|
18011
|
+
} catch (err) {
|
|
18012
|
+
getLogger().warn("[memory/topics] relink failed", err);
|
|
18013
|
+
}
|
|
18014
|
+
}
|
|
18015
|
+
return relinked;
|
|
18016
|
+
}
|
|
18017
|
+
async function backfillMemoryTopicsOp(ctx, memoryIds) {
|
|
18018
|
+
const entityCtx = ctx.entityCtx;
|
|
18019
|
+
if (!entityCtx) {
|
|
18020
|
+
throw new Error("backfillMemoryTopicsOp requires ctx.entityCtx");
|
|
18021
|
+
}
|
|
18022
|
+
const filled = [];
|
|
18023
|
+
for (const id of Array.from(new Set(memoryIds))) {
|
|
18024
|
+
let record;
|
|
18025
|
+
try {
|
|
18026
|
+
record = await ctx.vaultMemoryCollection.find(id);
|
|
18027
|
+
} catch {
|
|
18028
|
+
continue;
|
|
18029
|
+
}
|
|
18030
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
|
|
18031
|
+
if (parseTopics(record.topics) !== null) continue;
|
|
18032
|
+
const links = await entityCtx.memoryEntityCollection.query(import_watermelondb17.Q.where("memory_id", id)).unsafeFetchRaw();
|
|
18033
|
+
const entityIds = links.map((l) => String(l.entity_id));
|
|
18034
|
+
if (entityIds.length === 0) continue;
|
|
18035
|
+
const entities = await entityCtx.entityCollection.query(import_watermelondb17.Q.where("id", import_watermelondb17.Q.oneOf(entityIds))).fetch();
|
|
18036
|
+
if (entities.length === 0) continue;
|
|
18037
|
+
const source = record.topicsUserManaged ? "user" : "auto";
|
|
18038
|
+
await ctx.database.write(async () => {
|
|
18039
|
+
const topicsOp = await prepareMemoryTopicsUpdate(entityCtx, id, entities, [], source);
|
|
18040
|
+
if (!topicsOp) return;
|
|
18041
|
+
await ctx.database.batch(topicsOp);
|
|
18042
|
+
filled.push(id);
|
|
18043
|
+
});
|
|
18044
|
+
}
|
|
18045
|
+
return filled;
|
|
18046
|
+
}
|
|
17818
18047
|
async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
|
|
17819
18048
|
if (ctx.userId !== void 0 && ctx.userId !== userId) return 0;
|
|
17820
18049
|
const records = await ctx.vaultMemoryCollection.query(import_watermelondb17.Q.where("user_id", userId), import_watermelondb17.Q.where("is_deleted", false)).fetch();
|
|
@@ -17956,6 +18185,7 @@ var init_operations3 = __esm({
|
|
|
17956
18185
|
import_watermelondb17 = __toESM(require_watermelondb());
|
|
17957
18186
|
init_logger();
|
|
17958
18187
|
init_operations2();
|
|
18188
|
+
init_types3();
|
|
17959
18189
|
init_encryption2();
|
|
17960
18190
|
NON_PRIVATE_VISIBILITIES = ["public"];
|
|
17961
18191
|
KNOWN_TRUST_TIERS = /* @__PURE__ */ new Set(["quarantined", "trusted"]);
|
|
@@ -23235,6 +23465,7 @@ __export(index_exports, {
|
|
|
23235
23465
|
activatedToolSetNames: () => activatedToolSetNames,
|
|
23236
23466
|
analyzeStyle: () => analyzeStyle,
|
|
23237
23467
|
archiveVaultMemoryOp: () => archiveVaultMemoryOp,
|
|
23468
|
+
backfillMemoryTopicsOp: () => backfillMemoryTopicsOp,
|
|
23238
23469
|
buildConnectorGuidance: () => buildConnectorGuidance,
|
|
23239
23470
|
buildDeniedToolsRider: () => buildDeniedToolsRider,
|
|
23240
23471
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
@@ -23392,6 +23623,7 @@ __export(index_exports, {
|
|
|
23392
23623
|
rankProfileCandidates: () => rankProfileCandidates,
|
|
23393
23624
|
recall: () => recall,
|
|
23394
23625
|
reflect: () => reflect,
|
|
23626
|
+
relinkMemoryTopicsOp: () => relinkMemoryTopicsOp,
|
|
23395
23627
|
relinkMisclassifiedVideosOp: () => relinkMisclassifiedVideosOp,
|
|
23396
23628
|
renderTemplate: () => renderTemplate,
|
|
23397
23629
|
requestEncryptionKey: () => requestEncryptionKey,
|
|
@@ -23778,6 +24010,12 @@ __decorateClass([
|
|
|
23778
24010
|
__decorateClass([
|
|
23779
24011
|
(0, import_decorators6.field)("topics_user_managed")
|
|
23780
24012
|
], VaultMemory.prototype, "topicsUserManaged", 2);
|
|
24013
|
+
__decorateClass([
|
|
24014
|
+
(0, import_decorators6.field)("topics")
|
|
24015
|
+
], VaultMemory.prototype, "topics", 2);
|
|
24016
|
+
__decorateClass([
|
|
24017
|
+
(0, import_decorators6.field)("topics_updated_at")
|
|
24018
|
+
], VaultMemory.prototype, "topicsUpdatedAt", 2);
|
|
23781
24019
|
__decorateClass([
|
|
23782
24020
|
(0, import_decorators6.field)("topics_extracted_at")
|
|
23783
24021
|
], VaultMemory.prototype, "topicsExtractedAt", 2);
|
|
@@ -23961,7 +24199,7 @@ __decorateClass([
|
|
|
23961
24199
|
], VaultFolder.prototype, "context", 2);
|
|
23962
24200
|
|
|
23963
24201
|
// src/lib/db/schema.ts
|
|
23964
|
-
var SDK_SCHEMA_VERSION =
|
|
24202
|
+
var SDK_SCHEMA_VERSION = 42;
|
|
23965
24203
|
var sdkSchema = (0, import_watermelondb12.appSchema)({
|
|
23966
24204
|
version: SDK_SCHEMA_VERSION,
|
|
23967
24205
|
tables: [
|
|
@@ -24083,9 +24321,33 @@ var sdkSchema = (0, import_watermelondb12.appSchema)({
|
|
|
24083
24321
|
// links). Auto-extraction then leaves its links alone — the user owns
|
|
24084
24322
|
// them. Null/false = topics are auto-derived (default).
|
|
24085
24323
|
{ name: "topics_user_managed", type: "boolean", isOptional: true },
|
|
24324
|
+
// The memory's topics as a DURABLE, SYNCED record: a JSON array of
|
|
24325
|
+
// `{name, kind?, source}`, `name` in the caller's display casing (unlike
|
|
24326
|
+
// `entity.canonical_name`, which is lowercased and has no display
|
|
24327
|
+
// column). `entity` / `memory_entity` are a device-local INDEX over this
|
|
24328
|
+
// — their ids are locally generated, so they can never sync — and a
|
|
24329
|
+
// restored device rebuilds them from these names with no LLM call.
|
|
24330
|
+
// Null = predates v42 (backfilled from the row's current links by the
|
|
24331
|
+
// sweep's topicsBackfill bucket). See getMemoriesNeedingTopicExtractionOp.
|
|
24332
|
+
{ name: "topics", type: "string", isOptional: true },
|
|
24333
|
+
// Unix ms of the last write to `topics`. A SECOND timestamp is required:
|
|
24334
|
+
// every topic writer deliberately pins `updated_at` so a topic change
|
|
24335
|
+
// doesn't inflate recall's recency multiplier, and both client sync
|
|
24336
|
+
// paths key on `updated_at` — so without this a topic-only change would
|
|
24337
|
+
// neither upload nor merge. Null = `topics` never written.
|
|
24338
|
+
{ name: "topics_updated_at", type: "number", isOptional: true },
|
|
24086
24339
|
// Unix ms of the last LLM topic-extraction pass over this memory's
|
|
24087
24340
|
// content. Null = never extracted standalone (legacy rows with entity
|
|
24088
24341
|
// links are grandfathered — see getMemoriesNeedingTopicExtractionOp).
|
|
24342
|
+
//
|
|
24343
|
+
// DEPRECATED (v42) — `topics_updated_at` subsumes this and
|
|
24344
|
+
// `topics_extracted_version` both: null there means never processed, and
|
|
24345
|
+
// non-null with an empty `topics` means processed and found nothing
|
|
24346
|
+
// (today's "answered empty" case), while a release-time
|
|
24347
|
+
// EXTRACTOR_CHANGED_AT constant compared against `topics_updated_at`
|
|
24348
|
+
// replaces the version check. Both columns are kept only to avoid a
|
|
24349
|
+
// column-drop migration in an otherwise additive list; removing them is
|
|
24350
|
+
// a follow-up once `topics` is proven in production.
|
|
24089
24351
|
{ name: "topics_extracted_at", type: "number", isOptional: true },
|
|
24090
24352
|
// Write-time supersession (A2). When set, this fact was replaced by a
|
|
24091
24353
|
// newer, incompatible-value fact (e.g. "Lives in Portland" superseded by
|
|
@@ -24099,6 +24361,9 @@ var sdkSchema = (0, import_watermelondb12.appSchema)({
|
|
|
24099
24361
|
// The extraction-logic version this memory was last stamped under. Null
|
|
24100
24362
|
// (pre-v38) is treated as version 0, so a bump of TOPICS_EXTRACTION_VERSION
|
|
24101
24363
|
// re-extracts stale rows. See getMemoriesNeedingTopicExtractionOp.
|
|
24364
|
+
//
|
|
24365
|
+
// DEPRECATED (v42) alongside `topics_extracted_at` — same rationale and
|
|
24366
|
+
// same removal follow-up; see that column's note above.
|
|
24102
24367
|
{ name: "topics_extracted_version", type: "number", isOptional: true },
|
|
24103
24368
|
// Re-observation watermark (C3). Unix ms of the last time retain() merged
|
|
24104
24369
|
// a duplicate observation into this fact (proof_count++). Distinct from
|
|
@@ -24832,6 +25097,23 @@ var sdkMigrations = (0, import_migrations.schemaMigrations)({
|
|
|
24832
25097
|
]
|
|
24833
25098
|
})
|
|
24834
25099
|
]
|
|
25100
|
+
},
|
|
25101
|
+
// v41 -> v42: topics + topics_updated_at on memory_vault — the durable,
|
|
25102
|
+
// synced record of a memory's topics. Existing rows keep both NULL: the
|
|
25103
|
+
// sweep's topicsBackfill bucket fills them from each row's current entity
|
|
25104
|
+
// links (no LLM), capped under the worker's `limit` so the one-time
|
|
25105
|
+
// re-upload it triggers drains across sweeps instead of spiking.
|
|
25106
|
+
{
|
|
25107
|
+
toVersion: 42,
|
|
25108
|
+
steps: [
|
|
25109
|
+
(0, import_migrations.addColumns)({
|
|
25110
|
+
table: "memory_vault",
|
|
25111
|
+
columns: [
|
|
25112
|
+
{ name: "topics", type: "string", isOptional: true },
|
|
25113
|
+
{ name: "topics_updated_at", type: "number", isOptional: true }
|
|
25114
|
+
]
|
|
25115
|
+
})
|
|
25116
|
+
]
|
|
24835
25117
|
}
|
|
24836
25118
|
]
|
|
24837
25119
|
});
|
|
@@ -35748,6 +36030,7 @@ function buildSystemPrompt(ctx, options) {
|
|
|
35748
36030
|
activatedToolSetNames,
|
|
35749
36031
|
analyzeStyle,
|
|
35750
36032
|
archiveVaultMemoryOp,
|
|
36033
|
+
backfillMemoryTopicsOp,
|
|
35751
36034
|
buildConnectorGuidance,
|
|
35752
36035
|
buildDeniedToolsRider,
|
|
35753
36036
|
buildSystemPrompt,
|
|
@@ -35905,6 +36188,7 @@ function buildSystemPrompt(ctx, options) {
|
|
|
35905
36188
|
rankProfileCandidates,
|
|
35906
36189
|
recall,
|
|
35907
36190
|
reflect,
|
|
36191
|
+
relinkMemoryTopicsOp,
|
|
35908
36192
|
relinkMisclassifiedVideosOp,
|
|
35909
36193
|
renderTemplate,
|
|
35910
36194
|
requestEncryptionKey,
|