@anuma/sdk 9.1.3 → 9.1.5
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 +221 -121
- package/dist/expo/index.d.mts +13 -0
- package/dist/expo/index.d.ts +13 -0
- package/dist/expo/index.mjs +221 -121
- package/dist/memory/context.d.mts +123 -0
- package/dist/memory/context.d.ts +123 -0
- package/dist/react/index.cjs +221 -121
- package/dist/react/index.d.mts +13 -0
- package/dist/react/index.d.ts +13 -0
- package/dist/react/index.mjs +221 -121
- package/dist/server/index.cjs +132 -39
- package/dist/server/index.d.mts +9 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.mjs +132 -39
- package/package.json +1 -1
package/dist/expo/index.mjs
CHANGED
|
@@ -923,15 +923,16 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
|
|
|
923
923
|
if (!parsed) {
|
|
924
924
|
return false;
|
|
925
925
|
}
|
|
926
|
+
const startEpoch = sessionEpoch;
|
|
926
927
|
const cached = refreshCandidatesByAddress.get(walletAddress);
|
|
927
928
|
if (cached) {
|
|
928
|
-
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached);
|
|
929
|
+
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached, startEpoch);
|
|
929
930
|
}
|
|
930
931
|
let derivePromise = pendingCandidateDerives.get(walletAddress);
|
|
931
932
|
if (!derivePromise) {
|
|
932
933
|
derivePromise = (async () => {
|
|
933
934
|
const candidates2 = await deriveRefreshCandidates(signMessage, embeddedWalletSigner);
|
|
934
|
-
if (candidates2) {
|
|
935
|
+
if (candidates2 && sessionEpoch === startEpoch) {
|
|
935
936
|
refreshCandidatesByAddress.set(walletAddress, candidates2);
|
|
936
937
|
}
|
|
937
938
|
return candidates2;
|
|
@@ -943,10 +944,10 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
|
|
|
943
944
|
pendingCandidateDerives.set(walletAddress, derivePromise);
|
|
944
945
|
}
|
|
945
946
|
const candidates = await derivePromise;
|
|
946
|
-
if (!candidates) {
|
|
947
|
+
if (!candidates || sessionEpoch !== startEpoch) {
|
|
947
948
|
return false;
|
|
948
949
|
}
|
|
949
|
-
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates);
|
|
950
|
+
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch);
|
|
950
951
|
}
|
|
951
952
|
function parsePrefixedCiphertext(probeCiphertext) {
|
|
952
953
|
const version = probeCiphertext.startsWith("enc:v3:") ? "v3" : probeCiphertext.startsWith("enc:v2:") ? "v2" : null;
|
|
@@ -989,7 +990,7 @@ async function deriveRefreshCandidates(signMessage, embeddedWalletSigner) {
|
|
|
989
990
|
}
|
|
990
991
|
return { legacy: legacyKey, current: currentKey };
|
|
991
992
|
}
|
|
992
|
-
async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates) {
|
|
993
|
+
async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch) {
|
|
993
994
|
const candidateHex = parsed.version === "v2" ? candidates.legacy : candidates.current;
|
|
994
995
|
const keyBytes = hexToBytes(candidateHex);
|
|
995
996
|
const candidateCryptoKey = await crypto.subtle.importKey(
|
|
@@ -1007,6 +1008,9 @@ async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidat
|
|
|
1007
1008
|
);
|
|
1008
1009
|
return false;
|
|
1009
1010
|
}
|
|
1011
|
+
if (sessionEpoch !== startEpoch) {
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1010
1014
|
setStoredKey(walletAddress, { legacy: candidates.legacy, current: candidates.current });
|
|
1011
1015
|
cryptoKeyCache.delete(`${walletAddress}:v2`);
|
|
1012
1016
|
cryptoKeyCache.delete(`${walletAddress}:v3`);
|
|
@@ -15635,6 +15639,104 @@ function validate(parsed, originalQuery) {
|
|
|
15635
15639
|
return { mode, subQueries };
|
|
15636
15640
|
}
|
|
15637
15641
|
|
|
15642
|
+
// src/lib/memoryVault/vaultPreparation.ts
|
|
15643
|
+
function createVaultPreparation() {
|
|
15644
|
+
return { corpora: /* @__PURE__ */ new WeakMap(), rows: /* @__PURE__ */ new WeakMap() };
|
|
15645
|
+
}
|
|
15646
|
+
var pendingRows = /* @__PURE__ */ new WeakMap();
|
|
15647
|
+
function loadVaultCorpus(ctx, options, preparation) {
|
|
15648
|
+
if (!preparation) return getAllVaultMemoriesOp(ctx, options);
|
|
15649
|
+
const corpusCache = preparation.corpora;
|
|
15650
|
+
let pending = corpusCache.get(ctx);
|
|
15651
|
+
if (!pending) {
|
|
15652
|
+
pending = /* @__PURE__ */ new Map();
|
|
15653
|
+
corpusCache.set(ctx, pending);
|
|
15654
|
+
}
|
|
15655
|
+
const key = JSON.stringify(options ?? {});
|
|
15656
|
+
let request = pending.get(key);
|
|
15657
|
+
if (!request) {
|
|
15658
|
+
const entries = pending;
|
|
15659
|
+
request = getAllVaultMemoriesOp(ctx, options).catch((error) => {
|
|
15660
|
+
entries.delete(key);
|
|
15661
|
+
throw error;
|
|
15662
|
+
});
|
|
15663
|
+
pending.set(key, request);
|
|
15664
|
+
}
|
|
15665
|
+
return request;
|
|
15666
|
+
}
|
|
15667
|
+
function rowRequestsFor(registry2, ctx, maskInput) {
|
|
15668
|
+
let byMask = registry2.get(ctx);
|
|
15669
|
+
if (!byMask) {
|
|
15670
|
+
byMask = /* @__PURE__ */ new Map();
|
|
15671
|
+
registry2.set(ctx, byMask);
|
|
15672
|
+
}
|
|
15673
|
+
let requests = byMask.get(maskInput);
|
|
15674
|
+
if (!requests) {
|
|
15675
|
+
requests = /* @__PURE__ */ new Map();
|
|
15676
|
+
byMask.set(maskInput, requests);
|
|
15677
|
+
}
|
|
15678
|
+
return requests;
|
|
15679
|
+
}
|
|
15680
|
+
function embedVaultRows(ctx, options, rows, preparation) {
|
|
15681
|
+
const pending = rowRequestsFor(pendingRows, ctx, options.maskInput);
|
|
15682
|
+
const prepared = preparation && rowRequestsFor(preparation.rows, ctx, options.maskInput);
|
|
15683
|
+
const model = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
15684
|
+
const keys = rows.map(
|
|
15685
|
+
(row) => JSON.stringify([
|
|
15686
|
+
row.uniqueId,
|
|
15687
|
+
row.updatedAt?.getTime(),
|
|
15688
|
+
row.content,
|
|
15689
|
+
model,
|
|
15690
|
+
options.baseUrl,
|
|
15691
|
+
options.timeoutMs,
|
|
15692
|
+
options.tokenTimeoutMs,
|
|
15693
|
+
options.totalTimeoutMs
|
|
15694
|
+
])
|
|
15695
|
+
);
|
|
15696
|
+
const missing = /* @__PURE__ */ new Map();
|
|
15697
|
+
rows.forEach((row, index) => {
|
|
15698
|
+
if (!pending.has(keys[index]) && !prepared?.has(keys[index])) missing.set(keys[index], row);
|
|
15699
|
+
});
|
|
15700
|
+
if (missing.size > 0) {
|
|
15701
|
+
const entries = pending;
|
|
15702
|
+
const batch = Promise.resolve().then(
|
|
15703
|
+
() => generateEmbeddings(
|
|
15704
|
+
[...missing.values()].map((row) => row.content),
|
|
15705
|
+
options
|
|
15706
|
+
)
|
|
15707
|
+
);
|
|
15708
|
+
[...missing].forEach(([key, row], index) => {
|
|
15709
|
+
const request = batch.then((vectors) => {
|
|
15710
|
+
const vector = vectors[index];
|
|
15711
|
+
updateVaultMemoryEmbeddingOp(ctx, row.uniqueId, JSON.stringify(vector), model, {
|
|
15712
|
+
content: row.content,
|
|
15713
|
+
updatedAt: row.updatedAt?.getTime()
|
|
15714
|
+
}).catch(() => {
|
|
15715
|
+
});
|
|
15716
|
+
return vector;
|
|
15717
|
+
}).finally(() => {
|
|
15718
|
+
entries.delete(key);
|
|
15719
|
+
if (entries.size === 0) pendingRows.get(ctx)?.delete(options.maskInput);
|
|
15720
|
+
});
|
|
15721
|
+
pending.set(key, request);
|
|
15722
|
+
});
|
|
15723
|
+
}
|
|
15724
|
+
return Promise.all(
|
|
15725
|
+
keys.map((key) => {
|
|
15726
|
+
const cached = prepared?.get(key);
|
|
15727
|
+
if (cached) return cached;
|
|
15728
|
+
const request = pending.get(key);
|
|
15729
|
+
if (!prepared) return request;
|
|
15730
|
+
const retained = request.catch((error) => {
|
|
15731
|
+
prepared.delete(key);
|
|
15732
|
+
throw error;
|
|
15733
|
+
});
|
|
15734
|
+
prepared.set(key, retained);
|
|
15735
|
+
return retained;
|
|
15736
|
+
})
|
|
15737
|
+
);
|
|
15738
|
+
}
|
|
15739
|
+
|
|
15638
15740
|
// src/lib/memoryVault/vectorVersion.ts
|
|
15639
15741
|
var vectorRowVersion = /* @__PURE__ */ new WeakMap();
|
|
15640
15742
|
function contentFingerprint(content) {
|
|
@@ -16197,11 +16299,11 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
|
|
|
16197
16299
|
return combined.slice(0, limit);
|
|
16198
16300
|
}
|
|
16199
16301
|
async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options) {
|
|
16200
|
-
const memories = await
|
|
16302
|
+
const memories = await loadVaultCorpus(vaultCtx, options);
|
|
16201
16303
|
const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
16202
16304
|
const uncachedTexts = [];
|
|
16203
16305
|
const uncachedKeys = [];
|
|
16204
|
-
const
|
|
16306
|
+
const uncachedRows = [];
|
|
16205
16307
|
const uncachedVersions = [];
|
|
16206
16308
|
for (const m of memories) {
|
|
16207
16309
|
const content = m.content;
|
|
@@ -16220,12 +16322,12 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
|
|
|
16220
16322
|
}
|
|
16221
16323
|
uncachedTexts.push(m.content);
|
|
16222
16324
|
uncachedKeys.push(m.uniqueId);
|
|
16223
|
-
|
|
16325
|
+
uncachedRows.push(m);
|
|
16224
16326
|
uncachedVersions.push(m.updatedAt);
|
|
16225
16327
|
}
|
|
16226
16328
|
}
|
|
16227
16329
|
if (uncachedTexts.length > 0) {
|
|
16228
|
-
const embeddings = await
|
|
16330
|
+
const embeddings = await embedVaultRows(vaultCtx, embeddingOptions, uncachedRows);
|
|
16229
16331
|
for (let i = 0; i < uncachedKeys.length; i++) {
|
|
16230
16332
|
cacheRowVector(
|
|
16231
16333
|
cache,
|
|
@@ -16234,13 +16336,6 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
|
|
|
16234
16336
|
uncachedVersions[i],
|
|
16235
16337
|
uncachedTexts[i]
|
|
16236
16338
|
);
|
|
16237
|
-
updateVaultMemoryEmbeddingOp(
|
|
16238
|
-
vaultCtx,
|
|
16239
|
-
uncachedIds[i],
|
|
16240
|
-
JSON.stringify(embeddings[i]),
|
|
16241
|
-
currentModel
|
|
16242
|
-
).catch(() => {
|
|
16243
|
-
});
|
|
16244
16339
|
}
|
|
16245
16340
|
}
|
|
16246
16341
|
}
|
|
@@ -16382,10 +16477,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
16382
16477
|
let laneVecs;
|
|
16383
16478
|
if (opts.embedStats) opts.embedStats.rowsEmbedded += laneRows.length;
|
|
16384
16479
|
try {
|
|
16385
|
-
laneVecs = await
|
|
16386
|
-
laneRows.map((m) => m.content),
|
|
16387
|
-
embeddingOptions
|
|
16388
|
-
);
|
|
16480
|
+
laneVecs = await embedVaultRows(vaultCtx, embeddingOptions, laneRows);
|
|
16389
16481
|
} catch (err) {
|
|
16390
16482
|
laneEmbedFailed = true;
|
|
16391
16483
|
getLogger().warn(
|
|
@@ -16399,13 +16491,6 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
16399
16491
|
cacheRowVector(cache, m.uniqueId, vec, m.updatedAt, m.content);
|
|
16400
16492
|
resolvedThisCall.add(m.uniqueId);
|
|
16401
16493
|
vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
|
|
16402
|
-
updateVaultMemoryEmbeddingOp(
|
|
16403
|
-
vaultCtx,
|
|
16404
|
-
m.uniqueId,
|
|
16405
|
-
JSON.stringify(vecs[i]),
|
|
16406
|
-
currentModel
|
|
16407
|
-
).catch(() => {
|
|
16408
|
-
});
|
|
16409
16494
|
});
|
|
16410
16495
|
}
|
|
16411
16496
|
}
|
|
@@ -16597,9 +16682,10 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
16597
16682
|
};
|
|
16598
16683
|
}
|
|
16599
16684
|
} else {
|
|
16600
|
-
const loaded = await
|
|
16685
|
+
const loaded = await loadVaultCorpus(
|
|
16601
16686
|
vaultCtx,
|
|
16602
|
-
Object.keys(queryOpts).length > 0 ? queryOpts : void 0
|
|
16687
|
+
Object.keys(queryOpts).length > 0 ? queryOpts : void 0,
|
|
16688
|
+
searchOptions?.vaultPreparation
|
|
16603
16689
|
);
|
|
16604
16690
|
vaultSize = loaded.length;
|
|
16605
16691
|
rowsDecrypted = loaded.length;
|
|
@@ -16672,7 +16758,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
16672
16758
|
let newEmbeddings;
|
|
16673
16759
|
embedStats.rowsEmbedded += uncachedTexts.length;
|
|
16674
16760
|
try {
|
|
16675
|
-
newEmbeddings = await
|
|
16761
|
+
newEmbeddings = await embedVaultRows(
|
|
16762
|
+
vaultCtx,
|
|
16763
|
+
embeddingOptions,
|
|
16764
|
+
uncachedIndices.map((index) => memories[index]),
|
|
16765
|
+
searchOptions?.vaultPreparation
|
|
16766
|
+
);
|
|
16676
16767
|
} catch (err) {
|
|
16677
16768
|
embeddingFailure = true;
|
|
16678
16769
|
getLogger().warn(
|
|
@@ -16688,13 +16779,6 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
16688
16779
|
memories[uncachedIndices[j]].updatedAt,
|
|
16689
16780
|
memories[uncachedIndices[j]].content
|
|
16690
16781
|
);
|
|
16691
|
-
updateVaultMemoryEmbeddingOp(
|
|
16692
|
-
vaultCtx,
|
|
16693
|
-
memories[uncachedIndices[j]].uniqueId,
|
|
16694
|
-
JSON.stringify(newEmbeddings[j]),
|
|
16695
|
-
currentModel
|
|
16696
|
-
).catch(() => {
|
|
16697
|
-
});
|
|
16698
16782
|
}
|
|
16699
16783
|
}
|
|
16700
16784
|
}
|
|
@@ -19235,6 +19319,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
19235
19319
|
},
|
|
19236
19320
|
...options.rrfK !== void 0 && { rrfK: options.rrfK },
|
|
19237
19321
|
...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
|
|
19322
|
+
...options.vaultPreparation && { vaultPreparation: options.vaultPreparation },
|
|
19238
19323
|
...subQueries && { subQueries },
|
|
19239
19324
|
...options.scopes && { scopes: options.scopes },
|
|
19240
19325
|
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
@@ -21252,6 +21337,8 @@ async function backfillTopics(ctx) {
|
|
|
21252
21337
|
if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
|
|
21253
21338
|
}
|
|
21254
21339
|
async function migrateMemoriesToRemote(options) {
|
|
21340
|
+
if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
|
|
21341
|
+
throw new Error("A write barrier and a remote store handoff are required");
|
|
21255
21342
|
const { canonical, signal } = options;
|
|
21256
21343
|
const rings = [canonical, ...options.fallbacks ?? []];
|
|
21257
21344
|
const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
@@ -21279,93 +21366,98 @@ async function migrateMemoriesToRemote(options) {
|
|
|
21279
21366
|
const placeholders = /* @__PURE__ */ new Set();
|
|
21280
21367
|
const truncated = /* @__PURE__ */ new Set();
|
|
21281
21368
|
let imported = 0;
|
|
21282
|
-
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
);
|
|
21296
|
-
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
21297
|
-
for (const raw of candidates) {
|
|
21298
|
-
const server = remote.get(raw.id);
|
|
21299
|
-
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
21300
|
-
pending.set(raw.id, raw.updated_at + 1);
|
|
21301
|
-
}
|
|
21302
|
-
}
|
|
21303
|
-
if (pending.size === 0) {
|
|
21304
|
-
const result = (state) => ({
|
|
21305
|
-
state,
|
|
21306
|
-
total: rows.length,
|
|
21307
|
-
imported,
|
|
21308
|
-
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
21309
|
-
reembedded: reembedded.size,
|
|
21310
|
-
unembedded: unembedded.size,
|
|
21311
|
-
placeholderTombstones: placeholders.size,
|
|
21312
|
-
truncatedSourceChunkIds: truncated.size
|
|
21313
|
-
});
|
|
21314
|
-
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
21315
|
-
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
21316
|
-
if (!active)
|
|
21317
|
-
await request(
|
|
21318
|
-
"/account",
|
|
21319
|
-
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
21369
|
+
const uploadPasses = async (finalizing = false) => {
|
|
21370
|
+
for (let pass = 0; ; pass++) {
|
|
21371
|
+
const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
|
|
21372
|
+
for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
|
|
21373
|
+
const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
|
|
21374
|
+
const missing = await missingIds(request, candidates.map(revision), signal);
|
|
21375
|
+
const pending = /* @__PURE__ */ new Map();
|
|
21376
|
+
for (const raw of candidates)
|
|
21377
|
+
if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
|
|
21378
|
+
if (pending.size === 0 && !active) {
|
|
21379
|
+
const remote = await remoteMetadata(
|
|
21380
|
+
request,
|
|
21381
|
+
candidates.map((raw) => raw.id),
|
|
21320
21382
|
signal
|
|
21321
21383
|
);
|
|
21322
|
-
|
|
21323
|
-
|
|
21324
|
-
|
|
21325
|
-
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
"migration_incomplete"
|
|
21329
|
-
);
|
|
21330
|
-
const prepared = [];
|
|
21331
|
-
for (const raw of candidates) {
|
|
21332
|
-
const updatedAt = pending.get(raw.id);
|
|
21333
|
-
if (updatedAt === void 0) continue;
|
|
21334
|
-
try {
|
|
21335
|
-
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
21336
|
-
} catch (error) {
|
|
21337
|
-
if (!(error instanceof MemoryKeyError)) throw error;
|
|
21338
|
-
skipped.set(raw.id, "unreadable");
|
|
21339
|
-
}
|
|
21340
|
-
}
|
|
21341
|
-
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
21342
|
-
if (options.embeddingOptions)
|
|
21343
|
-
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
21344
|
-
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
21345
|
-
const upload = [];
|
|
21346
|
-
for (const { row, plaintext } of prepared) {
|
|
21347
|
-
const id2 = row.memory_id;
|
|
21348
|
-
if (!fitsNearby(row)) {
|
|
21349
|
-
skipped.set(id2, "invalid");
|
|
21350
|
-
continue;
|
|
21384
|
+
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
21385
|
+
for (const raw of candidates) {
|
|
21386
|
+
const server = remote.get(raw.id);
|
|
21387
|
+
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
21388
|
+
pending.set(raw.id, raw.updated_at + 1);
|
|
21389
|
+
}
|
|
21351
21390
|
}
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21391
|
+
if (pending.size === 0) {
|
|
21392
|
+
const result = (state) => ({
|
|
21393
|
+
state,
|
|
21394
|
+
total: rows.length,
|
|
21395
|
+
imported,
|
|
21396
|
+
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
21397
|
+
reembedded: reembedded.size,
|
|
21398
|
+
unembedded: unembedded.size,
|
|
21399
|
+
placeholderTombstones: placeholders.size,
|
|
21400
|
+
truncatedSourceChunkIds: truncated.size
|
|
21401
|
+
});
|
|
21402
|
+
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
21403
|
+
if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
|
|
21404
|
+
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
21405
|
+
if (!active)
|
|
21406
|
+
await request(
|
|
21407
|
+
"/account",
|
|
21408
|
+
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
21409
|
+
signal
|
|
21410
|
+
);
|
|
21411
|
+
await options.onActivated();
|
|
21412
|
+
return result("active");
|
|
21413
|
+
}
|
|
21414
|
+
if (pass >= MAX_UPLOAD_PASSES)
|
|
21415
|
+
throw new RemoteMemoryError(
|
|
21416
|
+
"Nearby still reports memories missing after importing them",
|
|
21417
|
+
409,
|
|
21418
|
+
"migration_incomplete"
|
|
21419
|
+
);
|
|
21420
|
+
const prepared = [];
|
|
21421
|
+
for (const raw of candidates) {
|
|
21422
|
+
const updatedAt = pending.get(raw.id);
|
|
21423
|
+
if (updatedAt === void 0) continue;
|
|
21424
|
+
try {
|
|
21425
|
+
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
21426
|
+
} catch (error) {
|
|
21427
|
+
if (!(error instanceof MemoryKeyError)) throw error;
|
|
21428
|
+
skipped.set(raw.id, "unreadable");
|
|
21429
|
+
}
|
|
21430
|
+
}
|
|
21431
|
+
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
21432
|
+
if (options.embeddingOptions)
|
|
21433
|
+
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
21434
|
+
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
21435
|
+
const upload = [];
|
|
21436
|
+
for (const { row, plaintext } of prepared) {
|
|
21437
|
+
const id2 = row.memory_id;
|
|
21438
|
+
if (!fitsNearby(row)) {
|
|
21439
|
+
skipped.set(id2, "invalid");
|
|
21440
|
+
continue;
|
|
21441
|
+
}
|
|
21442
|
+
upload.push(row);
|
|
21443
|
+
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
21444
|
+
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
21445
|
+
track(unembedded, !row.embedding);
|
|
21446
|
+
track(placeholders, plaintext === void 0);
|
|
21447
|
+
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
21448
|
+
}
|
|
21449
|
+
let sent = 0;
|
|
21365
21450
|
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
21451
|
+
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
21452
|
+
await importBatch(request, canonical.keyId, batch, signal);
|
|
21453
|
+
claimed = true;
|
|
21454
|
+
sent += batch.length;
|
|
21455
|
+
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
21456
|
+
}
|
|
21457
|
+
imported += sent;
|
|
21366
21458
|
}
|
|
21367
|
-
|
|
21368
|
-
|
|
21459
|
+
};
|
|
21460
|
+
return uploadPasses();
|
|
21369
21461
|
}
|
|
21370
21462
|
|
|
21371
21463
|
// src/lib/memory/store/remoteStore.ts
|
|
@@ -22818,13 +22910,20 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
22818
22910
|
}
|
|
22819
22911
|
}
|
|
22820
22912
|
}
|
|
22913
|
+
const vaultPreparation = createVaultPreparation();
|
|
22821
22914
|
const settled = await Promise.allSettled(
|
|
22822
22915
|
facets.map(async (facet) => {
|
|
22823
22916
|
const prior = previous?.sections.find((s) => s.key === facet.key);
|
|
22824
22917
|
if (prior && !staleKeys.has(facet.key)) {
|
|
22825
22918
|
return prior;
|
|
22826
22919
|
}
|
|
22827
|
-
return synthesizeFacet(
|
|
22920
|
+
return synthesizeFacet(
|
|
22921
|
+
facet,
|
|
22922
|
+
recallCtx,
|
|
22923
|
+
options,
|
|
22924
|
+
fallbackPriors.get(facet.key),
|
|
22925
|
+
vaultPreparation
|
|
22926
|
+
);
|
|
22828
22927
|
})
|
|
22829
22928
|
);
|
|
22830
22929
|
const sections = settled.map((r, i) => {
|
|
@@ -22978,7 +23077,7 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
22978
23077
|
}
|
|
22979
23078
|
return keys;
|
|
22980
23079
|
}
|
|
22981
|
-
async function synthesizeFacet(facet, ctx, options, prior) {
|
|
23080
|
+
async function synthesizeFacet(facet, ctx, options, prior, vaultPreparation) {
|
|
22982
23081
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
22983
23082
|
const limit = options.limit ?? DEFAULT_FACET_RECALL_LIMIT;
|
|
22984
23083
|
const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
|
|
@@ -22995,6 +23094,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
|
|
|
22995
23094
|
};
|
|
22996
23095
|
}
|
|
22997
23096
|
const recalled = await recall(facet.query, ctx, {
|
|
23097
|
+
vaultPreparation,
|
|
22998
23098
|
scopes,
|
|
22999
23099
|
limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
|
|
23000
23100
|
types: ["fact"],
|
|
@@ -183,6 +183,51 @@ interface EntityOperationsContext {
|
|
|
183
183
|
canWrite?: () => Promise<boolean>;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
|
|
188
|
+
*
|
|
189
|
+
* A Class-B flow has ONE fixed purpose and no conversation: extract memories
|
|
190
|
+
* from a snippet, pick a topic, consolidate duplicates, classify a query. Today
|
|
191
|
+
* each of those builds its own system prompt on the client and sends it as a
|
|
192
|
+
* `role:system` message, which makes the prompt a suggestion rather than a
|
|
193
|
+
* contract — a caller can reword it into "you are a general assistant" and get
|
|
194
|
+
* general chat at internal-endpoint prices.
|
|
195
|
+
*
|
|
196
|
+
* The fix is that the SERVER owns the text and the client only names the task.
|
|
197
|
+
* This header is the name. The portal allowlists it against its own copy of this
|
|
198
|
+
* enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
|
|
199
|
+
* not recognise, so an unknown value degrades to "no task declared" rather than
|
|
200
|
+
* failing the request.
|
|
201
|
+
*
|
|
202
|
+
* ⚠ These string values are a wire contract with ai-portal. Adding one here
|
|
203
|
+
* before the portal knows it is harmless (it is dropped); renaming or removing
|
|
204
|
+
* one silently stops that flow being counted. Keep them in step with
|
|
205
|
+
* `AllTaskTypes` on the portal side, and see ai-portal
|
|
206
|
+
* `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
|
|
207
|
+
*
|
|
208
|
+
* ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
|
|
209
|
+
* directions and has to be sequenced:
|
|
210
|
+
*
|
|
211
|
+
* 1. merge + publish this package;
|
|
212
|
+
* 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
|
|
213
|
+
* names in the same commit.
|
|
214
|
+
*
|
|
215
|
+
* A consumer on the OLD version cannot compile the new names, and a consumer on
|
|
216
|
+
* the NEW version cannot compile the retired ones (`app_inspiration` is gone
|
|
217
|
+
* here, replaced by the three `app_inspiration_*` names). Both halves of that
|
|
218
|
+
* are type-only — the wire itself always degrades safely, because the portal
|
|
219
|
+
* drops any value it does not recognise. Concretely: ai-memoryless-client#5782
|
|
220
|
+
* is blocked on this publish for exactly that reason.
|
|
221
|
+
*
|
|
222
|
+
* Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
|
|
223
|
+
* the app-idea chat. Those carry an open conversation with the user, so a fixed
|
|
224
|
+
* server-owned prompt would be wrong rather than merely premature — the same
|
|
225
|
+
* boundary {@link INTERNAL_FLOW_MARKER} draws.
|
|
226
|
+
*
|
|
227
|
+
* @public
|
|
228
|
+
*/
|
|
229
|
+
type TaskType = "title" | "commit_message" | "memory_extract" | "memory_import_extract" | "memory_dedup" | "memory_decay" | "memory_injection_check" | "memory_verify_support" | "memory_autosort" | "memory_topic" | "memory_consolidate" | "memory_decompose" | "memory_profile_synth" | "memory_graph" | "memory_recall_embed" | "memory_retain_embed" | "folder_context" | "selection_memory" | "summarize" | "style_analysis" | "classify_search" | "app_inspiration_generate" | "app_inspiration_trends" | "app_inspiration_remix" | "spotlight_image" | "slide_image" | "slide_video" | "slide_music" | "slide_sfx" | "media_video" | "media_audio" | "media_bg_removal" | "media_image_preset" | "tool_app_builder" | "tool_document_builder" | "tool_slides" | "tool_slides_image_gen" | "connector_guidance" | "cf_task" | "xbot_intent" | "xbot_council" | "scheduled_task";
|
|
230
|
+
|
|
186
231
|
/**
|
|
187
232
|
* Auth for portal LLM calls (extraction, consolidation, decomposition,
|
|
188
233
|
* reflection). Mirrors `memoryEngine`'s `EmbeddingOptions` dual-auth:
|
|
@@ -202,6 +247,76 @@ interface PortalLlmAuth {
|
|
|
202
247
|
getToken?: () => Promise<string | null>;
|
|
203
248
|
}
|
|
204
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Options for embedding generation
|
|
252
|
+
*
|
|
253
|
+
* Supports two auth methods:
|
|
254
|
+
* - `getToken`: For Privy identity tokens (uses Authorization: Bearer header)
|
|
255
|
+
* - `apiKey`: For direct API keys (uses X-API-Key header)
|
|
256
|
+
*
|
|
257
|
+
* At least one of `getToken` or `apiKey` must be provided.
|
|
258
|
+
*/
|
|
259
|
+
interface EmbeddingOptions {
|
|
260
|
+
taskType?: TaskType;
|
|
261
|
+
/** Function to get auth token (e.g., Privy's getIdentityToken). Uses Authorization: Bearer header. */
|
|
262
|
+
getToken?: () => Promise<string | null>;
|
|
263
|
+
/** Direct API key for server-side usage. Uses X-API-Key header. */
|
|
264
|
+
apiKey?: string;
|
|
265
|
+
/** Base URL for the API */
|
|
266
|
+
baseUrl?: string;
|
|
267
|
+
/** Embedding model to use */
|
|
268
|
+
model?: string;
|
|
269
|
+
/** Max texts per API call for batch embeddings (default: 100). Larger arrays are split into chunks. */
|
|
270
|
+
batchSize?: number;
|
|
271
|
+
/**
|
|
272
|
+
* Optional in-memory cache for embedding vectors. When provided, texts
|
|
273
|
+
* are looked up in this map before calling the API, and new embeddings
|
|
274
|
+
* are stored after generation. Useful when the same texts are embedded
|
|
275
|
+
* repeatedly (e.g., across eval iterations or re-indexing runs).
|
|
276
|
+
*
|
|
277
|
+
* Values are stored as `Float32Array` (the embedding model's native
|
|
278
|
+
* precision) rather than a float64 `number[]`, halving the resident RAM of
|
|
279
|
+
* the cache with no precision loss. `generateEmbedding(s)` still return
|
|
280
|
+
* `number[]` at the API boundary, so callers are unaffected.
|
|
281
|
+
*/
|
|
282
|
+
cache?: Map<string, Float32Array>;
|
|
283
|
+
/** Called after each embedding API call with the token usage from the response. */
|
|
284
|
+
onUsage?: (usage: {
|
|
285
|
+
promptTokens: number;
|
|
286
|
+
totalTokens: number;
|
|
287
|
+
}) => void;
|
|
288
|
+
/**
|
|
289
|
+
* Optional transform applied to each text immediately before it is sent to
|
|
290
|
+
* the embeddings endpoint (e.g. `PiiRedactor.maskText`). The cache and result
|
|
291
|
+
* ordering still key on the original text — only the API request body is
|
|
292
|
+
* transformed — so callers can keep storing/displaying the original value
|
|
293
|
+
* while real PII never reaches the server. Used when PII redaction is active.
|
|
294
|
+
*/
|
|
295
|
+
maskInput?: (text: string) => string;
|
|
296
|
+
/**
|
|
297
|
+
* Deadline, in ms, for EACH embeddings HTTP attempt (default 15000). An
|
|
298
|
+
* attempt that exceeds it is aborted and counts as a transient failure, so the
|
|
299
|
+
* bounded retry still applies. `0` disables the deadline.
|
|
300
|
+
*/
|
|
301
|
+
timeoutMs?: number;
|
|
302
|
+
/**
|
|
303
|
+
* Deadline, in ms, for the `getToken()` read that precedes a request (default
|
|
304
|
+
* 10000). A provider that never settles rejects instead of hanging the
|
|
305
|
+
* embedding — and the recall waiting on it. `0` disables the deadline.
|
|
306
|
+
*/
|
|
307
|
+
tokenTimeoutMs?: number;
|
|
308
|
+
/**
|
|
309
|
+
* Overall deadline, in ms, for one `generateEmbedding` or `generateEmbeddings` call — the token read,
|
|
310
|
+
* every retry attempt and the backoff between them. Unset (the default) means
|
|
311
|
+
* only the per-attempt deadlines apply, which is right for background/bulk
|
|
312
|
+
* embeds. The recall query path sets it (see
|
|
313
|
+
* `RecallOptions.queryEmbedTotalTimeoutMs`) so an outage degrades a turn to
|
|
314
|
+
* BM25 within a few seconds instead of ~4 x `timeoutMs`. Batch calls share
|
|
315
|
+
* one budget across authentication, all chunks, retries and backoff.
|
|
316
|
+
*/
|
|
317
|
+
totalTimeoutMs?: number;
|
|
318
|
+
}
|
|
319
|
+
|
|
205
320
|
interface RecencyOptions {
|
|
206
321
|
/** Override "now" — useful for deterministic tests and back-dated benchmarks. */
|
|
207
322
|
now?: Date;
|
|
@@ -213,6 +328,13 @@ interface RecencyOptions {
|
|
|
213
328
|
noDateMultiplier?: number;
|
|
214
329
|
}
|
|
215
330
|
|
|
331
|
+
type VaultCorpusCache = WeakMap<VaultMemoryOperationsContext, Map<string, Promise<StoredVaultMemory[]>>>;
|
|
332
|
+
type RowRequests = Map<EmbeddingOptions["maskInput"], Map<string, Promise<number[]>>>;
|
|
333
|
+
interface VaultPreparation {
|
|
334
|
+
corpora: VaultCorpusCache;
|
|
335
|
+
rows: WeakMap<VaultMemoryOperationsContext, RowRequests>;
|
|
336
|
+
}
|
|
337
|
+
|
|
216
338
|
/**
|
|
217
339
|
* How a fact's evidence has been evolving.
|
|
218
340
|
*
|
|
@@ -300,6 +422,7 @@ interface RankedMemory {
|
|
|
300
422
|
updatedAt: Date;
|
|
301
423
|
}
|
|
302
424
|
interface RecallOptions {
|
|
425
|
+
/** @internal */ vaultPreparation?: VaultPreparation;
|
|
303
426
|
/** Embedding cache shared with the rest of the turn, so the query is embedded once across lanes and the send. */
|
|
304
427
|
embeddingCache?: Map<string, Float32Array>;
|
|
305
428
|
/** Which kinds to search. Default: ['fact']. */
|