@anuma/sdk 4.0.1 → 4.0.2
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-BOAKGY4R.mjs → chunk-VBO6KSNQ.mjs} +44 -4
- package/dist/expo/index.cjs +43 -3
- package/dist/expo/index.d.mts +15 -3
- package/dist/expo/index.d.ts +15 -3
- package/dist/expo/index.mjs +1 -1
- package/dist/expo/{recall-O65PGPIQ.mjs → recall-OCQOA7M3.mjs} +1 -1
- package/dist/react/{chunk-BAJL46NX.mjs → chunk-BS7C4CUW.mjs} +38 -26
- package/dist/react/index.cjs +37 -25
- package/dist/react/index.d.mts +26 -4
- package/dist/react/index.d.ts +26 -4
- package/dist/react/index.mjs +1 -1
- package/dist/react/{recall-R3VX2UST.mjs → recall-WXCZHXHZ.mjs} +1 -1
- package/dist/server/{chunk-WZE6ZVKE.mjs → chunk-WADHFDMA.mjs} +44 -4
- package/dist/server/index.cjs +43 -3
- package/dist/server/index.d.mts +15 -3
- package/dist/server/index.d.ts +15 -3
- package/dist/server/index.mjs +1 -1
- package/dist/server/{recall-YZWIV6IH.mjs → recall-QTVT7HCZ.mjs} +1 -1
- package/package.json +1 -1
|
@@ -2740,6 +2740,30 @@ async function updateVaultMemoryOp(ctx, id, opts) {
|
|
|
2740
2740
|
return null;
|
|
2741
2741
|
}
|
|
2742
2742
|
}
|
|
2743
|
+
async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
|
|
2744
|
+
let record;
|
|
2745
|
+
try {
|
|
2746
|
+
record = await ctx.vaultMemoryCollection.find(memoryId);
|
|
2747
|
+
} catch {
|
|
2748
|
+
return false;
|
|
2749
|
+
}
|
|
2750
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
2751
|
+
const originalUpdatedAt = record.updatedAt.getTime();
|
|
2752
|
+
let cleared = false;
|
|
2753
|
+
await ctx.database.write(async () => {
|
|
2754
|
+
if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
|
|
2755
|
+
cleared = true;
|
|
2756
|
+
await record.update((r) => {
|
|
2757
|
+
r._setRaw("topics_user_managed", false);
|
|
2758
|
+
r._setRaw("topics_extracted_version", null);
|
|
2759
|
+
if (record.topicsExtractedAt === null) {
|
|
2760
|
+
r._setRaw("topics_extracted_at", originalUpdatedAt);
|
|
2761
|
+
}
|
|
2762
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
2763
|
+
});
|
|
2764
|
+
});
|
|
2765
|
+
return cleared;
|
|
2766
|
+
}
|
|
2743
2767
|
async function setMemoryVisibilityOp(ctx, id, opts) {
|
|
2744
2768
|
let record;
|
|
2745
2769
|
try {
|
|
@@ -2901,11 +2925,13 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
2901
2925
|
entityCtx,
|
|
2902
2926
|
rows.map((r) => r.id)
|
|
2903
2927
|
);
|
|
2928
|
+
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
2904
2929
|
const pendingRaw = [];
|
|
2905
2930
|
const stampedPendingRaw = [];
|
|
2906
2931
|
const linkedUnstampedAll = [];
|
|
2907
2932
|
const topicsToRelinkAll = [];
|
|
2908
2933
|
const topicsBackfillAll = [];
|
|
2934
|
+
const emptyCurationToClear = [];
|
|
2909
2935
|
for (const raw of rows) {
|
|
2910
2936
|
const id = raw.id;
|
|
2911
2937
|
const topics = parseTopics(raw.topics);
|
|
@@ -2915,8 +2941,12 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
2915
2941
|
continue;
|
|
2916
2942
|
}
|
|
2917
2943
|
if (raw.topics_user_managed) {
|
|
2918
|
-
if (topics
|
|
2919
|
-
|
|
2944
|
+
if (topics !== null || linked !== void 0) {
|
|
2945
|
+
if (topics === null) topicsBackfillAll.push(id);
|
|
2946
|
+
continue;
|
|
2947
|
+
}
|
|
2948
|
+
if (cap !== void 0 && emptyCurationToClear.length >= cap) continue;
|
|
2949
|
+
emptyCurationToClear.push(id);
|
|
2920
2950
|
}
|
|
2921
2951
|
const stamp = raw.topics_extracted_at ?? null;
|
|
2922
2952
|
let isPending = false;
|
|
@@ -2932,11 +2962,21 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
2932
2962
|
pendingRaw.push(raw);
|
|
2933
2963
|
isPending = true;
|
|
2934
2964
|
}
|
|
2965
|
+
if (!isPending && linked === void 0 && topics === null) {
|
|
2966
|
+
pendingRaw.push(raw);
|
|
2967
|
+
isPending = true;
|
|
2968
|
+
}
|
|
2935
2969
|
if (!isPending && topics === null && linked !== void 0) {
|
|
2936
2970
|
topicsBackfillAll.push(id);
|
|
2937
2971
|
}
|
|
2938
2972
|
}
|
|
2939
|
-
const
|
|
2973
|
+
for (const id of emptyCurationToClear) {
|
|
2974
|
+
try {
|
|
2975
|
+
await clearMemoryTopicsOverrideOp(ctx, id, { unlessTopicsRecorded: true });
|
|
2976
|
+
} catch (err) {
|
|
2977
|
+
getLogger().warn("[memory/topics] repair clear failed", err);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2940
2980
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
2941
2981
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
2942
2982
|
const pending = await mapInBatches(
|
|
@@ -6119,7 +6159,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
6119
6159
|
legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
|
|
6120
6160
|
);
|
|
6121
6161
|
}
|
|
6122
|
-
const { recall: recall2 } = await import("./recall-
|
|
6162
|
+
const { recall: recall2 } = await import("./recall-OCQOA7M3.mjs");
|
|
6123
6163
|
let recallDegraded = [];
|
|
6124
6164
|
const result = await recall2(
|
|
6125
6165
|
query,
|
package/dist/expo/index.cjs
CHANGED
|
@@ -4980,6 +4980,30 @@ async function updateVaultMemoryOp(ctx, id, opts) {
|
|
|
4980
4980
|
return null;
|
|
4981
4981
|
}
|
|
4982
4982
|
}
|
|
4983
|
+
async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
|
|
4984
|
+
let record;
|
|
4985
|
+
try {
|
|
4986
|
+
record = await ctx.vaultMemoryCollection.find(memoryId);
|
|
4987
|
+
} catch {
|
|
4988
|
+
return false;
|
|
4989
|
+
}
|
|
4990
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
4991
|
+
const originalUpdatedAt = record.updatedAt.getTime();
|
|
4992
|
+
let cleared = false;
|
|
4993
|
+
await ctx.database.write(async () => {
|
|
4994
|
+
if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
|
|
4995
|
+
cleared = true;
|
|
4996
|
+
await record.update((r) => {
|
|
4997
|
+
r._setRaw("topics_user_managed", false);
|
|
4998
|
+
r._setRaw("topics_extracted_version", null);
|
|
4999
|
+
if (record.topicsExtractedAt === null) {
|
|
5000
|
+
r._setRaw("topics_extracted_at", originalUpdatedAt);
|
|
5001
|
+
}
|
|
5002
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
5003
|
+
});
|
|
5004
|
+
});
|
|
5005
|
+
return cleared;
|
|
5006
|
+
}
|
|
4983
5007
|
async function setMemoryVisibilityOp(ctx, id, opts) {
|
|
4984
5008
|
let record;
|
|
4985
5009
|
try {
|
|
@@ -5140,11 +5164,13 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
5140
5164
|
entityCtx,
|
|
5141
5165
|
rows.map((r) => r.id)
|
|
5142
5166
|
);
|
|
5167
|
+
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
5143
5168
|
const pendingRaw = [];
|
|
5144
5169
|
const stampedPendingRaw = [];
|
|
5145
5170
|
const linkedUnstampedAll = [];
|
|
5146
5171
|
const topicsToRelinkAll = [];
|
|
5147
5172
|
const topicsBackfillAll = [];
|
|
5173
|
+
const emptyCurationToClear = [];
|
|
5148
5174
|
for (const raw of rows) {
|
|
5149
5175
|
const id = raw.id;
|
|
5150
5176
|
const topics = parseTopics(raw.topics);
|
|
@@ -5154,8 +5180,12 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
5154
5180
|
continue;
|
|
5155
5181
|
}
|
|
5156
5182
|
if (raw.topics_user_managed) {
|
|
5157
|
-
if (topics
|
|
5158
|
-
|
|
5183
|
+
if (topics !== null || linked !== void 0) {
|
|
5184
|
+
if (topics === null) topicsBackfillAll.push(id);
|
|
5185
|
+
continue;
|
|
5186
|
+
}
|
|
5187
|
+
if (cap !== void 0 && emptyCurationToClear.length >= cap) continue;
|
|
5188
|
+
emptyCurationToClear.push(id);
|
|
5159
5189
|
}
|
|
5160
5190
|
const stamp = raw.topics_extracted_at ?? null;
|
|
5161
5191
|
let isPending = false;
|
|
@@ -5171,11 +5201,21 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
5171
5201
|
pendingRaw.push(raw);
|
|
5172
5202
|
isPending = true;
|
|
5173
5203
|
}
|
|
5204
|
+
if (!isPending && linked === void 0 && topics === null) {
|
|
5205
|
+
pendingRaw.push(raw);
|
|
5206
|
+
isPending = true;
|
|
5207
|
+
}
|
|
5174
5208
|
if (!isPending && topics === null && linked !== void 0) {
|
|
5175
5209
|
topicsBackfillAll.push(id);
|
|
5176
5210
|
}
|
|
5177
5211
|
}
|
|
5178
|
-
const
|
|
5212
|
+
for (const id of emptyCurationToClear) {
|
|
5213
|
+
try {
|
|
5214
|
+
await clearMemoryTopicsOverrideOp(ctx, id, { unlessTopicsRecorded: true });
|
|
5215
|
+
} catch (err) {
|
|
5216
|
+
getLogger().warn("[memory/topics] repair clear failed", err);
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5179
5219
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
5180
5220
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
5181
5221
|
const pending = await mapInBatches(
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -6083,6 +6083,12 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
6083
6083
|
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
6084
6084
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
6085
6085
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
6086
|
+
*
|
|
6087
|
+
* Bumping this is a WHOLE-VAULT re-extraction: the gate is unconditional across
|
|
6088
|
+
* every stamped row, and `stampTopicsExtractedAtOp` stamps this version on every
|
|
6089
|
+
* healthy row, so a bump sends each user's entire extracted vault back to the
|
|
6090
|
+
* LLM. Never reach for it to repair a subset — see the pre-v42-restore repair in
|
|
6091
|
+
* {@link getMemoriesNeedingTopicExtractionOp}, which targets the damaged rows.
|
|
6086
6092
|
*/
|
|
6087
6093
|
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
6088
6094
|
/**
|
|
@@ -6095,9 +6101,10 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6095
6101
|
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
6096
6102
|
* with no entity links, plus stamped rows edited since their last pass
|
|
6097
6103
|
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
6098
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}
|
|
6099
|
-
*
|
|
6100
|
-
*
|
|
6104
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}, plus the
|
|
6105
|
+
* pre-v42-restore repair: a stamped row left with neither links nor a `topics`
|
|
6106
|
+
* record, which no no-LLM bucket can reach. Edited / stale-version rows come
|
|
6107
|
+
* first (they get priority under `limit`), each group newest-created first.
|
|
6101
6108
|
*/
|
|
6102
6109
|
pending: StoredVaultMemory[];
|
|
6103
6110
|
/**
|
|
@@ -6149,6 +6156,11 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6149
6156
|
* between the record and the index and never re-derive them. That's why the
|
|
6150
6157
|
* ownership filter lives in the partition below rather than in the query: a
|
|
6151
6158
|
* restored curated memory is exactly the row whose index must be rebuilt.
|
|
6159
|
+
*
|
|
6160
|
+
* NOT purely a read: a curated row with no `topics` record AND no usable link is
|
|
6161
|
+
* a contradiction only a pre-v42 restore produces, and this is the one place
|
|
6162
|
+
* that can see all three facts at once, so it clears the flag there (capped by
|
|
6163
|
+
* `limit`) before returning. See the branch for why that's safe.
|
|
6152
6164
|
*/
|
|
6153
6165
|
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
6154
6166
|
limit?: number;
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -6083,6 +6083,12 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
6083
6083
|
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
6084
6084
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
6085
6085
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
6086
|
+
*
|
|
6087
|
+
* Bumping this is a WHOLE-VAULT re-extraction: the gate is unconditional across
|
|
6088
|
+
* every stamped row, and `stampTopicsExtractedAtOp` stamps this version on every
|
|
6089
|
+
* healthy row, so a bump sends each user's entire extracted vault back to the
|
|
6090
|
+
* LLM. Never reach for it to repair a subset — see the pre-v42-restore repair in
|
|
6091
|
+
* {@link getMemoriesNeedingTopicExtractionOp}, which targets the damaged rows.
|
|
6086
6092
|
*/
|
|
6087
6093
|
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
6088
6094
|
/**
|
|
@@ -6095,9 +6101,10 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6095
6101
|
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
6096
6102
|
* with no entity links, plus stamped rows edited since their last pass
|
|
6097
6103
|
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
6098
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}
|
|
6099
|
-
*
|
|
6100
|
-
*
|
|
6104
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}, plus the
|
|
6105
|
+
* pre-v42-restore repair: a stamped row left with neither links nor a `topics`
|
|
6106
|
+
* record, which no no-LLM bucket can reach. Edited / stale-version rows come
|
|
6107
|
+
* first (they get priority under `limit`), each group newest-created first.
|
|
6101
6108
|
*/
|
|
6102
6109
|
pending: StoredVaultMemory[];
|
|
6103
6110
|
/**
|
|
@@ -6149,6 +6156,11 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6149
6156
|
* between the record and the index and never re-derive them. That's why the
|
|
6150
6157
|
* ownership filter lives in the partition below rather than in the query: a
|
|
6151
6158
|
* restored curated memory is exactly the row whose index must be rebuilt.
|
|
6159
|
+
*
|
|
6160
|
+
* NOT purely a read: a curated row with no `topics` record AND no usable link is
|
|
6161
|
+
* a contradiction only a pre-v42 restore produces, and this is the one place
|
|
6162
|
+
* that can see all three facts at once, so it clears the flag there (capped by
|
|
6163
|
+
* `limit`) before returning. See the branch for why that's safe.
|
|
6152
6164
|
*/
|
|
6153
6165
|
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
6154
6166
|
limit?: number;
|
package/dist/expo/index.mjs
CHANGED
|
@@ -2927,7 +2927,7 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
|
|
|
2927
2927
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return null;
|
|
2928
2928
|
let stale = false;
|
|
2929
2929
|
const originalUpdatedAt = record.updatedAt.getTime();
|
|
2930
|
-
await ctx.database.write(async () => {
|
|
2930
|
+
await ctx.database.write(async (writer) => {
|
|
2931
2931
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
|
|
2932
2932
|
stale = true;
|
|
2933
2933
|
return;
|
|
@@ -2936,30 +2936,23 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
|
|
|
2936
2936
|
r._setRaw("topics_user_managed", true);
|
|
2937
2937
|
r._setRaw("updated_at", originalUpdatedAt);
|
|
2938
2938
|
});
|
|
2939
|
+
const linked = entities.length > 0 ? await writer.callWriter(
|
|
2940
|
+
() => linkMemoryEntitiesOp(entityCtx, memoryId, entities, { topicsSource: "user" })
|
|
2941
|
+
) : [];
|
|
2942
|
+
const keep = new Set(linked.map((e) => e.uniqueId));
|
|
2943
|
+
const existing = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", memoryId)).fetch();
|
|
2944
|
+
const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
|
|
2945
|
+
if (staleLinks.length === 0 && entities.length > 0) return;
|
|
2946
|
+
const topicsOp = await prepareMemoryTopicsUpdate(entityCtx, memoryId, linked, entities, "user");
|
|
2947
|
+
await ctx.database.batch(
|
|
2948
|
+
...staleLinks.map((l) => l.prepareDestroyPermanently()),
|
|
2949
|
+
...topicsOp ? [topicsOp] : []
|
|
2950
|
+
);
|
|
2939
2951
|
});
|
|
2940
2952
|
if (stale) return null;
|
|
2941
|
-
const linked = entities.length > 0 ? await linkMemoryEntitiesOp(entityCtx, memoryId, entities, { topicsSource: "user" }) : [];
|
|
2942
|
-
const keep = new Set(linked.map((e) => e.uniqueId));
|
|
2943
|
-
const existing = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", memoryId)).fetch();
|
|
2944
|
-
const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
|
|
2945
|
-
if (staleLinks.length > 0 || entities.length === 0) {
|
|
2946
|
-
await ctx.database.write(async () => {
|
|
2947
|
-
const topicsOp = await prepareMemoryTopicsUpdate(
|
|
2948
|
-
entityCtx,
|
|
2949
|
-
memoryId,
|
|
2950
|
-
linked,
|
|
2951
|
-
entities,
|
|
2952
|
-
"user"
|
|
2953
|
-
);
|
|
2954
|
-
await ctx.database.batch(
|
|
2955
|
-
...staleLinks.map((l) => l.prepareDestroyPermanently()),
|
|
2956
|
-
...topicsOp ? [topicsOp] : []
|
|
2957
|
-
);
|
|
2958
|
-
});
|
|
2959
|
-
}
|
|
2960
2953
|
return vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
2961
2954
|
}
|
|
2962
|
-
async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
2955
|
+
async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
|
|
2963
2956
|
let record;
|
|
2964
2957
|
try {
|
|
2965
2958
|
record = await ctx.vaultMemoryCollection.find(memoryId);
|
|
@@ -2968,7 +2961,10 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
|
2968
2961
|
}
|
|
2969
2962
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
2970
2963
|
const originalUpdatedAt = record.updatedAt.getTime();
|
|
2964
|
+
let cleared = false;
|
|
2971
2965
|
await ctx.database.write(async () => {
|
|
2966
|
+
if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
|
|
2967
|
+
cleared = true;
|
|
2972
2968
|
await record.update((r) => {
|
|
2973
2969
|
r._setRaw("topics_user_managed", false);
|
|
2974
2970
|
r._setRaw("topics_extracted_version", null);
|
|
@@ -2978,7 +2974,7 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
|
2978
2974
|
r._setRaw("updated_at", originalUpdatedAt);
|
|
2979
2975
|
});
|
|
2980
2976
|
});
|
|
2981
|
-
return
|
|
2977
|
+
return cleared;
|
|
2982
2978
|
}
|
|
2983
2979
|
async function setMemoryVisibilityOp(ctx, id, opts) {
|
|
2984
2980
|
let record;
|
|
@@ -3141,11 +3137,13 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
3141
3137
|
entityCtx,
|
|
3142
3138
|
rows.map((r) => r.id)
|
|
3143
3139
|
);
|
|
3140
|
+
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
3144
3141
|
const pendingRaw = [];
|
|
3145
3142
|
const stampedPendingRaw = [];
|
|
3146
3143
|
const linkedUnstampedAll = [];
|
|
3147
3144
|
const topicsToRelinkAll = [];
|
|
3148
3145
|
const topicsBackfillAll = [];
|
|
3146
|
+
const emptyCurationToClear = [];
|
|
3149
3147
|
for (const raw of rows) {
|
|
3150
3148
|
const id = raw.id;
|
|
3151
3149
|
const topics = parseTopics(raw.topics);
|
|
@@ -3155,8 +3153,12 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
3155
3153
|
continue;
|
|
3156
3154
|
}
|
|
3157
3155
|
if (raw.topics_user_managed) {
|
|
3158
|
-
if (topics
|
|
3159
|
-
|
|
3156
|
+
if (topics !== null || linked !== void 0) {
|
|
3157
|
+
if (topics === null) topicsBackfillAll.push(id);
|
|
3158
|
+
continue;
|
|
3159
|
+
}
|
|
3160
|
+
if (cap !== void 0 && emptyCurationToClear.length >= cap) continue;
|
|
3161
|
+
emptyCurationToClear.push(id);
|
|
3160
3162
|
}
|
|
3161
3163
|
const stamp = raw.topics_extracted_at ?? null;
|
|
3162
3164
|
let isPending = false;
|
|
@@ -3172,11 +3174,21 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
3172
3174
|
pendingRaw.push(raw);
|
|
3173
3175
|
isPending = true;
|
|
3174
3176
|
}
|
|
3177
|
+
if (!isPending && linked === void 0 && topics === null) {
|
|
3178
|
+
pendingRaw.push(raw);
|
|
3179
|
+
isPending = true;
|
|
3180
|
+
}
|
|
3175
3181
|
if (!isPending && topics === null && linked !== void 0) {
|
|
3176
3182
|
topicsBackfillAll.push(id);
|
|
3177
3183
|
}
|
|
3178
3184
|
}
|
|
3179
|
-
const
|
|
3185
|
+
for (const id of emptyCurationToClear) {
|
|
3186
|
+
try {
|
|
3187
|
+
await clearMemoryTopicsOverrideOp(ctx, id, { unlessTopicsRecorded: true });
|
|
3188
|
+
} catch (err) {
|
|
3189
|
+
getLogger().warn("[memory/topics] repair clear failed", err);
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3180
3192
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
3181
3193
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
3182
3194
|
const pending = await mapInBatches(
|
|
@@ -6430,7 +6442,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
6430
6442
|
legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
|
|
6431
6443
|
);
|
|
6432
6444
|
}
|
|
6433
|
-
const { recall: recall2 } = await import("./recall-
|
|
6445
|
+
const { recall: recall2 } = await import("./recall-WXCZHXHZ.mjs");
|
|
6434
6446
|
let recallDegraded = [];
|
|
6435
6447
|
const result = await recall2(
|
|
6436
6448
|
query,
|
package/dist/react/index.cjs
CHANGED
|
@@ -5238,7 +5238,7 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
|
|
|
5238
5238
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return null;
|
|
5239
5239
|
let stale = false;
|
|
5240
5240
|
const originalUpdatedAt = record.updatedAt.getTime();
|
|
5241
|
-
await ctx.database.write(async () => {
|
|
5241
|
+
await ctx.database.write(async (writer) => {
|
|
5242
5242
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
|
|
5243
5243
|
stale = true;
|
|
5244
5244
|
return;
|
|
@@ -5247,30 +5247,23 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
|
|
|
5247
5247
|
r._setRaw("topics_user_managed", true);
|
|
5248
5248
|
r._setRaw("updated_at", originalUpdatedAt);
|
|
5249
5249
|
});
|
|
5250
|
+
const linked = entities.length > 0 ? await writer.callWriter(
|
|
5251
|
+
() => linkMemoryEntitiesOp(entityCtx, memoryId, entities, { topicsSource: "user" })
|
|
5252
|
+
) : [];
|
|
5253
|
+
const keep = new Set(linked.map((e) => e.uniqueId));
|
|
5254
|
+
const existing = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", memoryId)).fetch();
|
|
5255
|
+
const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
|
|
5256
|
+
if (staleLinks.length === 0 && entities.length > 0) return;
|
|
5257
|
+
const topicsOp = await prepareMemoryTopicsUpdate(entityCtx, memoryId, linked, entities, "user");
|
|
5258
|
+
await ctx.database.batch(
|
|
5259
|
+
...staleLinks.map((l) => l.prepareDestroyPermanently()),
|
|
5260
|
+
...topicsOp ? [topicsOp] : []
|
|
5261
|
+
);
|
|
5250
5262
|
});
|
|
5251
5263
|
if (stale) return null;
|
|
5252
|
-
const linked = entities.length > 0 ? await linkMemoryEntitiesOp(entityCtx, memoryId, entities, { topicsSource: "user" }) : [];
|
|
5253
|
-
const keep = new Set(linked.map((e) => e.uniqueId));
|
|
5254
|
-
const existing = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", memoryId)).fetch();
|
|
5255
|
-
const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
|
|
5256
|
-
if (staleLinks.length > 0 || entities.length === 0) {
|
|
5257
|
-
await ctx.database.write(async () => {
|
|
5258
|
-
const topicsOp = await prepareMemoryTopicsUpdate(
|
|
5259
|
-
entityCtx,
|
|
5260
|
-
memoryId,
|
|
5261
|
-
linked,
|
|
5262
|
-
entities,
|
|
5263
|
-
"user"
|
|
5264
|
-
);
|
|
5265
|
-
await ctx.database.batch(
|
|
5266
|
-
...staleLinks.map((l) => l.prepareDestroyPermanently()),
|
|
5267
|
-
...topicsOp ? [topicsOp] : []
|
|
5268
|
-
);
|
|
5269
|
-
});
|
|
5270
|
-
}
|
|
5271
5264
|
return vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
5272
5265
|
}
|
|
5273
|
-
async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
5266
|
+
async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
|
|
5274
5267
|
let record;
|
|
5275
5268
|
try {
|
|
5276
5269
|
record = await ctx.vaultMemoryCollection.find(memoryId);
|
|
@@ -5279,7 +5272,10 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
|
5279
5272
|
}
|
|
5280
5273
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
5281
5274
|
const originalUpdatedAt = record.updatedAt.getTime();
|
|
5275
|
+
let cleared = false;
|
|
5282
5276
|
await ctx.database.write(async () => {
|
|
5277
|
+
if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
|
|
5278
|
+
cleared = true;
|
|
5283
5279
|
await record.update((r) => {
|
|
5284
5280
|
r._setRaw("topics_user_managed", false);
|
|
5285
5281
|
r._setRaw("topics_extracted_version", null);
|
|
@@ -5289,7 +5285,7 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
|
5289
5285
|
r._setRaw("updated_at", originalUpdatedAt);
|
|
5290
5286
|
});
|
|
5291
5287
|
});
|
|
5292
|
-
return
|
|
5288
|
+
return cleared;
|
|
5293
5289
|
}
|
|
5294
5290
|
async function setMemoryVisibilityOp(ctx, id, opts) {
|
|
5295
5291
|
let record;
|
|
@@ -5451,11 +5447,13 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
5451
5447
|
entityCtx,
|
|
5452
5448
|
rows.map((r) => r.id)
|
|
5453
5449
|
);
|
|
5450
|
+
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
5454
5451
|
const pendingRaw = [];
|
|
5455
5452
|
const stampedPendingRaw = [];
|
|
5456
5453
|
const linkedUnstampedAll = [];
|
|
5457
5454
|
const topicsToRelinkAll = [];
|
|
5458
5455
|
const topicsBackfillAll = [];
|
|
5456
|
+
const emptyCurationToClear = [];
|
|
5459
5457
|
for (const raw of rows) {
|
|
5460
5458
|
const id = raw.id;
|
|
5461
5459
|
const topics = parseTopics(raw.topics);
|
|
@@ -5465,8 +5463,12 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
5465
5463
|
continue;
|
|
5466
5464
|
}
|
|
5467
5465
|
if (raw.topics_user_managed) {
|
|
5468
|
-
if (topics
|
|
5469
|
-
|
|
5466
|
+
if (topics !== null || linked !== void 0) {
|
|
5467
|
+
if (topics === null) topicsBackfillAll.push(id);
|
|
5468
|
+
continue;
|
|
5469
|
+
}
|
|
5470
|
+
if (cap !== void 0 && emptyCurationToClear.length >= cap) continue;
|
|
5471
|
+
emptyCurationToClear.push(id);
|
|
5470
5472
|
}
|
|
5471
5473
|
const stamp = raw.topics_extracted_at ?? null;
|
|
5472
5474
|
let isPending = false;
|
|
@@ -5482,11 +5484,21 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
5482
5484
|
pendingRaw.push(raw);
|
|
5483
5485
|
isPending = true;
|
|
5484
5486
|
}
|
|
5487
|
+
if (!isPending && linked === void 0 && topics === null) {
|
|
5488
|
+
pendingRaw.push(raw);
|
|
5489
|
+
isPending = true;
|
|
5490
|
+
}
|
|
5485
5491
|
if (!isPending && topics === null && linked !== void 0) {
|
|
5486
5492
|
topicsBackfillAll.push(id);
|
|
5487
5493
|
}
|
|
5488
5494
|
}
|
|
5489
|
-
const
|
|
5495
|
+
for (const id of emptyCurationToClear) {
|
|
5496
|
+
try {
|
|
5497
|
+
await clearMemoryTopicsOverrideOp(ctx, id, { unlessTopicsRecorded: true });
|
|
5498
|
+
} catch (err) {
|
|
5499
|
+
getLogger().warn("[memory/topics] repair clear failed", err);
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5490
5502
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
5491
5503
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
5492
5504
|
const pending = await mapInBatches(
|
package/dist/react/index.d.mts
CHANGED
|
@@ -7457,8 +7457,18 @@ declare function setMemoryEntitiesOp(ctx: VaultMemoryOperationsContext, memoryId
|
|
|
7457
7457
|
* see the schema note. This op's version-invalidation trick is the reason the
|
|
7458
7458
|
* earlier plan to exclude them from sync was dropped, and it's the last piece
|
|
7459
7459
|
* that has to move before they can go.
|
|
7460
|
+
*
|
|
7461
|
+
* `options.unlessTopicsRecorded` declines the reset when the row already has a
|
|
7462
|
+
* `topics` record, re-checked INSIDE the serialized writer. Only the repair path
|
|
7463
|
+
* in {@link getMemoriesNeedingTopicExtractionOp} passes it: that path clears the
|
|
7464
|
+
* flag off rows whose curation is provably empty, and a `setMemoryEntitiesOp`
|
|
7465
|
+
* committing in the gap would have written a real record the autotagger must not
|
|
7466
|
+
* be handed. The user-facing reset leaves it off — resetting a memory that HAS
|
|
7467
|
+
* curated topics is the whole point there.
|
|
7460
7468
|
*/
|
|
7461
|
-
declare function clearMemoryTopicsOverrideOp(ctx: VaultMemoryOperationsContext, memoryId: string
|
|
7469
|
+
declare function clearMemoryTopicsOverrideOp(ctx: VaultMemoryOperationsContext, memoryId: string, options?: {
|
|
7470
|
+
unlessTopicsRecorded?: boolean;
|
|
7471
|
+
}): Promise<boolean>;
|
|
7462
7472
|
/**
|
|
7463
7473
|
* Set a memory's People Nearby visibility (and optionally its twin opt-in).
|
|
7464
7474
|
*
|
|
@@ -7500,6 +7510,12 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
7500
7510
|
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
7501
7511
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
7502
7512
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
7513
|
+
*
|
|
7514
|
+
* Bumping this is a WHOLE-VAULT re-extraction: the gate is unconditional across
|
|
7515
|
+
* every stamped row, and `stampTopicsExtractedAtOp` stamps this version on every
|
|
7516
|
+
* healthy row, so a bump sends each user's entire extracted vault back to the
|
|
7517
|
+
* LLM. Never reach for it to repair a subset — see the pre-v42-restore repair in
|
|
7518
|
+
* {@link getMemoriesNeedingTopicExtractionOp}, which targets the damaged rows.
|
|
7503
7519
|
*/
|
|
7504
7520
|
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
7505
7521
|
/**
|
|
@@ -7512,9 +7528,10 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
7512
7528
|
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
7513
7529
|
* with no entity links, plus stamped rows edited since their last pass
|
|
7514
7530
|
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
7515
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}
|
|
7516
|
-
*
|
|
7517
|
-
*
|
|
7531
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}, plus the
|
|
7532
|
+
* pre-v42-restore repair: a stamped row left with neither links nor a `topics`
|
|
7533
|
+
* record, which no no-LLM bucket can reach. Edited / stale-version rows come
|
|
7534
|
+
* first (they get priority under `limit`), each group newest-created first.
|
|
7518
7535
|
*/
|
|
7519
7536
|
pending: StoredVaultMemory[];
|
|
7520
7537
|
/**
|
|
@@ -7566,6 +7583,11 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
7566
7583
|
* between the record and the index and never re-derive them. That's why the
|
|
7567
7584
|
* ownership filter lives in the partition below rather than in the query: a
|
|
7568
7585
|
* restored curated memory is exactly the row whose index must be rebuilt.
|
|
7586
|
+
*
|
|
7587
|
+
* NOT purely a read: a curated row with no `topics` record AND no usable link is
|
|
7588
|
+
* a contradiction only a pre-v42 restore produces, and this is the one place
|
|
7589
|
+
* that can see all three facts at once, so it clears the flag there (capped by
|
|
7590
|
+
* `limit`) before returning. See the branch for why that's safe.
|
|
7569
7591
|
*/
|
|
7570
7592
|
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
7571
7593
|
limit?: number;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -7457,8 +7457,18 @@ declare function setMemoryEntitiesOp(ctx: VaultMemoryOperationsContext, memoryId
|
|
|
7457
7457
|
* see the schema note. This op's version-invalidation trick is the reason the
|
|
7458
7458
|
* earlier plan to exclude them from sync was dropped, and it's the last piece
|
|
7459
7459
|
* that has to move before they can go.
|
|
7460
|
+
*
|
|
7461
|
+
* `options.unlessTopicsRecorded` declines the reset when the row already has a
|
|
7462
|
+
* `topics` record, re-checked INSIDE the serialized writer. Only the repair path
|
|
7463
|
+
* in {@link getMemoriesNeedingTopicExtractionOp} passes it: that path clears the
|
|
7464
|
+
* flag off rows whose curation is provably empty, and a `setMemoryEntitiesOp`
|
|
7465
|
+
* committing in the gap would have written a real record the autotagger must not
|
|
7466
|
+
* be handed. The user-facing reset leaves it off — resetting a memory that HAS
|
|
7467
|
+
* curated topics is the whole point there.
|
|
7460
7468
|
*/
|
|
7461
|
-
declare function clearMemoryTopicsOverrideOp(ctx: VaultMemoryOperationsContext, memoryId: string
|
|
7469
|
+
declare function clearMemoryTopicsOverrideOp(ctx: VaultMemoryOperationsContext, memoryId: string, options?: {
|
|
7470
|
+
unlessTopicsRecorded?: boolean;
|
|
7471
|
+
}): Promise<boolean>;
|
|
7462
7472
|
/**
|
|
7463
7473
|
* Set a memory's People Nearby visibility (and optionally its twin opt-in).
|
|
7464
7474
|
*
|
|
@@ -7500,6 +7510,12 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
7500
7510
|
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
7501
7511
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
7502
7512
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
7513
|
+
*
|
|
7514
|
+
* Bumping this is a WHOLE-VAULT re-extraction: the gate is unconditional across
|
|
7515
|
+
* every stamped row, and `stampTopicsExtractedAtOp` stamps this version on every
|
|
7516
|
+
* healthy row, so a bump sends each user's entire extracted vault back to the
|
|
7517
|
+
* LLM. Never reach for it to repair a subset — see the pre-v42-restore repair in
|
|
7518
|
+
* {@link getMemoriesNeedingTopicExtractionOp}, which targets the damaged rows.
|
|
7503
7519
|
*/
|
|
7504
7520
|
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
7505
7521
|
/**
|
|
@@ -7512,9 +7528,10 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
7512
7528
|
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
7513
7529
|
* with no entity links, plus stamped rows edited since their last pass
|
|
7514
7530
|
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
7515
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}
|
|
7516
|
-
*
|
|
7517
|
-
*
|
|
7531
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}, plus the
|
|
7532
|
+
* pre-v42-restore repair: a stamped row left with neither links nor a `topics`
|
|
7533
|
+
* record, which no no-LLM bucket can reach. Edited / stale-version rows come
|
|
7534
|
+
* first (they get priority under `limit`), each group newest-created first.
|
|
7518
7535
|
*/
|
|
7519
7536
|
pending: StoredVaultMemory[];
|
|
7520
7537
|
/**
|
|
@@ -7566,6 +7583,11 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
7566
7583
|
* between the record and the index and never re-derive them. That's why the
|
|
7567
7584
|
* ownership filter lives in the partition below rather than in the query: a
|
|
7568
7585
|
* restored curated memory is exactly the row whose index must be rebuilt.
|
|
7586
|
+
*
|
|
7587
|
+
* NOT purely a read: a curated row with no `topics` record AND no usable link is
|
|
7588
|
+
* a contradiction only a pre-v42 restore produces, and this is the one place
|
|
7589
|
+
* that can see all three facts at once, so it clears the flag there (capped by
|
|
7590
|
+
* `limit`) before returning. See the branch for why that's safe.
|
|
7569
7591
|
*/
|
|
7570
7592
|
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
7571
7593
|
limit?: number;
|
package/dist/react/index.mjs
CHANGED
|
@@ -16942,6 +16942,30 @@ async function updateVaultMemoryOp(ctx, id, opts) {
|
|
|
16942
16942
|
return null;
|
|
16943
16943
|
}
|
|
16944
16944
|
}
|
|
16945
|
+
async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
|
|
16946
|
+
let record;
|
|
16947
|
+
try {
|
|
16948
|
+
record = await ctx.vaultMemoryCollection.find(memoryId);
|
|
16949
|
+
} catch {
|
|
16950
|
+
return false;
|
|
16951
|
+
}
|
|
16952
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
16953
|
+
const originalUpdatedAt = record.updatedAt.getTime();
|
|
16954
|
+
let cleared = false;
|
|
16955
|
+
await ctx.database.write(async () => {
|
|
16956
|
+
if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
|
|
16957
|
+
cleared = true;
|
|
16958
|
+
await record.update((r) => {
|
|
16959
|
+
r._setRaw("topics_user_managed", false);
|
|
16960
|
+
r._setRaw("topics_extracted_version", null);
|
|
16961
|
+
if (record.topicsExtractedAt === null) {
|
|
16962
|
+
r._setRaw("topics_extracted_at", originalUpdatedAt);
|
|
16963
|
+
}
|
|
16964
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
16965
|
+
});
|
|
16966
|
+
});
|
|
16967
|
+
return cleared;
|
|
16968
|
+
}
|
|
16945
16969
|
async function setMemoryVisibilityOp(ctx, id, opts) {
|
|
16946
16970
|
let record;
|
|
16947
16971
|
try {
|
|
@@ -17103,11 +17127,13 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17103
17127
|
entityCtx,
|
|
17104
17128
|
rows.map((r) => r.id)
|
|
17105
17129
|
);
|
|
17130
|
+
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
17106
17131
|
const pendingRaw = [];
|
|
17107
17132
|
const stampedPendingRaw = [];
|
|
17108
17133
|
const linkedUnstampedAll = [];
|
|
17109
17134
|
const topicsToRelinkAll = [];
|
|
17110
17135
|
const topicsBackfillAll = [];
|
|
17136
|
+
const emptyCurationToClear = [];
|
|
17111
17137
|
for (const raw of rows) {
|
|
17112
17138
|
const id = raw.id;
|
|
17113
17139
|
const topics = parseTopics(raw.topics);
|
|
@@ -17117,8 +17143,12 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17117
17143
|
continue;
|
|
17118
17144
|
}
|
|
17119
17145
|
if (raw.topics_user_managed) {
|
|
17120
|
-
if (topics
|
|
17121
|
-
|
|
17146
|
+
if (topics !== null || linked !== void 0) {
|
|
17147
|
+
if (topics === null) topicsBackfillAll.push(id);
|
|
17148
|
+
continue;
|
|
17149
|
+
}
|
|
17150
|
+
if (cap !== void 0 && emptyCurationToClear.length >= cap) continue;
|
|
17151
|
+
emptyCurationToClear.push(id);
|
|
17122
17152
|
}
|
|
17123
17153
|
const stamp = raw.topics_extracted_at ?? null;
|
|
17124
17154
|
let isPending = false;
|
|
@@ -17134,11 +17164,21 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17134
17164
|
pendingRaw.push(raw);
|
|
17135
17165
|
isPending = true;
|
|
17136
17166
|
}
|
|
17167
|
+
if (!isPending && linked === void 0 && topics === null) {
|
|
17168
|
+
pendingRaw.push(raw);
|
|
17169
|
+
isPending = true;
|
|
17170
|
+
}
|
|
17137
17171
|
if (!isPending && topics === null && linked !== void 0) {
|
|
17138
17172
|
topicsBackfillAll.push(id);
|
|
17139
17173
|
}
|
|
17140
17174
|
}
|
|
17141
|
-
const
|
|
17175
|
+
for (const id of emptyCurationToClear) {
|
|
17176
|
+
try {
|
|
17177
|
+
await clearMemoryTopicsOverrideOp(ctx, id, { unlessTopicsRecorded: true });
|
|
17178
|
+
} catch (err) {
|
|
17179
|
+
getLogger().warn("[memory/topics] repair clear failed", err);
|
|
17180
|
+
}
|
|
17181
|
+
}
|
|
17142
17182
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
17143
17183
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
17144
17184
|
const pending = await mapInBatches(
|
|
@@ -20310,7 +20350,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
20310
20350
|
legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
|
|
20311
20351
|
);
|
|
20312
20352
|
}
|
|
20313
|
-
const { recall: recall2 } = await import("./recall-
|
|
20353
|
+
const { recall: recall2 } = await import("./recall-QTVT7HCZ.mjs");
|
|
20314
20354
|
let recallDegraded = [];
|
|
20315
20355
|
const result = await recall2(
|
|
20316
20356
|
query,
|
package/dist/server/index.cjs
CHANGED
|
@@ -17747,6 +17747,30 @@ async function updateVaultMemoryOp(ctx, id, opts) {
|
|
|
17747
17747
|
return null;
|
|
17748
17748
|
}
|
|
17749
17749
|
}
|
|
17750
|
+
async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
|
|
17751
|
+
let record;
|
|
17752
|
+
try {
|
|
17753
|
+
record = await ctx.vaultMemoryCollection.find(memoryId);
|
|
17754
|
+
} catch {
|
|
17755
|
+
return false;
|
|
17756
|
+
}
|
|
17757
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
17758
|
+
const originalUpdatedAt = record.updatedAt.getTime();
|
|
17759
|
+
let cleared = false;
|
|
17760
|
+
await ctx.database.write(async () => {
|
|
17761
|
+
if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
|
|
17762
|
+
cleared = true;
|
|
17763
|
+
await record.update((r) => {
|
|
17764
|
+
r._setRaw("topics_user_managed", false);
|
|
17765
|
+
r._setRaw("topics_extracted_version", null);
|
|
17766
|
+
if (record.topicsExtractedAt === null) {
|
|
17767
|
+
r._setRaw("topics_extracted_at", originalUpdatedAt);
|
|
17768
|
+
}
|
|
17769
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
17770
|
+
});
|
|
17771
|
+
});
|
|
17772
|
+
return cleared;
|
|
17773
|
+
}
|
|
17750
17774
|
async function setMemoryVisibilityOp(ctx, id, opts) {
|
|
17751
17775
|
let record;
|
|
17752
17776
|
try {
|
|
@@ -17907,11 +17931,13 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17907
17931
|
entityCtx,
|
|
17908
17932
|
rows.map((r) => r.id)
|
|
17909
17933
|
);
|
|
17934
|
+
const cap = options?.limit !== void 0 && options.limit > 0 ? options.limit : void 0;
|
|
17910
17935
|
const pendingRaw = [];
|
|
17911
17936
|
const stampedPendingRaw = [];
|
|
17912
17937
|
const linkedUnstampedAll = [];
|
|
17913
17938
|
const topicsToRelinkAll = [];
|
|
17914
17939
|
const topicsBackfillAll = [];
|
|
17940
|
+
const emptyCurationToClear = [];
|
|
17915
17941
|
for (const raw of rows) {
|
|
17916
17942
|
const id = raw.id;
|
|
17917
17943
|
const topics = parseTopics(raw.topics);
|
|
@@ -17921,8 +17947,12 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17921
17947
|
continue;
|
|
17922
17948
|
}
|
|
17923
17949
|
if (raw.topics_user_managed) {
|
|
17924
|
-
if (topics
|
|
17925
|
-
|
|
17950
|
+
if (topics !== null || linked !== void 0) {
|
|
17951
|
+
if (topics === null) topicsBackfillAll.push(id);
|
|
17952
|
+
continue;
|
|
17953
|
+
}
|
|
17954
|
+
if (cap !== void 0 && emptyCurationToClear.length >= cap) continue;
|
|
17955
|
+
emptyCurationToClear.push(id);
|
|
17926
17956
|
}
|
|
17927
17957
|
const stamp = raw.topics_extracted_at ?? null;
|
|
17928
17958
|
let isPending = false;
|
|
@@ -17938,11 +17968,21 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17938
17968
|
pendingRaw.push(raw);
|
|
17939
17969
|
isPending = true;
|
|
17940
17970
|
}
|
|
17971
|
+
if (!isPending && linked === void 0 && topics === null) {
|
|
17972
|
+
pendingRaw.push(raw);
|
|
17973
|
+
isPending = true;
|
|
17974
|
+
}
|
|
17941
17975
|
if (!isPending && topics === null && linked !== void 0) {
|
|
17942
17976
|
topicsBackfillAll.push(id);
|
|
17943
17977
|
}
|
|
17944
17978
|
}
|
|
17945
|
-
const
|
|
17979
|
+
for (const id of emptyCurationToClear) {
|
|
17980
|
+
try {
|
|
17981
|
+
await clearMemoryTopicsOverrideOp(ctx, id, { unlessTopicsRecorded: true });
|
|
17982
|
+
} catch (err) {
|
|
17983
|
+
getLogger().warn("[memory/topics] repair clear failed", err);
|
|
17984
|
+
}
|
|
17985
|
+
}
|
|
17946
17986
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
17947
17987
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
17948
17988
|
const pending = await mapInBatches(
|
package/dist/server/index.d.mts
CHANGED
|
@@ -6025,6 +6025,12 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
6025
6025
|
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
6026
6026
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
6027
6027
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
6028
|
+
*
|
|
6029
|
+
* Bumping this is a WHOLE-VAULT re-extraction: the gate is unconditional across
|
|
6030
|
+
* every stamped row, and `stampTopicsExtractedAtOp` stamps this version on every
|
|
6031
|
+
* healthy row, so a bump sends each user's entire extracted vault back to the
|
|
6032
|
+
* LLM. Never reach for it to repair a subset — see the pre-v42-restore repair in
|
|
6033
|
+
* {@link getMemoriesNeedingTopicExtractionOp}, which targets the damaged rows.
|
|
6028
6034
|
*/
|
|
6029
6035
|
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
6030
6036
|
/**
|
|
@@ -6037,9 +6043,10 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6037
6043
|
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
6038
6044
|
* with no entity links, plus stamped rows edited since their last pass
|
|
6039
6045
|
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
6040
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}
|
|
6041
|
-
*
|
|
6042
|
-
*
|
|
6046
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}, plus the
|
|
6047
|
+
* pre-v42-restore repair: a stamped row left with neither links nor a `topics`
|
|
6048
|
+
* record, which no no-LLM bucket can reach. Edited / stale-version rows come
|
|
6049
|
+
* first (they get priority under `limit`), each group newest-created first.
|
|
6043
6050
|
*/
|
|
6044
6051
|
pending: StoredVaultMemory[];
|
|
6045
6052
|
/**
|
|
@@ -6091,6 +6098,11 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6091
6098
|
* between the record and the index and never re-derive them. That's why the
|
|
6092
6099
|
* ownership filter lives in the partition below rather than in the query: a
|
|
6093
6100
|
* restored curated memory is exactly the row whose index must be rebuilt.
|
|
6101
|
+
*
|
|
6102
|
+
* NOT purely a read: a curated row with no `topics` record AND no usable link is
|
|
6103
|
+
* a contradiction only a pre-v42 restore produces, and this is the one place
|
|
6104
|
+
* that can see all three facts at once, so it clears the flag there (capped by
|
|
6105
|
+
* `limit`) before returning. See the branch for why that's safe.
|
|
6094
6106
|
*/
|
|
6095
6107
|
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
6096
6108
|
limit?: number;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -6025,6 +6025,12 @@ declare function getUnfiledVaultMemoriesOp(ctx: VaultMemoryOperationsContext): P
|
|
|
6025
6025
|
* older version (including pre-v37 rows, read as version 0) is then re-extracted
|
|
6026
6026
|
* by the next sweep, so topic-quality improvements propagate across the existing
|
|
6027
6027
|
* vault. The worker's `limit` drains that re-extraction across sweeps.
|
|
6028
|
+
*
|
|
6029
|
+
* Bumping this is a WHOLE-VAULT re-extraction: the gate is unconditional across
|
|
6030
|
+
* every stamped row, and `stampTopicsExtractedAtOp` stamps this version on every
|
|
6031
|
+
* healthy row, so a bump sends each user's entire extracted vault back to the
|
|
6032
|
+
* LLM. Never reach for it to repair a subset — see the pre-v42-restore repair in
|
|
6033
|
+
* {@link getMemoriesNeedingTopicExtractionOp}, which targets the damaged rows.
|
|
6028
6034
|
*/
|
|
6029
6035
|
declare const TOPICS_EXTRACTION_VERSION = 3;
|
|
6030
6036
|
/**
|
|
@@ -6037,9 +6043,10 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6037
6043
|
* Memories to run LLM topic extraction on (decrypted): never-extracted rows
|
|
6038
6044
|
* with no entity links, plus stamped rows edited since their last pass
|
|
6039
6045
|
* (`updated_at` > `topics_extracted_at`) or extracted under an older
|
|
6040
|
-
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}
|
|
6041
|
-
*
|
|
6042
|
-
*
|
|
6046
|
+
* `topics_extracted_version` than {@link TOPICS_EXTRACTION_VERSION}, plus the
|
|
6047
|
+
* pre-v42-restore repair: a stamped row left with neither links nor a `topics`
|
|
6048
|
+
* record, which no no-LLM bucket can reach. Edited / stale-version rows come
|
|
6049
|
+
* first (they get priority under `limit`), each group newest-created first.
|
|
6043
6050
|
*/
|
|
6044
6051
|
pending: StoredVaultMemory[];
|
|
6045
6052
|
/**
|
|
@@ -6091,6 +6098,11 @@ interface MemoriesNeedingTopicExtraction {
|
|
|
6091
6098
|
* between the record and the index and never re-derive them. That's why the
|
|
6092
6099
|
* ownership filter lives in the partition below rather than in the query: a
|
|
6093
6100
|
* restored curated memory is exactly the row whose index must be rebuilt.
|
|
6101
|
+
*
|
|
6102
|
+
* NOT purely a read: a curated row with no `topics` record AND no usable link is
|
|
6103
|
+
* a contradiction only a pre-v42 restore produces, and this is the one place
|
|
6104
|
+
* that can see all three facts at once, so it clears the flag there (capped by
|
|
6105
|
+
* `limit`) before returning. See the branch for why that's safe.
|
|
6094
6106
|
*/
|
|
6095
6107
|
declare function getMemoriesNeedingTopicExtractionOp(ctx: VaultMemoryOperationsContext, options?: {
|
|
6096
6108
|
limit?: number;
|
package/dist/server/index.mjs
CHANGED