@anuma/sdk 6.32.0 → 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.
@@ -12324,7 +12324,7 @@ var require_Query = __commonJS({
12324
12324
  var _subscribeToCount = _interopRequireDefault(require_subscribeToCount());
12325
12325
  var _subscribeToQuery = _interopRequireDefault(require_subscribeToQuery());
12326
12326
  var _subscribeToQueryWithColumns = _interopRequireDefault(require_subscribeToQueryWithColumns());
12327
- var Q11 = _interopRequireWildcard(require_QueryDescription());
12327
+ var Q12 = _interopRequireWildcard(require_QueryDescription());
12328
12328
  var _helpers = require_helpers2();
12329
12329
  var _class;
12330
12330
  var _descriptor;
@@ -12356,8 +12356,8 @@ var require_Query = __commonJS({
12356
12356
  (0, _initializerDefineProperty2.default)(this, "_cachedCountSubscribable", _descriptor2, this);
12357
12357
  (0, _initializerDefineProperty2.default)(this, "_cachedCountThrottledSubscribable", _descriptor3, this);
12358
12358
  this.collection = collection;
12359
- this._rawDescription = Q11.buildQueryDescription(clauses);
12360
- this.description = Q11.queryWithoutDeleted(this._rawDescription);
12359
+ this._rawDescription = Q12.buildQueryDescription(clauses);
12360
+ this.description = Q12.queryWithoutDeleted(this._rawDescription);
12361
12361
  }
12362
12362
  var _proto = Query2.prototype;
12363
12363
  _proto.extend = function(...args) {
@@ -12376,12 +12376,12 @@ var require_Query = __commonJS({
12376
12376
  sql
12377
12377
  } = this._rawDescription;
12378
12378
  (0, _invariant.default)(!sql, "Cannot extend an unsafe SQL query");
12379
- return new Query2(collection, [Q11.experimentalJoinTables(joinTables)].concat((0, _toConsumableArray2.default)(nestedJoinTables.map(function({
12379
+ return new Query2(collection, [Q12.experimentalJoinTables(joinTables)].concat((0, _toConsumableArray2.default)(nestedJoinTables.map(function({
12380
12380
  from,
12381
12381
  to
12382
12382
  }) {
12383
- return Q11.experimentalNestedJoin(from, to);
12384
- })), (0, _toConsumableArray2.default)(where), (0, _toConsumableArray2.default)(sortBy), (0, _toConsumableArray2.default)(take ? [Q11.take(take)] : []), (0, _toConsumableArray2.default)(skip ? [Q11.skip(skip)] : []), (0, _toConsumableArray2.default)(lokiTransform ? [Q11.unsafeLokiTransform(lokiTransform)] : []), (0, _toConsumableArray2.default)(clauses)));
12383
+ return Q12.experimentalNestedJoin(from, to);
12384
+ })), (0, _toConsumableArray2.default)(where), (0, _toConsumableArray2.default)(sortBy), (0, _toConsumableArray2.default)(take ? [Q12.take(take)] : []), (0, _toConsumableArray2.default)(skip ? [Q12.skip(skip)] : []), (0, _toConsumableArray2.default)(lokiTransform ? [Q12.unsafeLokiTransform(lokiTransform)] : []), (0, _toConsumableArray2.default)(clauses)));
12385
12385
  };
12386
12386
  _proto.pipe = function(transform) {
12387
12387
  return transform(this);
@@ -13823,7 +13823,7 @@ var require_helpers5 = __commonJS({
13823
13823
  exports.createTimestampsFor = void 0;
13824
13824
  exports.fetchDescendants = fetchDescendants;
13825
13825
  var _fp = require_fp();
13826
- var Q11 = _interopRequireWildcard(require_QueryDescription());
13826
+ var Q12 = _interopRequireWildcard(require_QueryDescription());
13827
13827
  function _getRequireWildcardCache(e) {
13828
13828
  if ("function" != typeof WeakMap) return null;
13829
13829
  var r = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap();
@@ -13861,7 +13861,7 @@ var require_helpers5 = __commonJS({
13861
13861
  });
13862
13862
  var childrenQueries = hasManyAssociations.map(function([key, value]) {
13863
13863
  var childCollection = model.collections.get(key);
13864
- return childCollection.query(Q11.where(value.foreignKey, model.id));
13864
+ return childCollection.query(Q12.where(value.foreignKey, model.id));
13865
13865
  });
13866
13866
  return childrenQueries;
13867
13867
  }
@@ -14242,8 +14242,8 @@ var require_watermelondb = __commonJS({
14242
14242
  var _interopRequireDefault = require_interopRequireDefault();
14243
14243
  exports.__esModule = true;
14244
14244
  exports.tableSchema = exports.tableName = exports.localStorageKey = exports.columnName = exports.associations = exports.appSchema = exports.Relation = exports.Query = exports.Q = exports.Model = exports.Database = exports.Collection = void 0;
14245
- var Q11 = _interopRequireWildcard(require_QueryDescription());
14246
- exports.Q = Q11;
14245
+ var Q12 = _interopRequireWildcard(require_QueryDescription());
14246
+ exports.Q = Q12;
14247
14247
  var _Collection = _interopRequireDefault(require_Collection());
14248
14248
  exports.Collection = _Collection.default;
14249
14249
  var _Database = _interopRequireDefault(require_Database());
@@ -14447,7 +14447,7 @@ var require_children = __commonJS({
14447
14447
  var _makeDecorator = _interopRequireDefault(require_makeDecorator());
14448
14448
  var _logError = _interopRequireDefault(require_logError());
14449
14449
  var _invariant = _interopRequireDefault(require_invariant());
14450
- var Q11 = _interopRequireWildcard(require_QueryDescription());
14450
+ var Q12 = _interopRequireWildcard(require_QueryDescription());
14451
14451
  function _getRequireWildcardCache(e) {
14452
14452
  if ("function" != typeof WeakMap) return null;
14453
14453
  var r = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap();
@@ -14481,7 +14481,7 @@ var require_children = __commonJS({
14481
14481
  var childCollection = model.collections.get(childTable);
14482
14482
  var association = model.constructor.associations[childTable];
14483
14483
  (0, _invariant.default)(association && "has_many" === association.type, "@children decorator used for a table that's not has_many");
14484
- var query = childCollection.query(Q11.where(association.foreignKey, model.id));
14484
+ var query = childCollection.query(Q12.where(association.foreignKey, model.id));
14485
14485
  that._childrenQueryCache[childTable] = query;
14486
14486
  return query;
14487
14487
  },
@@ -21009,6 +21009,51 @@ async function updateVaultMemoryOp(ctx, id, opts) {
21009
21009
  return null;
21010
21010
  }
21011
21011
  }
21012
+ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
21013
+ const entityCtx = ctx.entityCtx;
21014
+ if (!entityCtx) {
21015
+ throw new Error("setMemoryEntitiesOp requires ctx.entityCtx (entity collections)");
21016
+ }
21017
+ let record;
21018
+ try {
21019
+ record = await ctx.vaultMemoryCollection.find(memoryId);
21020
+ } catch {
21021
+ return null;
21022
+ }
21023
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return null;
21024
+ let stale = false;
21025
+ const originalUpdatedAt = record.updatedAt.getTime();
21026
+ await ctx.database.write(async (writer) => {
21027
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
21028
+ stale = true;
21029
+ return;
21030
+ }
21031
+ await record.update((r) => {
21032
+ r._setRaw("topics_user_managed", true);
21033
+ r._setRaw("updated_at", originalUpdatedAt);
21034
+ });
21035
+ const linked = entities.length > 0 ? await writer.callWriter(
21036
+ () => linkMemoryEntitiesOp(entityCtx, memoryId, entities, { topicsSource: "user" })
21037
+ ) : [];
21038
+ const keep = new Set(linked.map((e) => e.uniqueId));
21039
+ const existing = await entityCtx.memoryEntityCollection.query(import_watermelondb18.Q.where("memory_id", memoryId)).fetch();
21040
+ const staleLinks = existing.filter((l) => !keep.has(String(l.entityId)));
21041
+ if (staleLinks.length === 0 && entities.length > 0) return;
21042
+ const topicsWrite = await resolveMemoryTopicsWrite(
21043
+ entityCtx,
21044
+ memoryId,
21045
+ linked,
21046
+ entities,
21047
+ "user"
21048
+ );
21049
+ await ctx.database.batch(
21050
+ ...staleLinks.map((l) => l.prepareDestroyPermanently()),
21051
+ ...topicsWrite ? [prepareMemoryTopicsUpdate(topicsWrite)] : []
21052
+ );
21053
+ });
21054
+ if (stale) return null;
21055
+ return vaultMemoryToStored(record, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
21056
+ }
21012
21057
  async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
21013
21058
  let record;
21014
21059
  try {
@@ -25502,7 +25547,10 @@ async function eagerEmbedContent(content, embeddingOptions, cache, vaultCtx, mem
25502
25547
  if (memoryId) cacheRowVector(cache, memoryId, Float32Array.from(embedding), updatedAt, content);
25503
25548
  if (vaultCtx && memoryId) {
25504
25549
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
25505
- updateVaultMemoryEmbeddingOp(vaultCtx, memoryId, JSON.stringify(embedding), currentModel).catch(
25550
+ updateVaultMemoryEmbeddingOp(vaultCtx, memoryId, JSON.stringify(embedding), currentModel, {
25551
+ content,
25552
+ ...updatedAt !== void 0 && { updatedAt: updatedAt.getTime() }
25553
+ }).catch(
25506
25554
  // Silently swallow – SDK must not use console.*; embedding will be retried on next search
25507
25555
  () => {
25508
25556
  }
@@ -29785,6 +29833,349 @@ function parseAnswer(body, base, parseSchema) {
29785
29833
  };
29786
29834
  }
29787
29835
 
29836
+ // src/lib/memory/store/local.ts
29837
+ var import_watermelondb24 = __toESM(require_watermelondb());
29838
+
29839
+ // src/lib/memory/topicExtract.ts
29840
+ var TOPIC_EXTRACTION_BATCH_SIZE = 10;
29841
+ var MAX_CHARS_PER_MEMORY = 300;
29842
+ var MAX_VOCABULARY_NAMES = 100;
29843
+ var MAX_COMPLETION_TOKENS = 8192;
29844
+ var SYSTEM_PROMPT5 = `You assign topics to saved memories for a personal memory system.
29845
+
29846
+ 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.
29847
+
29848
+ 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:
29849
+ - 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.
29850
+ - KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
29851
+ - Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
29852
+ ${ENTITY_KIND_GUIDELINES}
29853
+
29854
+ Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
29855
+ - exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
29856
+ - "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
29857
+ No prose.`;
29858
+ async function extractEntitiesForMemories(memories, options) {
29859
+ const out = /* @__PURE__ */ new Map();
29860
+ if (memories.length === 0) return out;
29861
+ const redactor = resolvePiiRedactor(options.piiRedaction);
29862
+ const vocabulary = [];
29863
+ for (const name of (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES)) {
29864
+ vocabulary.push(redactor ? (await redactor.redactTextAsync(name)).text : name);
29865
+ }
29866
+ const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
29867
+ 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".
29868
+
29869
+ ` : "";
29870
+ for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
29871
+ const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
29872
+ const rows = [];
29873
+ for (const m of batch) {
29874
+ const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
29875
+ rows.push(`${m.id}: ${redactor ? (await redactor.redactTextAsync(content)).text : content}`);
29876
+ }
29877
+ const listing = rows.join("\n");
29878
+ const parsed = await callPortalJsonCompletion({
29879
+ ...options.apiKey !== void 0 && { apiKey: options.apiKey },
29880
+ ...options.getToken !== void 0 && { getToken: options.getToken },
29881
+ ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
29882
+ taskType: "memory_topic",
29883
+ ...options.endpointOverride !== void 0 && {
29884
+ endpointOverride: options.endpointOverride
29885
+ },
29886
+ model: options.model ?? DEFAULT_EXTRACTION_MODEL,
29887
+ systemPrompt: SYSTEM_PROMPT5,
29888
+ userMessage: `${vocabularyNote}Memories:
29889
+ ${listing}
29890
+
29891
+ List each memory's named entities.`,
29892
+ tag: "memory/topics",
29893
+ extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
29894
+ ...options.fetchFn && { fetchFn: options.fetchFn },
29895
+ ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
29896
+ ...options.timeoutMs !== void 0 && { timeoutMs: options.timeoutMs },
29897
+ ...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
29898
+ ...options.backoffMs && { backoffMs: options.backoffMs }
29899
+ });
29900
+ if (parsed === null) {
29901
+ getLogger().warn(
29902
+ `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
29903
+ );
29904
+ continue;
29905
+ }
29906
+ const validIds = new Set(batch.map((m) => m.id));
29907
+ const byId = parseTopicResponse(parsed, validIds);
29908
+ if (byId === null) {
29909
+ getLogger().warn(
29910
+ `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
29911
+ );
29912
+ continue;
29913
+ }
29914
+ for (const m of batch) {
29915
+ const entities = byId.get(m.id);
29916
+ if (entities === void 0) {
29917
+ continue;
29918
+ }
29919
+ out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
29920
+ }
29921
+ }
29922
+ return out;
29923
+ }
29924
+ function restoreEntities(entities, redactor) {
29925
+ return entities.map((e) => ({ kind: e.kind, restored: redactor.restoreForStorage(e.name) })).filter((e) => !e.restored.unresolved).map(
29926
+ (e) => e.kind !== void 0 ? { name: e.restored.text, kind: e.kind } : { name: e.restored.text }
29927
+ );
29928
+ }
29929
+ function parseTopicResponse(parsed, validIds) {
29930
+ if (typeof parsed !== "object" || parsed === null) return null;
29931
+ const list = parsed.memories;
29932
+ if (!Array.isArray(list)) return null;
29933
+ const out = /* @__PURE__ */ new Map();
29934
+ for (const raw of list) {
29935
+ if (typeof raw !== "object" || raw === null) continue;
29936
+ const obj = raw;
29937
+ if (typeof obj.id !== "string") continue;
29938
+ const id = obj.id.trim().replace(/:$/, "").replace(/^\[([\s\S]*)\]$/, "$1").trim();
29939
+ if (!validIds.has(id) || out.has(id)) continue;
29940
+ const entities = Array.isArray(obj.entities) ? parseEntities(obj.entities) : [];
29941
+ out.set(id, entities);
29942
+ }
29943
+ return out;
29944
+ }
29945
+ var DEGRADED_TOPIC_SKIP = {
29946
+ excluded: false,
29947
+ "link-declined": false,
29948
+ "stamp-declined": false,
29949
+ "llm-unanswered": true,
29950
+ unreadable: true,
29951
+ "not-found": true,
29952
+ "link-failed": true
29953
+ };
29954
+ function isDegradedTopicSkip(reason) {
29955
+ return DEGRADED_TOPIC_SKIP[reason] === true;
29956
+ }
29957
+ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
29958
+ const entityCtx = ctx.entityCtx;
29959
+ if (!entityCtx) {
29960
+ throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
29961
+ }
29962
+ const log = getLogger();
29963
+ const extractedAt = options.now ?? Date.now();
29964
+ const skippedIds = [];
29965
+ const skippedReasons = /* @__PURE__ */ new Map();
29966
+ const skip = (id, reason) => {
29967
+ if (skippedReasons.has(id)) return;
29968
+ skippedIds.push(id);
29969
+ skippedReasons.set(id, reason);
29970
+ };
29971
+ const inputs = [];
29972
+ for (const id of new Set(memoryIds)) {
29973
+ let record;
29974
+ try {
29975
+ record = await ctx.vaultMemoryCollection.find(id);
29976
+ } catch (err) {
29977
+ log.warn("[memory/topics] vault lookup failed for extraction", err);
29978
+ skip(id, "not-found");
29979
+ continue;
29980
+ }
29981
+ if (record.isDeleted || ctx.userId !== void 0 && record.userId !== ctx.userId || record.topicsUserManaged) {
29982
+ skip(id, "excluded");
29983
+ continue;
29984
+ }
29985
+ try {
29986
+ const stored = await vaultMemoryToStored(
29987
+ record,
29988
+ ctx.walletAddress,
29989
+ ctx.signMessage,
29990
+ ctx.embeddedWalletSigner
29991
+ );
29992
+ inputs.push({ id, content: stored.content });
29993
+ } catch (err) {
29994
+ log.warn("[memory/topics] failed to load memory for extraction", err);
29995
+ skip(id, "unreadable");
29996
+ }
29997
+ }
29998
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
29999
+ const toStamp = [];
30000
+ for (const input of inputs) {
30001
+ const entities = entitiesByMemory.get(input.id);
30002
+ if (entities === void 0) {
30003
+ skip(input.id, "llm-unanswered");
30004
+ continue;
30005
+ }
30006
+ try {
30007
+ const linked = await replaceMemoryEntitiesGuardedOp(entityCtx, input.id, entities);
30008
+ if (linked === null) {
30009
+ skip(input.id, "link-declined");
30010
+ entitiesByMemory.delete(input.id);
30011
+ continue;
30012
+ }
30013
+ } catch (err) {
30014
+ log.warn("[memory/topics] replaceMemoryEntitiesGuardedOp failed", err);
30015
+ skip(input.id, "link-failed");
30016
+ entitiesByMemory.delete(input.id);
30017
+ continue;
30018
+ }
30019
+ toStamp.push(input.id);
30020
+ }
30021
+ const stampedIds = await stampTopicsExtractedAtOp(ctx, toStamp, extractedAt);
30022
+ const stampedSet = new Set(stampedIds);
30023
+ for (const id of toStamp) {
30024
+ if (!stampedSet.has(id)) {
30025
+ skip(id, "stamp-declined");
30026
+ entitiesByMemory.delete(id);
30027
+ }
30028
+ }
30029
+ return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
30030
+ }
30031
+
30032
+ // src/lib/memory/store/local.ts
30033
+ function createLocalMemoryStore(options) {
30034
+ const { database, userId } = options;
30035
+ const entityCtx = {
30036
+ database,
30037
+ entityCollection: database.get("entity"),
30038
+ memoryEntityCollection: database.get("memory_entity"),
30039
+ ...userId !== void 0 && { userId },
30040
+ ...options.allowUnscopedRows !== void 0 && {
30041
+ allowUnscopedRows: options.allowUnscopedRows
30042
+ }
30043
+ };
30044
+ const vaultCtx = {
30045
+ database,
30046
+ vaultMemoryCollection: database.get("memory_vault"),
30047
+ walletAddress: options.walletAddress,
30048
+ signMessage: options.signMessage,
30049
+ embeddedWalletSigner: options.embeddedWalletSigner,
30050
+ ...userId !== void 0 && { userId },
30051
+ ...options.singleTenant !== void 0 && { singleTenant: options.singleTenant },
30052
+ // Deletes cascade to memory_entity, so the graph lane never serves a
30053
+ // deleted memory's id.
30054
+ entityCtx
30055
+ };
30056
+ const vaultCache = options.vaultCache ?? createVaultEmbeddingCache();
30057
+ const ownedBy = userId !== void 0 ? [import_watermelondb24.Q.where("user_id", userId)] : [];
30058
+ const watchedColumns = [
30059
+ "content",
30060
+ "scope",
30061
+ "updated_at",
30062
+ "fact_type",
30063
+ "event_time_start",
30064
+ "event_time_end",
30065
+ "event_time_kind",
30066
+ "archived_at",
30067
+ "trust_tier",
30068
+ "superseded_by",
30069
+ "visibility",
30070
+ "published_at",
30071
+ "geohash",
30072
+ "topics",
30073
+ "topics_user_managed",
30074
+ "media",
30075
+ "source"
30076
+ ];
30077
+ const embedInBackground = (memory) => {
30078
+ eagerEmbedContent(
30079
+ memory.content,
30080
+ options.embeddingOptions,
30081
+ vaultCache,
30082
+ vaultCtx,
30083
+ memory.uniqueId,
30084
+ memory.updatedAt
30085
+ ).catch((err) => {
30086
+ getLogger().warn("[memory/store] Failed to embed memory:", err);
30087
+ });
30088
+ };
30089
+ return {
30090
+ list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
30091
+ get: (id) => getVaultMemoryOp(vaultCtx, id),
30092
+ listArchived: async () => {
30093
+ const ids = await vaultCtx.vaultMemoryCollection.query(import_watermelondb24.Q.where("is_deleted", false), import_watermelondb24.Q.where("archived_at", import_watermelondb24.Q.notEq(null)), ...ownedBy).fetchIds();
30094
+ if (ids.length === 0) return [];
30095
+ const rows = await getAllVaultMemoriesOp(vaultCtx, { memoryIds: ids, includeArchived: true });
30096
+ return rows.sort((a, b) => (b.archivedAt ?? 0) - (a.archivedAt ?? 0));
30097
+ },
30098
+ memoriesByTopics: (names) => getMemoriesByEntityNamesOp(entityCtx, names),
30099
+ topicsByMemories: (memoryIds) => getEntitiesByMemoryIdsOp(entityCtx, memoryIds),
30100
+ create: async (input) => {
30101
+ const created = await createVaultMemoryOp(vaultCtx, input);
30102
+ if (input.embedding === void 0) embedInBackground(created);
30103
+ return created;
30104
+ },
30105
+ createMany: async (inputs) => {
30106
+ const created = await createVaultMemoriesBatchOp(vaultCtx, inputs);
30107
+ created.forEach((memory, i) => {
30108
+ if (inputs[i].embedding === void 0) embedInBackground(memory);
30109
+ });
30110
+ return created;
30111
+ },
30112
+ update: async (id, patch) => {
30113
+ const reembed = patch.embedding === void 0;
30114
+ const updated = await updateVaultMemoryOp(
30115
+ vaultCtx,
30116
+ id,
30117
+ reembed ? { ...patch, embedding: null } : patch
30118
+ );
30119
+ if (!updated) return null;
30120
+ vaultCache.delete(id);
30121
+ if (reembed) embedInBackground(updated);
30122
+ return updated;
30123
+ },
30124
+ delete: async (id) => {
30125
+ const deleted = await deleteVaultMemoryOp(vaultCtx, id);
30126
+ if (deleted) vaultCache.delete(id);
30127
+ return deleted;
30128
+ },
30129
+ supersede: (id, supersededById) => supersedeVaultMemoryOp(vaultCtx, id, supersededById),
30130
+ archive: (id) => archiveVaultMemoryOp(vaultCtx, id),
30131
+ restore: (id) => restoreVaultMemoryOp(vaultCtx, id),
30132
+ setTopics: (memoryId, topics) => setMemoryEntitiesOp(vaultCtx, memoryId, topics),
30133
+ addTopics: (memoryId, topics) => database.write(async (writer) => {
30134
+ const owned = await vaultCtx.vaultMemoryCollection.query(import_watermelondb24.Q.where("id", memoryId), import_watermelondb24.Q.where("is_deleted", false), ...ownedBy).fetchCount();
30135
+ return owned > 0 ? writer.callWriter(() => linkMemoryEntitiesOp(entityCtx, memoryId, topics)) : [];
30136
+ }),
30137
+ setVisibility: (id, visibility, visibilityOptions) => setMemoryVisibilityOp(vaultCtx, id, { visibility, ...visibilityOptions }),
30138
+ recall: (query, recallOptions) => recall(
30139
+ query,
30140
+ { vaultCtx, entityCtx, embeddingOptions: options.embeddingOptions, vaultCache },
30141
+ { ...recallOptions, types: ["fact"] }
30142
+ ),
30143
+ retain: (content, retainOptions) => retain(
30144
+ content,
30145
+ { vaultCtx, embeddingOptions: options.embeddingOptions, vaultCache },
30146
+ retainOptions
30147
+ ),
30148
+ subscribe: (onChange, subscribeOptions) => {
30149
+ const afterFirst = () => {
30150
+ let first = true;
30151
+ return () => {
30152
+ if (first) first = false;
30153
+ else onChange();
30154
+ };
30155
+ };
30156
+ const memories = subscribeOptions?.includeDeleted ? vaultCtx.vaultMemoryCollection.query(...ownedBy).observe() : vaultCtx.vaultMemoryCollection.query(import_watermelondb24.Q.where("is_deleted", false), ...ownedBy).observeWithColumns(
30157
+ subscribeOptions?.embeddings ? [...watchedColumns, "embedding", "embedding_model"] : watchedColumns
30158
+ );
30159
+ const subscriptions = [memories.subscribe(afterFirst())];
30160
+ if (subscribeOptions?.topics) {
30161
+ const linksOwnedBy = userId === void 0 ? [] : options.allowUnscopedRows ? [import_watermelondb24.Q.or(import_watermelondb24.Q.where("user_id", userId), import_watermelondb24.Q.where("user_id", null))] : [import_watermelondb24.Q.where("user_id", userId)];
30162
+ subscriptions.push(
30163
+ entityCtx.memoryEntityCollection.query(...linksOwnedBy).observe().subscribe(afterFirst())
30164
+ );
30165
+ }
30166
+ return () => subscriptions.forEach((s) => s.unsubscribe());
30167
+ },
30168
+ maintenance: {
30169
+ createDecaySweeper: (sweeperOptions) => createDecaySweeper({ ...sweeperOptions, vaultCtx }),
30170
+ getTopicBacklog: (backlogOptions) => getMemoriesNeedingTopicExtractionOp(vaultCtx, backlogOptions),
30171
+ extractTopics: (memoryIds, extractOptions) => extractAndLinkEntitiesForMemoriesOp(vaultCtx, memoryIds, extractOptions),
30172
+ stampTopicsExtracted: (memoryIds, extractedAt, version) => stampTopicsExtractedAtOp(vaultCtx, memoryIds, extractedAt, version),
30173
+ relinkTopics: (memoryIds) => relinkMemoryTopicsOp(vaultCtx, memoryIds),
30174
+ backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
30175
+ }
30176
+ };
30177
+ }
30178
+
29788
30179
  // src/lib/memory/synthesizeProfile.ts
29789
30180
  var DEFAULT_SYNTHESIS_MODEL = "inclusionai/ling-2.6-flash";
29790
30181
  var DEFAULT_FACET_RECALL_LIMIT = 20;
@@ -30312,199 +30703,6 @@ function normalizeInterests(values) {
30312
30703
  return out.length > 0 ? out : void 0;
30313
30704
  }
30314
30705
 
30315
- // src/lib/memory/topicExtract.ts
30316
- var TOPIC_EXTRACTION_BATCH_SIZE = 10;
30317
- var MAX_CHARS_PER_MEMORY = 300;
30318
- var MAX_VOCABULARY_NAMES = 100;
30319
- var MAX_COMPLETION_TOKENS = 8192;
30320
- var SYSTEM_PROMPT5 = `You assign topics to saved memories for a personal memory system.
30321
-
30322
- 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.
30323
-
30324
- 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:
30325
- - 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.
30326
- - KEEP the specific thing instead, when the memory names one: "Blue Bottle on Valencia" over "cafe", "Chicago Marathon" over "event", "Hollowpoint Labs" over "work".
30327
- - Prefer 0 entities over a weak one. A memory whose only candidate is a generic noun has NO entities.
30328
- ${ENTITY_KIND_GUIDELINES}
30329
-
30330
- Each memory below is written as "<id>: <text>". Output strict JSON: {"memories": [{"id": string, "entities": [{"name": string, "kind": string}]}]}
30331
- - exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
30332
- - "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
30333
- No prose.`;
30334
- async function extractEntitiesForMemories(memories, options) {
30335
- const out = /* @__PURE__ */ new Map();
30336
- if (memories.length === 0) return out;
30337
- const redactor = resolvePiiRedactor(options.piiRedaction);
30338
- const vocabulary = [];
30339
- for (const name of (options.existingEntityNames ?? []).filter((n) => n.trim().length > 0).slice(0, MAX_VOCABULARY_NAMES)) {
30340
- vocabulary.push(redactor ? (await redactor.redactTextAsync(name)).text : name);
30341
- }
30342
- const vocabularyNote = vocabulary.length > 0 ? `The user's existing topics: ${vocabulary.join(", ")}.
30343
- 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".
30344
-
30345
- ` : "";
30346
- for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
30347
- const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
30348
- const rows = [];
30349
- for (const m of batch) {
30350
- const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
30351
- rows.push(`${m.id}: ${redactor ? (await redactor.redactTextAsync(content)).text : content}`);
30352
- }
30353
- const listing = rows.join("\n");
30354
- const parsed = await callPortalJsonCompletion({
30355
- ...options.apiKey !== void 0 && { apiKey: options.apiKey },
30356
- ...options.getToken !== void 0 && { getToken: options.getToken },
30357
- ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
30358
- taskType: "memory_topic",
30359
- ...options.endpointOverride !== void 0 && {
30360
- endpointOverride: options.endpointOverride
30361
- },
30362
- model: options.model ?? DEFAULT_EXTRACTION_MODEL,
30363
- systemPrompt: SYSTEM_PROMPT5,
30364
- userMessage: `${vocabularyNote}Memories:
30365
- ${listing}
30366
-
30367
- List each memory's named entities.`,
30368
- tag: "memory/topics",
30369
- extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
30370
- ...options.fetchFn && { fetchFn: options.fetchFn },
30371
- ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
30372
- ...options.timeoutMs !== void 0 && { timeoutMs: options.timeoutMs },
30373
- ...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
30374
- ...options.backoffMs && { backoffMs: options.backoffMs }
30375
- });
30376
- if (parsed === null) {
30377
- getLogger().warn(
30378
- `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
30379
- );
30380
- continue;
30381
- }
30382
- const validIds = new Set(batch.map((m) => m.id));
30383
- const byId = parseTopicResponse(parsed, validIds);
30384
- if (byId === null) {
30385
- getLogger().warn(
30386
- `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
30387
- );
30388
- continue;
30389
- }
30390
- for (const m of batch) {
30391
- const entities = byId.get(m.id);
30392
- if (entities === void 0) {
30393
- continue;
30394
- }
30395
- out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
30396
- }
30397
- }
30398
- return out;
30399
- }
30400
- function restoreEntities(entities, redactor) {
30401
- return entities.map((e) => ({ kind: e.kind, restored: redactor.restoreForStorage(e.name) })).filter((e) => !e.restored.unresolved).map(
30402
- (e) => e.kind !== void 0 ? { name: e.restored.text, kind: e.kind } : { name: e.restored.text }
30403
- );
30404
- }
30405
- function parseTopicResponse(parsed, validIds) {
30406
- if (typeof parsed !== "object" || parsed === null) return null;
30407
- const list = parsed.memories;
30408
- if (!Array.isArray(list)) return null;
30409
- const out = /* @__PURE__ */ new Map();
30410
- for (const raw of list) {
30411
- if (typeof raw !== "object" || raw === null) continue;
30412
- const obj = raw;
30413
- if (typeof obj.id !== "string") continue;
30414
- const id = obj.id.trim().replace(/:$/, "").replace(/^\[([\s\S]*)\]$/, "$1").trim();
30415
- if (!validIds.has(id) || out.has(id)) continue;
30416
- const entities = Array.isArray(obj.entities) ? parseEntities(obj.entities) : [];
30417
- out.set(id, entities);
30418
- }
30419
- return out;
30420
- }
30421
- var DEGRADED_TOPIC_SKIP = {
30422
- excluded: false,
30423
- "link-declined": false,
30424
- "stamp-declined": false,
30425
- "llm-unanswered": true,
30426
- unreadable: true,
30427
- "not-found": true,
30428
- "link-failed": true
30429
- };
30430
- function isDegradedTopicSkip(reason) {
30431
- return DEGRADED_TOPIC_SKIP[reason] === true;
30432
- }
30433
- async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
30434
- const entityCtx = ctx.entityCtx;
30435
- if (!entityCtx) {
30436
- throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
30437
- }
30438
- const log = getLogger();
30439
- const extractedAt = options.now ?? Date.now();
30440
- const skippedIds = [];
30441
- const skippedReasons = /* @__PURE__ */ new Map();
30442
- const skip = (id, reason) => {
30443
- if (skippedReasons.has(id)) return;
30444
- skippedIds.push(id);
30445
- skippedReasons.set(id, reason);
30446
- };
30447
- const inputs = [];
30448
- for (const id of new Set(memoryIds)) {
30449
- let record;
30450
- try {
30451
- record = await ctx.vaultMemoryCollection.find(id);
30452
- } catch (err) {
30453
- log.warn("[memory/topics] vault lookup failed for extraction", err);
30454
- skip(id, "not-found");
30455
- continue;
30456
- }
30457
- if (record.isDeleted || ctx.userId !== void 0 && record.userId !== ctx.userId || record.topicsUserManaged) {
30458
- skip(id, "excluded");
30459
- continue;
30460
- }
30461
- try {
30462
- const stored = await vaultMemoryToStored(
30463
- record,
30464
- ctx.walletAddress,
30465
- ctx.signMessage,
30466
- ctx.embeddedWalletSigner
30467
- );
30468
- inputs.push({ id, content: stored.content });
30469
- } catch (err) {
30470
- log.warn("[memory/topics] failed to load memory for extraction", err);
30471
- skip(id, "unreadable");
30472
- }
30473
- }
30474
- const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
30475
- const toStamp = [];
30476
- for (const input of inputs) {
30477
- const entities = entitiesByMemory.get(input.id);
30478
- if (entities === void 0) {
30479
- skip(input.id, "llm-unanswered");
30480
- continue;
30481
- }
30482
- try {
30483
- const linked = await replaceMemoryEntitiesGuardedOp(entityCtx, input.id, entities);
30484
- if (linked === null) {
30485
- skip(input.id, "link-declined");
30486
- entitiesByMemory.delete(input.id);
30487
- continue;
30488
- }
30489
- } catch (err) {
30490
- log.warn("[memory/topics] replaceMemoryEntitiesGuardedOp failed", err);
30491
- skip(input.id, "link-failed");
30492
- entitiesByMemory.delete(input.id);
30493
- continue;
30494
- }
30495
- toStamp.push(input.id);
30496
- }
30497
- const stampedIds = await stampTopicsExtractedAtOp(ctx, toStamp, extractedAt);
30498
- const stampedSet = new Set(stampedIds);
30499
- for (const id of toStamp) {
30500
- if (!stampedSet.has(id)) {
30501
- skip(id, "stamp-declined");
30502
- entitiesByMemory.delete(id);
30503
- }
30504
- }
30505
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
30506
- }
30507
-
30508
30706
  // src/lib/memory/verifySupport.ts
30509
30707
  var DEFAULT_MODEL5 = "inclusionai/ling-2.6-flash";
30510
30708
  var DEFAULT_ATTEMPTS3 = 2;
@@ -30895,7 +31093,7 @@ async function assembleMemoryContext(options) {
30895
31093
  }
30896
31094
 
30897
31095
  // src/lib/memory/durableExtraction.ts
30898
- var import_watermelondb24 = __toESM(require_watermelondb());
31096
+ var import_watermelondb25 = __toESM(require_watermelondb());
30899
31097
  var MAX_JOBS_PER_PASS = 3;
30900
31098
  var MAX_ATTEMPTS = 3;
30901
31099
  var MAX_FAILED_SESSIONS = 3;
@@ -31042,7 +31240,7 @@ function createDurableAutoExtractor(options) {
31042
31240
  ...options.retainCtx,
31043
31241
  vaultCtx: {
31044
31242
  ...vault,
31045
- canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("id", import_watermelondb24.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
31243
+ canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(import_watermelondb25.Q.where("conversation_id", conversationId), import_watermelondb25.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb25.Q.where("conversation_id", conversationId), import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
31046
31244
  }
31047
31245
  },
31048
31246
  // The model follows the job's scope, not the mode alive at drain time.
@@ -31184,7 +31382,7 @@ function createDurableAutoExtractor(options) {
31184
31382
  let scheduleAfterSuccess = false;
31185
31383
  try {
31186
31384
  await writes;
31187
- const pending = await jobs.query(import_watermelondb24.Q.where("owner_key", owner), import_watermelondb24.Q.where("message_ids", import_watermelondb24.Q.notEq("[]"))).fetch();
31385
+ const pending = await jobs.query(import_watermelondb25.Q.where("owner_key", owner), import_watermelondb25.Q.where("message_ids", import_watermelondb25.Q.notEq("[]"))).fetch();
31188
31386
  storeFailures = 0;
31189
31387
  let processed = 0;
31190
31388
  for (const job of pending) {
@@ -31200,7 +31398,7 @@ function createDurableAutoExtractor(options) {
31200
31398
  let ids = [];
31201
31399
  let loaded = [];
31202
31400
  try {
31203
- const conversations = await storage.conversationsCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount();
31401
+ const conversations = await storage.conversationsCollection.query(import_watermelondb25.Q.where("conversation_id", conversationId), import_watermelondb25.Q.where("is_deleted", false)).fetchCount();
31204
31402
  if (!conversations) {
31205
31403
  await database.write(() => job.destroyPermanently());
31206
31404
  continue;
@@ -31317,12 +31515,12 @@ function createDurableAutoExtractor(options) {
31317
31515
  const scope = currentScope();
31318
31516
  writes = writes.then(
31319
31517
  () => database.write(async () => {
31320
- const existing = await jobs.query(import_watermelondb24.Q.where("owner_key", owner), import_watermelondb24.Q.where("conversation_id", conversationId)).fetch();
31518
+ const existing = await jobs.query(import_watermelondb25.Q.where("owner_key", owner), import_watermelondb25.Q.where("conversation_id", conversationId)).fetch();
31321
31519
  const job = existing.find(
31322
31520
  (candidate) => candidate._getRaw("scope") === scope && (candidate._getRaw("folder_id") ?? null) === folderId
31323
31521
  );
31324
31522
  const positions = new Map(ids.map((id, index) => [id, index]));
31325
- const rows = await storage.messagesCollection.query(import_watermelondb24.Q.where("id", import_watermelondb24.Q.oneOf(ids))).fetch();
31523
+ const rows = await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).fetch();
31326
31524
  const sequences = /* @__PURE__ */ new Map();
31327
31525
  for (const row of rows) {
31328
31526
  const sequence = Number(row._getRaw("message_id"));
@@ -37423,6 +37621,7 @@ export {
37423
37621
  createDurableAutoExtractor,
37424
37622
  createLlmDecayClassifier,
37425
37623
  createLlmNeighborRefiner,
37624
+ createLocalMemoryStore,
37426
37625
  createMediaBatchOp,
37427
37626
  createMediaOp,
37428
37627
  createMemoryEngineTool,