@anuma/sdk 2.4.2 → 2.6.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-UOUJUZM4.mjs → chunk-TCRDSUC6.mjs} +107 -5
- package/dist/expo/{consolidate-HEMQGOUD.mjs → consolidate-3QQPUWD6.mjs} +15 -9
- package/dist/expo/index.cjs +264 -70
- package/dist/expo/index.d.mts +77 -17
- package/dist/expo/index.d.ts +77 -17
- package/dist/expo/index.mjs +148 -58
- package/dist/{react/recall-G3CS444F.mjs → expo/recall-FQU3NUTL.mjs} +1 -1
- package/dist/react/{chunk-IYDQBFS2.mjs → chunk-ZXS3KOY4.mjs} +111 -6
- package/dist/react/{consolidate-IJH7LD2T.mjs → consolidate-3MXZV3G7.mjs} +15 -9
- package/dist/react/index.cjs +268 -71
- package/dist/react/index.d.mts +85 -20
- package/dist/react/index.d.ts +85 -20
- package/dist/react/index.mjs +148 -58
- package/dist/{expo/recall-HI7VO3VM.mjs → react/recall-PV7L3SN3.mjs} +1 -1
- package/dist/server/{chunk-3MLG7SUH.mjs → chunk-Z6II54I7.mjs} +107 -5
- package/dist/server/{consolidate-TDT4HRHG.mjs → consolidate-CN2GQV4D.mjs} +15 -9
- package/dist/server/index.cjs +264 -70
- package/dist/server/index.d.mts +77 -17
- package/dist/server/index.d.ts +77 -17
- package/dist/server/index.mjs +148 -58
- package/dist/server/{recall-MJHGXMHZ.mjs → recall-CQT24XLH.mjs} +1 -1
- package/package.json +1 -1
package/dist/expo/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ENTITY_KINDS,
|
|
9
9
|
LRUCache,
|
|
10
10
|
RerankerUnavailableError,
|
|
11
|
+
TOPICS_EXTRACTION_VERSION,
|
|
11
12
|
chunkAndEmbedAllMessages,
|
|
12
13
|
chunkAndEmbedMessage,
|
|
13
14
|
chunkText,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
createMemoryVaultSearchTool,
|
|
22
23
|
createMessageOp,
|
|
23
24
|
createSseClient,
|
|
25
|
+
createSupersedingMemoryOp,
|
|
24
26
|
createVaultEmbeddingCache,
|
|
25
27
|
createVaultMemoriesBatchOp,
|
|
26
28
|
createVaultMemoryOp,
|
|
@@ -77,6 +79,7 @@ import {
|
|
|
77
79
|
shouldChunkMessage,
|
|
78
80
|
stampTopicsExtractedAtOp,
|
|
79
81
|
stripToolCalls,
|
|
82
|
+
supersedeVaultMemoryOp,
|
|
80
83
|
updateConversationPinnedOp,
|
|
81
84
|
updateConversationTitleOp,
|
|
82
85
|
updateMessageEmbeddingOp,
|
|
@@ -86,7 +89,7 @@ import {
|
|
|
86
89
|
upsertMessageOp,
|
|
87
90
|
useEncryption,
|
|
88
91
|
vaultMemoryToStored
|
|
89
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-TCRDSUC6.mjs";
|
|
90
93
|
import {
|
|
91
94
|
PII_PATTERNS,
|
|
92
95
|
PiiRedactor,
|
|
@@ -5508,6 +5511,15 @@ __decorateClass([
|
|
|
5508
5511
|
__decorateClass([
|
|
5509
5512
|
field3("topics_extracted_at")
|
|
5510
5513
|
], VaultMemory.prototype, "topicsExtractedAt", 2);
|
|
5514
|
+
__decorateClass([
|
|
5515
|
+
field3("superseded_by")
|
|
5516
|
+
], VaultMemory.prototype, "supersededBy", 2);
|
|
5517
|
+
__decorateClass([
|
|
5518
|
+
field3("superseded_at")
|
|
5519
|
+
], VaultMemory.prototype, "supersededAt", 2);
|
|
5520
|
+
__decorateClass([
|
|
5521
|
+
field3("topics_extracted_version")
|
|
5522
|
+
], VaultMemory.prototype, "topicsExtractedVersion", 2);
|
|
5511
5523
|
__decorateClass([
|
|
5512
5524
|
readonly2,
|
|
5513
5525
|
date3("created_at")
|
|
@@ -5874,53 +5886,62 @@ async function retain(content, ctx, options = {}) {
|
|
|
5874
5886
|
const enableAutoMerge = options.enableAutoMerge ?? true;
|
|
5875
5887
|
const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
|
|
5876
5888
|
const resolvedScope = options.scope ?? DEFAULT_SCOPE;
|
|
5889
|
+
let supersedeTargetId;
|
|
5890
|
+
let supersedeContent;
|
|
5877
5891
|
if (enableAutoMerge) {
|
|
5878
5892
|
if (options.consolidateOptions) {
|
|
5879
|
-
const
|
|
5880
|
-
if (
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
);
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5893
|
+
const outcome = await tryConsolidate(trimmed, ctx, options);
|
|
5894
|
+
if (outcome) {
|
|
5895
|
+
if ("done" in outcome) return outcome.done;
|
|
5896
|
+
supersedeTargetId = outcome.supersede;
|
|
5897
|
+
supersedeContent = outcome.content;
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
if (!supersedeTargetId) {
|
|
5901
|
+
const matches = await searchVaultMemories(
|
|
5902
|
+
trimmed,
|
|
5903
|
+
ctx.vaultCtx,
|
|
5904
|
+
ctx.embeddingOptions,
|
|
5905
|
+
ctx.vaultCache,
|
|
5906
|
+
{
|
|
5907
|
+
limit: 1,
|
|
5908
|
+
minSimilarity: threshold,
|
|
5909
|
+
useFusion: false,
|
|
5910
|
+
scopes: [resolvedScope],
|
|
5911
|
+
...options.folderId !== void 0 && { folderId: options.folderId }
|
|
5912
|
+
}
|
|
5913
|
+
);
|
|
5914
|
+
if (matches.length > 0) {
|
|
5915
|
+
const targetId = matches[0].uniqueId;
|
|
5916
|
+
const existing = await getVaultMemoryOp(ctx.vaultCtx, targetId);
|
|
5917
|
+
if (existing && !existing.supersededBy) {
|
|
5918
|
+
const mergedSourceIds = unionStrings(
|
|
5919
|
+
existing.sourceChunkIds ?? [],
|
|
5920
|
+
options.sourceChunkIds ?? []
|
|
5921
|
+
);
|
|
5922
|
+
const eventTimeUpdate = pickEventTimeUpdate(existing, options.eventTime);
|
|
5923
|
+
const updated = await updateVaultMemoryOp(ctx.vaultCtx, targetId, {
|
|
5924
|
+
content: existing.content,
|
|
5925
|
+
proofCountIncrement: 1,
|
|
5926
|
+
sourceChunkIds: mergedSourceIds,
|
|
5927
|
+
preserveUpdatedAt: true,
|
|
5928
|
+
...eventTimeUpdate && { eventTime: eventTimeUpdate }
|
|
5929
|
+
});
|
|
5930
|
+
if (updated) {
|
|
5931
|
+
return {
|
|
5932
|
+
action: "merge",
|
|
5933
|
+
memoryId: targetId,
|
|
5934
|
+
targetId,
|
|
5935
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
5936
|
+
};
|
|
5937
|
+
}
|
|
5938
|
+
await assertMergeTargetGoneOrThrow(ctx, targetId);
|
|
5918
5939
|
}
|
|
5919
|
-
await assertMergeTargetGoneOrThrow(ctx, targetId);
|
|
5920
5940
|
}
|
|
5921
5941
|
}
|
|
5922
5942
|
}
|
|
5923
|
-
const
|
|
5943
|
+
const contentToWrite = supersedeContent ?? trimmed;
|
|
5944
|
+
const embedding = await generateEmbedding(contentToWrite, ctx.embeddingOptions);
|
|
5924
5945
|
const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
5925
5946
|
if (options.respectTombstones) {
|
|
5926
5947
|
const tombstoneId = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
|
|
@@ -5931,8 +5952,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
5931
5952
|
return { action: "suppressed", memoryId: tombstoneId, tombstoneId, proofCount: 0 };
|
|
5932
5953
|
}
|
|
5933
5954
|
}
|
|
5934
|
-
const
|
|
5935
|
-
content:
|
|
5955
|
+
const createOpts = {
|
|
5956
|
+
content: contentToWrite,
|
|
5936
5957
|
scope: resolvedScope,
|
|
5937
5958
|
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
5938
5959
|
embedding: JSON.stringify(embedding),
|
|
@@ -5949,7 +5970,24 @@ async function retain(content, ctx, options = {}) {
|
|
|
5949
5970
|
kind: options.eventTime.kind
|
|
5950
5971
|
}
|
|
5951
5972
|
}
|
|
5952
|
-
}
|
|
5973
|
+
};
|
|
5974
|
+
if (supersedeTargetId) {
|
|
5975
|
+
const { created: created2, retired } = await createSupersedingMemoryOp(
|
|
5976
|
+
ctx.vaultCtx,
|
|
5977
|
+
createOpts,
|
|
5978
|
+
supersedeTargetId
|
|
5979
|
+
);
|
|
5980
|
+
if (created2 && retired) {
|
|
5981
|
+
ctx.vaultCache.set(created2.uniqueId, Float32Array.from(embedding));
|
|
5982
|
+
return {
|
|
5983
|
+
action: "supersede",
|
|
5984
|
+
memoryId: created2.uniqueId,
|
|
5985
|
+
targetId: supersedeTargetId,
|
|
5986
|
+
proofCount: 1
|
|
5987
|
+
};
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
const created = await createVaultMemoryOp(ctx.vaultCtx, createOpts);
|
|
5953
5991
|
ctx.vaultCache.set(created.uniqueId, Float32Array.from(embedding));
|
|
5954
5992
|
return {
|
|
5955
5993
|
action: "create",
|
|
@@ -6016,12 +6054,17 @@ async function tryConsolidate(trimmed, ctx, options) {
|
|
|
6016
6054
|
content: m.content,
|
|
6017
6055
|
similarity: m.similarity
|
|
6018
6056
|
}));
|
|
6019
|
-
const { consolidateMemory: doConsolidate } = await import("./consolidate-
|
|
6057
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-3QQPUWD6.mjs");
|
|
6020
6058
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
6021
6059
|
if (decision.action === "create") return null;
|
|
6060
|
+
if (decision.action === "supersede" && decision.targetId && decision.content) {
|
|
6061
|
+
const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
|
|
6062
|
+
if (!existing || existing.supersededBy) return null;
|
|
6063
|
+
return { supersede: decision.targetId, content: decision.content };
|
|
6064
|
+
}
|
|
6022
6065
|
if (decision.action === "noop" && decision.targetId) {
|
|
6023
6066
|
const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
|
|
6024
|
-
if (!existing) return null;
|
|
6067
|
+
if (!existing || existing.supersededBy) return null;
|
|
6025
6068
|
const mergedSourceIds = unionStrings(
|
|
6026
6069
|
existing.sourceChunkIds ?? [],
|
|
6027
6070
|
options.sourceChunkIds ?? []
|
|
@@ -6039,15 +6082,17 @@ async function tryConsolidate(trimmed, ctx, options) {
|
|
|
6039
6082
|
return null;
|
|
6040
6083
|
}
|
|
6041
6084
|
return {
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6085
|
+
done: {
|
|
6086
|
+
action: "merge",
|
|
6087
|
+
memoryId: decision.targetId,
|
|
6088
|
+
targetId: decision.targetId,
|
|
6089
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
6090
|
+
}
|
|
6046
6091
|
};
|
|
6047
6092
|
}
|
|
6048
6093
|
if (decision.action === "update" && decision.targetId && decision.content) {
|
|
6049
6094
|
const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
|
|
6050
|
-
if (!existing) return null;
|
|
6095
|
+
if (!existing || existing.supersededBy) return null;
|
|
6051
6096
|
const mergedSourceIds = unionStrings(
|
|
6052
6097
|
existing.sourceChunkIds ?? [],
|
|
6053
6098
|
options.sourceChunkIds ?? []
|
|
@@ -6074,10 +6119,12 @@ async function tryConsolidate(trimmed, ctx, options) {
|
|
|
6074
6119
|
}
|
|
6075
6120
|
ctx.vaultCache.set(decision.targetId, Float32Array.from(newEmbedding));
|
|
6076
6121
|
return {
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6122
|
+
done: {
|
|
6123
|
+
action: "update",
|
|
6124
|
+
memoryId: decision.targetId,
|
|
6125
|
+
targetId: decision.targetId,
|
|
6126
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
6127
|
+
}
|
|
6081
6128
|
};
|
|
6082
6129
|
}
|
|
6083
6130
|
return null;
|
|
@@ -9530,7 +9577,7 @@ __decorateClass([
|
|
|
9530
9577
|
], VaultFolder.prototype, "context", 2);
|
|
9531
9578
|
|
|
9532
9579
|
// src/lib/db/schema.ts
|
|
9533
|
-
var SDK_SCHEMA_VERSION =
|
|
9580
|
+
var SDK_SCHEMA_VERSION = 38;
|
|
9534
9581
|
var sdkSchema = appSchema2({
|
|
9535
9582
|
version: SDK_SCHEMA_VERSION,
|
|
9536
9583
|
tables: [
|
|
@@ -9655,7 +9702,20 @@ var sdkSchema = appSchema2({
|
|
|
9655
9702
|
// Unix ms of the last LLM topic-extraction pass over this memory's
|
|
9656
9703
|
// content. Null = never extracted standalone (legacy rows with entity
|
|
9657
9704
|
// links are grandfathered — see getMemoriesNeedingTopicExtractionOp).
|
|
9658
|
-
{ name: "topics_extracted_at", type: "number", isOptional: true }
|
|
9705
|
+
{ name: "topics_extracted_at", type: "number", isOptional: true },
|
|
9706
|
+
// Write-time supersession (A2). When set, this fact was replaced by a
|
|
9707
|
+
// newer, incompatible-value fact (e.g. "Lives in Portland" superseded by
|
|
9708
|
+
// "Lives in SF"); `superseded_by` points at the newer memory's id and
|
|
9709
|
+
// `superseded_at` is the Unix ms it happened. Superseded rows stay in
|
|
9710
|
+
// the table (history / read-time fallback) but are excluded from recall
|
|
9711
|
+
// and dedup by default. Null = live (not superseded). Indexed so the
|
|
9712
|
+
// recall filter stays cheap.
|
|
9713
|
+
{ name: "superseded_by", type: "string", isOptional: true, isIndexed: true },
|
|
9714
|
+
{ name: "superseded_at", type: "number", isOptional: true },
|
|
9715
|
+
// The extraction-logic version this memory was last stamped under. Null
|
|
9716
|
+
// (pre-v38) is treated as version 0, so a bump of TOPICS_EXTRACTION_VERSION
|
|
9717
|
+
// re-extracts stale rows. See getMemoriesNeedingTopicExtractionOp.
|
|
9718
|
+
{ name: "topics_extracted_version", type: "number", isOptional: true }
|
|
9659
9719
|
]
|
|
9660
9720
|
}),
|
|
9661
9721
|
// Entity table — canonical names extracted from auto-extraction (W5).
|
|
@@ -10266,6 +10326,34 @@ var sdkMigrations = schemaMigrations2({
|
|
|
10266
10326
|
columns: [{ name: "topics_extracted_at", type: "number", isOptional: true }]
|
|
10267
10327
|
})
|
|
10268
10328
|
]
|
|
10329
|
+
},
|
|
10330
|
+
// v36 -> v37: write-time supersession columns on memory_vault. Existing rows
|
|
10331
|
+
// keep both NULL (= live / not superseded), so no backfill — legacy
|
|
10332
|
+
// contradictory rows are handled by the read-time supersession fallback.
|
|
10333
|
+
{
|
|
10334
|
+
toVersion: 37,
|
|
10335
|
+
steps: [
|
|
10336
|
+
addColumns2({
|
|
10337
|
+
table: "memory_vault",
|
|
10338
|
+
columns: [
|
|
10339
|
+
{ name: "superseded_by", type: "string", isOptional: true, isIndexed: true },
|
|
10340
|
+
{ name: "superseded_at", type: "number", isOptional: true }
|
|
10341
|
+
]
|
|
10342
|
+
})
|
|
10343
|
+
]
|
|
10344
|
+
},
|
|
10345
|
+
// v37 -> v38: topics_extracted_version on memory_vault. Existing rows keep it
|
|
10346
|
+
// NULL, read as version 0 by getMemoriesNeedingTopicExtractionOp — so the
|
|
10347
|
+
// first sweep after a TOPICS_EXTRACTION_VERSION bump re-extracts them (drained
|
|
10348
|
+
// across sweeps by the worker's limit). See topicExtract.ts.
|
|
10349
|
+
{
|
|
10350
|
+
toVersion: 38,
|
|
10351
|
+
steps: [
|
|
10352
|
+
addColumns2({
|
|
10353
|
+
table: "memory_vault",
|
|
10354
|
+
columns: [{ name: "topics_extracted_version", type: "number", isOptional: true }]
|
|
10355
|
+
})
|
|
10356
|
+
]
|
|
10269
10357
|
}
|
|
10270
10358
|
]
|
|
10271
10359
|
});
|
|
@@ -11625,6 +11713,7 @@ export {
|
|
|
11625
11713
|
VaultFolder as StoredVaultFolderModel,
|
|
11626
11714
|
VaultMemory as StoredVaultMemoryModel,
|
|
11627
11715
|
StreamExpiredError,
|
|
11716
|
+
TOPICS_EXTRACTION_VERSION,
|
|
11628
11717
|
TOPIC_EXTRACTION_BATCH_SIZE,
|
|
11629
11718
|
WalletPoller,
|
|
11630
11719
|
addConversationMemoriesOp,
|
|
@@ -11726,6 +11815,7 @@ export {
|
|
|
11726
11815
|
stampTopicsExtractedAtOp,
|
|
11727
11816
|
streamCancelPath,
|
|
11728
11817
|
streamReplayPath,
|
|
11818
|
+
supersedeVaultMemoryOp,
|
|
11729
11819
|
updateVaultFolderContextOp,
|
|
11730
11820
|
updateVaultFolderOp,
|
|
11731
11821
|
updateVaultMemoryEmbeddingOp,
|
|
@@ -2027,6 +2027,7 @@ function isOwnedByCtxUser(ctx, record) {
|
|
|
2027
2027
|
function baseVaultConditions(ctx, options) {
|
|
2028
2028
|
return [
|
|
2029
2029
|
...options?.includeDeleted ? [] : [Q3.where("is_deleted", false)],
|
|
2030
|
+
...options?.includeSuperseded ? [] : [Q3.where("superseded_by", null)],
|
|
2030
2031
|
...ctx.userId !== void 0 ? [Q3.where("user_id", ctx.userId)] : [],
|
|
2031
2032
|
...options?.since ? [Q3.where("updated_at", Q3.gt(options.since.getTime()))] : []
|
|
2032
2033
|
];
|
|
@@ -2067,6 +2068,9 @@ function vaultMemoryToStoredRaw(memory) {
|
|
|
2067
2068
|
eventTimeKind: memory.eventTimeKind ?? null,
|
|
2068
2069
|
topicsUserManaged: memory.topicsUserManaged ?? false,
|
|
2069
2070
|
topicsExtractedAt: memory.topicsExtractedAt ?? null,
|
|
2071
|
+
topicsExtractedVersion: memory.topicsExtractedVersion ?? null,
|
|
2072
|
+
supersededBy: memory.supersededBy ?? null,
|
|
2073
|
+
supersededAt: memory.supersededAt ?? null,
|
|
2070
2074
|
createdAt: memory.createdAt,
|
|
2071
2075
|
updatedAt: memory.updatedAt,
|
|
2072
2076
|
isDeleted: memory.isDeleted
|
|
@@ -2112,6 +2116,59 @@ async function createVaultMemoryOp(ctx, opts) {
|
|
|
2112
2116
|
});
|
|
2113
2117
|
return vaultMemoryToStored(created, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
2114
2118
|
}
|
|
2119
|
+
async function createSupersedingMemoryOp(ctx, opts, targetId) {
|
|
2120
|
+
if (!targetId) return { created: null, retired: false };
|
|
2121
|
+
const scope = opts.scope ?? "private";
|
|
2122
|
+
const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
|
|
2123
|
+
opts.content,
|
|
2124
|
+
ctx.walletAddress,
|
|
2125
|
+
ctx.signMessage,
|
|
2126
|
+
ctx.embeddedWalletSigner
|
|
2127
|
+
) : opts.content;
|
|
2128
|
+
let createdRecord = null;
|
|
2129
|
+
await ctx.database.write(async () => {
|
|
2130
|
+
let target;
|
|
2131
|
+
try {
|
|
2132
|
+
target = await ctx.vaultMemoryCollection.find(targetId);
|
|
2133
|
+
} catch {
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
if (target.isDeleted || target.supersededBy || !isOwnedByCtxUser(ctx, target)) return;
|
|
2137
|
+
createdRecord = await ctx.vaultMemoryCollection.create((record) => {
|
|
2138
|
+
record._setRaw("content", encryptedContent);
|
|
2139
|
+
record._setRaw("scope", scope);
|
|
2140
|
+
record._setRaw("folder_id", opts.folderId ?? null);
|
|
2141
|
+
record._setRaw("user_id", ctx.userId ?? null);
|
|
2142
|
+
record._setRaw("is_deleted", false);
|
|
2143
|
+
if (opts.embedding !== void 0) {
|
|
2144
|
+
record._setRaw("embedding", opts.embedding);
|
|
2145
|
+
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
2146
|
+
}
|
|
2147
|
+
if (opts.sourceChunkIds !== void 0) {
|
|
2148
|
+
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
2149
|
+
}
|
|
2150
|
+
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
2151
|
+
record._setRaw("source", opts.source ?? "manual");
|
|
2152
|
+
if (opts.eventTime) {
|
|
2153
|
+
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
2154
|
+
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
2155
|
+
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
2156
|
+
}
|
|
2157
|
+
});
|
|
2158
|
+
await target.update((r) => {
|
|
2159
|
+
r._setRaw("superseded_by", createdRecord.id);
|
|
2160
|
+
r._setRaw("superseded_at", Date.now());
|
|
2161
|
+
});
|
|
2162
|
+
});
|
|
2163
|
+
if (!createdRecord) return { created: null, retired: false };
|
|
2164
|
+
const created = await vaultMemoryToStored(
|
|
2165
|
+
createdRecord,
|
|
2166
|
+
ctx.walletAddress,
|
|
2167
|
+
ctx.signMessage,
|
|
2168
|
+
ctx.embeddedWalletSigner
|
|
2169
|
+
);
|
|
2170
|
+
return { created, retired: true };
|
|
2171
|
+
}
|
|
2115
2172
|
async function getMemoriesByEventTimeOp(ctx, windowStart, windowEnd) {
|
|
2116
2173
|
const records = await ctx.vaultMemoryCollection.query(
|
|
2117
2174
|
...baseVaultConditions(ctx),
|
|
@@ -2251,6 +2308,9 @@ function vaultMemoryRawToStoredRaw(raw) {
|
|
|
2251
2308
|
// SQLite stores booleans as 0/1, LokiJS as true/false — coerce both.
|
|
2252
2309
|
topicsUserManaged: raw.topics_user_managed === true || raw.topics_user_managed === 1,
|
|
2253
2310
|
topicsExtractedAt: raw.topics_extracted_at ?? null,
|
|
2311
|
+
topicsExtractedVersion: raw.topics_extracted_version ?? null,
|
|
2312
|
+
supersededBy: raw.superseded_by ?? null,
|
|
2313
|
+
supersededAt: raw.superseded_at ?? null,
|
|
2254
2314
|
createdAt: new Date(raw.created_at),
|
|
2255
2315
|
updatedAt: new Date(raw.updated_at),
|
|
2256
2316
|
isDeleted: raw.is_deleted === true || raw.is_deleted === 1
|
|
@@ -2292,7 +2352,7 @@ async function getAllVaultMemoryContentsOp(ctx, options) {
|
|
|
2292
2352
|
async function updateVaultMemoryOp(ctx, id, opts) {
|
|
2293
2353
|
try {
|
|
2294
2354
|
const probe = await ctx.vaultMemoryCollection.find(id);
|
|
2295
|
-
if (probe.isDeleted || !isOwnedByCtxUser(ctx, probe)) return null;
|
|
2355
|
+
if (probe.isDeleted || probe.supersededBy || !isOwnedByCtxUser(ctx, probe)) return null;
|
|
2296
2356
|
const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
|
|
2297
2357
|
opts.content,
|
|
2298
2358
|
ctx.walletAddress,
|
|
@@ -2303,7 +2363,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
|
|
|
2303
2363
|
const record = probe;
|
|
2304
2364
|
const originalUpdatedAt = record.updatedAt.getTime();
|
|
2305
2365
|
await ctx.database.write(async () => {
|
|
2306
|
-
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
|
|
2366
|
+
if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record)) {
|
|
2307
2367
|
stale = true;
|
|
2308
2368
|
return;
|
|
2309
2369
|
}
|
|
@@ -2403,7 +2463,10 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId) {
|
|
|
2403
2463
|
await ctx.database.write(async () => {
|
|
2404
2464
|
await record.update((r) => {
|
|
2405
2465
|
r._setRaw("topics_user_managed", false);
|
|
2406
|
-
r._setRaw("
|
|
2466
|
+
r._setRaw("topics_extracted_version", null);
|
|
2467
|
+
if (record.topicsExtractedAt === null) {
|
|
2468
|
+
r._setRaw("topics_extracted_at", originalUpdatedAt);
|
|
2469
|
+
}
|
|
2407
2470
|
r._setRaw("updated_at", originalUpdatedAt);
|
|
2408
2471
|
});
|
|
2409
2472
|
});
|
|
@@ -2435,6 +2498,40 @@ async function deleteVaultMemoryOp(ctx, id) {
|
|
|
2435
2498
|
return false;
|
|
2436
2499
|
}
|
|
2437
2500
|
}
|
|
2501
|
+
async function supersedeVaultMemoryOp(ctx, id, supersededById) {
|
|
2502
|
+
if (id === supersededById) return false;
|
|
2503
|
+
try {
|
|
2504
|
+
const record = await ctx.vaultMemoryCollection.find(id);
|
|
2505
|
+
if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record)) return false;
|
|
2506
|
+
let successor;
|
|
2507
|
+
try {
|
|
2508
|
+
successor = await ctx.vaultMemoryCollection.find(supersededById);
|
|
2509
|
+
} catch {
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
if (successor.isDeleted || successor.supersededBy || !isOwnedByCtxUser(ctx, successor)) {
|
|
2513
|
+
return false;
|
|
2514
|
+
}
|
|
2515
|
+
let stale = false;
|
|
2516
|
+
await ctx.database.write(async () => {
|
|
2517
|
+
if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record)) {
|
|
2518
|
+
stale = true;
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
if (successor.isDeleted || successor.supersededBy || !isOwnedByCtxUser(ctx, successor)) {
|
|
2522
|
+
stale = true;
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
await record.update((r) => {
|
|
2526
|
+
r._setRaw("superseded_by", supersededById);
|
|
2527
|
+
r._setRaw("superseded_at", Date.now());
|
|
2528
|
+
});
|
|
2529
|
+
});
|
|
2530
|
+
return !stale;
|
|
2531
|
+
} catch {
|
|
2532
|
+
return false;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2438
2535
|
async function getUnfiledVaultMemoriesOp(ctx) {
|
|
2439
2536
|
const conditions = [
|
|
2440
2537
|
Q3.where("folder_id", null),
|
|
@@ -2447,6 +2544,7 @@ async function getUnfiledVaultMemoriesOp(ctx) {
|
|
|
2447
2544
|
(record) => vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
|
|
2448
2545
|
);
|
|
2449
2546
|
}
|
|
2547
|
+
var TOPICS_EXTRACTION_VERSION = 1;
|
|
2450
2548
|
async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
2451
2549
|
const entityCtx = ctx.entityCtx;
|
|
2452
2550
|
if (!entityCtx) {
|
|
@@ -2465,7 +2563,10 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
2465
2563
|
for (const raw of rows) {
|
|
2466
2564
|
const stamp = raw.topics_extracted_at ?? null;
|
|
2467
2565
|
if (stamp !== null) {
|
|
2468
|
-
|
|
2566
|
+
const version = raw.topics_extracted_version ?? 0;
|
|
2567
|
+
if (raw.updated_at > stamp || version < TOPICS_EXTRACTION_VERSION) {
|
|
2568
|
+
stampedPending.push(raw);
|
|
2569
|
+
}
|
|
2469
2570
|
} else {
|
|
2470
2571
|
unstamped.push(raw);
|
|
2471
2572
|
}
|
|
@@ -2495,7 +2596,7 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
2495
2596
|
);
|
|
2496
2597
|
return { pending, linkedUnstamped };
|
|
2497
2598
|
}
|
|
2498
|
-
async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt) {
|
|
2599
|
+
async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) {
|
|
2499
2600
|
if (memoryIds.length === 0) return [];
|
|
2500
2601
|
const uniqueIds = Array.from(new Set(memoryIds));
|
|
2501
2602
|
const CHUNK = 500;
|
|
@@ -2518,6 +2619,7 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt) {
|
|
|
2518
2619
|
prepared.push(
|
|
2519
2620
|
record.prepareUpdate((r) => {
|
|
2520
2621
|
r._setRaw("topics_extracted_at", extractedAt);
|
|
2622
|
+
r._setRaw("topics_extracted_version", version);
|
|
2521
2623
|
r._setRaw("updated_at", originalUpdatedAt);
|
|
2522
2624
|
})
|
|
2523
2625
|
);
|
|
@@ -5282,7 +5384,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
5282
5384
|
legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
|
|
5283
5385
|
);
|
|
5284
5386
|
}
|
|
5285
|
-
const { recall: recall2 } = await import("./recall-
|
|
5387
|
+
const { recall: recall2 } = await import("./recall-PV7L3SN3.mjs");
|
|
5286
5388
|
const result = await recall2(
|
|
5287
5389
|
query,
|
|
5288
5390
|
{ vaultCtx, embeddingOptions, vaultCache: cache },
|
|
@@ -6008,6 +6110,7 @@ export {
|
|
|
6008
6110
|
getMemoriesByEntityNamesOp,
|
|
6009
6111
|
vaultMemoryToStored,
|
|
6010
6112
|
createVaultMemoryOp,
|
|
6113
|
+
createSupersedingMemoryOp,
|
|
6011
6114
|
createVaultMemoriesBatchOp,
|
|
6012
6115
|
getVaultMemoryOp,
|
|
6013
6116
|
getAllVaultMemoriesOp,
|
|
@@ -6016,7 +6119,9 @@ export {
|
|
|
6016
6119
|
setMemoryEntitiesOp,
|
|
6017
6120
|
clearMemoryTopicsOverrideOp,
|
|
6018
6121
|
deleteVaultMemoryOp,
|
|
6122
|
+
supersedeVaultMemoryOp,
|
|
6019
6123
|
getUnfiledVaultMemoriesOp,
|
|
6124
|
+
TOPICS_EXTRACTION_VERSION,
|
|
6020
6125
|
getMemoriesNeedingTopicExtractionOp,
|
|
6021
6126
|
stampTopicsExtractedAtOp,
|
|
6022
6127
|
deleteAllVaultMemoriesForUserOp,
|
|
@@ -15,10 +15,11 @@ var SYSTEM_PROMPT = `You consolidate a new memory against existing memories from
|
|
|
15
15
|
|
|
16
16
|
A "memory" is a self-contained natural-language fact about the user. Multiple memories about the same EXACT FACET (the same dimension of the same subject) should never coexist \u2014 they should be merged into one. Different facets on the same subject (e.g. user's dog's name vs user's dog's age) are SEPARATE memories.
|
|
17
17
|
|
|
18
|
-
Decide one of
|
|
18
|
+
Decide one of four actions:
|
|
19
19
|
|
|
20
20
|
- "create": the new memory describes a distinct fact not covered by any existing memory. Write it as new.
|
|
21
|
-
- "update": the new memory describes the SAME FACET as exactly one existing memory
|
|
21
|
+
- "update": the new memory describes the SAME FACET as exactly one existing memory and is COMPATIBLE with it \u2014 it adds detail, fixes an incomplete value, or rephrases the same still-true fact. Return targetId of that existing memory + the consolidated content (richest version of the two).
|
|
22
|
+
- "supersede": the new memory REPLACES a standing fact whose value has CHANGED and is now INCOMPATIBLE with an existing memory \u2014 the old fact was true before but is false now (a state change). Return targetId of the STALE existing memory to retire + content = the NEW fact. The old memory is kept as history, not overwritten or deleted; do not re-state it.
|
|
22
23
|
- "noop": the new memory is already fully captured by an existing memory \u2014 same facet, no new information. Skip the write.
|
|
23
24
|
|
|
24
25
|
RULES (carry these strictly):
|
|
@@ -26,18 +27,21 @@ RULES (carry these strictly):
|
|
|
26
27
|
1. ONE OBSERVATION PER DISTINCT FACET. If the new memory is about "user's aunt's twins" and an existing memory is also about "user's aunt's twins", they are the same facet \u2192 update, never create.
|
|
27
28
|
2. MATCH BY FACET, NOT TOPIC. Two memories both mentioning "Zara" is not enough to merge \u2014 only merge if they describe the same property (e.g. both about "user's pending Zara boot exchange"). Two memories about Zara on different topics (e.g. "user shops at Zara" + "user returned a sweater to Zara last week") are separate facets.
|
|
28
29
|
3. NO COMPUTATION. Do not sum counts, decrement quantities, or derive new facts from existing ones. If the new memory says "user spent $200 today" and an existing memory says "user spent $150 yesterday", these are TWO SEPARATE EVENTS \u2014 create.
|
|
29
|
-
4.
|
|
30
|
-
|
|
30
|
+
4. EVENTS vs STANDING STATE.
|
|
31
|
+
- Distinct EVENTS (episodic \u2014 something that happened on a date) are separate memories, even if the same activity: "user went to gym Monday" vs "user went to gym Friday" \u2192 create; keep both.
|
|
32
|
+
- A STANDING ATTRIBUTE (an ongoing state: where the user lives/works, relationship status, current role) has ONE current value. When the new memory changes that value ("Lives in Portland" \u2192 "Lives in San Francisco"; "Works at Google" \u2192 "Works at Riverbend"; "Engaged" \u2192 "Broke up"), the old value is no longer true \u2192 supersede (retire the old, record the new). Do NOT create (that leaves a stale contradiction) and do NOT update (that erases the history that the old value was once true).
|
|
33
|
+
5. When in doubt between create and update, choose create \u2014 EXCEPT when the new memory directly contradicts the current value of a standing attribute in an existing memory; then choose supersede. Over-merging loses information; a live contradiction is worse than either.
|
|
31
34
|
|
|
32
35
|
OUTPUT \u2014 strict JSON, no prose:
|
|
33
36
|
{
|
|
34
|
-
"action": "create" | "update" | "noop",
|
|
35
|
-
"targetId": "<existing memory id
|
|
36
|
-
"content": "<
|
|
37
|
+
"action": "create" | "update" | "supersede" | "noop",
|
|
38
|
+
"targetId": "<existing memory id \u2014 required for update/supersede/noop, omit for create>",
|
|
39
|
+
"content": "<content \u2014 required for create/update/supersede, omit for noop>"
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
For "create": content is the new memory verbatim (or a slight refinement).
|
|
40
43
|
For "update": content is the merged richest-version, \u226480 words.
|
|
44
|
+
For "supersede": content is the NEW fact only (\u226480 words); targetId is the stale memory being retired.
|
|
41
45
|
For "noop": no content (existing memory is already correct).`;
|
|
42
46
|
async function consolidateMemory(newContent, candidates, options) {
|
|
43
47
|
const fallback = {
|
|
@@ -112,7 +116,9 @@ function validate(parsed, newContent, validIds) {
|
|
|
112
116
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
113
117
|
const obj = parsed;
|
|
114
118
|
const action = obj.action;
|
|
115
|
-
if (action !== "create" && action !== "update" && action !== "noop")
|
|
119
|
+
if (action !== "create" && action !== "update" && action !== "noop" && action !== "supersede") {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
116
122
|
if (action === "create") {
|
|
117
123
|
const c2 = typeof obj.content === "string" ? obj.content.trim() : "";
|
|
118
124
|
return { action: "create", content: c2.length > 0 ? c2 : newContent };
|
|
@@ -124,7 +130,7 @@ function validate(parsed, newContent, validIds) {
|
|
|
124
130
|
}
|
|
125
131
|
const c = typeof obj.content === "string" ? obj.content.trim() : "";
|
|
126
132
|
if (c.length === 0) return null;
|
|
127
|
-
return { action
|
|
133
|
+
return { action, targetId, content: c };
|
|
128
134
|
}
|
|
129
135
|
export {
|
|
130
136
|
consolidateMemory
|