@anuma/sdk 6.8.2 → 6.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6768,6 +6768,14 @@ declare function relinkMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryI
6768
6768
  * row has: a curated memory's topics are recorded as `user`, everything else as
6769
6769
  * `auto`. Skips deleted, foreign-user, unlinked rows, and rows that already have
6770
6770
  * a record. Returns the ids filled.
6771
+ *
6772
+ * Runs as ONE writer over the whole (caller-bounded) list, in two phases:
6773
+ * resolve every row's write — all the awaits — and only then prepare and batch,
6774
+ * synchronously. Resolving and preparing per row inside its own writer is what
6775
+ * fired WatermelonDB's "wasn't sent to batch() synchronously" diagnostic once
6776
+ * per memory (~107 on a single dev launch, sdk#891); it also cost N writes.
6777
+ * Same treatment as {@link stampTopicsExtractedAtOp}, including its
6778
+ * transpilation hazard — the prepare pass MUST stay a `.map()`.
6771
6779
  */
6772
6780
  declare function backfillMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[]): Promise<string[]>;
6773
6781
  declare function deleteAllVaultMemoriesForUserOp(ctx: VaultMemoryOperationsContext, userId: string): Promise<number>;
@@ -7230,6 +7238,22 @@ interface TurnCompleteEvent {
7230
7238
  * previously indistinguishable (both surfaced as zero candidates).
7231
7239
  */
7232
7240
  outcome: ExtractOutcome;
7241
+ /**
7242
+ * Present only alongside `outcome: "empty-after-retry"` — WHICH failure ended
7243
+ * the turn (#888).
7244
+ *
7245
+ * `outcome` says extraction gave up; this says why, from a stable enum. The
7246
+ * distinction is the whole point: a 2026-08-11 audit measured ~63% of
7247
+ * production extraction turns ending in `empty-after-retry` and could not tell
7248
+ * from telemetry whether the cause was the freeloader 403 everyone assumed or
7249
+ * something else. It took a Prometheus cross-check to find the real one — the
7250
+ * portal returning HTTP 200 with an empty body, which it counts as a success.
7251
+ *
7252
+ * Forward `failure.reason` into your extraction analytics event; all three
7253
+ * fields are bounded (an enum, an HTTP status, a small attempt count) and none
7254
+ * carries content.
7255
+ */
7256
+ failure?: PortalLlmFailure;
7233
7257
  }
7234
7258
  /** @public */
7235
7259
  /**
@@ -8495,6 +8519,39 @@ interface TopicExtractOptions extends PortalLlmAuth {
8495
8519
  * later sweep, never stamp them.
8496
8520
  */
8497
8521
  declare function extractEntitiesForMemories(memories: readonly TopicExtractionInput[], options: TopicExtractOptions): Promise<Map<string, ExtractedEntity[]>>;
8522
+ /**
8523
+ * Why one memory was skipped by a topic sweep.
8524
+ *
8525
+ * Split along the line that actually matters operationally — did the sweep
8526
+ * DECLINE this row on purpose, or did something BREAK:
8527
+ *
8528
+ * Deliberate (healthy; a sweep of nothing but these is a success):
8529
+ * - `excluded` — deleted, owned by another user, or `topicsUserManaged`.
8530
+ * - `link-declined` — the entity-link write's in-row guard declined (the row
8531
+ * became user-managed / deleted / absent mid-run).
8532
+ * - `stamp-declined` — same, caught by the stamp op's own re-check.
8533
+ *
8534
+ * Degraded (something is wrong; see {@link isDegradedTopicSkip}):
8535
+ * - `llm-unanswered` — the batch failed or the model omitted this id. THE one
8536
+ * to alarm on: it is how a wholly broken sweep looks.
8537
+ * - `unreadable` — the row could not be loaded/decrypted.
8538
+ * - `not-found` — the lookup threw (absent row or read fault).
8539
+ * - `link-failed` — the entity-link write threw.
8540
+ *
8541
+ * @public
8542
+ */
8543
+ type TopicSkipReason = "excluded" | "link-declined" | "stamp-declined" | "llm-unanswered" | "unreadable" | "not-found" | "link-failed";
8544
+ /**
8545
+ * Whether a skip reason means the sweep FAILED on that row, rather than
8546
+ * deliberately passing over it.
8547
+ *
8548
+ * Exported so callers classify identically instead of each re-deriving the
8549
+ * split — the reason this type exists is that one wrong grouping turns a broken
8550
+ * sweep back into a healthy-looking one.
8551
+ *
8552
+ * @public
8553
+ */
8554
+ declare function isDegradedTopicSkip(reason: TopicSkipReason): boolean;
8498
8555
  /** Outcome of one {@link extractAndLinkEntitiesForMemoriesOp} run. */
