@anuma/sdk 9.1.4 → 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 +132 -39
- package/dist/expo/index.d.mts +9 -0
- package/dist/expo/index.d.ts +9 -0
- package/dist/expo/index.mjs +132 -39
- package/dist/memory/context.d.mts +123 -0
- package/dist/memory/context.d.ts +123 -0
- package/dist/react/index.cjs +132 -39
- package/dist/react/index.d.mts +9 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.mjs +132 -39
- 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
|
@@ -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/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 },
|
|
@@ -26816,13 +26901,20 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
26816
26901
|
}
|
|
26817
26902
|
}
|
|
26818
26903
|
}
|
|
26904
|
+
const vaultPreparation = createVaultPreparation();
|
|
26819
26905
|
const settled = await Promise.allSettled(
|
|
26820
26906
|
facets.map(async (facet) => {
|
|
26821
26907
|
const prior = previous?.sections.find((s) => s.key === facet.key);
|
|
26822
26908
|
if (prior && !staleKeys.has(facet.key)) {
|
|
26823
26909
|
return prior;
|
|
26824
26910
|
}
|
|
26825
|
-
return synthesizeFacet(
|
|
26911
|
+
return synthesizeFacet(
|
|
26912
|
+
facet,
|
|
26913
|
+
recallCtx,
|
|
26914
|
+
options,
|
|
26915
|
+
fallbackPriors.get(facet.key),
|
|
26916
|
+
vaultPreparation
|
|
26917
|
+
);
|
|
26826
26918
|
})
|
|
26827
26919
|
);
|
|
26828
26920
|
const sections = settled.map((r, i) => {
|
|
@@ -26976,7 +27068,7 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
26976
27068
|
}
|
|
26977
27069
|
return keys;
|
|
26978
27070
|
}
|
|
26979
|
-
async function synthesizeFacet(facet, ctx, options, prior) {
|
|
27071
|
+
async function synthesizeFacet(facet, ctx, options, prior, vaultPreparation) {
|
|
26980
27072
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
26981
27073
|
const limit = options.limit ?? DEFAULT_FACET_RECALL_LIMIT;
|
|
26982
27074
|
const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
|
|
@@ -26993,6 +27085,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
|
|
|
26993
27085
|
};
|
|
26994
27086
|
}
|
|
26995
27087
|
const recalled = await recall(facet.query, ctx, {
|
|
27088
|
+
vaultPreparation,
|
|
26996
27089
|
scopes,
|
|
26997
27090
|
limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
|
|
26998
27091
|
types: ["fact"],
|
package/dist/react/index.d.mts
CHANGED
|
@@ -4402,6 +4402,13 @@ interface RecencyOptions {
|
|
|
4402
4402
|
noDateMultiplier?: number;
|
|
4403
4403
|
}
|
|
4404
4404
|
|
|
4405
|
+
type VaultCorpusCache = WeakMap<VaultMemoryOperationsContext, Map<string, Promise<StoredVaultMemory[]>>>;
|
|
4406
|
+
type RowRequests = Map<EmbeddingOptions["maskInput"], Map<string, Promise<number[]>>>;
|
|
4407
|
+
interface VaultPreparation {
|
|
4408
|
+
corpora: VaultCorpusCache;
|
|
4409
|
+
rows: WeakMap<VaultMemoryOperationsContext, RowRequests>;
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4405
4412
|
declare const DEFAULT_VAULT_CACHE_SIZE = 5000;
|
|
4406
4413
|
/**
|
|
4407
4414
|
* Create a VaultEmbeddingCache backed by an LRU with a default cap of
|
|
@@ -4419,6 +4426,7 @@ type VaultEmbeddingCache = Map<string, Float32Array>;
|
|
|
4419
4426
|
* Options for the vault search tool.
|
|
4420
4427
|
*/
|
|
4421
4428
|
interface MemoryVaultSearchOptions {
|
|
4429
|
+
/** @internal */ vaultPreparation?: VaultPreparation;
|
|
4422
4430
|
onCandidateCount?: (count: number) => void;
|
|
4423
4431
|
/** Maximum number of results to return (default: 5) */
|
|
4424
4432
|
limit?: number;
|
|
@@ -4957,6 +4965,7 @@ interface RankedMemory {
|
|
|
4957
4965
|
updatedAt: Date;
|
|
4958
4966
|
}
|
|
4959
4967
|
interface RecallOptions {
|
|
4968
|
+
/** @internal */ vaultPreparation?: VaultPreparation;
|
|
4960
4969
|
/** Embedding cache shared with the rest of the turn, so the query is embedded once across lanes and the send. */
|
|
4961
4970
|
embeddingCache?: Map<string, Float32Array>;
|
|
4962
4971
|
/** Which kinds to search. Default: ['fact']. */
|
package/dist/react/index.d.ts
CHANGED
|
@@ -4402,6 +4402,13 @@ interface RecencyOptions {
|
|
|
4402
4402
|
noDateMultiplier?: number;
|
|
4403
4403
|
}
|
|
4404
4404
|
|
|
4405
|
+
type VaultCorpusCache = WeakMap<VaultMemoryOperationsContext, Map<string, Promise<StoredVaultMemory[]>>>;
|
|
4406
|
+
type RowRequests = Map<EmbeddingOptions["maskInput"], Map<string, Promise<number[]>>>;
|
|
4407
|
+
interface VaultPreparation {
|
|
4408
|
+
corpora: VaultCorpusCache;
|
|
4409
|
+
rows: WeakMap<VaultMemoryOperationsContext, RowRequests>;
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4405
4412
|
declare const DEFAULT_VAULT_CACHE_SIZE = 5000;
|
|
4406
4413
|
/**
|
|
4407
4414
|
* Create a VaultEmbeddingCache backed by an LRU with a default cap of
|
|
@@ -4419,6 +4426,7 @@ type VaultEmbeddingCache = Map<string, Float32Array>;
|
|
|
4419
4426
|
* Options for the vault search tool.
|
|
4420
4427
|
*/
|
|
4421
4428
|
interface MemoryVaultSearchOptions {
|
|
4429
|
+
/** @internal */ vaultPreparation?: VaultPreparation;
|
|
4422
4430
|
onCandidateCount?: (count: number) => void;
|
|
4423
4431
|
/** Maximum number of results to return (default: 5) */
|
|
4424
4432
|
limit?: number;
|
|
@@ -4957,6 +4965,7 @@ interface RankedMemory {
|
|
|
4957
4965
|
updatedAt: Date;
|
|
4958
4966
|
}
|
|
4959
4967
|
interface RecallOptions {
|
|
4968
|
+
/** @internal */ vaultPreparation?: VaultPreparation;
|
|
4960
4969
|
/** Embedding cache shared with the rest of the turn, so the query is embedded once across lanes and the send. */
|
|
4961
4970
|
embeddingCache?: Map<string, Float32Array>;
|
|
4962
4971
|
/** Which kinds to search. Default: ['fact']. */
|