@anuma/sdk 6.31.4 → 6.33.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.
@@ -8704,6 +8704,8 @@ function useChat(options) {
8704
8704
  onServerToolCall,
8705
8705
  onToolCallArgumentsDelta,
8706
8706
  onStepFinish,
8707
+ resumable,
8708
+ onStreamMeta,
8707
8709
  apiType: defaultApiType = "auto",
8708
8710
  smoothing,
8709
8711
  preProcessors,
@@ -8713,25 +8715,37 @@ function useChat(options) {
8713
8715
  const [isLoading, setIsLoading] = useState(false);
8714
8716
  const abortControllerRef = useRef(null);
8715
8717
  const requestIdRef = useRef(0);
8718
+ const pendingInferenceRef = useRef(
8719
+ null
8720
+ );
8721
+ const cancelInference = useCallback(
8722
+ (inferenceId, requestToken) => {
8723
+ void (async () => {
8724
+ const token = requestToken ?? (getToken ? await getToken() : null);
8725
+ const response = await fetch(`${baseUrl}${streamCancelPath(inferenceId)}`, {
8726
+ method: "POST",
8727
+ headers: token ? { Authorization: `Bearer ${token}` } : void 0
8728
+ });
8729
+ if (!response.ok) throw new Error(`Stream cancellation failed: ${response.status}`);
8730
+ })().catch((error) => getLogger().warn("[useChat] stream cancel POST failed:", error));
8731
+ },
8732
+ [getToken, baseUrl]
8733
+ );
8716
8734
  const piiRedactorRef = useRef(null);
8717
8735
  if (piiRedaction === true && !piiRedactorRef.current) {
8718
8736
  piiRedactorRef.current = new PiiRedactor();
8719
8737
  }
8720
8738
  const resolvedPiiRedaction = piiRedaction === true ? piiRedactorRef.current : piiRedaction;
8721
8739
  const stop = useCallback(() => {
8740
+ const pending = pendingInferenceRef.current;
8741
+ pendingInferenceRef.current = null;
8742
+ pending?.cancel();
8722
8743
  if (abortControllerRef.current) {
8723
8744
  abortControllerRef.current.abort();
8724
8745
  abortControllerRef.current = null;
8725
8746
  }
8726
8747
  }, []);
8727
- useEffect(() => {
8728
- return () => {
8729
- if (abortControllerRef.current) {
8730
- abortControllerRef.current.abort();
8731
- abortControllerRef.current = null;
8732
- }
8733
- };
8734
- }, []);
8748
+ useEffect(() => stop, [stop]);
8735
8749
  const sendMessage = useCallback(
8736
8750
  async ({
8737
8751
  messages,
@@ -8757,13 +8771,12 @@ function useChat(options) {
8757
8771
  endpointOverride,
8758
8772
  piiRedaction: requestPiiRedaction
8759
8773
  }) => {
8760
- if (abortControllerRef.current) {
8761
- abortControllerRef.current.abort();
8762
- }
8774
+ stop();
8763
8775
  const abortController = new AbortController();
8764
8776
  abortControllerRef.current = abortController;
8765
8777
  const requestId = ++requestIdRef.current;
8766
8778
  setIsLoading(true);
8779
+ let succeeded = false;
8767
8780
  try {
8768
8781
  const tokenGetterValidation = validateTokenGetter(getToken);
8769
8782
  if (!tokenGetterValidation.valid) {
@@ -8826,6 +8839,22 @@ function useChat(options) {
8826
8839
  headers,
8827
8840
  apiType: requestApiType ?? defaultApiType,
8828
8841
  endpointOverride,
8842
+ resumable,
8843
+ onStreamMeta: resumable || onStreamMeta ? (meta) => {
8844
+ if (requestId !== requestIdRef.current || abortController.signal.aborted) {
8845
+ if (resumable) cancelInference(meta.inferenceId, token);
8846
+ return;
8847
+ }
8848
+ const previous = pendingInferenceRef.current;
8849
+ if (previous && previous.round === meta.round && previous.id !== meta.inferenceId)
8850
+ previous.cancel();
8851
+ pendingInferenceRef.current = {
8852
+ id: meta.inferenceId,
8853
+ round: meta.round,
8854
+ cancel: resumable ? () => cancelInference(meta.inferenceId, token) : () => void 0
8855
+ };
8856
+ onStreamMeta?.(meta);
8857
+ } : void 0,
8829
8858
  temperature,
8830
8859
  maxOutputTokens,
8831
8860
  tools,
@@ -8855,6 +8884,7 @@ function useChat(options) {
8855
8884
  piiRedaction: requestPiiRedaction ?? resolvedPiiRedaction,
8856
8885
  onPiiRedacted
8857
8886
  });
8887
+ succeeded = result.error === null;
8858
8888
  return result;
8859
8889
  } catch (err) {
8860
8890
  return createErrorResult(
@@ -8865,12 +8895,16 @@ function useChat(options) {
8865
8895
  if (requestIdRef.current === requestId) {
8866
8896
  setIsLoading(false);
8867
8897
  abortControllerRef.current = null;
8898
+ if (!succeeded) pendingInferenceRef.current?.cancel();
8899
+ pendingInferenceRef.current = null;
8868
8900
  }
8869
8901
  }
8870
8902
  },
8871
8903
  [
8872
8904
  getToken,
8873
8905
  baseUrl,
8906
+ stop,
8907
+ cancelInference,
8874
8908
  globalOnData,
8875
8909
  globalOnThinking,
8876
8910
  onFinish,
@@ -8879,6 +8913,8 @@ function useChat(options) {
8879
8913
  onServerToolCall,
8880
8914
  onToolCallArgumentsDelta,
8881
8915
  onStepFinish,
8916
+ resumable,
8917
+ onStreamMeta,
8882
8918
  defaultApiType,
8883
8919
  smoothing,
8884
8920
  preProcessors,
@@ -15231,7 +15267,10 @@ async function eagerEmbedContent(content, embeddingOptions, cache, vaultCtx, mem
15231
15267
  if (memoryId) cacheRowVector(cache, memoryId, Float32Array.from(embedding), updatedAt, content);
15232
15268
  if (vaultCtx && memoryId) {
15233
15269
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
15234
- updateVaultMemoryEmbeddingOp(vaultCtx, memoryId, JSON.stringify(embedding), currentModel).catch(
15270
+ updateVaultMemoryEmbeddingOp(vaultCtx, memoryId, JSON.stringify(embedding), currentModel, {
15271
+ content,
15272
+ ...updatedAt !== void 0 && { updatedAt: updatedAt.getTime() }
15273
+ }).catch(
15235
15274
  // Silently swallow – SDK must not use console.*; embedding will be retried on next search
15236
15275
  () => {
15237
15276
  }
@@ -18752,6 +18791,349 @@ function parseAnswer(body, base, parseSchema) {
18752
18791
  };
18753
18792
  }
18754
18793
 
18794
+ // src/lib/memory/store/local.ts
18795
+ import { Q as Q8 } from "@nozbe/watermelondb";
18796
+
18797
+ // src/lib/memory/topicExtract.ts
18798
+ var TOPIC_EXTRACTION_BATCH_SIZE = 10;
18799
+ var MAX_CHARS_PER_MEMORY = 300;
18800
+ var MAX_VOCABULARY_NAMES = 100;
18801
+ var MAX_COMPLETION_TOKENS = 8192;
18802
+ var SYSTEM_PROMPT5 = `You assign topics to saved memories for a personal memory system.
18803
+
18804
+ Each memory is a short statement about the user. For each memory, list the NAMED entities it mentions \u2014 these become the memory's topics, used to connect related memories in a knowledge graph.
18805
+
18806
+ Include only NAMED entities, skip generic/common nouns. A topic must identify a specific thing the user could recognize by name; if it would be equally true of thousands of unrelated memories, leave it out:
18807
+ - SKIP bare generic nouns even when the memory capitalizes them or a calendar entry is titled that way \u2014 "Home", "Work", "Meeting", "Lunch", "Birthday", "Appointment", "Trip", "Today", "Friday", "March" are NOT topics.
18808
+ - KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
18809
+ - Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
18810
+ ${ENTITY_KIND_GUIDELINES}
18811
+
18812
+ Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
18813
+ - exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
18814
+ - "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
18815
+ No prose.`;
18816
+ async function extractEntitiesForMemories(memories, options) {
18817
+ const out = /* @__PURE__ */ new Map();
18818
+ if (memories.length === 0) return out;
18819
+ const redactor = resolvePiiRedactor(options.piiRedaction);
18820
+ const vocabulary = [];
18821
+ for (const name of (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES)) {
18822
+ vocabulary.push(redactor ? (await redactor.redactTextAsync(name)).text : name);
18823
+ }
18824
+ const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
18825
+ When a memory refers to one of these, reuse the EXACT existing name (same spelling, casing, spacing and punctuation) instead of a variant \u2014 if "Foo.ai" is listed, never emit "Foo ai" or "Foo AI", and if "Jane Roe" is listed, emit that rather than a bare "Jane".
18826
+
18827
+ ` : "";
18828
+ for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
18829
+ const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
18830
+ const rows = [];
18831
+ for (const m of batch) {
18832
+ const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
18833
+ rows.push(`${m.id}: ${redactor ? (await redactor.redactTextAsync(content)).text : content}`);
18834
+ }
18835
+ const listing = rows.join("\n");
18836
+ const parsed = await callPortalJsonCompletion({
18837
+ ...options.apiKey !== void 0 && { apiKey: options.apiKey },
18838
+ ...options.getToken !== void 0 && { getToken: options.getToken },
18839
+ ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
18840
+ taskType: "memory_topic",
18841
+ ...options.endpointOverride !== void 0 && {
18842
+ endpointOverride: options.endpointOverride
18843
+ },
18844
+ model: options.model ?? DEFAULT_EXTRACTION_MODEL,
18845
+ systemPrompt: SYSTEM_PROMPT5,
18846
+ userMessage: `${vocabularyNote}Memories:
18847
+ ${listing}
18848
+
18849
+ List each memory's named entities.`,
18850
+ tag: "memory/topics",
18851
+ extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
18852
+ ...options.fetchFn && { fetchFn: options.fetchFn },
18853
+ ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
18854
+ ...options.timeoutMs !== void 0 && { timeoutMs: options.timeoutMs },
18855
+ ...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
18856
+ ...options.backoffMs && { backoffMs: options.backoffMs }
18857
+ });
18858
+ if (parsed === null) {
18859
+ getLogger().warn(
18860
+ `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
18861
+ );
18862
+ continue;
18863
+ }
18864
+ const validIds = new Set(batch.map((m) => m.id));
18865
+ const byId = parseTopicResponse(parsed, validIds);
18866
+ if (byId === null) {
18867
+ getLogger().warn(
18868
+ `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
18869
+ );
18870
+ continue;
18871
+ }
18872
+ for (const m of batch) {
18873
+ const entities = byId.get(m.id);
18874
+ if (entities === void 0) {
18875
+ continue;
18876
+ }
18877
+ out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
18878
+ }
18879
+ }
18880
+ return out;
18881
+ }
18882
+ function restoreEntities(entities, redactor) {
18883
+ return entities.map((e) => ({ kind: e.kind, restored: redactor.restoreForStorage(e.name) })).filter((e) => !e.restored.unresolved).map(
18884
+ (e) => e.kind !== void 0 ? { name: e.restored.text, kind: e.kind } : { name: e.restored.text }
18885
+ );
18886
+ }
18887
+ function parseTopicResponse(parsed, validIds) {
18888
+ if (typeof parsed !== "object" || parsed === null) return null;
18889
+ const list = parsed.memories;
18890
+ if (!Array.isArray(list)) return null;
18891
+ const out = /* @__PURE__ */ new Map();
18892
+ for (const raw of list) {
18893
+ if (typeof raw !== "object" || raw === null) continue;
18894
+ const obj = raw;
18895
+ if (typeof obj.id !== "string") continue;
18896
+ const id = obj.id.trim().replace(/:$/, "").replace(/^\[([\s\S]*)\]$/, "$1").trim();
18897
+ if (!validIds.has(id) || out.has(id)) continue;
18898
+ const entities = Array.isArray(obj.entities) ? parseEntities(obj.entities) : [];
18899
+ out.set(id, entities);
18900
+ }
18901
+ return out;
18902
+ }
18903
+ var DEGRADED_TOPIC_SKIP = {
18904
+ excluded: false,
18905
+ "link-declined": false,
18906
+ "stamp-declined": false,
18907
+ "llm-unanswered": true,
18908
+ unreadable: true,
18909
+ "not-found": true,
18910
+ "link-failed": true
18911
+ };
18912
+ function isDegradedTopicSkip(reason) {
18913
+ return DEGRADED_TOPIC_SKIP[reason] === true;
18914
+ }
18915
+ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
18916
+ const entityCtx = ctx.entityCtx;
18917
+ if (!entityCtx) {
18918
+ throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
18919
+ }
18920
+ const log = getLogger();
18921
+ const extractedAt = options.now ?? Date.now();
18922
+ const skippedIds = [];
18923
+ const skippedReasons = /* @__PURE__ */ new Map();
18924
+ const skip = (id, reason) => {
18925
+ if (skippedReasons.has(id)) return;
18926
+ skippedIds.push(id);
18927
+ skippedReasons.set(id, reason);
18928
+ };
18929
+ const inputs = [];
18930
+ for (const id of new Set(memoryIds)) {
18931
+ let record;
18932
+ try {
18933
+ record = await ctx.vaultMemoryCollection.find(id);
18934
+ } catch (err) {
18935
+ log.warn("[memory/topics] vault lookup failed for extraction", err);
18936
+ skip(id, "not-found");
18937
+ continue;
18938
+ }
18939
+ if (record.isDeleted || ctx.userId !== void 0 && record.userId !== ctx.userId || record.topicsUserManaged) {
18940
+ skip(id, "excluded");
18941
+ continue;
18942
+ }
18943
+ try {
18944
+ const stored = await vaultMemoryToStored(
18945
+ record,
18946
+ ctx.walletAddress,
18947
+ ctx.signMessage,
18948
+ ctx.embeddedWalletSigner
18949
+ );
18950
+ inputs.push({ id, content: stored.content });
18951
+ } catch (err) {
18952
+ log.warn("[memory/topics] failed to load memory for extraction", err);
18953
+ skip(id, "unreadable");
18954
+ }
18955
+ }
18956
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
18957
+ const toStamp = [];
18958
+ for (const input of inputs) {
18959
+ const entities = entitiesByMemory.get(input.id);
18960
+ if (entities === void 0) {
18961
+ skip(input.id, "llm-unanswered");
18962
+ continue;
18963
+ }
18964
+ try {
18965
+ const linked = await replaceMemoryEntitiesGuardedOp(entityCtx, input.id, entities);
18966
+ if (linked === null) {
18967
+ skip(input.id, "link-declined");
18968
+ entitiesByMemory.delete(input.id);
18969
+ continue;
18970
+ }
18971
+ } catch (err) {
18972
+ log.warn("[memory/topics] replaceMemoryEntitiesGuardedOp failed", err);
18973
+ skip(input.id, "link-failed");
18974
+ entitiesByMemory.delete(input.id);
18975
+ continue;
18976
+ }
18977
+ toStamp.push(input.id);
18978
+ }
18979
+ const stampedIds = await stampTopicsExtractedAtOp(ctx, toStamp, extractedAt);
18980
+ const stampedSet = new Set(stampedIds);
18981
+ for (const id of toStamp) {
18982
+ if (!stampedSet.has(id)) {
18983
+ skip(id, "stamp-declined");
18984
+ entitiesByMemory.delete(id);
18985
+ }
18986
+ }
18987
+ return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
18988
+ }
18989
+
18990
+ // src/lib/memory/store/local.ts
18991
+ function createLocalMemoryStore(options) {
18992
+ const { database, userId } = options;
18993
+ const entityCtx = {
18994
+ database,
18995
+ entityCollection: database.get("entity"),
18996
+ memoryEntityCollection: database.get("memory_entity"),
18997
+ ...userId !== void 0 && { userId },
18998
+ ...options.allowUnscopedRows !== void 0 && {
18999
+ allowUnscopedRows: options.allowUnscopedRows
19000
+ }
19001
+ };
19002
+ const vaultCtx = {
19003
+ database,
19004
+ vaultMemoryCollection: database.get("memory_vault"),
19005
+ walletAddress: options.walletAddress,
19006
+ signMessage: options.signMessage,
19007
+ embeddedWalletSigner: options.embeddedWalletSigner,
19008
+ ...userId !== void 0 && { userId },
19009
+ ...options.singleTenant !== void 0 && { singleTenant: options.singleTenant },
19010
+ // Deletes cascade to memory_entity, so the graph lane never serves a
19011
+ // deleted memory's id.
19012
+ entityCtx
19013
+ };
19014
+ const vaultCache = options.vaultCache ?? createVaultEmbeddingCache();
19015
+ const ownedBy = userId !== void 0 ? [Q8.where("user_id", userId)] : [];
19016
+ const watchedColumns = [
19017
+ "content",
19018
+ "scope",
19019
+ "updated_at",
19020
+ "fact_type",
19021
+ "event_time_start",
19022
+ "event_time_end",
19023
+ "event_time_kind",
19024
+ "archived_at",
19025
+ "trust_tier",
19026
+ "superseded_by",
19027
+ "visibility",
19028
+ "published_at",
19029
+ "geohash",
19030
+ "topics",
19031
+ "topics_user_managed",
19032
+ "media",
19033
+ "source"
19034
+ ];
19035
+ const embedInBackground = (memory) => {
19036
+ eagerEmbedContent(
19037
+ memory.content,
19038
+ options.embeddingOptions,
19039
+ vaultCache,
19040
+ vaultCtx,
19041
+ memory.uniqueId,
19042
+ memory.updatedAt
19043
+ ).catch((err) => {
19044
+ getLogger().warn("[memory/store] Failed to embed memory:", err);
19045
+ });
19046
+ };
19047
+ return {
19048
+ list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
19049
+ get: (id) => getVaultMemoryOp(vaultCtx, id),
19050
+ listArchived: async () => {
19051
+ const ids = await vaultCtx.vaultMemoryCollection.query(Q8.where("is_deleted", false), Q8.where("archived_at", Q8.notEq(null)), ...ownedBy).fetchIds();
19052
+ if (ids.length === 0) return [];
19053
+ const rows = await getAllVaultMemoriesOp(vaultCtx, { memoryIds: ids, includeArchived: true });
19054
+ return rows.sort((a, b) => (b.archivedAt ?? 0) - (a.archivedAt ?? 0));
19055
+ },
19056
+ memoriesByTopics: (names) => getMemoriesByEntityNamesOp(entityCtx, names),
19057
+ topicsByMemories: (memoryIds) => getEntitiesByMemoryIdsOp(entityCtx, memoryIds),
19058
+ create: async (input) => {
19059
+ const created = await createVaultMemoryOp(vaultCtx, input);
19060
+ if (input.embedding === void 0) embedInBackground(created);
19061
+ return created;
19062
+ },
19063
+ createMany: async (inputs) => {
19064
+ const created = await createVaultMemoriesBatchOp(vaultCtx, inputs);
19065
+ created.forEach((memory, i) => {
19066
+ if (inputs[i].embedding === void 0) embedInBackground(memory);
19067
+ });
19068
+ return created;
19069
+ },
19070
+ update: async (id, patch) => {
19071
+ const reembed = patch.embedding === void 0;
19072
+ const updated = await updateVaultMemoryOp(
19073
+ vaultCtx,
19074
+ id,
19075
+ reembed ? { ...patch, embedding: null } : patch
19076
+ );
19077
+ if (!updated) return null;
19078
+ vaultCache.delete(id);
19079
+ if (reembed) embedInBackground(updated);
19080
+ return updated;
19081
+ },
19082
+ delete: async (id) => {
19083
+ const deleted = await deleteVaultMemoryOp(vaultCtx, id);
19084
+ if (deleted) vaultCache.delete(id);
19085
+ return deleted;
19086
+ },
19087
+ supersede: (id, supersededById) => supersedeVaultMemoryOp(vaultCtx, id, supersededById),
19088
+ archive: (id) => archiveVaultMemoryOp(vaultCtx, id),
19089
+ restore: (id) => restoreVaultMemoryOp(vaultCtx, id),
19090
+ setTopics: (memoryId, topics) => setMemoryEntitiesOp(vaultCtx, memoryId, topics),
19091
+ addTopics: (memoryId, topics) => database.write(async (writer) => {
19092
+ const owned = await vaultCtx.vaultMemoryCollection.query(Q8.where("id", memoryId), Q8.where("is_deleted", false), ...ownedBy).fetchCount();
19093
+ return owned > 0 ? writer.callWriter(() => linkMemoryEntitiesOp(entityCtx, memoryId, topics)) : [];
19094
+ }),
19095
+ setVisibility: (id, visibility, visibilityOptions) => setMemoryVisibilityOp(vaultCtx, id, { visibility, ...visibilityOptions }),
19096
+ recall: (query, recallOptions) => recall(
19097
+ query,
19098
+ { vaultCtx, entityCtx, embeddingOptions: options.embeddingOptions, vaultCache },
19099
+ { ...recallOptions, types: ["fact"] }
19100
+ ),
19101
+ retain: (content, retainOptions) => retain(
19102
+ content,
19103
+ { vaultCtx, embeddingOptions: options.embeddingOptions, vaultCache },
19104
+ retainOptions
19105
+ ),
19106
+ subscribe: (onChange, subscribeOptions) => {
19107
+ const afterFirst = () => {
19108
+ let first = true;
19109
+ return () => {
19110
+ if (first) first = false;
19111
+ else onChange();
19112
+ };
19113
+ };
19114
+ const memories = subscribeOptions?.includeDeleted ? vaultCtx.vaultMemoryCollection.query(...ownedBy).observe() : vaultCtx.vaultMemoryCollection.query(Q8.where("is_deleted", false), ...ownedBy).observeWithColumns(
19115
+ subscribeOptions?.embeddings ? [...watchedColumns, "embedding", "embedding_model"] : watchedColumns
19116
+ );
19117
+ const subscriptions = [memories.subscribe(afterFirst())];
19118
+ if (subscribeOptions?.topics) {
19119
+ const linksOwnedBy = userId === void 0 ? [] : options.allowUnscopedRows ? [Q8.or(Q8.where("user_id", userId), Q8.where("user_id", null))] : [Q8.where("user_id", userId)];
19120
+ subscriptions.push(
19121
+ entityCtx.memoryEntityCollection.query(...linksOwnedBy).observe().subscribe(afterFirst())
19122
+ );
19123
+ }
19124
+ return () => subscriptions.forEach((s) => s.unsubscribe());
19125
+ },
19126
+ maintenance: {
19127
+ createDecaySweeper: (sweeperOptions) => createDecaySweeper({ ...sweeperOptions, vaultCtx }),
19128
+ getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
19129
+ extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
19130
+ stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
19131
+ relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
19132
+ backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
19133
+ }
19134
+ };
19135
+ }
19136
+
18755
19137
  // src/lib/memory/synthesizeProfile.ts
18756
19138
  var DEFAULT_SYNTHESIS_MODEL = "inclusionai/ling-2.6-flash";
18757
19139
  var DEFAULT_FACET_RECALL_LIMIT = 20;
@@ -19279,199 +19661,6 @@ function normalizeInterests(values) {
19279
19661
  return out.length > 0 ? out : void 0;
19280
19662
  }
19281
19663
 
19282
- // src/lib/memory/topicExtract.ts
19283
- var TOPIC_EXTRACTION_BATCH_SIZE = 10;
19284
- var MAX_CHARS_PER_MEMORY = 300;
19285
- var MAX_VOCABULARY_NAMES = 100;
19286
- var MAX_COMPLETION_TOKENS = 8192;
19287
- var SYSTEM_PROMPT5 = `You assign topics to saved memories for a personal memory system.
19288
-
19289
- Each memory is a short statement about the user. For each memory, list the NAMED entities it mentions \u2014 these become the memory's topics, used to connect related memories in a knowledge graph.
19290
-
19291
- Include only NAMED entities, skip generic/common nouns. A topic must identify a specific thing the user could recognize by name; if it would be equally true of thousands of unrelated memories, leave it out:
19292
- - SKIP bare generic nouns even when the memory capitalizes them or a calendar entry is titled that way \u2014 "Home", "Work", "Meeting", "Lunch", "Birthday", "Appointment", "Trip", "Today", "Friday", "March" are NOT topics.
19293
- - KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
19294
- - Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
19295
- ${ENTITY_KIND_GUIDELINES}
19296
-
19297
- Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
19298
- - exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
19299
- - "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
19300
- No prose.`;
19301
- async function extractEntitiesForMemories(memories, options) {
19302
- const out = /* @__PURE__ */ new Map();
19303
- if (memories.length === 0) return out;
19304
- const redactor = resolvePiiRedactor(options.piiRedaction);
19305
- const vocabulary = [];
19306
- for (const name of (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES)) {
19307
- vocabulary.push(redactor ? (await redactor.redactTextAsync(name)).text : name);
19308
- }
19309
- const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
19310
- When a memory refers to one of these, reuse the EXACT existing name (same spelling, casing, spacing and punctuation) instead of a variant \u2014 if "Foo.ai" is listed, never emit "Foo ai" or "Foo AI", and if "Jane Roe" is listed, emit that rather than a bare "Jane".
19311
-
19312
- ` : "";
19313
- for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
19314
- const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
19315
- const rows = [];
19316
- for (const m of batch) {
19317
- const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
19318
- rows.push(`${m.id}: ${redactor ? (await redactor.redactTextAsync(content)).text : content}`);
19319
- }
19320
- const listing = rows.join("\n");
19321
- const parsed = await callPortalJsonCompletion({
19322
- ...options.apiKey !== void 0 && { apiKey: options.apiKey },
19323
- ...options.getToken !== void 0 && { getToken: options.getToken },
19324
- ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
19325
- taskType: "memory_topic",
19326
- ...options.endpointOverride !== void 0 && {
19327
- endpointOverride: options.endpointOverride
19328
- },
19329
- model: options.model ?? DEFAULT_EXTRACTION_MODEL,
19330
- systemPrompt: SYSTEM_PROMPT5,
19331
- userMessage: `${vocabularyNote}Memories:
19332
- ${listing}
19333
-
19334
- List each memory's named entities.`,
19335
- tag: "memory/topics",
19336
- extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
19337
- ...options.fetchFn && { fetchFn: options.fetchFn },
19338
- ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
19339
- ...options.timeoutMs !== void 0 && { timeoutMs: options.timeoutMs },
19340
- ...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
19341
- ...options.backoffMs && { backoffMs: options.backoffMs }
19342
- });
19343
- if (parsed === null) {
19344
- getLogger().warn(
19345
- `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
19346
- );
19347
- continue;
19348
- }
19349
- const validIds = new Set(batch.map((m) => m.id));
19350
- const byId = parseTopicResponse(parsed, validIds);
19351
- if (byId === null) {
19352
- getLogger().warn(
19353
- `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
19354
- );
19355
- continue;
19356
- }
19357
- for (const m of batch) {
19358
- const entities = byId.get(m.id);
19359
- if (entities === void 0) {
19360
- continue;
19361
- }
19362
- out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
19363
- }
19364
- }
19365
- return out;
19366
- }
19367
- function restoreEntities(entities, redactor) {
19368
- return entities.map((e) => ({ kind: e.kind, restored: redactor.restoreForStorage(e.name) })).filter((e) => !e.restored.unresolved).map(
19369
- (e) => e.kind !== void 0 ? { name: e.restored.text, kind: e.kind } : { name: e.restored.text }
19370
- );
19371
- }
19372
- function parseTopicResponse(parsed, validIds) {
19373
- if (typeof parsed !== "object" || parsed === null) return null;
19374
- const list = parsed.memories;
19375
- if (!Array.isArray(list)) return null;
19376
- const out = /* @__PURE__ */ new Map();
19377
- for (const raw of list) {
19378
- if (typeof raw !== "object" || raw === null) continue;
19379
- const obj = raw;
19380
- if (typeof obj.id !== "string") continue;
19381
- const id = obj.id.trim().replace(/:$/, "").replace(/^\[([\s\S]*)\]$/, "$1").trim();
19382
- if (!validIds.has(id) || out.has(id)) continue;
19383
- const entities = Array.isArray(obj.entities) ? parseEntities(obj.entities) : [];
19384
- out.set(id, entities);
19385
- }
19386
- return out;
19387
- }
19388
- var DEGRADED_TOPIC_SKIP = {
19389
- excluded: false,
19390
- "link-declined": false,
19391
- "stamp-declined": false,
19392
- "llm-unanswered": true,
19393
- unreadable: true,
19394
- "not-found": true,
19395
- "link-failed": true
19396
- };
19397
- function isDegradedTopicSkip(reason) {
19398
- return DEGRADED_TOPIC_SKIP[reason] === true;
19399
- }
19400
- async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
19401
- const entityCtx = ctx.entityCtx;
19402
- if (!entityCtx) {
19403
- throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
19404
- }
19405
- const log = getLogger();
19406
- const extractedAt = options.now ?? Date.now();
19407
- const skippedIds = [];
19408
- const skippedReasons = /* @__PURE__ */ new Map();
19409
- const skip = (id, reason) => {
19410
- if (skippedReasons.has(id)) return;
19411
- skippedIds.push(id);
19412
- skippedReasons.set(id, reason);
19413
- };
19414
- const inputs = [];
19415
- for (const id of new Set(memoryIds)) {
19416
- let record;
19417
- try {
19418
- record = await ctx.vaultMemoryCollection.find(id);
19419
- } catch (err) {
19420
- log.warn("[memory/topics] vault lookup failed for extraction", err);
19421
- skip(id, "not-found");
19422
- continue;
19423
- }
19424
- if (record.isDeleted || ctx.userId !== void 0 && record.userId !== ctx.userId || record.topicsUserManaged) {
19425
- skip(id, "excluded");
19426
- continue;
19427
- }
19428
- try {
19429
- const stored = await vaultMemoryToStored(
19430
- record,
19431
- ctx.walletAddress,
19432
- ctx.signMessage,
19433
- ctx.embeddedWalletSigner
19434
- );
19435
- inputs.push({ id, content: stored.content });
19436
- } catch (err) {
19437
- log.warn("[memory/topics] failed to load memory for extraction", err);
19438
- skip(id, "unreadable");
19439
- }
19440
- }
19441
- const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
19442
- const toStamp = [];
19443
- for (const input of inputs) {
19444
- const entities = entitiesByMemory.get(input.id);
19445
- if (entities === void 0) {
19446
- skip(input.id, "llm-unanswered");
19447
- continue;
19448
- }
19449
- try {
19450
- const linked = await replaceMemoryEntitiesGuardedOp(entityCtx, input.id, entities);
19451
- if (linked === null) {
19452
- skip(input.id, "link-declined");
19453
- entitiesByMemory.delete(input.id);
19454
- continue;
19455
- }
19456
- } catch (err) {
19457
- log.warn("[memory/topics] replaceMemoryEntitiesGuardedOp failed", err);
19458
- skip(input.id, "link-failed");
19459
- entitiesByMemory.delete(input.id);
19460
- continue;
19461
- }
19462
- toStamp.push(input.id);
19463
- }
19464
- const stampedIds = await stampTopicsExtractedAtOp(ctx, toStamp, extractedAt);
19465
- const stampedSet = new Set(stampedIds);
19466
- for (const id of toStamp) {
19467
- if (!stampedSet.has(id)) {
19468
- skip(id, "stamp-declined");
19469
- entitiesByMemory.delete(id);
19470
- }
19471
- }
19472
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
19473
- }
19474
-
19475
19664
  // src/lib/memory/verifySupport.ts
19476
19665
  var DEFAULT_MODEL5 = "inclusionai/ling-2.6-flash";
19477
19666
  var DEFAULT_ATTEMPTS3 = 2;
@@ -19862,7 +20051,7 @@ async function assembleMemoryContext(options) {
19862
20051
  }
19863
20052
 
19864
20053
  // src/lib/memory/durableExtraction.ts
19865
- import { Q as Q8 } from "@nozbe/watermelondb";
20054
+ import { Q as Q9 } from "@nozbe/watermelondb";
19866
20055
  var MAX_JOBS_PER_PASS = 3;
19867
20056
  var MAX_ATTEMPTS = 3;
19868
20057
  var MAX_FAILED_SESSIONS = 3;
@@ -20009,7 +20198,7 @@ function createDurableAutoExtractor(options) {
20009
20198
  ...options.retainCtx,
20010
20199
  vaultCtx: {
20011
20200
  ...vault,
20012
- canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(Q8.where("conversation_id", conversationId), Q8.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(Q8.where("conversation_id", conversationId), Q8.where("id", Q8.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
20201
+ canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(Q9.where("conversation_id", conversationId), Q9.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(Q9.where("conversation_id", conversationId), Q9.where("id", Q9.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
20013
20202
  }
20014
20203
  },
20015
20204
  // The model follows the job's scope, not the mode alive at drain time.
@@ -20151,7 +20340,7 @@ function createDurableAutoExtractor(options) {
20151
20340
  let scheduleAfterSuccess = false;
20152
20341
  try {
20153
20342
  await writes;
20154
- const pending = await jobs.query(Q8.where("owner_key", owner), Q8.where("message_ids", Q8.notEq("[]"))).fetch();
20343
+ const pending = await jobs.query(Q9.where("owner_key", owner), Q9.where("message_ids", Q9.notEq("[]"))).fetch();
20155
20344
  storeFailures = 0;
20156
20345
  let processed = 0;
20157
20346
  for (const job of pending) {
@@ -20167,7 +20356,7 @@ function createDurableAutoExtractor(options) {
20167
20356
  let ids = [];
20168
20357
  let loaded = [];
20169
20358
  try {
20170
- const conversations = await storage.conversationsCollection.query(Q8.where("conversation_id", conversationId), Q8.where("is_deleted", false)).fetchCount();
20359
+ const conversations = await storage.conversationsCollection.query(Q9.where("conversation_id", conversationId), Q9.where("is_deleted", false)).fetchCount();
20171
20360
  if (!conversations) {
20172
20361
  await database.write(() => job.destroyPermanently());
20173
20362
  continue;
@@ -20284,12 +20473,12 @@ function createDurableAutoExtractor(options) {
20284
20473
  const scope = currentScope();
20285
20474
  writes = writes.then(
20286
20475
  () => database.write(async () => {
20287
- const existing = await jobs.query(Q8.where("owner_key", owner), Q8.where("conversation_id", conversationId)).fetch();
20476
+ const existing = await jobs.query(Q9.where("owner_key", owner), Q9.where("conversation_id", conversationId)).fetch();
20288
20477
  const job = existing.find(
20289
20478
  (candidate) => candidate._getRaw("scope") === scope && (candidate._getRaw("folder_id") ?? null) === folderId
20290
20479
  );
20291
20480
  const positions = new Map(ids.map((id, index) => [id, index]));
20292
- const rows = await storage.messagesCollection.query(Q8.where("id", Q8.oneOf(ids))).fetch();
20481
+ const rows = await storage.messagesCollection.query(Q9.where("id", Q9.oneOf(ids))).fetch();
20293
20482
  const sequences = /* @__PURE__ */ new Map();
20294
20483
  for (const row of rows) {
20295
20484
  const sequence = Number(row._getRaw("message_id"));
@@ -22719,6 +22908,8 @@ function useChatStorage(options) {
22719
22908
  onError,
22720
22909
  onServerToolCall,
22721
22910
  onToolCallArgumentsDelta,
22911
+ resumable,
22912
+ onStreamMeta,
22722
22913
  apiType,
22723
22914
  walletAddress,
22724
22915
  signMessage,
@@ -23283,6 +23474,8 @@ function useChatStorage(options) {
23283
23474
  onError,
23284
23475
  onServerToolCall,
23285
23476
  onToolCallArgumentsDelta,
23477
+ resumable,
23478
+ onStreamMeta,
23286
23479
  apiType,
23287
23480
  preProcessors,
23288
23481
  smoothing,
@@ -23705,6 +23898,7 @@ function useChatStorage(options) {
23705
23898
  return {
23706
23899
  data: result2.data,
23707
23900
  error: null,
23901
+ autoExecutedToolResults: "autoExecutedToolResults" in result2 ? result2.autoExecutedToolResults : void 0,
23708
23902
  skipped: true
23709
23903
  };
23710
23904
  }
@@ -25939,7 +26133,7 @@ function useDatabaseManager(walletAddress, manager) {
25939
26133
  }
25940
26134
 
25941
26135
  // src/lib/db/project/operations.ts
25942
- import { Q as Q9 } from "@nozbe/watermelondb";
26136
+ import { Q as Q10 } from "@nozbe/watermelondb";
25943
26137
 
25944
26138
  // src/lib/db/project/types.ts
25945
26139
  function generateProjectId() {
@@ -25970,15 +26164,15 @@ async function createProjectOp(ctx, opts, defaultName = "New Project") {
25970
26164
  return projectToStored(created);
25971
26165
  }
25972
26166
  async function getProjectOp(ctx, id) {
25973
- const results = await ctx.projectsCollection.query(Q9.where("project_id", id), Q9.where("is_deleted", false)).fetch();
26167
+ const results = await ctx.projectsCollection.query(Q10.where("project_id", id), Q10.where("is_deleted", false)).fetch();
25974
26168
  return results.length > 0 ? projectToStored(results[0]) : null;
25975
26169
  }
25976
26170
  async function getProjectsOp(ctx) {
25977
- const results = await ctx.projectsCollection.query(Q9.where("is_deleted", false), Q9.sortBy("created_at", Q9.desc)).fetch();
26171
+ const results = await ctx.projectsCollection.query(Q10.where("is_deleted", false), Q10.sortBy("created_at", Q10.desc)).fetch();
25978
26172
  return results.map(projectToStored);
25979
26173
  }
25980
26174
  async function updateProjectNameOp(ctx, id, name) {
25981
- const results = await ctx.projectsCollection.query(Q9.where("project_id", id), Q9.where("is_deleted", false)).fetch();
26175
+ const results = await ctx.projectsCollection.query(Q10.where("project_id", id), Q10.where("is_deleted", false)).fetch();
25982
26176
  if (results.length > 0) {
25983
26177
  await ctx.database.write(async () => {
25984
26178
  await results[0].update((proj) => {
@@ -25990,7 +26184,7 @@ async function updateProjectNameOp(ctx, id, name) {
25990
26184
  return false;
25991
26185
  }
25992
26186
  async function updateProjectOp(ctx, id, opts) {
25993
- const results = await ctx.projectsCollection.query(Q9.where("project_id", id), Q9.where("is_deleted", false)).fetch();
26187
+ const results = await ctx.projectsCollection.query(Q10.where("project_id", id), Q10.where("is_deleted", false)).fetch();
25994
26188
  if (results.length > 0) {
25995
26189
  await ctx.database.write(async () => {
25996
26190
  await results[0].update((proj) => {
@@ -26002,7 +26196,7 @@ async function updateProjectOp(ctx, id, opts) {
26002
26196
  return false;
26003
26197
  }
26004
26198
  async function deleteProjectOp(ctx, id) {
26005
- const results = await ctx.projectsCollection.query(Q9.where("project_id", id), Q9.where("is_deleted", false)).fetch();
26199
+ const results = await ctx.projectsCollection.query(Q10.where("project_id", id), Q10.where("is_deleted", false)).fetch();
26006
26200
  if (results.length > 0) {
26007
26201
  await ctx.database.write(async () => {
26008
26202
  await results[0].update((proj) => {
@@ -26015,18 +26209,18 @@ async function deleteProjectOp(ctx, id) {
26015
26209
  }
26016
26210
  async function getProjectConversationsOp(ctx, projectId) {
26017
26211
  const results = await ctx.conversationsCollection.query(
26018
- Q9.where("project_id", projectId),
26019
- Q9.where("is_deleted", false),
26020
- Q9.sortBy("created_at", Q9.desc)
26212
+ Q10.where("project_id", projectId),
26213
+ Q10.where("is_deleted", false),
26214
+ Q10.sortBy("created_at", Q10.desc)
26021
26215
  ).fetch();
26022
26216
  return results.map(conversationToStoredRaw);
26023
26217
  }
26024
26218
  async function getProjectConversationCountOp(ctx, projectId) {
26025
- return await ctx.conversationsCollection.query(Q9.where("project_id", projectId), Q9.where("is_deleted", false)).fetchCount();
26219
+ return await ctx.conversationsCollection.query(Q10.where("project_id", projectId), Q10.where("is_deleted", false)).fetchCount();
26026
26220
  }
26027
26221
 
26028
26222
  // src/react/useFiles.ts
26029
- import { Q as Q10 } from "@nozbe/watermelondb";
26223
+ import { Q as Q11 } from "@nozbe/watermelondb";
26030
26224
  import { useCallback as useCallback5, useEffect as useEffect4, useMemo as useMemo5, useState as useState5 } from "react";
26031
26225
  function useFiles(options) {
26032
26226
  const { database, walletAddress } = options;
@@ -26044,7 +26238,7 @@ function useFiles(options) {
26044
26238
  setIsLoading(true);
26045
26239
  try {
26046
26240
  const coll = database.get("media");
26047
- await coll.query(Q10.take(1)).fetch();
26241
+ await coll.query(Q11.take(1)).fetch();
26048
26242
  setMediaCollection(coll);
26049
26243
  setIsReady(true);
26050
26244
  } catch (error) {
@@ -26452,7 +26646,7 @@ function useFiles(options) {
26452
26646
  }
26453
26647
 
26454
26648
  // src/react/useProjects.ts
26455
- import { Q as Q11 } from "@nozbe/watermelondb";
26649
+ import { Q as Q12 } from "@nozbe/watermelondb";
26456
26650
  import { useCallback as useCallback6, useEffect as useEffect5, useMemo as useMemo6, useState as useState6 } from "react";
26457
26651
  function useProjects(options) {
26458
26652
  const { database, initialProjectId } = options;
@@ -26474,7 +26668,7 @@ function useProjects(options) {
26474
26668
  conversations: convColl?.table
26475
26669
  });
26476
26670
  getLogger().debug("[useProjects] Running test query on projects table...");
26477
- const testResult = await projColl.query(Q11.take(1)).fetch();
26671
+ const testResult = await projColl.query(Q12.take(1)).fetch();
26478
26672
  getLogger().debug(
26479
26673
  "[useProjects] Test query succeeded, found",
26480
26674
  testResult.length,
@@ -26561,7 +26755,7 @@ function useProjects(options) {
26561
26755
  if (!projectsCollection) {
26562
26756
  return;
26563
26757
  }
26564
- const subscription = projectsCollection.query(Q11.where("is_deleted", false)).observe().subscribe((records) => {
26758
+ const subscription = projectsCollection.query(Q12.where("is_deleted", false)).observe().subscribe((records) => {
26565
26759
  setProjects(records.map(projectToStored));
26566
26760
  });
26567
26761
  return () => subscription.unsubscribe();
@@ -26684,7 +26878,7 @@ function useProjects(options) {
26684
26878
  }
26685
26879
 
26686
26880
  // src/lib/db/settings/operations.ts
26687
- import { Q as Q12 } from "@nozbe/watermelondb";
26881
+ import { Q as Q13 } from "@nozbe/watermelondb";
26688
26882
  function modelPreferenceToStored(preference) {
26689
26883
  return {
26690
26884
  uniqueId: preference.id,
@@ -26693,12 +26887,12 @@ function modelPreferenceToStored(preference) {
26693
26887
  };
26694
26888
  }
26695
26889
  async function getModelPreferenceOp(ctx, walletAddress) {
26696
- const results = await ctx.modelPreferencesCollection.query(Q12.where("wallet_address", walletAddress)).fetch();
26890
+ const results = await ctx.modelPreferencesCollection.query(Q13.where("wallet_address", walletAddress)).fetch();
26697
26891
  return results.length > 0 ? modelPreferenceToStored(results[0]) : null;
26698
26892
  }
26699
26893
  async function setModelPreferenceOp(ctx, walletAddress, models) {
26700
26894
  const result = await ctx.database.write(async () => {
26701
- const results = await ctx.modelPreferencesCollection.query(Q12.where("wallet_address", walletAddress)).fetch();
26895
+ const results = await ctx.modelPreferencesCollection.query(Q13.where("wallet_address", walletAddress)).fetch();
26702
26896
  if (results.length > 0) {
26703
26897
  const preference = results[0];
26704
26898
  await preference.update((pref) => {
@@ -26716,7 +26910,7 @@ async function setModelPreferenceOp(ctx, walletAddress, models) {
26716
26910
  return modelPreferenceToStored(result);
26717
26911
  }
26718
26912
  async function deleteModelPreferenceOp(ctx, walletAddress) {
26719
- const results = await ctx.modelPreferencesCollection.query(Q12.where("wallet_address", walletAddress)).fetch();
26913
+ const results = await ctx.modelPreferencesCollection.query(Q13.where("wallet_address", walletAddress)).fetch();
26720
26914
  if (results.length === 0) return false;
26721
26915
  await ctx.database.write(async () => {
26722
26916
  await results[0].destroyPermanently();
@@ -26740,7 +26934,7 @@ var settingsStorageSchema = appSchema3({
26740
26934
  });
26741
26935
 
26742
26936
  // src/lib/db/vaultFolders/operations.ts
26743
- import { Q as Q13 } from "@nozbe/watermelondb";
26937
+ import { Q as Q14 } from "@nozbe/watermelondb";
26744
26938
  function folderToStored(folder) {
26745
26939
  return {
26746
26940
  uniqueId: folder.id,
@@ -26765,7 +26959,7 @@ async function createVaultFolderOp(ctx, opts) {
26765
26959
  return folderToStored(created);
26766
26960
  }
26767
26961
  async function getAllVaultFoldersOp(ctx) {
26768
- const results = await ctx.vaultFolderCollection.query(Q13.where("is_deleted", false), Q13.sortBy("created_at", Q13.desc)).fetch();
26962
+ const results = await ctx.vaultFolderCollection.query(Q14.where("is_deleted", false), Q14.sortBy("created_at", Q14.desc)).fetch();
26769
26963
  return results.map(folderToStored);
26770
26964
  }
26771
26965
  async function updateVaultFolderOp(ctx, id, opts) {
@@ -26774,7 +26968,7 @@ async function updateVaultFolderOp(ctx, id, opts) {
26774
26968
  if (record.isDeleted) return null;
26775
26969
  const scopeChanged = opts.scope !== void 0 && opts.scope !== record.scope;
26776
26970
  const updated = await ctx.database.write(async () => {
26777
- const memories = scopeChanged ? await ctx.vaultMemoryCollection.query(Q13.where("folder_id", id), Q13.where("is_deleted", false)).fetch() : [];
26971
+ const memories = scopeChanged ? await ctx.vaultMemoryCollection.query(Q14.where("folder_id", id), Q14.where("is_deleted", false)).fetch() : [];
26778
26972
  const updates = [];
26779
26973
  updates.push(
26780
26974
  record.prepareUpdate((r) => {
@@ -26802,7 +26996,7 @@ async function deleteVaultFolderOp(ctx, id) {
26802
26996
  const record = await ctx.vaultFolderCollection.find(id);
26803
26997
  if (record.isDeleted) return false;
26804
26998
  await ctx.database.write(async () => {
26805
- const memories = await ctx.vaultMemoryCollection.query(Q13.where("folder_id", id), Q13.where("is_deleted", false)).fetch();
26999
+ const memories = await ctx.vaultMemoryCollection.query(Q14.where("folder_id", id), Q14.where("is_deleted", false)).fetch();
26806
27000
  const preparedMemories = memories.map(
26807
27001
  (memory) => memory.prepareUpdate((r) => {
26808
27002
  r._setRaw("folder_id", null);
@@ -26871,7 +27065,7 @@ async function updateVaultFolderContextOp(ctx, id, context) {
26871
27065
  }
26872
27066
  }
26873
27067
  async function getVaultFolderMemoryCountOp(ctx, folderId) {
26874
- return await ctx.vaultMemoryCollection.query(Q13.where("folder_id", folderId), Q13.where("is_deleted", false)).fetchCount();
27068
+ return await ctx.vaultMemoryCollection.query(Q14.where("folder_id", folderId), Q14.where("is_deleted", false)).fetchCount();
26875
27069
  }
26876
27070
 
26877
27071
  // src/lib/db/vaultFolders/defaults.ts
@@ -26917,7 +27111,7 @@ async function _ensureDefaultFoldersImpl(ctx) {
26917
27111
  import { useCallback as useCallback7, useMemo as useMemo7, useSyncExternalStore } from "react";
26918
27112
 
26919
27113
  // src/lib/db/userPreferences/operations.ts
26920
- import { Q as Q14 } from "@nozbe/watermelondb";
27114
+ import { Q as Q15 } from "@nozbe/watermelondb";
26921
27115
  function userPreferenceToStored(preference) {
26922
27116
  return {
26923
27117
  uniqueId: preference.id,
@@ -26932,7 +27126,7 @@ function userPreferenceToStored(preference) {
26932
27126
  };
26933
27127
  }
26934
27128
  async function getUserPreferenceOp(ctx, walletAddress) {
26935
- const results = await ctx.userPreferencesCollection.query(Q14.where("wallet_address", walletAddress)).fetch();
27129
+ const results = await ctx.userPreferencesCollection.query(Q15.where("wallet_address", walletAddress)).fetch();
26936
27130
  return results.length > 0 ? userPreferenceToStored(results[0]) : null;
26937
27131
  }
26938
27132
  async function createUserPreferenceOp(ctx, opts) {
@@ -26952,7 +27146,7 @@ async function createUserPreferenceOp(ctx, opts) {
26952
27146
  return userPreferenceToStored(created);
26953
27147
  }
26954
27148
  async function updateUserPreferenceOp(ctx, walletAddress, opts) {
26955
- const results = await ctx.userPreferencesCollection.query(Q14.where("wallet_address", walletAddress)).fetch();
27149
+ const results = await ctx.userPreferencesCollection.query(Q15.where("wallet_address", walletAddress)).fetch();
26956
27150
  if (results.length === 0) return null;
26957
27151
  const preference = results[0];
26958
27152
  await ctx.database.write(async () => {
@@ -26980,7 +27174,7 @@ async function updateUserPreferenceOp(ctx, walletAddress, opts) {
26980
27174
  async function setUserPreferenceOp(ctx, walletAddress, opts) {
26981
27175
  const now = Date.now();
26982
27176
  const result = await ctx.database.write(async () => {
26983
- const results = await ctx.userPreferencesCollection.query(Q14.where("wallet_address", walletAddress)).fetch();
27177
+ const results = await ctx.userPreferencesCollection.query(Q15.where("wallet_address", walletAddress)).fetch();
26984
27178
  if (results.length > 0) {
26985
27179
  const preference = results[0];
26986
27180
  await preference.update((pref) => {
@@ -27028,7 +27222,7 @@ async function updateModelsOp(ctx, walletAddress, models) {
27028
27222
  return updateUserPreferenceOp(ctx, walletAddress, { models });
27029
27223
  }
27030
27224
  async function deleteUserPreferenceOp(ctx, walletAddress) {
27031
- const results = await ctx.userPreferencesCollection.query(Q14.where("wallet_address", walletAddress)).fetch();
27225
+ const results = await ctx.userPreferencesCollection.query(Q15.where("wallet_address", walletAddress)).fetch();
27032
27226
  if (results.length === 0) return false;
27033
27227
  await ctx.database.write(async () => {
27034
27228
  await results[0].destroyPermanently();
@@ -27043,9 +27237,9 @@ async function migrateFromModelPreferencesOp(ctx, walletAddress) {
27043
27237
  if (existing) {
27044
27238
  return existing;
27045
27239
  }
27046
- const modelResults = await ctx.modelPreferencesCollection.query(Q14.where("wallet_address", walletAddress)).fetch();
27240
+ const modelResults = await ctx.modelPreferencesCollection.query(Q15.where("wallet_address", walletAddress)).fetch();
27047
27241
  const personalityKey = `${walletAddress}_personality_settings`;
27048
- const personalityResults = await ctx.modelPreferencesCollection.query(Q14.where("wallet_address", personalityKey)).fetch();
27242
+ const personalityResults = await ctx.modelPreferencesCollection.query(Q15.where("wallet_address", personalityKey)).fetch();
27049
27243
  if (modelResults.length === 0 && personalityResults.length === 0) {
27050
27244
  return null;
27051
27245
  }
@@ -27151,7 +27345,7 @@ var SLIDER_CONFIG = [
27151
27345
  ];
27152
27346
 
27153
27347
  // src/react/userSettingsStore.ts
27154
- import { Q as Q15 } from "@nozbe/watermelondb";
27348
+ import { Q as Q16 } from "@nozbe/watermelondb";
27155
27349
  var EMPTY_SNAPSHOT = Object.freeze({
27156
27350
  modelPreference: null,
27157
27351
  userPreference: null,
@@ -27213,7 +27407,7 @@ async function startLoad(entry, walletAddress) {
27213
27407
  }
27214
27408
  function startObserve(entry, walletAddress) {
27215
27409
  let isInitialEmission = true;
27216
- entry.observeSub = entry.storageCtx.userPreferencesCollection.query(Q15.where("wallet_address", walletAddress)).observeWithColumns([
27410
+ entry.observeSub = entry.storageCtx.userPreferencesCollection.query(Q16.where("wallet_address", walletAddress)).observeWithColumns([
27217
27411
  "nickname",
27218
27412
  "occupation",
27219
27413
  "description",
@@ -27477,7 +27671,7 @@ function useSettings(options) {
27477
27671
  }
27478
27672
 
27479
27673
  // src/lib/db/appFiles/operations.ts
27480
- import { Q as Q16 } from "@nozbe/watermelondb";
27674
+ import { Q as Q17 } from "@nozbe/watermelondb";
27481
27675
 
27482
27676
  // src/utils/paths.ts
27483
27677
  function normalizePath(p) {
@@ -27503,7 +27697,7 @@ async function putAppFileOp(ctx, conversationId, path, content) {
27503
27697
  const normalized = normalizePath(path);
27504
27698
  const now = Date.now();
27505
27699
  const result = await ctx.database.write(async () => {
27506
- const existing = await ctx.appFilesCollection.query(Q16.where("conversation_id", conversationId), Q16.where("path", normalized)).fetch();
27700
+ const existing = await ctx.appFilesCollection.query(Q17.where("conversation_id", conversationId), Q17.where("path", normalized)).fetch();
27507
27701
  if (existing.length > 0) {
27508
27702
  await existing[0].update((f) => {
27509
27703
  f._setRaw("content", content);
@@ -27523,11 +27717,11 @@ async function putAppFileOp(ctx, conversationId, path, content) {
27523
27717
  }
27524
27718
  async function getAppFileOp(ctx, conversationId, path) {
27525
27719
  const normalized = normalizePath(path);
27526
- const results = await ctx.appFilesCollection.query(Q16.where("conversation_id", conversationId), Q16.where("path", normalized)).fetch();
27720
+ const results = await ctx.appFilesCollection.query(Q17.where("conversation_id", conversationId), Q17.where("path", normalized)).fetch();
27527
27721
  return results.length > 0 ? appFileToStored(results[0]) : null;
27528
27722
  }
27529
27723
  async function getAppFilesOp(ctx, conversationId) {
27530
- const results = await ctx.appFilesCollection.query(Q16.where("conversation_id", conversationId)).fetch();
27724
+ const results = await ctx.appFilesCollection.query(Q17.where("conversation_id", conversationId)).fetch();
27531
27725
  return results.map(appFileToStored);
27532
27726
  }
27533
27727
  async function getAppFileMapOp(ctx, conversationId) {
@@ -27538,7 +27732,7 @@ async function getAppFileMapOp(ctx, conversationId) {
27538
27732
  }
27539
27733
  async function deleteAppFileOp(ctx, conversationId, path) {
27540
27734
  const normalized = normalizePath(path);
27541
- const results = await ctx.appFilesCollection.query(Q16.where("conversation_id", conversationId), Q16.where("path", normalized)).fetch();
27735
+ const results = await ctx.appFilesCollection.query(Q17.where("conversation_id", conversationId), Q17.where("path", normalized)).fetch();
27542
27736
  if (results.length === 0) return false;
27543
27737
  await ctx.database.write(async () => {
27544
27738
  await results[0].destroyPermanently();
@@ -27546,7 +27740,7 @@ async function deleteAppFileOp(ctx, conversationId, path) {
27546
27740
  return true;
27547
27741
  }
27548
27742
  async function deleteAllAppFilesOp(ctx, conversationId) {
27549
- const results = await ctx.appFilesCollection.query(Q16.where("conversation_id", conversationId)).fetch();
27743
+ const results = await ctx.appFilesCollection.query(Q17.where("conversation_id", conversationId)).fetch();
27550
27744
  if (results.length === 0) return;
27551
27745
  await ctx.database.write(async () => {
27552
27746
  const batch = results.map((file) => file.prepareDestroyPermanently());
@@ -27555,7 +27749,7 @@ async function deleteAllAppFilesOp(ctx, conversationId) {
27555
27749
  }
27556
27750
 
27557
27751
  // src/lib/db/conversationMemory/operations.ts
27558
- import { Q as Q17 } from "@nozbe/watermelondb";
27752
+ import { Q as Q18 } from "@nozbe/watermelondb";
27559
27753
  var MAX_PER_CONVERSATION = 200;
27560
27754
  function conversationMemoryToStored(row) {
27561
27755
  return {
@@ -27570,7 +27764,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
27570
27764
  if (!conversationId || items.length === 0) return;
27571
27765
  const now = Date.now();
27572
27766
  await ctx.database.write(async () => {
27573
- const existing = await ctx.conversationMemoryCollection.query(Q17.where("conversation_id", conversationId), Q17.sortBy("created_at", Q17.asc)).fetch();
27767
+ const existing = await ctx.conversationMemoryCollection.query(Q18.where("conversation_id", conversationId), Q18.sortBy("created_at", Q18.asc)).fetch();
27574
27768
  const seen = new Set(existing.map((r) => r.memoryId));
27575
27769
  const batchSeen = /* @__PURE__ */ new Set();
27576
27770
  const deduped = [];
@@ -27605,20 +27799,20 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
27605
27799
  }
27606
27800
  async function getConversationMemoriesOp(ctx, conversationId) {
27607
27801
  if (!conversationId) return [];
27608
- const rows = await ctx.conversationMemoryCollection.query(Q17.where("conversation_id", conversationId), Q17.sortBy("created_at", Q17.asc)).fetch();
27802
+ const rows = await ctx.conversationMemoryCollection.query(Q18.where("conversation_id", conversationId), Q18.sortBy("created_at", Q18.asc)).fetch();
27609
27803
  return rows.map(conversationMemoryToStored);
27610
27804
  }
27611
27805
  async function clearConversationMemoriesOp(ctx, conversationId) {
27612
27806
  if (!conversationId) return;
27613
27807
  await ctx.database.write(async () => {
27614
- const rows = await ctx.conversationMemoryCollection.query(Q17.where("conversation_id", conversationId)).fetch();
27808
+ const rows = await ctx.conversationMemoryCollection.query(Q18.where("conversation_id", conversationId)).fetch();
27615
27809
  if (rows.length === 0) return;
27616
27810
  await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
27617
27811
  });
27618
27812
  }
27619
27813
 
27620
27814
  // src/lib/db/savedTools/operations.ts
27621
- import { Q as Q18 } from "@nozbe/watermelondb";
27815
+ import { Q as Q19 } from "@nozbe/watermelondb";
27622
27816
  function savedToolToStored(tool) {
27623
27817
  return {
27624
27818
  uniqueId: tool.id,
@@ -27651,7 +27845,7 @@ async function createSavedToolOp(ctx, opts) {
27651
27845
  return savedToolToStored(created);
27652
27846
  }
27653
27847
  async function getAllSavedToolsOp(ctx) {
27654
- const results = await ctx.savedToolsCollection.query(Q18.where("is_deleted", false), Q18.sortBy("created_at", Q18.desc)).fetch();
27848
+ const results = await ctx.savedToolsCollection.query(Q19.where("is_deleted", false), Q19.sortBy("created_at", Q19.desc)).fetch();
27655
27849
  return results.map(savedToolToStored);
27656
27850
  }
27657
27851
  async function getSavedToolByIdOp(ctx, uniqueId) {
@@ -30307,8 +30501,8 @@ function createDropboxFileIndex(backupFolder) {
30307
30501
  };
30308
30502
  }
30309
30503
  async function readLocalUpdatedAt(database, conversationId) {
30310
- const { Q: Q19 } = await import("@nozbe/watermelondb");
30311
- const records = await database.get("conversations").query(Q19.where("conversation_id", conversationId)).fetch();
30504
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
30505
+ const records = await database.get("conversations").query(Q20.where("conversation_id", conversationId)).fetch();
30312
30506
  const match = records.map(conversationToStoredRaw).find((c) => c.conversationId === conversationId);
30313
30507
  return match ? match.updatedAt : null;
30314
30508
  }
@@ -30364,9 +30558,9 @@ async function pushConversationToDropbox(database, conversationId, userAddress,
30364
30558
  }
30365
30559
  async function performDropboxExport(database, userAddress, token, deps, onProgress, backupFolder = DEFAULT_BACKUP_FOLDER) {
30366
30560
  await deps.requestEncryptionKey(userAddress);
30367
- const { Q: Q19 } = await import("@nozbe/watermelondb");
30561
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
30368
30562
  const conversationsCollection = database.get("conversations");
30369
- const records = await conversationsCollection.query(Q19.where("is_deleted", false)).fetch();
30563
+ const records = await conversationsCollection.query(Q20.where("is_deleted", false)).fetch();
30370
30564
  const conversations = records.map(conversationToStoredRaw);
30371
30565
  const total = conversations.length;
30372
30566
  if (total === 0) {
@@ -31048,8 +31242,8 @@ function createDriveFileIndex(folderId) {
31048
31242
  };
31049
31243
  }
31050
31244
  async function readLocalUpdatedAt2(database, conversationId) {
31051
- const { Q: Q19 } = await import("@nozbe/watermelondb");
31052
- const records = await database.get("conversations").query(Q19.where("conversation_id", conversationId)).fetch();
31245
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
31246
+ const records = await database.get("conversations").query(Q20.where("conversation_id", conversationId)).fetch();
31053
31247
  const match = records.map(conversationToStoredRaw).find((c) => c.conversationId === conversationId);
31054
31248
  return match ? match.updatedAt : null;
31055
31249
  }
@@ -31129,9 +31323,9 @@ async function performGoogleDriveExport(database, userAddress, token, deps, onPr
31129
31323
  }
31130
31324
  const { folderId, token: activeToken } = folderResult;
31131
31325
  const fileIndex = createDriveFileIndex(folderId);
31132
- const { Q: Q19 } = await import("@nozbe/watermelondb");
31326
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
31133
31327
  const conversationsCollection = database.get("conversations");
31134
- const records = await conversationsCollection.query(Q19.where("is_deleted", false)).fetch();
31328
+ const records = await conversationsCollection.query(Q20.where("is_deleted", false)).fetch();
31135
31329
  const conversations = records.map(conversationToStoredRaw);
31136
31330
  const total = conversations.length;
31137
31331
  if (total === 0) {
@@ -31716,9 +31910,9 @@ async function pushConversationToICloud(database, conversationId, userAddress, d
31716
31910
  const filename = `${conversationId}.json`;
31717
31911
  const existingFile = await findICloudFile(filename);
31718
31912
  if (existingFile) {
31719
- const { Q: Q19 } = await import("@nozbe/watermelondb");
31913
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
31720
31914
  const conversationsCollection = database.get("conversations");
31721
- const records = await conversationsCollection.query(Q19.where("conversation_id", conversationId)).fetch();
31915
+ const records = await conversationsCollection.query(Q20.where("conversation_id", conversationId)).fetch();
31722
31916
  if (records.length > 0) {
31723
31917
  const conversation = conversationToStoredRaw(records[0]);
31724
31918
  const localUpdated = conversation.updatedAt.getTime();
@@ -31748,9 +31942,9 @@ async function pushConversationToICloud(database, conversationId, userAddress, d
31748
31942
  }
31749
31943
  async function performICloudExport(database, userAddress, deps, onProgress) {
31750
31944
  await deps.requestEncryptionKey(userAddress);
31751
- const { Q: Q19 } = await import("@nozbe/watermelondb");
31945
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
31752
31946
  const conversationsCollection = database.get("conversations");
31753
- const records = await conversationsCollection.query(Q19.where("is_deleted", false)).fetch();
31947
+ const records = await conversationsCollection.query(Q20.where("is_deleted", false)).fetch();
31754
31948
  const conversations = records.map(conversationToStoredRaw);
31755
31949
  const total = conversations.length;
31756
31950
  if (total === 0) {
@@ -35725,6 +35919,7 @@ export {
35725
35919
  createGitHubTools,
35726
35920
  createLlmDecayClassifier,
35727
35921
  createLlmNeighborRefiner,
35922
+ createLocalMemoryStore,
35728
35923
  createMediaBatchOp,
35729
35924
  createMediaOp,
35730
35925
  createMemoryEngineTool,
@@ -35783,6 +35978,7 @@ export {
35783
35978
  extractEntitiesForMemories,
35784
35979
  extractFacts,
35785
35980
  extractFileIds,
35981
+ extractMCPImageUrls,
35786
35982
  fileExists,
35787
35983
  findById,
35788
35984
  findMatchingTools,