8499
8556
  interface TopicExtractionRunResult {
8500
8557
  /** memoryId → entities the LLM returned (post-validation, post-linking). */
@@ -8507,6 +8564,24 @@ interface TopicExtractionRunResult {
8507
8564
  * LLM batches. Skipped ids are not stamped, so failed batches are retried
8508
8565
  * by a later sweep — callers should apply their own attempt caps. */
8509
8566
  skippedIds: string[];
8567
+ /**
8568
+ * Why each id in {@link skippedIds} was skipped.
8569
+ *
8570
+ * `skippedIds` alone cannot distinguish a sweep that deliberately declined
8571
+ * work from one that FAILED: a run where every LLM batch errored and a run
8572
+ * over rows the user has taken manual control of produce an identical array.
8573
+ * That is the same blindness `outcome: 'empty-after-retry'` had before #888 —
8574
+ * a degradation wearing the shape of normal control flow.
8575
+ *
8576
+ * It matters here because topics feed the entity recall lane, so a silently
8577
+ * failing sweep means memories that exist but cannot be found by entity, with
8578
+ * nothing in production saying so.
8579
+ *
8580
+ * Populated in lockstep with `skippedIds` (single `skip()` helper), so the two
8581
+ * cannot drift. Group by {@link isDegradedTopicSkip} to separate "declined" from
8582
+ * "broke".
8583
+ */
8584
+ skippedReasons: Map<string, TopicSkipReason>;
8510
8585
  }
8511
8586
  /**
8512
8587
  * Run LLM topic extraction over existing vault memories and persist the
@@ -10353,4 +10428,4 @@ declare function refreshNotionAccessToken(params: NotionRefreshTokenParams): Pro
10353
10428
  */
10354
10429
  declare function createNotionTools(getAccessToken: () => string | null, requestNotionAccess: () => Promise<string>): ToolConfig[];
10355
10430
 
10356
- export { type AutoExtractMessage, type AutoExtractor, type Budget, CHUNKS_DISCARDED_ORIGIN, type CachedChunkVectors, type CachedServerTools, Conversation as ChatConversation, Message as ChatMessage, type ChatRole, type ChunkVectorCache, type ChunkingOptions, type ConsolidationFallbackReason, type ConversationMemoryInput, ConversationMemory as ConversationMemoryModel, type ConversationMemoryOperationsContext, type CreateAutoExtractorOptions, type CreateConversationOptions, type CreateDecaySweeperOptions, type CreateMessageOptions, type CreateVaultFolderOptions, type CreateVaultMemoryOptions, type CryptoPriceClassification, type CryptoPricePreProcessorOptions, DEFAULT_CACHE_EXPIRATION_MS, DEFAULT_CHUNK_CACHE_SIZE, DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE, DEFAULT_DECAY_POLICY, DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP, DEFAULT_MIN_CHUNK_SIZE, DEFAULT_PROFILE_FACETS, DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, DEFAULT_PROFILE_PROOF_ALPHA, DEFAULT_PROFILE_TREND_MULTIPLIERS, DEFAULT_VAULT_CACHE_SIZE, DatabaseManager, type DatabaseManagerLogger, type DatabaseManagerOptions, type DecayCandidateRaw, type DecayClassifier, type DecayInput, type DecayPolicy, type DecaySweepResult, type DecaySweeper, type DecayVerdict, ENTITY_FANOUT, type EmbeddedWalletSignerFn, EncryptionKeyMissingError, type ExtractFactsOptions, type ExtractOutcome, type ExtractedCandidate, type ExtractedEntity, type ExtractionCursorStore, type FactType, type FileMetadata, type FlushResult, type GetConversationsPageOptions, type GetMessagesPageOptions, type GraphTraversalOptions, HARD_DELETE_WINDOW_MS, INFERENCE_ID_HEADER, type InjectionClassifierOptions, type InjectionReason, type LazyStoredConversation, type LlmDecayClassifierOptions, type LlmNeighborRefinerOptions, type Logger, LoggerProvider, type LoggerProviderProps, MAX_HOPS, MEDIUM_TTL_MS, type MemoriesNeedingTopicExtraction, type EmbeddingOptions as MemoryEngineEmbeddingOptions, type MemoryEngineResult, type MemoryEngineSearchOptions, type MemoryExtractedEvent, type MemoryKind, type MemoryQuarantinedEvent, type MemoryToVerify, type MemoryVaultSearchOptions, type MemoryVaultToolOptions, type MemoryVerification, type MessageRedactionResult, type MessageSkeleton, NEVER_TTL_MS, NODE_BUDGET, NOTION_OAUTH_CONFIG, type NeighborRefiner, type NotionAuthUrlParams, type NotionClientRegistration, type NotionExchangeCodeParams, type NotionOAuthEndpoints, type NotionPKCEChallenge, type NotionRefreshTokenParams, type NotionTokenResponse, type NowSource, type ObservationTrend, type ObservationTrendInput, PAST_EVENT_GRACE_MS, PII_PATTERNS, PROFILE_DOC_VERSION, type PhotoIngestResult, type PhotoMediaRef, type PiiCategory, type PiiMatch, type PiiPattern, PiiRedactor, type PiiRedactorOptions, type PlatformStorage, type PortalLlmAuth, type PortalLlmFailure, type PortalLlmFailureReason, type ProfileConfigFingerprint, type ProfileDoc, type ProfileFacet, type ProfileFacetKey, type ProfileSalienceInput, type ProfileSection, type PromptPreProcessor, type PromptPreProcessorContext, type PublishedPhotoMemory, type QuantizedEmbedding, type QuarantinedMemoryInfo, QueueManager, type QueueStatus, RECALL_MAX_LIMIT, RECALL_TOOL_NAME, type RankableVaultMemory, type RankedMemory, type RankedProfileCandidate, type RecallContext, type RecallDegradation, type RecallDiagnostics, type RecallOptions, type RecallResult, type RecallToolCallbacks, type RecallToolOptions, type RecencyOptions, type RedactionResult, type ReflectOptions, type ReflectResult, type RequestEncryptionKeyOptions, RerankerUnavailableError, type ResumeStreamOptions, type ResumeStreamResult, type ResumeStreamWithStorageResult, type RetainAction, type RetainContext, type RetainOptions, type RetainResult, type RetainSource, SDK_SCHEMA_VERSION, SHORT_TTL_MS, STREAM_RESUMABLE_HEADER, type ScoreBreakdown, type ScoreProfileSalienceOptions, type ScreenResult, type ScreenedCandidate, type SearchSource, type SendMessageWithStorageArgs, type SendMessageWithStorageDetachedResult, type SendMessageWithStorageResult, type ServerToolsOptions, type ServerToolsResponse, type SignMessageFn, type StockPriceClassification, type StockPricePreProcessorOptions, type ChatCompletionUsage as StoredChatCompletionUsage, type StoredConversation, type StoredConversationMemory, type StoredMessage, type StoredMessageWithSimilarity, type StoredVaultFolder, VaultFolder as StoredVaultFolderModel, type StoredVaultMemory, VaultMemory as StoredVaultMemoryModel, StreamExpiredError, type StreamMetaEvent, type StreamResumeHandle, type SynthesizeProfileOptions, TOOL_RESULT_ORIGIN, TOPICS_EXTRACTION_VERSION, TOPIC_EXTRACTION_BATCH_SIZE, TREND_RECENT_WINDOW_DAYS, TREND_STALE_WINDOW_DAYS, type TextChunk, type TopicExtractOptions, type TopicExtractionInput, type TopicExtractionRunResult, type TurnCompleteEvent, type TurnSkippedEvent, type UncheckedReason, type UnverifiableReason, type UpdateVaultFolderOptions, type UpdateVaultMemoryOptions, type UseChatStorageOptions, type UseChatStorageResult, type UseCreditsOptions, type UseCreditsResult, type UseModelsOptions, type UseModelsResult, VAULT_SIZE_HOP_CAP, type VaultEmbeddingCache, type VaultFolderOperationsContext, type VaultMemoryOperationsContext, type VaultMemoryVisibility, type VaultSaveOperation, type VaultSearchResult, type VerificationSources, type VerifyMemoriesForPublishOptions, WalletPoller, type WeatherClassification, type WeatherPreProcessorOptions, type WebSearchClassification, type WebSearchPreProcessorOptions, addConversationMemoriesOp, archiveVaultMemoryOp, backfillMemoryTopicsOp, buildNotionAuthUrl, capHopsForDensity, chatStorageMigrations, chatStorageSchema, chunkAndEmbedAllMessages, chunkAndEmbedMessage, chunkText, classifyCryptoPrice, classifyCryptoPriceBatch, classifyDecay, classifyInjectionCandidates, classifyObservationTrend, classifyStockPrice, classifyStockPriceBatch, classifyWeather, classifyWeatherBatch, classifyWebSearch, classifyWebSearchBatch, clearAllEncryptionKeys, clearAllEncryptionState, clearConversationMemoriesOp, clearEncryptionKey, clearLazyTitleCache, clearServerToolsCache, consoleLogger, conversationMemoryToStored, cosineInt8, createAutoExtractor, createChunkVectorCache, createCryptoPricePreProcessor, createDecaySweeper, createLlmDecayClassifier, createLlmNeighborRefiner, createMemoryEngineTool, createMemoryVaultSearchTool, createMemoryVaultTool, createMessageSourceResolver, createNotionTools, createPlatformCursorStore, createRecallTool, createStockPricePreProcessor, createStreamingDeAnonymizer, createVaultEmbeddingCache, createVaultFolderOp, createVaultMemoriesBatchOp, createVaultMemoryOp, createWeatherPreProcessor, createWebSearchPreProcessor, decodeChunkVector, decryptConversationTitle, deleteAllVaultMemoriesForUserOp, deleteVaultFolderOp, deleteVaultMemoryOp, dequantizeEmbedding, discoverNotionOAuthEndpoints, eagerEmbedContent, embedAllMessages, embedMessage, encodeChunkVector, ensureDefaultFoldersOp, exchangeNotionCode, extractAndLinkEntitiesForMemoriesOp, extractAndRetain, extractEntitiesForMemories, extractFacts, generateConversationId, generateEmbedding, generateEmbeddings, generateNotionPKCE, getAllVaultFoldersOp, getAllVaultMemoriesOp, getAllVaultMemoryContentsOp, getCachedServerTools, getConversationMemoriesOp, getConversationsByProjectLazyOp, getConversationsLazyOp, getConversationsPageOp, getDecayCandidatesRawOp, getLogger, getMemoriesNeedingTopicExtractionOp, getServerTools, getUnfiledVaultMemoriesOp, getVaultFolderMemoryCountOp, getVaultMemoriesByIdsOp, getVaultMemoryOp, getVaultRankingProjectionsOp, hardDeleteDecayedOp, hasEncryptionKey, ingestPublishedPhotoMemoriesOp, injectionSignatureCatalog, isPiiRedactor, isRerankerAvailable, moveMemoriesToFolderOp, noopLogger, onKeyAvailable, parseMedia, preEmbedVaultMemories, quantizeEmbedding, queueManager, rankProfileCandidates, recall, reflect, refreshEncryptionKeyIfMatches, refreshNotionAccessToken, registerNotionClient, relinkMemoryTopicsOp, requestEncryptionKey, resolvePiiRedactor, restoreVaultMemoryOp, resumeStream, retain, scoreProfileSalience, screenCandidatesForInjection, sdkMigrations, sdkModelClasses, sdkSchema, searchVaultMemories, seedEncryptionKeys, setLogger, setMemoryVisibilityOp, shouldChunkMessage, stampTopicsExtractedAtOp, streamCancelPath, streamReplayPath, summarizeObservationTrends, supersedeVaultMemoryOp, synthesizeProfile, traverseGraphLane, ttlForType, updateVaultFolderContextOp, updateVaultFolderOp, updateVaultMemoryEmbeddingOp, updateVaultMemoryOp, upsertMessageOp, useChat, useChatStorage, useCredits, useEncryption, useModels, verifyMemoriesForPublish, xhrTransport };
10431
+ export { type AutoExtractMessage, type AutoExtractor, type Budget, CHUNKS_DISCARDED_ORIGIN, type CachedChunkVectors, type CachedServerTools, Conversation as ChatConversation, Message as ChatMessage, type ChatRole, type ChunkVectorCache, type ChunkingOptions, type ConsolidationFallbackReason, type ConversationMemoryInput, ConversationMemory as ConversationMemoryModel, type ConversationMemoryOperationsContext, type CreateAutoExtractorOptions, type CreateConversationOptions, type CreateDecaySweeperOptions, type CreateMessageOptions, type CreateVaultFolderOptions, type CreateVaultMemoryOptions, type CryptoPriceClassification, type CryptoPricePreProcessorOptions, DEFAULT_CACHE_EXPIRATION_MS, DEFAULT_CHUNK_CACHE_SIZE, DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE, DEFAULT_DECAY_POLICY, DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP, DEFAULT_MIN_CHUNK_SIZE, DEFAULT_PROFILE_FACETS, DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, DEFAULT_PROFILE_PROOF_ALPHA, DEFAULT_PROFILE_TREND_MULTIPLIERS, DEFAULT_VAULT_CACHE_SIZE, DatabaseManager, type DatabaseManagerLogger, type DatabaseManagerOptions, type DecayCandidateRaw, type DecayClassifier, type DecayInput, type DecayPolicy, type DecaySweepResult, type DecaySweeper, type DecayVerdict, ENTITY_FANOUT, type EmbeddedWalletSignerFn, EncryptionKeyMissingError, type ExtractFactsOptions, type ExtractOutcome, type ExtractedCandidate, type ExtractedEntity, type ExtractionCursorStore, type FactType, type FileMetadata, type FlushResult, type GetConversationsPageOptions, type GetMessagesPageOptions, type GraphTraversalOptions, HARD_DELETE_WINDOW_MS, INFERENCE_ID_HEADER, type InjectionClassifierOptions, type InjectionReason, type LazyStoredConversation, type LlmDecayClassifierOptions, type LlmNeighborRefinerOptions, type Logger, LoggerProvider, type LoggerProviderProps, MAX_HOPS, MEDIUM_TTL_MS, type MemoriesNeedingTopicExtraction, type EmbeddingOptions as MemoryEngineEmbeddingOptions, type MemoryEngineResult, type MemoryEngineSearchOptions, type MemoryExtractedEvent, type MemoryKind, type MemoryQuarantinedEvent, type MemoryToVerify, type MemoryVaultSearchOptions, type MemoryVaultToolOptions, type MemoryVerification, type MessageRedactionResult, type MessageSkeleton, NEVER_TTL_MS, NODE_BUDGET, NOTION_OAUTH_CONFIG, type NeighborRefiner, type NotionAuthUrlParams, type NotionClientRegistration, type NotionExchangeCodeParams, type NotionOAuthEndpoints, type NotionPKCEChallenge, type NotionRefreshTokenParams, type NotionTokenResponse, type NowSource, type ObservationTrend, type ObservationTrendInput, PAST_EVENT_GRACE_MS, PII_PATTERNS, PROFILE_DOC_VERSION, type PhotoIngestResult, type PhotoMediaRef, type PiiCategory, type PiiMatch, type PiiPattern, PiiRedactor, type PiiRedactorOptions, type PlatformStorage, type PortalLlmAuth, type PortalLlmFailure, type PortalLlmFailureReason, type ProfileConfigFingerprint, type ProfileDoc, type ProfileFacet, type ProfileFacetKey, type ProfileSalienceInput, type ProfileSection, type PromptPreProcessor, type PromptPreProcessorContext, type PublishedPhotoMemory, type QuantizedEmbedding, type QuarantinedMemoryInfo, QueueManager, type QueueStatus, RECALL_MAX_LIMIT, RECALL_TOOL_NAME, type RankableVaultMemory, type RankedMemory, type RankedProfileCandidate, type RecallContext, type RecallDegradation, type RecallDiagnostics, type RecallOptions, type RecallResult, type RecallToolCallbacks, type RecallToolOptions, type RecencyOptions, type RedactionResult, type ReflectOptions, type ReflectResult, type RequestEncryptionKeyOptions, RerankerUnavailableError, type ResumeStreamOptions, type ResumeStreamResult, type ResumeStreamWithStorageResult, type RetainAction, type RetainContext, type RetainOptions, type RetainResult, type RetainSource, SDK_SCHEMA_VERSION, SHORT_TTL_MS, STREAM_RESUMABLE_HEADER, type ScoreBreakdown, type ScoreProfileSalienceOptions, type ScreenResult, type ScreenedCandidate, type SearchSource, type SendMessageWithStorageArgs, type SendMessageWithStorageDetachedResult, type SendMessageWithStorageResult, type ServerToolsOptions, type ServerToolsResponse, type SignMessageFn, type StockPriceClassification, type StockPricePreProcessorOptions, type ChatCompletionUsage as StoredChatCompletionUsage, type StoredConversation, type StoredConversationMemory, type StoredMessage, type StoredMessageWithSimilarity, type StoredVaultFolder, VaultFolder as StoredVaultFolderModel, type StoredVaultMemory, VaultMemory as StoredVaultMemoryModel, StreamExpiredError, type StreamMetaEvent, type StreamResumeHandle, type SynthesizeProfileOptions, TOOL_RESULT_ORIGIN, TOPICS_EXTRACTION_VERSION, TOPIC_EXTRACTION_BATCH_SIZE, TREND_RECENT_WINDOW_DAYS, TREND_STALE_WINDOW_DAYS, type TextChunk, type TopicExtractOptions, type TopicExtractionInput, type TopicExtractionRunResult, type TopicSkipReason, type TurnCompleteEvent, type TurnSkippedEvent, type UncheckedReason, type UnverifiableReason, type UpdateVaultFolderOptions, type UpdateVaultMemoryOptions, type UseChatStorageOptions, type UseChatStorageResult, type UseCreditsOptions, type UseCreditsResult, type UseModelsOptions, type UseModelsResult, VAULT_SIZE_HOP_CAP, type VaultEmbeddingCache, type VaultFolderOperationsContext, type VaultMemoryOperationsContext, type VaultMemoryVisibility, type VaultSaveOperation, type VaultSearchResult, type VerificationSources, type VerifyMemoriesForPublishOptions, WalletPoller, type WeatherClassification, type WeatherPreProcessorOptions, type WebSearchClassification, type WebSearchPreProcessorOptions, addConversationMemoriesOp, archiveVaultMemoryOp, backfillMemoryTopicsOp, buildNotionAuthUrl, capHopsForDensity, chatStorageMigrations, chatStorageSchema, chunkAndEmbedAllMessages, chunkAndEmbedMessage, chunkText, classifyCryptoPrice, classifyCryptoPriceBatch, classifyDecay, classifyInjectionCandidates, classifyObservationTrend, classifyStockPrice, classifyStockPriceBatch, classifyWeather, classifyWeatherBatch, classifyWebSearch, classifyWebSearchBatch, clearAllEncryptionKeys, clearAllEncryptionState, clearConversationMemoriesOp, clearEncryptionKey, clearLazyTitleCache, clearServerToolsCache, consoleLogger, conversationMemoryToStored, cosineInt8, createAutoExtractor, createChunkVectorCache, createCryptoPricePreProcessor, createDecaySweeper, createLlmDecayClassifier, createLlmNeighborRefiner, createMemoryEngineTool, createMemoryVaultSearchTool, createMemoryVaultTool, createMessageSourceResolver, createNotionTools, createPlatformCursorStore, createRecallTool, createStockPricePreProcessor, createStreamingDeAnonymizer, createVaultEmbeddingCache, createVaultFolderOp, createVaultMemoriesBatchOp, createVaultMemoryOp, createWeatherPreProcessor, createWebSearchPreProcessor, decodeChunkVector, decryptConversationTitle, deleteAllVaultMemoriesForUserOp, deleteVaultFolderOp, deleteVaultMemoryOp, dequantizeEmbedding, discoverNotionOAuthEndpoints, eagerEmbedContent, embedAllMessages, embedMessage, encodeChunkVector, ensureDefaultFoldersOp, exchangeNotionCode, extractAndLinkEntitiesForMemoriesOp, extractAndRetain, extractEntitiesForMemories, extractFacts, generateConversationId, generateEmbedding, generateEmbeddings, generateNotionPKCE, getAllVaultFoldersOp, getAllVaultMemoriesOp, getAllVaultMemoryContentsOp, getCachedServerTools, getConversationMemoriesOp, getConversationsByProjectLazyOp, getConversationsLazyOp, getConversationsPageOp, getDecayCandidatesRawOp, getLogger, getMemoriesNeedingTopicExtractionOp, getServerTools, getUnfiledVaultMemoriesOp, getVaultFolderMemoryCountOp, getVaultMemoriesByIdsOp, getVaultMemoryOp, getVaultRankingProjectionsOp, hardDeleteDecayedOp, hasEncryptionKey, ingestPublishedPhotoMemoriesOp, injectionSignatureCatalog, isDegradedTopicSkip, isPiiRedactor, isRerankerAvailable, moveMemoriesToFolderOp, noopLogger, onKeyAvailable, parseMedia, preEmbedVaultMemories, quantizeEmbedding, queueManager, rankProfileCandidates, recall, reflect, refreshEncryptionKeyIfMatches, refreshNotionAccessToken, registerNotionClient, relinkMemoryTopicsOp, requestEncryptionKey, resolvePiiRedactor, restoreVaultMemoryOp, resumeStream, retain, scoreProfileSalience, screenCandidatesForInjection, sdkMigrations, sdkModelClasses, sdkSchema, searchVaultMemories, seedEncryptionKeys, setLogger, setMemoryVisibilityOp, shouldChunkMessage, stampTopicsExtractedAtOp, streamCancelPath, streamReplayPath, summarizeObservationTrends, supersedeVaultMemoryOp, synthesizeProfile, traverseGraphLane, ttlForType, updateVaultFolderContextOp, updateVaultFolderOp, updateVaultMemoryEmbeddingOp, updateVaultMemoryOp, upsertMessageOp, useChat, useChatStorage, useCredits, useEncryption, useModels, verifyMemoriesForPublish, xhrTransport };
@@ -132,7 +132,7 @@ import {
132
132
  upsertMessageOp,
133
133
  useEncryption,
134
134
  vaultMemoryToStored
135
- } from "./chunk-LUGYCFVI.mjs";
135
+ } from "./chunk-4M56MKBV.mjs";
136
136
  import {
137
137
  PII_PATTERNS,
138
138
  PiiRedactor,
@@ -7539,7 +7539,7 @@ function createAutoExtractor(options) {
7539
7539
  const t0 = Date.now();
7540
7540
  void (async () => {
7541
7541
  try {
7542
- const { candidates, results, failedCount, outcome } = await extractAndRetain(
7542
+ const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
7543
7543
  window,
7544
7544
  retainCtx,
7545
7545
  {
@@ -7579,7 +7579,11 @@ function createAutoExtractor(options) {
7579
7579
  failedCount,
7580
7580
  durationMs: Date.now() - t0,
7581
7581
  conversationId,
7582
- outcome
7582
+ outcome,
7583
+ // Only set alongside `outcome: "empty-after-retry"`. Spread so the key
7584
+ // is absent rather than explicitly undefined on a healthy turn —
7585
+ // analytics backends store an explicit undefined as a real value.
7586
+ ...failure !== void 0 && { failure }
7583
7587
  });
7584
7588
  } catch (err) {
7585
7589
  options.onError?.(err instanceof Error ? err : new Error(String(err)), conversationId);
@@ -8843,6 +8847,18 @@ function parseTopicResponse(parsed, validIds) {
8843
8847
  }
8844
8848
  return out;
8845
8849
  }
8850
+ var DEGRADED_TOPIC_SKIP = {
8851
+ excluded: false,
8852
+ "link-declined": false,
8853
+ "stamp-declined": false,
8854
+ "llm-unanswered": true,
8855
+ unreadable: true,
8856
+ "not-found": true,
8857
+ "link-failed": true
8858
+ };
8859
+ function isDegradedTopicSkip(reason) {
8860
+ return DEGRADED_TOPIC_SKIP[reason] === true;
8861
+ }
8846
8862
  async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
8847
8863
  const entityCtx = ctx.entityCtx;
8848
8864
  if (!entityCtx) {
@@ -8851,17 +8867,24 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
8851
8867
  const log = getLogger();
8852
8868
  const extractedAt = options.now ?? Date.now();
8853
8869
  const skippedIds = [];
8870
+ const skippedReasons = /* @__PURE__ */ new Map();
8871
+ const skip = (id, reason) => {
8872
+ if (skippedReasons.has(id)) return;
8873
+ skippedIds.push(id);
8874
+ skippedReasons.set(id, reason);
8875
+ };
8854
8876
  const inputs = [];
8855
- for (const id of memoryIds) {
8877
+ for (const id of new Set(memoryIds)) {
8856
8878
  let record;
8857
8879
  try {
8858
8880
  record = await ctx.vaultMemoryCollection.find(id);
8859
- } catch {
8860
- skippedIds.push(id);
8881
+ } catch (err) {
8882
+ log.warn("[memory/topics] vault lookup failed for extraction", err);
8883
+ skip(id, "not-found");
8861
8884
  continue;
8862
8885
  }
8863
8886
  if (record.isDeleted || ctx.userId !== void 0 && record.userId !== ctx.userId || record.topicsUserManaged) {
8864
- skippedIds.push(id);
8887
+ skip(id, "excluded");
8865
8888
  continue;
8866
8889
  }
8867
8890
  try {
@@ -8874,7 +8897,7 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
8874
8897
  inputs.push({ id, content: stored.content });
8875
8898
  } catch (err) {
8876
8899
  log.warn("[memory/topics] failed to load memory for extraction", err);
8877
- skippedIds.push(id);
8900
+ skip(id, "unreadable");
8878
8901
  }
8879
8902
  }
8880
8903
  const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
@@ -8882,19 +8905,19 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
8882
8905
  for (const input of inputs) {
8883
8906
  const entities = entitiesByMemory.get(input.id);
8884
8907
  if (entities === void 0) {
8885
- skippedIds.push(input.id);
8908
+ skip(input.id, "llm-unanswered");
8886
8909
  continue;
8887
8910
  }
8888
8911
  try {
8889
8912
  const linked = await replaceMemoryEntitiesGuardedOp(entityCtx, input.id, entities);
8890
8913
  if (linked === null) {
8891
- skippedIds.push(input.id);
8914
+ skip(input.id, "link-declined");
8892
8915
  entitiesByMemory.delete(input.id);
8893
8916
  continue;
8894
8917
  }
8895
8918
  } catch (err) {
8896
8919
  log.warn("[memory/topics] replaceMemoryEntitiesGuardedOp failed", err);
8897
- skippedIds.push(input.id);
8920
+ skip(input.id, "link-failed");
8898
8921
  entitiesByMemory.delete(input.id);
8899
8922
  continue;
8900
8923
  }
@@ -8904,11 +8927,11 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
8904
8927
  const stampedSet = new Set(stampedIds);
8905
8928
  for (const id of toStamp) {
8906
8929
  if (!stampedSet.has(id)) {
8907
- skippedIds.push(id);
8930
+ skip(id, "stamp-declined");
8908
8931
  entitiesByMemory.delete(id);
8909
8932
  }
8910
8933
  }
8911
- return { entitiesByMemory, stampedIds, skippedIds };
8934
+ return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
8912
8935
  }
8913
8936
 
8914
8937
  // src/lib/memory/verifySupport.ts
@@ -14898,6 +14921,7 @@ export {
14898
14921
  hasEncryptionKey,
14899
14922
  ingestPublishedPhotoMemoriesOp,
14900
14923
  injectionSignatureCatalog,
14924
+ isDegradedTopicSkip,
14901
14925
  isPiiRedactor,
14902
14926
  isRerankerAvailable,
14903
14927
  moveMemoriesToFolderOp,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-LUGYCFVI.mjs";
3
+ } from "./chunk-4M56MKBV.mjs";
4
4
  import "./chunk-CJ32HO6Q.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -2321,13 +2321,13 @@ async function upsertEntitiesInWrite(ctx, entities) {
2321
2321
  }
2322
2322
  const unique = Array.from(kindByName.keys());
2323
2323
  const out = /* @__PURE__ */ new Map();
2324
- if (unique.length === 0) return { entities: out, operations: [] };
2324
+ if (unique.length === 0) return { entities: out, operations: [], kindBackfills: [] };
2325
2325
  const existing = await ctx.entityCollection.query(Q2.where("canonical_name", Q2.oneOf(unique))).fetch();
2326
2326
  const existingNames = new Set(existing.map((e) => e.canonicalName));
2327
- const updates = existing.filter((e) => {
2327
+ const kindBackfills = existing.filter((e) => {
2328
2328
  const incoming = kindByName.get(e.canonicalName);
2329
2329
  return incoming !== void 0 && (e.kind === null || e.kind === void 0 || e.kind === "");
2330
- });
2330
+ }).map((record) => ({ record, kind: kindByName.get(record.canonicalName) }));
2331
2331
  const missing = unique.filter((n) => !existingNames.has(n));
2332
2332
  const created = missing.map(
2333
2333
  (name) => ctx.entityCollection.prepareCreate((record) => {
@@ -2336,14 +2336,19 @@ async function upsertEntitiesInWrite(ctx, entities) {
2336
2336
  if (kind !== void 0) record._setRaw("kind", kind);
2337
2337
  })
2338
2338
  );
2339
- const updated = updates.map(
2340
- (e) => e.prepareUpdate((record) => {
2341
- record._setRaw("kind", kindByName.get(e.canonicalName));
2342
- })
2343
- );
2344
2339
  for (const e of existing) out.set(e.canonicalName, entityToStored(e));
2345
2340
  for (const record of created) out.set(record.canonicalName, entityToStored(record));
2346
- return { entities: out, operations: [...created, ...updated] };
2341
+ for (const { record, kind } of kindBackfills) {
2342
+ out.set(record.canonicalName, { ...entityToStored(record), kind });
2343
+ }
2344
+ return { entities: out, operations: created, kindBackfills };
2345
+ }
2346
+ function prepareKindBackfills(backfills) {
2347
+ return backfills.map(
2348
+ ({ record, kind }) => record.prepareUpdate((r) => {
2349
+ r._setRaw("kind", kind);
2350
+ })
2351
+ );
2347
2352
  }
2348
2353
  async function findVaultRowInWrite(ctx, memoryId) {
2349
2354
  try {
@@ -2393,10 +2398,12 @@ function topicsForEntities(entities, displayNames, source) {
2393
2398
  function toEntityObjects(entityInputs) {
2394
2399
  return entityInputs.map((e) => typeof e === "string" ? { name: e } : e);
2395
2400
  }
2396
- async function prepareMemoryTopicsUpdate(ctx, memoryId, linked, inputs, source) {
2401
+ async function resolveMemoryTopicsWrite(ctx, memoryId, linked, inputs, source) {
2397
2402
  const row = await findVaultRowInWrite(ctx, memoryId);
2398
- const write = resolveTopicsWrite(row, linked, displayNamesOf(toEntityObjects(inputs)), source);
2399
- return write === null ? null : prepareTopicsUpdate(write.row, write.topics, Date.now());
2403
+ return resolveTopicsWrite(row, linked, displayNamesOf(toEntityObjects(inputs)), source);
2404
+ }
2405
+ function prepareMemoryTopicsUpdate(write) {
2406
+ return prepareTopicsUpdate(write.row, write.topics, Date.now());
2400
2407
  }
2401
2408
  async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
2402
2409
  if (entityInputs.length === 0) return [];
@@ -2405,15 +2412,16 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
2405
2412
  let skipped = false;
2406
2413
  let entities = [];
2407
2414
  await ctx.database.write(async () => {
2408
- const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
2409
- ctx,
2410
- normalized
2411
- );
2415
+ const {
2416
+ entities: byName,
2417
+ operations: entityOps,
2418
+ kindBackfills
2419
+ } = await upsertEntitiesInWrite(ctx, normalized);
2412
2420
  entities = Array.from(byName.values());
2413
2421
  const vaultRow = await findVaultRowInWrite(ctx, memoryId);
2414
2422
  if (options?.unlessTopicsUserManaged && autoLinkBlocked(vaultRow)) {
2415
- if (entityOps.length > 0) {
2416
- await ctx.database.batch(...entityOps);
2423
+ if (entityOps.length > 0 || kindBackfills.length > 0) {
2424
+ await ctx.database.batch(...entityOps, ...prepareKindBackfills(kindBackfills));
2417
2425
  }
2418
2426
  skipped = true;
2419
2427
  return;
@@ -2433,7 +2441,9 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
2433
2441
  displayNamesOf(normalized),
2434
2442
  options?.topicsSource ?? "auto"
2435
2443
  );
2436
- if (entityOps.length === 0 && toCreate.length === 0 && topicsWrite === null) return;
2444
+ if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && topicsWrite === null) {
2445
+ return;
2446
+ }
2437
2447
  const linkOps = toCreate.map(
2438
2448
  (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
2439
2449
  record._setRaw("memory_id", memoryId);
@@ -2443,6 +2453,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
2443
2453
  );
2444
2454
  await ctx.database.batch(
2445
2455
  ...entityOps,
2456
+ ...prepareKindBackfills(kindBackfills),
2446
2457
  ...linkOps,
2447
2458
  ...topicsWrite ? [prepareTopicsUpdate(topicsWrite.row, topicsWrite.topics, Date.now())] : []
2448
2459
  );
@@ -2480,10 +2491,11 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
2480
2491
  skipped = true;
2481
2492
  return;
2482
2493
  }
2483
- const { entities: byName, operations: entityOps } = await upsertEntitiesInWrite(
2484
- ctx,
2485
- normalized
2486
- );
2494
+ const {
2495
+ entities: byName,
2496
+ operations: entityOps,
2497
+ kindBackfills
2498
+ } = await upsertEntitiesInWrite(ctx, normalized);
2487
2499
  entities = Array.from(byName.values());
2488
2500
  const existingLinks = await ctx.memoryEntityCollection.query(Q2.where("memory_id", memoryId)).fetch();
2489
2501
  const keep = new Set(entities.map((e) => e.uniqueId));
@@ -2496,12 +2508,13 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
2496
2508
  displayNamesOf(normalized),
2497
2509
  options.topicsSource
2498
2510
  );
2499
- if (entityOps.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
2511
+ if (entityOps.length === 0 && kindBackfills.length === 0 && toCreate.length === 0 && toDestroy.length === 0 && topicsWrite === null) {
2500
2512
  return;
2501
2513
  }
2502
2514
  const orphans = await findOrphanedEntities(ctx, memoryId, toDestroy);
2503
2515
  await ctx.database.batch(
2504
2516
  ...entityOps,
2517
+ ...prepareKindBackfills(kindBackfills),
2505
2518
  ...toCreate.map(
2506
2519
  (e) => ctx.memoryEntityCollection.prepareCreate((record) => {
2507
2520
  record._setRaw("memory_id", memoryId);
@@ -3314,10 +3327,16 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
3314
3327
  const existing = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", memoryId)).fetch();
3315
3328
  const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
3316
3329
  if (staleLinks.length === 0 && entities.length > 0) return;
3317
- const topicsOp = await prepareMemoryTopicsUpdate(entityCtx, memoryId, linked, entities, "user");
3330
+ const topicsWrite = await resolveMemoryTopicsWrite(
3331
+ entityCtx,
3332
+ memoryId,
3333
+ linked,
3334
+ entities,
3335
+ "user"
3336
+ );
3318
3337
  await ctx.database.batch(
3319
3338
  ...staleLinks.map((l) => l.prepareDestroyPermanently()),
3320
- ...topicsOp ? [topicsOp] : []
3339
+ ...topicsWrite ? [prepareMemoryTopicsUpdate(topicsWrite)] : []
3321
3340
  );
3322
3341
  });
3323
3342
  if (stale) return null;
@@ -3636,27 +3655,36 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
3636
3655
  if (!entityCtx) {
3637
3656
  throw new Error("backfillMemoryTopicsOp requires ctx.entityCtx");
3638
3657
  }
3658
+ const uniqueIds = Array.from(new Set(memoryIds));
3659
+ if (uniqueIds.length === 0) return [];
3639
3660
  const filled = [];
3640
- for (const id of Array.from(new Set(memoryIds))) {
3641
- let record;
3642
- try {
3643
- record = await ctx.vaultMemoryCollection.find(id);
3644
- } catch {
3645
- continue;
3646
- }
3647
- if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
3648
- if (parseTopics(record.topics) !== null) continue;
3649
- const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", id)).unsafeFetchRaw();
3650
- const entityIds = links.map((l) => String(l.entity_id));
3651
- if (entityIds.length === 0) continue;
3652
- const entities = await entityCtx.entityCollection.query(Q3.where("id", Q3.oneOf(entityIds))).fetch();
3653
- if (entities.length === 0) continue;
3654
- const source = record.topicsUserManaged ? "user" : "auto";
3661
+ const CHUNK = 500;
3662
+ for (let i = 0; i < uniqueIds.length; i += CHUNK) {
3663
+ const chunkIds = uniqueIds.slice(i, i + CHUNK);
3655
3664
  await ctx.database.write(async () => {
3656
- const topicsOp = await prepareMemoryTopicsUpdate(entityCtx, id, entities, [], source);
3657
- if (!topicsOp) return;
3658
- await ctx.database.batch(topicsOp);
3659
- filled.push(id);
3665
+ const writes = [];
3666
+ for (const id of chunkIds) {
3667
+ let record;
3668
+ try {
3669
+ record = await ctx.vaultMemoryCollection.find(id);
3670
+ } catch {
3671
+ continue;
3672
+ }
3673
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
3674
+ if (parseTopics(record.topics) !== null) continue;
3675
+ const links = await entityCtx.memoryEntityCollection.query(Q3.where("memory_id", id)).unsafeFetchRaw();
3676
+ const entityIds = links.map((l) => String(l.entity_id));
3677
+ if (entityIds.length === 0) continue;
3678
+ const entities = await entityCtx.entityCollection.query(Q3.where("id", Q3.oneOf(entityIds))).fetch();
3679
+ if (entities.length === 0) continue;
3680
+ const source = record.topicsUserManaged ? "user" : "auto";
3681
+ const write = await resolveMemoryTopicsWrite(entityCtx, id, entities, [], source);
3682
+ if (write) writes.push(write);
3683
+ }
3684
+ if (writes.length === 0) return;
3685
+ const prepared = writes.map(prepareMemoryTopicsUpdate);
3686
+ await ctx.database.batch(...prepared);
3687
+ for (const write of writes) filled.push(write.row.id);
3660
3688
  });
3661
3689
  }
3662
3690
  return filled;
@@ -7096,7 +7124,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
7096
7124
  subQueries = decomp.subQueries;
7097
7125
  }
7098
7126
  }
7099
- const { recall: recall2 } = await import("./recall-3I5GDJZE.mjs");
7127
+ const { recall: recall2 } = await import("./recall-FWILZSFH.mjs");
7100
7128
  let recallDegraded = [];
7101
7129
  const result = await recall2(
7102
7130
  query,