@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/memory/context.d.ts
CHANGED
|
@@ -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']. */
|
package/dist/react/index.cjs
CHANGED
|
@@ -2338,15 +2338,16 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
|
|
|
2338
2338
|
if (!parsed) {
|
|
2339
2339
|
return false;
|
|
2340
2340
|
}
|
|
2341
|
+
const startEpoch = sessionEpoch;
|
|
2341
2342
|
const cached = refreshCandidatesByAddress.get(walletAddress);
|
|
2342
2343
|
if (cached) {
|
|
2343
|
-
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached);
|
|
2344
|
+
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached, startEpoch);
|
|
2344
2345
|
}
|
|
2345
2346
|
let derivePromise = pendingCandidateDerives.get(walletAddress);
|
|
2346
2347
|
if (!derivePromise) {
|
|
2347
2348
|
derivePromise = (async () => {
|
|
2348
2349
|
const candidates2 = await deriveRefreshCandidates(signMessage, embeddedWalletSigner);
|
|
2349
|
-
if (candidates2) {
|
|
2350
|
+
if (candidates2 && sessionEpoch === startEpoch) {
|
|
2350
2351
|
refreshCandidatesByAddress.set(walletAddress, candidates2);
|
|
2351
2352
|
}
|
|
2352
2353
|
return candidates2;
|
|
@@ -2358,10 +2359,10 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
|
|
|
2358
2359
|
pendingCandidateDerives.set(walletAddress, derivePromise);
|
|
2359
2360
|
}
|
|
2360
2361
|
const candidates = await derivePromise;
|
|
2361
|
-
if (!candidates) {
|
|
2362
|
+
if (!candidates || sessionEpoch !== startEpoch) {
|
|
2362
2363
|
return false;
|
|
2363
2364
|
}
|
|
2364
|
-
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates);
|
|
2365
|
+
return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch);
|
|
2365
2366
|
}
|
|
2366
2367
|
function parsePrefixedCiphertext(probeCiphertext) {
|
|
2367
2368
|
const version = probeCiphertext.startsWith("enc:v3:") ? "v3" : probeCiphertext.startsWith("enc:v2:") ? "v2" : null;
|
|
@@ -2404,7 +2405,7 @@ async function deriveRefreshCandidates(signMessage, embeddedWalletSigner) {
|
|
|
2404
2405
|
}
|
|
2405
2406
|
return { legacy: legacyKey, current: currentKey };
|
|
2406
2407
|
}
|
|
2407
|
-
async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates) {
|
|
2408
|
+
async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch) {
|
|
2408
2409
|
const candidateHex = parsed.version === "v2" ? candidates.legacy : candidates.current;
|
|
2409
2410
|
const keyBytes = hexToBytes(candidateHex);
|
|
2410
2411
|
const candidateCryptoKey = await crypto.subtle.importKey(
|
|
@@ -2422,6 +2423,9 @@ async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidat
|
|
|
2422
2423
|
);
|
|
2423
2424
|
return false;
|
|
2424
2425
|
}
|
|
2426
|
+
if (sessionEpoch !== startEpoch) {
|
|
2427
|
+
return false;
|
|
2428
|
+
}
|
|
2425
2429
|
setStoredKey(walletAddress, { legacy: candidates.legacy, current: candidates.current });
|
|
2426
2430
|
cryptoKeyCache.delete(`${walletAddress}:v2`);
|
|
2427
2431
|
cryptoKeyCache.delete(`${walletAddress}:v3`);
|
|
@@ -19571,6 +19575,104 @@ function validate(parsed, originalQuery) {
|
|
|
19571
19575
|
return { mode, subQueries };
|
|
19572
19576
|
}
|
|
19573
19577
|
|
|
19578
|
+
// src/lib/memoryVault/vaultPreparation.ts
|
|
19579
|
+
function createVaultPreparation() {
|
|
19580
|
+
return { corpora: /* @__PURE__ */ new WeakMap(), rows: /* @__PURE__ */ new WeakMap() };
|
|
19581
|
+
}
|
|
19582
|
+
var pendingRows = /* @__PURE__ */ new WeakMap();
|
|
19583
|
+
function loadVaultCorpus(ctx, options, preparation) {
|
|
19584
|
+
if (!preparation) return getAllVaultMemoriesOp(ctx, options);
|
|
19585
|
+
const corpusCache = preparation.corpora;
|
|
19586
|
+
let pending = corpusCache.get(ctx);
|
|
19587
|
+
if (!pending) {
|
|
19588
|
+
pending = /* @__PURE__ */ new Map();
|
|
19589
|
+
corpusCache.set(ctx, pending);
|
|
19590
|
+
}
|
|
19591
|
+
const key = JSON.stringify(options ?? {});
|
|
19592
|
+
let request = pending.get(key);
|
|
19593
|
+
if (!request) {
|
|
19594
|
+
const entries = pending;
|
|
19595
|
+
request = getAllVaultMemoriesOp(ctx, options).catch((error) => {
|
|
19596
|
+
entries.delete(key);
|
|
19597
|
+
throw error;
|
|
19598
|
+
});
|
|
19599
|
+
pending.set(key, request);
|
|
19600
|
+
}
|
|
19601
|
+
return request;
|
|
19602
|
+
}
|
|
19603
|
+
function rowRequestsFor(registry2, ctx, maskInput) {
|
|
19604
|
+
let byMask = registry2.get(ctx);
|
|
19605
|
+
if (!byMask) {
|
|
19606
|
+
byMask = /* @__PURE__ */ new Map();
|
|
19607
|
+
registry2.set(ctx, byMask);
|
|
19608
|
+
}
|
|
19609
|
+
let requests = byMask.get(maskInput);
|
|
19610
|
+
if (!requests) {
|
|
19611
|
+
requests = /* @__PURE__ */ new Map();
|
|
19612
|
+
byMask.set(maskInput, requests);
|
|
19613
|
+
}
|
|
19614
|
+
return requests;
|
|
19615
|
+
}
|
|
19616
|
+
function embedVaultRows(ctx, options, rows, preparation) {
|
|
19617
|
+
const pending = rowRequestsFor(pendingRows, ctx, options.maskInput);
|
|
19618
|
+
const prepared = preparation && rowRequestsFor(preparation.rows, ctx, options.maskInput);
|
|
19619
|
+
const model = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
19620
|
+
const keys = rows.map(
|
|
19621
|
+
(row) => JSON.stringify([
|
|
19622
|
+
row.uniqueId,
|
|
19623
|
+
row.updatedAt?.getTime(),
|
|
19624
|
+
row.content,
|
|
19625
|
+
model,
|
|
19626
|
+
options.baseUrl,
|
|
19627
|
+
options.timeoutMs,
|
|
19628
|
+
options.tokenTimeoutMs,
|
|
19629
|
+
options.totalTimeoutMs
|
|
19630
|
+
])
|
|
19631
|
+
);
|
|
19632
|
+
const missing = /* @__PURE__ */ new Map();
|
|
19633
|
+
rows.forEach((row, index) => {
|
|
19634
|
+
if (!pending.has(keys[index]) && !prepared?.has(keys[index])) missing.set(keys[index], row);
|
|
19635
|
+
});
|
|
19636
|
+
if (missing.size > 0) {
|
|
19637
|
+
const entries = pending;
|
|
19638
|
+
const batch = Promise.resolve().then(
|
|
19639
|
+
() => generateEmbeddings(
|
|
19640
|
+
[...missing.values()].map((row) => row.content),
|
|
19641
|
+
options
|
|
19642
|
+
)
|
|
19643
|
+
);
|
|
19644
|
+
[...missing].forEach(([key, row], index) => {
|
|
19645
|
+
const request = batch.then((vectors) => {
|
|
19646
|
+
const vector = vectors[index];
|
|
19647
|
+
updateVaultMemoryEmbeddingOp(ctx, row.uniqueId, JSON.stringify(vector), model, {
|
|
19648
|
+
content: row.content,
|
|
19649
|
+
updatedAt: row.updatedAt?.getTime()
|
|
19650
|
+
}).catch(() => {
|
|
19651
|
+
});
|
|
19652
|
+
return vector;
|
|
19653
|
+
}).finally(() => {
|
|
19654
|
+
entries.delete(key);
|
|
19655
|
+
if (entries.size === 0) pendingRows.get(ctx)?.delete(options.maskInput);
|
|
19656
|
+
});
|
|
19657
|
+
pending.set(key, request);
|
|
19658
|
+
});
|
|
19659
|
+
}
|
|
19660
|
+
return Promise.all(
|
|
19661
|
+
keys.map((key) => {
|
|
19662
|
+
const cached = prepared?.get(key);
|
|
19663
|
+
if (cached) return cached;
|
|
19664
|
+
const request = pending.get(key);
|
|
19665
|
+
if (!prepared) return request;
|
|
19666
|
+
const retained = request.catch((error) => {
|
|
19667
|
+
prepared.delete(key);
|
|
19668
|
+
throw error;
|
|
19669
|
+
});
|
|
19670
|
+
prepared.set(key, retained);
|
|
19671
|
+
return retained;
|
|
19672
|
+
})
|
|
19673
|
+
);
|
|
19674
|
+
}
|
|
19675
|
+
|
|
19574
19676
|
// src/lib/memoryVault/vectorVersion.ts
|
|
19575
19677
|
var vectorRowVersion = /* @__PURE__ */ new WeakMap();
|
|
19576
19678
|
function contentFingerprint(content) {
|
|
@@ -20133,11 +20235,11 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
|
|
|
20133
20235
|
return combined.slice(0, limit);
|
|
20134
20236
|
}
|
|
20135
20237
|
async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options) {
|
|
20136
|
-
const memories = await
|
|
20238
|
+
const memories = await loadVaultCorpus(vaultCtx, options);
|
|
20137
20239
|
const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
20138
20240
|
const uncachedTexts = [];
|
|
20139
20241
|
const uncachedKeys = [];
|
|
20140
|
-
const
|
|
20242
|
+
const uncachedRows = [];
|
|
20141
20243
|
const uncachedVersions = [];
|
|
20142
20244
|
for (const m of memories) {
|
|
20143
20245
|
const content = m.content;
|
|
@@ -20156,12 +20258,12 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
|
|
|
20156
20258
|
}
|
|
20157
20259
|
uncachedTexts.push(m.content);
|
|
20158
20260
|
uncachedKeys.push(m.uniqueId);
|
|
20159
|
-
|
|
20261
|
+
uncachedRows.push(m);
|
|
20160
20262
|
uncachedVersions.push(m.updatedAt);
|
|
20161
20263
|
}
|
|
20162
20264
|
}
|
|
20163
20265
|
if (uncachedTexts.length > 0) {
|
|
20164
|
-
const embeddings = await
|
|
20266
|
+
const embeddings = await embedVaultRows(vaultCtx, embeddingOptions, uncachedRows);
|
|
20165
20267
|
for (let i = 0; i < uncachedKeys.length; i++) {
|
|
20166
20268
|
cacheRowVector(
|
|
20167
20269
|
cache,
|
|
@@ -20170,13 +20272,6 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
|
|
|
20170
20272
|
uncachedVersions[i],
|
|
20171
20273
|
uncachedTexts[i]
|
|
20172
20274
|
);
|
|
20173
|
-
updateVaultMemoryEmbeddingOp(
|
|
20174
|
-
vaultCtx,
|
|
20175
|
-
uncachedIds[i],
|
|
20176
|
-
JSON.stringify(embeddings[i]),
|
|
20177
|
-
currentModel
|
|
20178
|
-
).catch(() => {
|
|
20179
|
-
});
|
|
20180
20275
|
}
|
|
20181
20276
|
}
|
|
20182
20277
|
}
|
|
@@ -20318,10 +20413,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
20318
20413
|
let laneVecs;
|
|
20319
20414
|
if (opts.embedStats) opts.embedStats.rowsEmbedded += laneRows.length;
|
|
20320
20415
|
try {
|
|
20321
|
-
laneVecs = await
|
|
20322
|
-
laneRows.map((m) => m.content),
|
|
20323
|
-
embeddingOptions
|
|
20324
|
-
);
|
|
20416
|
+
laneVecs = await embedVaultRows(vaultCtx, embeddingOptions, laneRows);
|
|
20325
20417
|
} catch (err) {
|
|
20326
20418
|
laneEmbedFailed = true;
|
|
20327
20419
|
getLogger().warn(
|
|
@@ -20335,13 +20427,6 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
20335
20427
|
cacheRowVector(cache, m.uniqueId, vec, m.updatedAt, m.content);
|
|
20336
20428
|
resolvedThisCall.add(m.uniqueId);
|
|
20337
20429
|
vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
|
|
20338
|
-
updateVaultMemoryEmbeddingOp(
|
|
20339
|
-
vaultCtx,
|
|
20340
|
-
m.uniqueId,
|
|
20341
|
-
JSON.stringify(vecs[i]),
|
|
20342
|
-
currentModel
|
|
20343
|
-
).catch(() => {
|
|
20344
|
-
});
|
|
20345
20430
|
});
|
|
20346
20431
|
}
|
|
20347
20432
|
}
|
|
@@ -20533,9 +20618,10 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20533
20618
|
};
|
|
20534
20619
|
}
|
|
20535
20620
|
} else {
|
|
20536
|
-
const loaded = await
|
|
20621
|
+
const loaded = await loadVaultCorpus(
|
|
20537
20622
|
vaultCtx,
|
|
20538
|
-
Object.keys(queryOpts).length > 0 ? queryOpts : void 0
|
|
20623
|
+
Object.keys(queryOpts).length > 0 ? queryOpts : void 0,
|
|
20624
|
+
searchOptions?.vaultPreparation
|
|
20539
20625
|
);
|
|
20540
20626
|
vaultSize = loaded.length;
|
|
20541
20627
|
rowsDecrypted = loaded.length;
|
|
@@ -20608,7 +20694,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20608
20694
|
let newEmbeddings;
|
|
20609
20695
|
embedStats.rowsEmbedded += uncachedTexts.length;
|
|
20610
20696
|
try {
|
|
20611
|
-
newEmbeddings = await
|
|
20697
|
+
newEmbeddings = await embedVaultRows(
|
|
20698
|
+
vaultCtx,
|
|
20699
|
+
embeddingOptions,
|
|
20700
|
+
uncachedIndices.map((index) => memories[index]),
|
|
20701
|
+
searchOptions?.vaultPreparation
|
|
20702
|
+
);
|
|
20612
20703
|
} catch (err) {
|
|
20613
20704
|
embeddingFailure = true;
|
|
20614
20705
|
getLogger().warn(
|
|
@@ -20624,13 +20715,6 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20624
20715
|
memories[uncachedIndices[j]].updatedAt,
|
|
20625
20716
|
memories[uncachedIndices[j]].content
|
|
20626
20717
|
);
|
|
20627
|
-
updateVaultMemoryEmbeddingOp(
|
|
20628
|
-
vaultCtx,
|
|
20629
|
-
memories[uncachedIndices[j]].uniqueId,
|
|
20630
|
-
JSON.stringify(newEmbeddings[j]),
|
|
20631
|
-
currentModel
|
|
20632
|
-
).catch(() => {
|
|
20633
|
-
});
|
|
20634
20718
|
}
|
|
20635
20719
|
}
|
|
20636
20720
|
}
|
|
@@ -23211,6 +23295,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
23211
23295
|
},
|
|
23212
23296
|
...options.rrfK !== void 0 && { rrfK: options.rrfK },
|
|
23213
23297
|
...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
|
|
23298
|
+
...options.vaultPreparation && { vaultPreparation: options.vaultPreparation },
|
|
23214
23299
|
...subQueries && { subQueries },
|
|
23215
23300
|
...options.scopes && { scopes: options.scopes },
|
|
23216
23301
|
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
@@ -25240,6 +25325,8 @@ async function backfillTopics(ctx) {
|
|
|
25240
25325
|
if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
|
|
25241
25326
|
}
|
|
25242
25327
|
async function migrateMemoriesToRemote(options) {
|
|
25328
|
+
if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
|
|
25329
|
+
throw new Error("A write barrier and a remote store handoff are required");
|
|
25243
25330
|
const { canonical, signal } = options;
|
|
25244
25331
|
const rings = [canonical, ...options.fallbacks ?? []];
|
|
25245
25332
|
const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
@@ -25267,93 +25354,98 @@ async function migrateMemoriesToRemote(options) {
|
|
|
25267
25354
|
const placeholders = /* @__PURE__ */ new Set();
|
|
25268
25355
|
const truncated = /* @__PURE__ */ new Set();
|
|
25269
25356
|
let imported = 0;
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
);
|
|
25284
|
-
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
25285
|
-
for (const raw of candidates) {
|
|
25286
|
-
const server = remote.get(raw.id);
|
|
25287
|
-
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
25288
|
-
pending.set(raw.id, raw.updated_at + 1);
|
|
25289
|
-
}
|
|
25290
|
-
}
|
|
25291
|
-
if (pending.size === 0) {
|
|
25292
|
-
const result = (state) => ({
|
|
25293
|
-
state,
|
|
25294
|
-
total: rows.length,
|
|
25295
|
-
imported,
|
|
25296
|
-
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
25297
|
-
reembedded: reembedded.size,
|
|
25298
|
-
unembedded: unembedded.size,
|
|
25299
|
-
placeholderTombstones: placeholders.size,
|
|
25300
|
-
truncatedSourceChunkIds: truncated.size
|
|
25301
|
-
});
|
|
25302
|
-
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
25303
|
-
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
25304
|
-
if (!active)
|
|
25305
|
-
await request(
|
|
25306
|
-
"/account",
|
|
25307
|
-
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
25357
|
+
const uploadPasses = async (finalizing = false) => {
|
|
25358
|
+
for (let pass = 0; ; pass++) {
|
|
25359
|
+
const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
|
|
25360
|
+
for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
|
|
25361
|
+
const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
|
|
25362
|
+
const missing = await missingIds(request, candidates.map(revision), signal);
|
|
25363
|
+
const pending = /* @__PURE__ */ new Map();
|
|
25364
|
+
for (const raw of candidates)
|
|
25365
|
+
if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
|
|
25366
|
+
if (pending.size === 0 && !active) {
|
|
25367
|
+
const remote = await remoteMetadata(
|
|
25368
|
+
request,
|
|
25369
|
+
candidates.map((raw) => raw.id),
|
|
25308
25370
|
signal
|
|
25309
25371
|
);
|
|
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
|
-
|
|
25335
|
-
|
|
25336
|
-
|
|
25337
|
-
|
|
25338
|
-
|
|
25372
|
+
const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
|
|
25373
|
+
for (const raw of candidates) {
|
|
25374
|
+
const server = remote.get(raw.id);
|
|
25375
|
+
if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
|
|
25376
|
+
pending.set(raw.id, raw.updated_at + 1);
|
|
25377
|
+
}
|
|
25378
|
+
}
|
|
25379
|
+
if (pending.size === 0) {
|
|
25380
|
+
const result = (state) => ({
|
|
25381
|
+
state,
|
|
25382
|
+
total: rows.length,
|
|
25383
|
+
imported,
|
|
25384
|
+
skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
|
|
25385
|
+
reembedded: reembedded.size,
|
|
25386
|
+
unembedded: unembedded.size,
|
|
25387
|
+
placeholderTombstones: placeholders.size,
|
|
25388
|
+
truncatedSourceChunkIds: truncated.size
|
|
25389
|
+
});
|
|
25390
|
+
if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
|
|
25391
|
+
if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
|
|
25392
|
+
if (!claimed) await importBatch(request, canonical.keyId, [], signal);
|
|
25393
|
+
if (!active)
|
|
25394
|
+
await request(
|
|
25395
|
+
"/account",
|
|
25396
|
+
{ method: "PUT", body: JSON.stringify({ state: "active" }) },
|
|
25397
|
+
signal
|
|
25398
|
+
);
|
|
25399
|
+
await options.onActivated();
|
|
25400
|
+
return result("active");
|
|
25401
|
+
}
|
|
25402
|
+
if (pass >= MAX_UPLOAD_PASSES)
|
|
25403
|
+
throw new RemoteMemoryError(
|
|
25404
|
+
"Nearby still reports memories missing after importing them",
|
|
25405
|
+
409,
|
|
25406
|
+
"migration_incomplete"
|
|
25407
|
+
);
|
|
25408
|
+
const prepared = [];
|
|
25409
|
+
for (const raw of candidates) {
|
|
25410
|
+
const updatedAt = pending.get(raw.id);
|
|
25411
|
+
if (updatedAt === void 0) continue;
|
|
25412
|
+
try {
|
|
25413
|
+
prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
|
|
25414
|
+
} catch (error) {
|
|
25415
|
+
if (!(error instanceof MemoryKeyError)) throw error;
|
|
25416
|
+
skipped.set(raw.id, "unreadable");
|
|
25417
|
+
}
|
|
25418
|
+
}
|
|
25419
|
+
const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
|
|
25420
|
+
if (options.embeddingOptions)
|
|
25421
|
+
await embedMissing(prepared, options.embeddingOptions, model, signal);
|
|
25422
|
+
const local = new Map(candidates.map((raw) => [raw.id, raw]));
|
|
25423
|
+
const upload = [];
|
|
25424
|
+
for (const { row, plaintext } of prepared) {
|
|
25425
|
+
const id2 = row.memory_id;
|
|
25426
|
+
if (!fitsNearby(row)) {
|
|
25427
|
+
skipped.set(id2, "invalid");
|
|
25428
|
+
continue;
|
|
25429
|
+
}
|
|
25430
|
+
upload.push(row);
|
|
25431
|
+
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
25432
|
+
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
25433
|
+
track(unembedded, !row.embedding);
|
|
25434
|
+
track(placeholders, plaintext === void 0);
|
|
25435
|
+
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
25339
25436
|
}
|
|
25340
|
-
|
|
25341
|
-
const track = (set, on) => on ? set.add(id2) : set.delete(id2);
|
|
25342
|
-
track(reembedded, !!row.embedding && !usable.has(id2));
|
|
25343
|
-
track(unembedded, !row.embedding);
|
|
25344
|
-
track(placeholders, plaintext === void 0);
|
|
25345
|
-
track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
|
|
25346
|
-
}
|
|
25347
|
-
let sent = 0;
|
|
25348
|
-
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
25349
|
-
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
25350
|
-
await importBatch(request, canonical.keyId, batch, signal);
|
|
25351
|
-
claimed = true;
|
|
25352
|
-
sent += batch.length;
|
|
25437
|
+
let sent = 0;
|
|
25353
25438
|
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
25439
|
+
for (const batch of chunks(upload, IMPORT_BATCH)) {
|
|
25440
|
+
await importBatch(request, canonical.keyId, batch, signal);
|
|
25441
|
+
claimed = true;
|
|
25442
|
+
sent += batch.length;
|
|
25443
|
+
options.onProgress?.({ imported: sent, toImport: upload.length });
|
|
25444
|
+
}
|
|
25445
|
+
imported += sent;
|
|
25354
25446
|
}
|
|
25355
|
-
|
|
25356
|
-
|
|
25447
|
+
};
|
|
25448
|
+
return uploadPasses();
|
|
25357
25449
|
}
|
|
25358
25450
|
|
|
25359
25451
|
// src/lib/memory/store/remoteStore.ts
|
|
@@ -26809,13 +26901,20 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
26809
26901
|
}
|
|
26810
26902
|
}
|
|
26811
26903
|
}
|
|
26904
|
+
const vaultPreparation = createVaultPreparation();
|
|
26812
26905
|
const settled = await Promise.allSettled(
|
|
26813
26906
|
facets.map(async (facet) => {
|
|
26814
26907
|
const prior = previous?.sections.find((s) => s.key === facet.key);
|
|
26815
26908
|
if (prior && !staleKeys.has(facet.key)) {
|
|
26816
26909
|
return prior;
|
|
26817
26910
|
}
|
|
26818
|
-
return synthesizeFacet(
|
|
26911
|
+
return synthesizeFacet(
|
|
26912
|
+
facet,
|
|
26913
|
+
recallCtx,
|
|
26914
|
+
options,
|
|
26915
|
+
fallbackPriors.get(facet.key),
|
|
26916
|
+
vaultPreparation
|
|
26917
|
+
);
|
|
26819
26918
|
})
|
|
26820
26919
|
);
|
|
26821
26920
|
const sections = settled.map((r, i) => {
|
|
@@ -26969,7 +27068,7 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
26969
27068
|
}
|
|
26970
27069
|
return keys;
|
|
26971
27070
|
}
|
|
26972
|
-
async function synthesizeFacet(facet, ctx, options, prior) {
|
|
27071
|
+
async function synthesizeFacet(facet, ctx, options, prior, vaultPreparation) {
|
|
26973
27072
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
26974
27073
|
const limit = options.limit ?? DEFAULT_FACET_RECALL_LIMIT;
|
|
26975
27074
|
const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
|
|
@@ -26986,6 +27085,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
|
|
|
26986
27085
|
};
|
|
26987
27086
|
}
|
|
26988
27087
|
const recalled = await recall(facet.query, ctx, {
|
|
27088
|
+
vaultPreparation,
|
|
26989
27089
|
scopes,
|
|
26990
27090
|
limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
|
|
26991
27091
|
types: ["fact"],
|