@anuma/sdk 9.1.2 → 9.1.4
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/index.cjs +108 -85
- package/dist/expo/index.d.mts +4 -0
- package/dist/expo/index.d.ts +4 -0
- package/dist/expo/index.mjs +108 -85
- package/dist/index.cjs +9 -4
- package/dist/index.mjs +9 -4
- package/dist/react/index.cjs +108 -85
- package/dist/react/index.d.mts +4 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.mjs +108 -85
- package/dist/server/index.cjs +19 -3
- package/dist/server/index.mjs +19 -3
- package/package.json +1 -1
package/dist/expo/index.cjs
CHANGED
|
@@ -2326,6 +2326,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2326
2326
|
if (cachedKey) {
|
|
2327
2327
|
return cachedKey;
|
|
2328
2328
|
}
|
|
2329
|
+
const startEpoch = sessionEpoch;
|
|
2329
2330
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
2330
2331
|
if (!keyHex) {
|
|
2331
2332
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -2338,6 +2339,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2338
2339
|
false,
|
|
2339
2340
|
["encrypt", "decrypt"]
|
|
2340
2341
|
);
|
|
2342
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
2343
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
2344
|
+
}
|
|
2341
2345
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
2342
2346
|
return cryptoKey;
|
|
2343
2347
|
}
|
|
@@ -22923,6 +22927,8 @@ async function backfillTopics(ctx) {
|
|
|
22923
22927
|
if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
|
|
22924
22928
|
}
|
|
22925
22929
|
async function migrateMemoriesToRemote(options) {
|
|
22930
|
+
if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
|
|
22931
|
+
throw new Error("A write barrier and a remote store handoff are required");
|
|
22926
22932
|
const { canonical, signal } = options;
|
|
22927
22933
|
const rings = [canonical, ...options.fallbacks ?? []];
|
|
22928
22934
|
const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
@@ -22950,93 +22956,98 @@ async function migrateMemoriesToRemote(options) {
|
|
|
22950
22956
|
const placeholders = /* @__PURE__ */ new Set();
|
|
22951
22957
|
const truncated = /* @__PURE__ */ new Set();
|
|
22952
22958
|
let imported = 0;
|
|
22953
|
-
|
|
22954
|
-
|
|
22955
|
-
|
|
22956
|
-
|
|
22957
|
-
|
|
22958
|
-
|
|
22959
|
-
|
|
22960
|
-
|
|
22961
|
-
|
|
22962
|
-
|
|
22963
|
-
|
|
22964
|
-
|
|
22965
|
-
|
|
22966
|
-
);
|
|
22967
|
-
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
22968
|
-
for (const raw of candidates) {
|
|
22969
|
-
const server = remote.get(raw.id);
|
|
22970
|
-
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
22971
|
-
pending.set(raw.id, raw.updated_at + 1);
|
|
22972
|
-
}
|
|
22973
|
-
}
|
|
22974
|
-
if (pending.size === 0) {
|
|
22975
|
-
const result = (state) => ({
|
|
22976
|
-
state,
|
|
22977
|
-
total: rows.length,
|
|
22978
|
-
imported,
|
|
22979
|
-
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
22980
|
-
reembedded: reembedded.size,
|
|
22981
|
-
unembedded: unembedded.size,
|
|
22982
|
-
placeholderTombstones: placeholders.size,
|
|
22983
|
-
truncatedSourceChunkIds: truncated.size
|
|
22984
|
-
});
|
|
22985
|
-
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
22986
|
-
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
22987
|
-
if (!active)
|
|
22988
|
-
await request(
|
|
22989
|
-
"/account",
|
|
22990
|
-
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
22959
|
+
const uploadPasses = async (finalizing = false) => {
|
|
22960
|
+
for (let pass = 0; ; pass++) {
|
|
22961
|
+
const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
|
|
22962
|
+
for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
|
|
22963
|
+
const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
|
|
22964
|
+
const missing = await missingIds(request, candidates.map(revision), signal);
|
|
22965
|
+
const pending = /* @__PURE__ */ new Map();
|
|
22966
|
+
for (const raw of candidates)
|
|
22967
|
+
if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
|
|
22968
|
+
if (pending.size === 0 && !active) {
|
|
22969
|
+
const remote = await remoteMetadata(
|
|
22970
|
+
request,
|
|
22971
|
+
candidates.map((raw) => raw.id),
|
|
22991
22972
|
signal
|
|
22992
22973
|
);
|
|
22993
|
-
|
|
22994
|
-
|
|
22995
|
-
|
|
22996
|
-
|
|
22997
|
-
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
)
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23016
|
-
|
|
23017
|
-
|
|
23018
|
-
|
|
23019
|
-
|
|
23020
|
-
|
|
23021
|
-
|
|
22974
|
+
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
22975
|
+
for (const raw of candidates) {
|
|
22976
|
+
const server = remote.get(raw.id);
|
|
22977
|
+
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
22978
|
+
pending.set(raw.id, raw.updated_at + 1);
|
|
22979
|
+
}
|
|
22980
|
+
}
|
|
22981
|
+
if (pending.size === 0) {
|
|
22982
|
+
const result = (state) => ({
|
|
22983
|
+
state,
|
|
22984
|
+
total: rows.length,
|
|
22985
|
+
imported,
|
|
22986
|
+
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
22987
|
+
reembedded: reembedded.size,
|
|
22988
|
+
unembedded: unembedded.size,
|
|
22989
|
+
placeholderTombstones: placeholders.size,
|
|
22990
|
+
truncatedSourceChunkIds: truncated.size
|
|
22991
|
+
});
|
|
22992
|
+
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
22993
|
+
if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
|
|
22994
|
+
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
22995
|
+
if (!active)
|
|
22996
|
+
await request(
|
|
22997
|
+
"/account",
|
|
22998
|
+
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
22999
|
+
signal
|
|
23000
|
+
);
|
|
23001
|
+
await options.onActivated();
|
|
23002
|
+
return result("active");
|
|
23003
|
+
}
|
|
23004
|
+
if (pass >= MAX_UPLOAD_PASSES)
|
|
23005
|
+
throw new RemoteMemoryError(
|
|
23006
|
+
"Nearby still reports memories missing after importing them",
|
|
23007
|
+
409,
|
|
23008
|
+
"migration_incomplete"
|
|
23009
|
+
);
|
|
23010
|
+
const prepared = [];
|
|
23011
|
+
for (const raw of candidates) {
|
|
23012
|
+
const updatedAt = pending.get(raw.id);
|
|
23013
|
+
if (updatedAt === void 0) continue;
|
|
23014
|
+
try {
|
|
23015
|
+
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
23016
|
+
} catch (error) {
|
|
23017
|
+
if (!(error instanceof MemoryKeyError)) throw error;
|
|
23018
|
+
skipped.set(raw.id, "unreadable");
|
|
23019
|
+
}
|
|
23020
|
+
}
|
|
23021
|
+
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
23022
|
+
if (options.embeddingOptions)
|
|
23023
|
+
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
23024
|
+
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
23025
|
+
const upload = [];
|
|
23026
|
+
for (const { row, plaintext } of prepared) {
|
|
23027
|
+
const id2 = row.memory_id;
|
|
23028
|
+
if (!fitsNearby(row)) {
|
|
23029
|
+
skipped.set(id2, "invalid");
|
|
23030
|
+
continue;
|
|
23031
|
+
}
|
|
23032
|
+
upload.push(row);
|
|
23033
|
+
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
23034
|
+
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
23035
|
+
track(unembedded, !row.embedding);
|
|
23036
|
+
track(placeholders, plaintext === void 0);
|
|
23037
|
+
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
23022
23038
|
}
|
|
23023
|
-
|
|
23024
|
-
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
23025
|
-
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
23026
|
-
track(unembedded, !row.embedding);
|
|
23027
|
-
track(placeholders, plaintext === void 0);
|
|
23028
|
-
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
23029
|
-
}
|
|
23030
|
-
let sent = 0;
|
|
23031
|
-
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
23032
|
-
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
23033
|
-
await importBatch(request, canonical.keyId, batch, signal);
|
|
23034
|
-
claimed = true;
|
|
23035
|
-
sent += batch.length;
|
|
23039
|
+
let sent = 0;
|
|
23036
23040
|
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
23041
|
+
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
23042
|
+
await importBatch(request, canonical.keyId, batch, signal);
|
|
23043
|
+
claimed = true;
|
|
23044
|
+
sent += batch.length;
|
|
23045
|
+
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
23046
|
+
}
|
|
23047
|
+
imported += sent;
|
|
23037
23048
|
}
|
|
23038
|
-
|
|
23039
|
-
|
|
23049
|
+
};
|
|
23050
|
+
return uploadPasses();
|
|
23040
23051
|
}
|
|
23041
23052
|
|
|
23042
23053
|
// src/lib/memory/store/remoteStore.ts
|
|
@@ -25015,6 +25026,18 @@ async function assembleMemoryContext(options) {
|
|
|
25015
25026
|
|
|
25016
25027
|
// src/lib/memory/durableExtraction.ts
|
|
25017
25028
|
var import_watermelondb25 = require("@nozbe/watermelondb");
|
|
25029
|
+
async function messagePositions(storage, ids) {
|
|
25030
|
+
try {
|
|
25031
|
+
return await storage.messagesCollection.query(
|
|
25032
|
+
import_watermelondb25.Q.unsafeSqlQuery(
|
|
25033
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
25034
|
+
ids
|
|
25035
|
+
)
|
|
25036
|
+
).unsafeFetchRaw();
|
|
25037
|
+
} catch {
|
|
25038
|
+
return await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).unsafeFetchRaw();
|
|
25039
|
+
}
|
|
25040
|
+
}
|
|
25018
25041
|
var MAX_JOBS_PER_PASS = 3;
|
|
25019
25042
|
var MAX_ATTEMPTS = 3;
|
|
25020
25043
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -25583,9 +25606,9 @@ function createDurableAutoExtractor(options) {
|
|
|
25583
25606
|
r._setRaw("watermark_seq", null);
|
|
25584
25607
|
});
|
|
25585
25608
|
}
|
|
25586
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
25609
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
25587
25610
|
const foreignRows = new Set(
|
|
25588
|
-
rows.filter((row) => row.
|
|
25611
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
25589
25612
|
);
|
|
25590
25613
|
const sequences = /* @__PURE__ */ new Map();
|
|
25591
25614
|
if (fromSourceConversation) {
|
|
@@ -25595,7 +25618,7 @@ function createDurableAutoExtractor(options) {
|
|
|
25595
25618
|
}
|
|
25596
25619
|
for (const row of rows) {
|
|
25597
25620
|
if (foreignRows.has(row.id)) continue;
|
|
25598
|
-
const sequence = Number(row.
|
|
25621
|
+
const sequence = Number(row.message_id);
|
|
25599
25622
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
25600
25623
|
}
|
|
25601
25624
|
let boundary = -1;
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -9931,6 +9931,10 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
|
|
|
9931
9931
|
name: string;
|
|
9932
9932
|
kind?: string;
|
|
9933
9933
|
}[];
|
|
9934
|
+
/** Drain all local memory writes, block new writes until the task returns, and retain the block if the remote handoff fails. */
|
|
9935
|
+
withWriteBarrier: (task: () => Promise<MemoryMigrationResult>) => Promise<MemoryMigrationResult>;
|
|
9936
|
+
/** Route all subsequent memory writes to the remote store before this callback returns. */
|
|
9937
|
+
onActivated: () => void | Promise<void>;
|
|
9934
9938
|
signal?: AbortSignal;
|
|
9935
9939
|
onProgress?: (progress: MemoryMigrationProgress) => void;
|
|
9936
9940
|
}
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -9931,6 +9931,10 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
|
|
|
9931
9931
|
name: string;
|
|
9932
9932
|
kind?: string;
|
|
9933
9933
|
}[];
|
|
9934
|
+
/** Drain all local memory writes, block new writes until the task returns, and retain the block if the remote handoff fails. */
|
|
9935
|
+
withWriteBarrier: (task: () => Promise<MemoryMigrationResult>) => Promise<MemoryMigrationResult>;
|
|
9936
|
+
/** Route all subsequent memory writes to the remote store before this callback returns. */
|
|
9937
|
+
onActivated: () => void | Promise<void>;
|
|
9934
9938
|
signal?: AbortSignal;
|
|
9935
9939
|
onProgress?: (progress: MemoryMigrationProgress) => void;
|
|
9936
9940
|
}
|
package/dist/expo/index.mjs
CHANGED
|
@@ -729,6 +729,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
729
729
|
if (cachedKey) {
|
|
730
730
|
return cachedKey;
|
|
731
731
|
}
|
|
732
|
+
const startEpoch = sessionEpoch;
|
|
732
733
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
733
734
|
if (!keyHex) {
|
|
734
735
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -741,6 +742,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
741
742
|
false,
|
|
742
743
|
["encrypt", "decrypt"]
|
|
743
744
|
);
|
|
745
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
746
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
747
|
+
}
|
|
744
748
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
745
749
|
return cryptoKey;
|
|
746
750
|
}
|
|
@@ -21248,6 +21252,8 @@ async function backfillTopics(ctx) {
|
|
|
21248
21252
|
if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
|
|
21249
21253
|
}
|
|
21250
21254
|
async function migrateMemoriesToRemote(options) {
|
|
21255
|
+
if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
|
|
21256
|
+
throw new Error("A write barrier and a remote store handoff are required");
|
|
21251
21257
|
const { canonical, signal } = options;
|
|
21252
21258
|
const rings = [canonical, ...options.fallbacks ?? []];
|
|
21253
21259
|
const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
@@ -21275,93 +21281,98 @@ async function migrateMemoriesToRemote(options) {
|
|
|
21275
21281
|
const placeholders = /* @__PURE__ */ new Set();
|
|
21276
21282
|
const truncated = /* @__PURE__ */ new Set();
|
|
21277
21283
|
let imported = 0;
|
|
21278
|
-
|
|
21279
|
-
|
|
21280
|
-
|
|
21281
|
-
|
|
21282
|
-
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
);
|
|
21292
|
-
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
21293
|
-
for (const raw of candidates) {
|
|
21294
|
-
const server = remote.get(raw.id);
|
|
21295
|
-
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
21296
|
-
pending.set(raw.id, raw.updated_at + 1);
|
|
21297
|
-
}
|
|
21298
|
-
}
|
|
21299
|
-
if (pending.size === 0) {
|
|
21300
|
-
const result = (state) => ({
|
|
21301
|
-
state,
|
|
21302
|
-
total: rows.length,
|
|
21303
|
-
imported,
|
|
21304
|
-
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
21305
|
-
reembedded: reembedded.size,
|
|
21306
|
-
unembedded: unembedded.size,
|
|
21307
|
-
placeholderTombstones: placeholders.size,
|
|
21308
|
-
truncatedSourceChunkIds: truncated.size
|
|
21309
|
-
});
|
|
21310
|
-
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
21311
|
-
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
21312
|
-
if (!active)
|
|
21313
|
-
await request(
|
|
21314
|
-
"/account",
|
|
21315
|
-
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
21284
|
+
const uploadPasses = async (finalizing = false) => {
|
|
21285
|
+
for (let pass = 0; ; pass++) {
|
|
21286
|
+
const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
|
|
21287
|
+
for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
|
|
21288
|
+
const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
|
|
21289
|
+
const missing = await missingIds(request, candidates.map(revision), signal);
|
|
21290
|
+
const pending = /* @__PURE__ */ new Map();
|
|
21291
|
+
for (const raw of candidates)
|
|
21292
|
+
if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
|
|
21293
|
+
if (pending.size === 0 && !active) {
|
|
21294
|
+
const remote = await remoteMetadata(
|
|
21295
|
+
request,
|
|
21296
|
+
candidates.map((raw) => raw.id),
|
|
21316
21297
|
signal
|
|
21317
21298
|
);
|
|
21318
|
-
|
|
21319
|
-
|
|
21320
|
-
|
|
21321
|
-
|
|
21322
|
-
|
|
21323
|
-
|
|
21324
|
-
|
|
21325
|
-
)
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
|
|
21329
|
-
|
|
21330
|
-
|
|
21331
|
-
|
|
21332
|
-
|
|
21333
|
-
|
|
21334
|
-
|
|
21335
|
-
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21299
|
+
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
21300
|
+
for (const raw of candidates) {
|
|
21301
|
+
const server = remote.get(raw.id);
|
|
21302
|
+
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
21303
|
+
pending.set(raw.id, raw.updated_at + 1);
|
|
21304
|
+
}
|
|
21305
|
+
}
|
|
21306
|
+
if (pending.size === 0) {
|
|
21307
|
+
const result = (state) => ({
|
|
21308
|
+
state,
|
|
21309
|
+
total: rows.length,
|
|
21310
|
+
imported,
|
|
21311
|
+
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
21312
|
+
reembedded: reembedded.size,
|
|
21313
|
+
unembedded: unembedded.size,
|
|
21314
|
+
placeholderTombstones: placeholders.size,
|
|
21315
|
+
truncatedSourceChunkIds: truncated.size
|
|
21316
|
+
});
|
|
21317
|
+
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
21318
|
+
if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
|
|
21319
|
+
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
21320
|
+
if (!active)
|
|
21321
|
+
await request(
|
|
21322
|
+
"/account",
|
|
21323
|
+
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
21324
|
+
signal
|
|
21325
|
+
);
|
|
21326
|
+
await options.onActivated();
|
|
21327
|
+
return result("active");
|
|
21328
|
+
}
|
|
21329
|
+
if (pass >= MAX_UPLOAD_PASSES)
|
|
21330
|
+
throw new RemoteMemoryError(
|
|
21331
|
+
"Nearby still reports memories missing after importing them",
|
|
21332
|
+
409,
|
|
21333
|
+
"migration_incomplete"
|
|
21334
|
+
);
|
|
21335
|
+
const prepared = [];
|
|
21336
|
+
for (const raw of candidates) {
|
|
21337
|
+
const updatedAt = pending.get(raw.id);
|
|
21338
|
+
if (updatedAt === void 0) continue;
|
|
21339
|
+
try {
|
|
21340
|
+
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
21341
|
+
} catch (error) {
|
|
21342
|
+
if (!(error instanceof MemoryKeyError)) throw error;
|
|
21343
|
+
skipped.set(raw.id, "unreadable");
|
|
21344
|
+
}
|
|
21347
21345
|
}
|
|
21348
|
-
|
|
21349
|
-
|
|
21350
|
-
|
|
21351
|
-
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
|
|
21346
|
+
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
21347
|
+
if (options.embeddingOptions)
|
|
21348
|
+
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
21349
|
+
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
21350
|
+
const upload = [];
|
|
21351
|
+
for (const { row, plaintext } of prepared) {
|
|
21352
|
+
const id2 = row.memory_id;
|
|
21353
|
+
if (!fitsNearby(row)) {
|
|
21354
|
+
skipped.set(id2, "invalid");
|
|
21355
|
+
continue;
|
|
21356
|
+
}
|
|
21357
|
+
upload.push(row);
|
|
21358
|
+
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
21359
|
+
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
21360
|
+
track(unembedded, !row.embedding);
|
|
21361
|
+
track(placeholders, plaintext === void 0);
|
|
21362
|
+
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
21363
|
+
}
|
|
21364
|
+
let sent = 0;
|
|
21361
21365
|
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
21366
|
+
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
21367
|
+
await importBatch(request, canonical.keyId, batch, signal);
|
|
21368
|
+
claimed = true;
|
|
21369
|
+
sent += batch.length;
|
|
21370
|
+
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
21371
|
+
}
|
|
21372
|
+
imported += sent;
|
|
21362
21373
|
}
|
|
21363
|
-
|
|
21364
|
-
|
|
21374
|
+
};
|
|
21375
|
+
return uploadPasses();
|
|
21365
21376
|
}
|
|
21366
21377
|
|
|
21367
21378
|
// src/lib/memory/store/remoteStore.ts
|
|
@@ -23337,6 +23348,18 @@ async function assembleMemoryContext(options) {
|
|
|
23337
23348
|
|
|
23338
23349
|
// src/lib/memory/durableExtraction.ts
|
|
23339
23350
|
import { Q as Q10 } from "@nozbe/watermelondb";
|
|
23351
|
+
async function messagePositions(storage, ids) {
|
|
23352
|
+
try {
|
|
23353
|
+
return await storage.messagesCollection.query(
|
|
23354
|
+
Q10.unsafeSqlQuery(
|
|
23355
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
23356
|
+
ids
|
|
23357
|
+
)
|
|
23358
|
+
).unsafeFetchRaw();
|
|
23359
|
+
} catch {
|
|
23360
|
+
return await storage.messagesCollection.query(Q10.where("id", Q10.oneOf(ids))).unsafeFetchRaw();
|
|
23361
|
+
}
|
|
23362
|
+
}
|
|
23340
23363
|
var MAX_JOBS_PER_PASS = 3;
|
|
23341
23364
|
var MAX_ATTEMPTS = 3;
|
|
23342
23365
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -23905,9 +23928,9 @@ function createDurableAutoExtractor(options) {
|
|
|
23905
23928
|
r._setRaw("watermark_seq", null);
|
|
23906
23929
|
});
|
|
23907
23930
|
}
|
|
23908
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
23931
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
23909
23932
|
const foreignRows = new Set(
|
|
23910
|
-
rows.filter((row) => row.
|
|
23933
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
23911
23934
|
);
|
|
23912
23935
|
const sequences = /* @__PURE__ */ new Map();
|
|
23913
23936
|
if (fromSourceConversation) {
|
|
@@ -23917,7 +23940,7 @@ function createDurableAutoExtractor(options) {
|
|
|
23917
23940
|
}
|
|
23918
23941
|
for (const row of rows) {
|
|
23919
23942
|
if (foreignRows.has(row.id)) continue;
|
|
23920
|
-
const sequence = Number(row.
|
|
23943
|
+
const sequence = Number(row.message_id);
|
|
23921
23944
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
23922
23945
|
}
|
|
23923
23946
|
let boundary = -1;
|
package/dist/index.cjs
CHANGED
|
@@ -1404,6 +1404,7 @@ var EncryptionKeyMissingError = class extends Error {
|
|
|
1404
1404
|
}
|
|
1405
1405
|
};
|
|
1406
1406
|
var encryptionKeyStore = /* @__PURE__ */ new Map();
|
|
1407
|
+
var sessionEpoch = 0;
|
|
1407
1408
|
var cryptoKeyCache = /* @__PURE__ */ new Map();
|
|
1408
1409
|
var clearAllEncryptionStateCallbacks = /* @__PURE__ */ new Set();
|
|
1409
1410
|
function onClearAllEncryptionState(callback) {
|
|
@@ -1455,6 +1456,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1455
1456
|
if (cachedKey) {
|
|
1456
1457
|
return cachedKey;
|
|
1457
1458
|
}
|
|
1459
|
+
const startEpoch = sessionEpoch;
|
|
1458
1460
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
1459
1461
|
if (!keyHex) {
|
|
1460
1462
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -1467,6 +1469,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1467
1469
|
false,
|
|
1468
1470
|
["encrypt", "decrypt"]
|
|
1469
1471
|
);
|
|
1472
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
1473
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
1474
|
+
}
|
|
1470
1475
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
1471
1476
|
return cryptoKey;
|
|
1472
1477
|
}
|
|
@@ -2159,7 +2164,7 @@ async function createRemoteChatStore(options) {
|
|
|
2159
2164
|
var LRU_CAPACITY = 256;
|
|
2160
2165
|
var titleCache = /* @__PURE__ */ new Map();
|
|
2161
2166
|
var pendingDecrypts = /* @__PURE__ */ new Map();
|
|
2162
|
-
var
|
|
2167
|
+
var sessionEpoch2 = 0;
|
|
2163
2168
|
function buildCacheKey(address, encryptedTitle) {
|
|
2164
2169
|
return `${address}:${encryptedTitle}`;
|
|
2165
2170
|
}
|
|
@@ -2178,7 +2183,7 @@ function insertWithEviction(key, value) {
|
|
|
2178
2183
|
function clearLazyTitleCache() {
|
|
2179
2184
|
titleCache.clear();
|
|
2180
2185
|
pendingDecrypts.clear();
|
|
2181
|
-
|
|
2186
|
+
sessionEpoch2 += 1;
|
|
2182
2187
|
}
|
|
2183
2188
|
onClearAllEncryptionState(clearLazyTitleCache);
|
|
2184
2189
|
async function decryptConversationTitle(encryptedTitle, address) {
|
|
@@ -2200,13 +2205,13 @@ async function decryptConversationTitle(encryptedTitle, address) {
|
|
|
2200
2205
|
}
|
|
2201
2206
|
const inFlight = pendingDecrypts.get(cacheKey);
|
|
2202
2207
|
if (inFlight) return inFlight;
|
|
2203
|
-
const epochAtStart =
|
|
2208
|
+
const epochAtStart = sessionEpoch2;
|
|
2204
2209
|
const promise = (async () => {
|
|
2205
2210
|
const plaintext = await decryptField(encryptedTitle, address);
|
|
2206
2211
|
if (isEncrypted(plaintext)) {
|
|
2207
2212
|
throw new Error("decryptConversationTitle: decryption failed (returned ciphertext)");
|
|
2208
2213
|
}
|
|
2209
|
-
if (
|
|
2214
|
+
if (sessionEpoch2 === epochAtStart) {
|
|
2210
2215
|
insertWithEviction(cacheKey, plaintext);
|
|
2211
2216
|
}
|
|
2212
2217
|
return plaintext;
|
package/dist/index.mjs
CHANGED
|
@@ -1201,6 +1201,7 @@ var EncryptionKeyMissingError = class extends Error {
|
|
|
1201
1201
|
}
|
|
1202
1202
|
};
|
|
1203
1203
|
var encryptionKeyStore = /* @__PURE__ */ new Map();
|
|
1204
|
+
var sessionEpoch = 0;
|
|
1204
1205
|
var cryptoKeyCache = /* @__PURE__ */ new Map();
|
|
1205
1206
|
var clearAllEncryptionStateCallbacks = /* @__PURE__ */ new Set();
|
|
1206
1207
|
function onClearAllEncryptionState(callback) {
|
|
@@ -1252,6 +1253,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1252
1253
|
if (cachedKey) {
|
|
1253
1254
|
return cachedKey;
|
|
1254
1255
|
}
|
|
1256
|
+
const startEpoch = sessionEpoch;
|
|
1255
1257
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
1256
1258
|
if (!keyHex) {
|
|
1257
1259
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -1264,6 +1266,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1264
1266
|
false,
|
|
1265
1267
|
["encrypt", "decrypt"]
|
|
1266
1268
|
);
|
|
1269
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
1270
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
1271
|
+
}
|
|
1267
1272
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
1268
1273
|
return cryptoKey;
|
|
1269
1274
|
}
|
|
@@ -1956,7 +1961,7 @@ async function createRemoteChatStore(options) {
|
|
|
1956
1961
|
var LRU_CAPACITY = 256;
|
|
1957
1962
|
var titleCache = /* @__PURE__ */ new Map();
|
|
1958
1963
|
var pendingDecrypts = /* @__PURE__ */ new Map();
|
|
1959
|
-
var
|
|
1964
|
+
var sessionEpoch2 = 0;
|
|
1960
1965
|
function buildCacheKey(address, encryptedTitle) {
|
|
1961
1966
|
return `${address}:${encryptedTitle}`;
|
|
1962
1967
|
}
|
|
@@ -1975,7 +1980,7 @@ function insertWithEviction(key, value) {
|
|
|
1975
1980
|
function clearLazyTitleCache() {
|
|
1976
1981
|
titleCache.clear();
|
|
1977
1982
|
pendingDecrypts.clear();
|
|
1978
|
-
|
|
1983
|
+
sessionEpoch2 += 1;
|
|
1979
1984
|
}
|
|
1980
1985
|
onClearAllEncryptionState(clearLazyTitleCache);
|
|
1981
1986
|
async function decryptConversationTitle(encryptedTitle, address) {
|
|
@@ -1997,13 +2002,13 @@ async function decryptConversationTitle(encryptedTitle, address) {
|
|
|
1997
2002
|
}
|
|
1998
2003
|
const inFlight = pendingDecrypts.get(cacheKey);
|
|
1999
2004
|
if (inFlight) return inFlight;
|
|
2000
|
-
const epochAtStart =
|
|
2005
|
+
const epochAtStart = sessionEpoch2;
|
|
2001
2006
|
const promise = (async () => {
|
|
2002
2007
|
const plaintext = await decryptField(encryptedTitle, address);
|
|
2003
2008
|
if (isEncrypted(plaintext)) {
|
|
2004
2009
|
throw new Error("decryptConversationTitle: decryption failed (returned ciphertext)");
|
|
2005
2010
|
}
|
|
2006
|
-
if (
|
|
2011
|
+
if (sessionEpoch2 === epochAtStart) {
|
|
2007
2012
|
insertWithEviction(cacheKey, plaintext);
|
|
2008
2013
|
}
|
|
2009
2014
|
return plaintext;
|
package/dist/react/index.cjs
CHANGED
|
@@ -2093,6 +2093,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2093
2093
|
if (cachedKey) {
|
|
2094
2094
|
return cachedKey;
|
|
2095
2095
|
}
|
|
2096
|
+
const startEpoch = sessionEpoch;
|
|
2096
2097
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
2097
2098
|
if (!keyHex) {
|
|
2098
2099
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -2105,6 +2106,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2105
2106
|
false,
|
|
2106
2107
|
["encrypt", "decrypt"]
|
|
2107
2108
|
);
|
|
2109
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
2110
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
2111
|
+
}
|
|
2108
2112
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
2109
2113
|
return cryptoKey;
|
|
2110
2114
|
}
|
|
@@ -25236,6 +25240,8 @@ async function backfillTopics(ctx) {
|
|
|
25236
25240
|
if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
|
|
25237
25241
|
}
|
|
25238
25242
|
async function migrateMemoriesToRemote(options) {
|
|
25243
|
+
if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
|
|
25244
|
+
throw new Error("A write barrier and a remote store handoff are required");
|
|
25239
25245
|
const { canonical, signal } = options;
|
|
25240
25246
|
const rings = [canonical, ...options.fallbacks ?? []];
|
|
25241
25247
|
const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
@@ -25263,93 +25269,98 @@ async function migrateMemoriesToRemote(options) {
|
|
|
25263
25269
|
const placeholders = /* @__PURE__ */ new Set();
|
|
25264
25270
|
const truncated = /* @__PURE__ */ new Set();
|
|
25265
25271
|
let imported = 0;
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
);
|
|
25280
|
-
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
25281
|
-
for (const raw of candidates) {
|
|
25282
|
-
const server = remote.get(raw.id);
|
|
25283
|
-
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
25284
|
-
pending.set(raw.id, raw.updated_at + 1);
|
|
25285
|
-
}
|
|
25286
|
-
}
|
|
25287
|
-
if (pending.size === 0) {
|
|
25288
|
-
const result = (state) => ({
|
|
25289
|
-
state,
|
|
25290
|
-
total: rows.length,
|
|
25291
|
-
imported,
|
|
25292
|
-
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
25293
|
-
reembedded: reembedded.size,
|
|
25294
|
-
unembedded: unembedded.size,
|
|
25295
|
-
placeholderTombstones: placeholders.size,
|
|
25296
|
-
truncatedSourceChunkIds: truncated.size
|
|
25297
|
-
});
|
|
25298
|
-
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
25299
|
-
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
25300
|
-
if (!active)
|
|
25301
|
-
await request(
|
|
25302
|
-
"/account",
|
|
25303
|
-
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
25272
|
+
const uploadPasses = async (finalizing = false) => {
|
|
25273
|
+
for (let pass = 0; ; pass++) {
|
|
25274
|
+
const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
|
|
25275
|
+
for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
|
|
25276
|
+
const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
|
|
25277
|
+
const missing = await missingIds(request, candidates.map(revision), signal);
|
|
25278
|
+
const pending = /* @__PURE__ */ new Map();
|
|
25279
|
+
for (const raw of candidates)
|
|
25280
|
+
if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
|
|
25281
|
+
if (pending.size === 0 && !active) {
|
|
25282
|
+
const remote = await remoteMetadata(
|
|
25283
|
+
request,
|
|
25284
|
+
candidates.map((raw) => raw.id),
|
|
25304
25285
|
signal
|
|
25305
25286
|
);
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25309
|
-
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25313
|
-
)
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
25320
|
-
|
|
25321
|
-
|
|
25322
|
-
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
25332
|
-
|
|
25333
|
-
|
|
25334
|
-
|
|
25287
|
+
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
25288
|
+
for (const raw of candidates) {
|
|
25289
|
+
const server = remote.get(raw.id);
|
|
25290
|
+
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
25291
|
+
pending.set(raw.id, raw.updated_at + 1);
|
|
25292
|
+
}
|
|
25293
|
+
}
|
|
25294
|
+
if (pending.size === 0) {
|
|
25295
|
+
const result = (state) => ({
|
|
25296
|
+
state,
|
|
25297
|
+
total: rows.length,
|
|
25298
|
+
imported,
|
|
25299
|
+
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
25300
|
+
reembedded: reembedded.size,
|
|
25301
|
+
unembedded: unembedded.size,
|
|
25302
|
+
placeholderTombstones: placeholders.size,
|
|
25303
|
+
truncatedSourceChunkIds: truncated.size
|
|
25304
|
+
});
|
|
25305
|
+
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
25306
|
+
if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
|
|
25307
|
+
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
25308
|
+
if (!active)
|
|
25309
|
+
await request(
|
|
25310
|
+
"/account",
|
|
25311
|
+
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
25312
|
+
signal
|
|
25313
|
+
);
|
|
25314
|
+
await options.onActivated();
|
|
25315
|
+
return result("active");
|
|
25316
|
+
}
|
|
25317
|
+
if (pass >= MAX_UPLOAD_PASSES)
|
|
25318
|
+
throw new RemoteMemoryError(
|
|
25319
|
+
"Nearby still reports memories missing after importing them",
|
|
25320
|
+
409,
|
|
25321
|
+
"migration_incomplete"
|
|
25322
|
+
);
|
|
25323
|
+
const prepared = [];
|
|
25324
|
+
for (const raw of candidates) {
|
|
25325
|
+
const updatedAt = pending.get(raw.id);
|
|
25326
|
+
if (updatedAt === void 0) continue;
|
|
25327
|
+
try {
|
|
25328
|
+
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
25329
|
+
} catch (error) {
|
|
25330
|
+
if (!(error instanceof MemoryKeyError)) throw error;
|
|
25331
|
+
skipped.set(raw.id, "unreadable");
|
|
25332
|
+
}
|
|
25333
|
+
}
|
|
25334
|
+
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
25335
|
+
if (options.embeddingOptions)
|
|
25336
|
+
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
25337
|
+
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
25338
|
+
const upload = [];
|
|
25339
|
+
for (const { row, plaintext } of prepared) {
|
|
25340
|
+
const id2 = row.memory_id;
|
|
25341
|
+
if (!fitsNearby(row)) {
|
|
25342
|
+
skipped.set(id2, "invalid");
|
|
25343
|
+
continue;
|
|
25344
|
+
}
|
|
25345
|
+
upload.push(row);
|
|
25346
|
+
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
25347
|
+
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
25348
|
+
track(unembedded, !row.embedding);
|
|
25349
|
+
track(placeholders, plaintext === void 0);
|
|
25350
|
+
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
25335
25351
|
}
|
|
25336
|
-
|
|
25337
|
-
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
25338
|
-
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
25339
|
-
track(unembedded, !row.embedding);
|
|
25340
|
-
track(placeholders, plaintext === void 0);
|
|
25341
|
-
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
25342
|
-
}
|
|
25343
|
-
let sent = 0;
|
|
25344
|
-
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
25345
|
-
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
25346
|
-
await importBatch(request, canonical.keyId, batch, signal);
|
|
25347
|
-
claimed = true;
|
|
25348
|
-
sent += batch.length;
|
|
25352
|
+
let sent = 0;
|
|
25349
25353
|
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
25354
|
+
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
25355
|
+
await importBatch(request, canonical.keyId, batch, signal);
|
|
25356
|
+
claimed = true;
|
|
25357
|
+
sent += batch.length;
|
|
25358
|
+
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
25359
|
+
}
|
|
25360
|
+
imported += sent;
|
|
25350
25361
|
}
|
|
25351
|
-
|
|
25352
|
-
|
|
25362
|
+
};
|
|
25363
|
+
return uploadPasses();
|
|
25353
25364
|
}
|
|
25354
25365
|
|
|
25355
25366
|
// src/lib/memory/store/remoteStore.ts
|
|
@@ -27328,6 +27339,18 @@ async function assembleMemoryContext(options) {
|
|
|
27328
27339
|
|
|
27329
27340
|
// src/lib/memory/durableExtraction.ts
|
|
27330
27341
|
var import_watermelondb34 = require("@nozbe/watermelondb");
|
|
27342
|
+
async function messagePositions(storage, ids) {
|
|
27343
|
+
try {
|
|
27344
|
+
return await storage.messagesCollection.query(
|
|
27345
|
+
import_watermelondb34.Q.unsafeSqlQuery(
|
|
27346
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
27347
|
+
ids
|
|
27348
|
+
)
|
|
27349
|
+
).unsafeFetchRaw();
|
|
27350
|
+
} catch {
|
|
27351
|
+
return await storage.messagesCollection.query(import_watermelondb34.Q.where("id", import_watermelondb34.Q.oneOf(ids))).unsafeFetchRaw();
|
|
27352
|
+
}
|
|
27353
|
+
}
|
|
27331
27354
|
var MAX_JOBS_PER_PASS = 3;
|
|
27332
27355
|
var MAX_ATTEMPTS = 3;
|
|
27333
27356
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -27896,9 +27919,9 @@ function createDurableAutoExtractor(options) {
|
|
|
27896
27919
|
r._setRaw("watermark_seq", null);
|
|
27897
27920
|
});
|
|
27898
27921
|
}
|
|
27899
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
27922
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
27900
27923
|
const foreignRows = new Set(
|
|
27901
|
-
rows.filter((row) => row.
|
|
27924
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
27902
27925
|
);
|
|
27903
27926
|
const sequences = /* @__PURE__ */ new Map();
|
|
27904
27927
|
if (fromSourceConversation) {
|
|
@@ -27908,7 +27931,7 @@ function createDurableAutoExtractor(options) {
|
|
|
27908
27931
|
}
|
|
27909
27932
|
for (const row of rows) {
|
|
27910
27933
|
if (foreignRows.has(row.id)) continue;
|
|
27911
|
-
const sequence = Number(row.
|
|
27934
|
+
const sequence = Number(row.message_id);
|
|
27912
27935
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
27913
27936
|
}
|
|
27914
27937
|
let boundary = -1;
|
package/dist/react/index.d.mts
CHANGED
|
@@ -11665,6 +11665,10 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
|
|
|
11665
11665
|
name: string;
|
|
11666
11666
|
kind?: string;
|
|
11667
11667
|
}[];
|
|
11668
|
+
/** Drain all local memory writes, block new writes until the task returns, and retain the block if the remote handoff fails. */
|
|
11669
|
+
withWriteBarrier: (task: () => Promise<MemoryMigrationResult>) => Promise<MemoryMigrationResult>;
|
|
11670
|
+
/** Route all subsequent memory writes to the remote store before this callback returns. */
|
|
11671
|
+
onActivated: () => void | Promise<void>;
|
|
11668
11672
|
signal?: AbortSignal;
|
|
11669
11673
|
onProgress?: (progress: MemoryMigrationProgress) => void;
|
|
11670
11674
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -11665,6 +11665,10 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
|
|
|
11665
11665
|
name: string;
|
|
11666
11666
|
kind?: string;
|
|
11667
11667
|
}[];
|
|
11668
|
+
/** Drain all local memory writes, block new writes until the task returns, and retain the block if the remote handoff fails. */
|
|
11669
|
+
withWriteBarrier: (task: () => Promise<MemoryMigrationResult>) => Promise<MemoryMigrationResult>;
|
|
11670
|
+
/** Route all subsequent memory writes to the remote store before this callback returns. */
|
|
11671
|
+
onActivated: () => void | Promise<void>;
|
|
11668
11672
|
signal?: AbortSignal;
|
|
11669
11673
|
onProgress?: (progress: MemoryMigrationProgress) => void;
|
|
11670
11674
|
}
|
package/dist/react/index.mjs
CHANGED
|
@@ -295,6 +295,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
295
295
|
if (cachedKey) {
|
|
296
296
|
return cachedKey;
|
|
297
297
|
}
|
|
298
|
+
const startEpoch = sessionEpoch;
|
|
298
299
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
299
300
|
if (!keyHex) {
|
|
300
301
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -307,6 +308,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
307
308
|
false,
|
|
308
309
|
["encrypt", "decrypt"]
|
|
309
310
|
);
|
|
311
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
312
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
313
|
+
}
|
|
310
314
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
311
315
|
return cryptoKey;
|
|
312
316
|
}
|
|
@@ -23353,6 +23357,8 @@ async function backfillTopics(ctx) {
|
|
|
23353
23357
|
if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
|
|
23354
23358
|
}
|
|
23355
23359
|
async function migrateMemoriesToRemote(options) {
|
|
23360
|
+
if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
|
|
23361
|
+
throw new Error("A write barrier and a remote store handoff are required");
|
|
23356
23362
|
const { canonical, signal } = options;
|
|
23357
23363
|
const rings = [canonical, ...options.fallbacks ?? []];
|
|
23358
23364
|
const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
@@ -23380,93 +23386,98 @@ async function migrateMemoriesToRemote(options) {
|
|
|
23380
23386
|
const placeholders = /* @__PURE__ */ new Set();
|
|
23381
23387
|
const truncated = /* @__PURE__ */ new Set();
|
|
23382
23388
|
let imported = 0;
|
|
23383
|
-
|
|
23384
|
-
|
|
23385
|
-
|
|
23386
|
-
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
|
|
23390
|
-
|
|
23391
|
-
|
|
23392
|
-
|
|
23393
|
-
|
|
23394
|
-
|
|
23395
|
-
|
|
23396
|
-
);
|
|
23397
|
-
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
23398
|
-
for (const raw of candidates) {
|
|
23399
|
-
const server = remote.get(raw.id);
|
|
23400
|
-
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
23401
|
-
pending.set(raw.id, raw.updated_at + 1);
|
|
23402
|
-
}
|
|
23403
|
-
}
|
|
23404
|
-
if (pending.size === 0) {
|
|
23405
|
-
const result = (state) => ({
|
|
23406
|
-
state,
|
|
23407
|
-
total: rows.length,
|
|
23408
|
-
imported,
|
|
23409
|
-
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
23410
|
-
reembedded: reembedded.size,
|
|
23411
|
-
unembedded: unembedded.size,
|
|
23412
|
-
placeholderTombstones: placeholders.size,
|
|
23413
|
-
truncatedSourceChunkIds: truncated.size
|
|
23414
|
-
});
|
|
23415
|
-
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
23416
|
-
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
23417
|
-
if (!active)
|
|
23418
|
-
await request(
|
|
23419
|
-
"/account",
|
|
23420
|
-
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
23389
|
+
const uploadPasses = async (finalizing = false) => {
|
|
23390
|
+
for (let pass = 0; ; pass++) {
|
|
23391
|
+
const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
|
|
23392
|
+
for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
|
|
23393
|
+
const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
|
|
23394
|
+
const missing = await missingIds(request, candidates.map(revision), signal);
|
|
23395
|
+
const pending = /* @__PURE__ */ new Map();
|
|
23396
|
+
for (const raw of candidates)
|
|
23397
|
+
if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
|
|
23398
|
+
if (pending.size === 0 && !active) {
|
|
23399
|
+
const remote = await remoteMetadata(
|
|
23400
|
+
request,
|
|
23401
|
+
candidates.map((raw) => raw.id),
|
|
23421
23402
|
signal
|
|
23422
23403
|
);
|
|
23423
|
-
|
|
23424
|
-
|
|
23425
|
-
|
|
23426
|
-
|
|
23427
|
-
|
|
23428
|
-
|
|
23429
|
-
|
|
23430
|
-
)
|
|
23431
|
-
|
|
23432
|
-
|
|
23433
|
-
|
|
23434
|
-
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
|
|
23439
|
-
|
|
23440
|
-
|
|
23441
|
-
|
|
23442
|
-
|
|
23443
|
-
|
|
23444
|
-
|
|
23445
|
-
|
|
23446
|
-
|
|
23447
|
-
|
|
23448
|
-
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23404
|
+
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
23405
|
+
for (const raw of candidates) {
|
|
23406
|
+
const server = remote.get(raw.id);
|
|
23407
|
+
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
23408
|
+
pending.set(raw.id, raw.updated_at + 1);
|
|
23409
|
+
}
|
|
23410
|
+
}
|
|
23411
|
+
if (pending.size === 0) {
|
|
23412
|
+
const result = (state) => ({
|
|
23413
|
+
state,
|
|
23414
|
+
total: rows.length,
|
|
23415
|
+
imported,
|
|
23416
|
+
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
23417
|
+
reembedded: reembedded.size,
|
|
23418
|
+
unembedded: unembedded.size,
|
|
23419
|
+
placeholderTombstones: placeholders.size,
|
|
23420
|
+
truncatedSourceChunkIds: truncated.size
|
|
23421
|
+
});
|
|
23422
|
+
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
23423
|
+
if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
|
|
23424
|
+
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
23425
|
+
if (!active)
|
|
23426
|
+
await request(
|
|
23427
|
+
"/account",
|
|
23428
|
+
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
23429
|
+
signal
|
|
23430
|
+
);
|
|
23431
|
+
await options.onActivated();
|
|
23432
|
+
return result("active");
|
|
23433
|
+
}
|
|
23434
|
+
if (pass >= MAX_UPLOAD_PASSES)
|
|
23435
|
+
throw new RemoteMemoryError(
|
|
23436
|
+
"Nearby still reports memories missing after importing them",
|
|
23437
|
+
409,
|
|
23438
|
+
"migration_incomplete"
|
|
23439
|
+
);
|
|
23440
|
+
const prepared = [];
|
|
23441
|
+
for (const raw of candidates) {
|
|
23442
|
+
const updatedAt = pending.get(raw.id);
|
|
23443
|
+
if (updatedAt === void 0) continue;
|
|
23444
|
+
try {
|
|
23445
|
+
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
23446
|
+
} catch (error) {
|
|
23447
|
+
if (!(error instanceof MemoryKeyError)) throw error;
|
|
23448
|
+
skipped.set(raw.id, "unreadable");
|
|
23449
|
+
}
|
|
23450
|
+
}
|
|
23451
|
+
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
23452
|
+
if (options.embeddingOptions)
|
|
23453
|
+
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
23454
|
+
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
23455
|
+
const upload = [];
|
|
23456
|
+
for (const { row, plaintext } of prepared) {
|
|
23457
|
+
const id2 = row.memory_id;
|
|
23458
|
+
if (!fitsNearby(row)) {
|
|
23459
|
+
skipped.set(id2, "invalid");
|
|
23460
|
+
continue;
|
|
23461
|
+
}
|
|
23462
|
+
upload.push(row);
|
|
23463
|
+
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
23464
|
+
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
23465
|
+
track(unembedded, !row.embedding);
|
|
23466
|
+
track(placeholders, plaintext === void 0);
|
|
23467
|
+
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
23452
23468
|
}
|
|
23453
|
-
|
|
23454
|
-
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
23455
|
-
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
23456
|
-
track(unembedded, !row.embedding);
|
|
23457
|
-
track(placeholders, plaintext === void 0);
|
|
23458
|
-
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
23459
|
-
}
|
|
23460
|
-
let sent = 0;
|
|
23461
|
-
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
23462
|
-
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
23463
|
-
await importBatch(request, canonical.keyId, batch, signal);
|
|
23464
|
-
claimed = true;
|
|
23465
|
-
sent += batch.length;
|
|
23469
|
+
let sent = 0;
|
|
23466
23470
|
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
23471
|
+
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
23472
|
+
await importBatch(request, canonical.keyId, batch, signal);
|
|
23473
|
+
claimed = true;
|
|
23474
|
+
sent += batch.length;
|
|
23475
|
+
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
23476
|
+
}
|
|
23477
|
+
imported += sent;
|
|
23467
23478
|
}
|
|
23468
|
-
|
|
23469
|
-
|
|
23479
|
+
};
|
|
23480
|
+
return uploadPasses();
|
|
23470
23481
|
}
|
|
23471
23482
|
|
|
23472
23483
|
// src/lib/memory/store/remoteStore.ts
|
|
@@ -25442,6 +25453,18 @@ async function assembleMemoryContext(options) {
|
|
|
25442
25453
|
|
|
25443
25454
|
// src/lib/memory/durableExtraction.ts
|
|
25444
25455
|
import { Q as Q17 } from "@nozbe/watermelondb";
|
|
25456
|
+
async function messagePositions(storage, ids) {
|
|
25457
|
+
try {
|
|
25458
|
+
return await storage.messagesCollection.query(
|
|
25459
|
+
Q17.unsafeSqlQuery(
|
|
25460
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
25461
|
+
ids
|
|
25462
|
+
)
|
|
25463
|
+
).unsafeFetchRaw();
|
|
25464
|
+
} catch {
|
|
25465
|
+
return await storage.messagesCollection.query(Q17.where("id", Q17.oneOf(ids))).unsafeFetchRaw();
|
|
25466
|
+
}
|
|
25467
|
+
}
|
|
25445
25468
|
var MAX_JOBS_PER_PASS = 3;
|
|
25446
25469
|
var MAX_ATTEMPTS = 3;
|
|
25447
25470
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -26010,9 +26033,9 @@ function createDurableAutoExtractor(options) {
|
|
|
26010
26033
|
r._setRaw("watermark_seq", null);
|
|
26011
26034
|
});
|
|
26012
26035
|
}
|
|
26013
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
26036
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
26014
26037
|
const foreignRows = new Set(
|
|
26015
|
-
rows.filter((row) => row.
|
|
26038
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
26016
26039
|
);
|
|
26017
26040
|
const sequences = /* @__PURE__ */ new Map();
|
|
26018
26041
|
if (fromSourceConversation) {
|
|
@@ -26022,7 +26045,7 @@ function createDurableAutoExtractor(options) {
|
|
|
26022
26045
|
}
|
|
26023
26046
|
for (const row of rows) {
|
|
26024
26047
|
if (foreignRows.has(row.id)) continue;
|
|
26025
|
-
const sequence = Number(row.
|
|
26048
|
+
const sequence = Number(row.message_id);
|
|
26026
26049
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
26027
26050
|
}
|
|
26028
26051
|
let boundary = -1;
|
package/dist/server/index.cjs
CHANGED
|
@@ -17078,6 +17078,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
17078
17078
|
if (cachedKey) {
|
|
17079
17079
|
return cachedKey;
|
|
17080
17080
|
}
|
|
17081
|
+
const startEpoch = sessionEpoch;
|
|
17081
17082
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
17082
17083
|
if (!keyHex) {
|
|
17083
17084
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -17090,6 +17091,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
17090
17091
|
false,
|
|
17091
17092
|
["encrypt", "decrypt"]
|
|
17092
17093
|
);
|
|
17094
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
17095
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
17096
|
+
}
|
|
17093
17097
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
17094
17098
|
return cryptoKey;
|
|
17095
17099
|
}
|
|
@@ -37141,6 +37145,18 @@ async function assembleMemoryContext(options) {
|
|
|
37141
37145
|
|
|
37142
37146
|
// src/lib/memory/durableExtraction.ts
|
|
37143
37147
|
var import_watermelondb25 = __toESM(require_watermelondb());
|
|
37148
|
+
async function messagePositions(storage, ids) {
|
|
37149
|
+
try {
|
|
37150
|
+
return await storage.messagesCollection.query(
|
|
37151
|
+
import_watermelondb25.Q.unsafeSqlQuery(
|
|
37152
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
37153
|
+
ids
|
|
37154
|
+
)
|
|
37155
|
+
).unsafeFetchRaw();
|
|
37156
|
+
} catch {
|
|
37157
|
+
return await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).unsafeFetchRaw();
|
|
37158
|
+
}
|
|
37159
|
+
}
|
|
37144
37160
|
var MAX_JOBS_PER_PASS = 3;
|
|
37145
37161
|
var MAX_ATTEMPTS = 3;
|
|
37146
37162
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -37709,9 +37725,9 @@ function createDurableAutoExtractor(options) {
|
|
|
37709
37725
|
r._setRaw("watermark_seq", null);
|
|
37710
37726
|
});
|
|
37711
37727
|
}
|
|
37712
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
37728
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
37713
37729
|
const foreignRows = new Set(
|
|
37714
|
-
rows.filter((row) => row.
|
|
37730
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
37715
37731
|
);
|
|
37716
37732
|
const sequences = /* @__PURE__ */ new Map();
|
|
37717
37733
|
if (fromSourceConversation) {
|
|
@@ -37721,7 +37737,7 @@ function createDurableAutoExtractor(options) {
|
|
|
37721
37737
|
}
|
|
37722
37738
|
for (const row of rows) {
|
|
37723
37739
|
if (foreignRows.has(row.id)) continue;
|
|
37724
|
-
const sequence = Number(row.
|
|
37740
|
+
const sequence = Number(row.message_id);
|
|
37725
37741
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
37726
37742
|
}
|
|
37727
37743
|
let boundary = -1;
|
package/dist/server/index.mjs
CHANGED
|
@@ -15407,6 +15407,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
15407
15407
|
if (cachedKey) {
|
|
15408
15408
|
return cachedKey;
|
|
15409
15409
|
}
|
|
15410
|
+
const startEpoch = sessionEpoch;
|
|
15410
15411
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
15411
15412
|
if (!keyHex) {
|
|
15412
15413
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -15419,6 +15420,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
15419
15420
|
false,
|
|
15420
15421
|
["encrypt", "decrypt"]
|
|
15421
15422
|
);
|
|
15423
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
15424
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
15425
|
+
}
|
|
15422
15426
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
15423
15427
|
return cryptoKey;
|
|
15424
15428
|
}
|
|
@@ -35388,6 +35392,18 @@ async function assembleMemoryContext(options) {
|
|
|
35388
35392
|
|
|
35389
35393
|
// src/lib/memory/durableExtraction.ts
|
|
35390
35394
|
var import_watermelondb25 = __toESM(require_watermelondb());
|
|
35395
|
+
async function messagePositions(storage, ids) {
|
|
35396
|
+
try {
|
|
35397
|
+
return await storage.messagesCollection.query(
|
|
35398
|
+
import_watermelondb25.Q.unsafeSqlQuery(
|
|
35399
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
35400
|
+
ids
|
|
35401
|
+
)
|
|
35402
|
+
).unsafeFetchRaw();
|
|
35403
|
+
} catch {
|
|
35404
|
+
return await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).unsafeFetchRaw();
|
|
35405
|
+
}
|
|
35406
|
+
}
|
|
35391
35407
|
var MAX_JOBS_PER_PASS = 3;
|
|
35392
35408
|
var MAX_ATTEMPTS = 3;
|
|
35393
35409
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -35956,9 +35972,9 @@ function createDurableAutoExtractor(options) {
|
|
|
35956
35972
|
r._setRaw("watermark_seq", null);
|
|
35957
35973
|
});
|
|
35958
35974
|
}
|
|
35959
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
35975
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
35960
35976
|
const foreignRows = new Set(
|
|
35961
|
-
rows.filter((row) => row.
|
|
35977
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
35962
35978
|
);
|
|
35963
35979
|
const sequences = /* @__PURE__ */ new Map();
|
|
35964
35980
|
if (fromSourceConversation) {
|
|
@@ -35968,7 +35984,7 @@ function createDurableAutoExtractor(options) {
|
|
|
35968
35984
|
}
|
|
35969
35985
|
for (const row of rows) {
|
|
35970
35986
|
if (foreignRows.has(row.id)) continue;
|
|
35971
|
-
const sequence = Number(row.
|
|
35987
|
+
const sequence = Number(row.message_id);
|
|
35972
35988
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
35973
35989
|
}
|
|
35974
35990
|
let boundary = -1;
|