@mastra/memory 1.40.0-alpha.1 → 1.40.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.
@@ -35748,14 +35748,26 @@ var ObservationStrategy = class ObservationStrategy {
35748
35748
  * Wrap observations in a thread attribution tag.
35749
35749
  * In resource scope, thread IDs can be obscured via xxhash.
35750
35750
  */
35751
- async wrapWithThreadTag(threadId, observations, messageRange) {
35752
- const cleanObservations = stripThreadTags(observations);
35753
- const groupedObservations = this.retrieval && messageRange ? wrapInObservationGroup(cleanObservations, messageRange) : cleanObservations;
35751
+ async wrapWithThreadTag(threadId, observations, group) {
35752
+ const groupedObservations = group ? wrapInObservationGroup(group.content, group.range, group.id) : stripThreadTags(observations);
35754
35753
  let displayId = threadId;
35755
35754
  if (this.deps.obscureThreadIds) displayId = (await hasherPromise).h32ToString(threadId);
35756
35755
  return `<thread id="${displayId}">\n${groupedObservations}\n</thread>`;
35757
35756
  }
35758
35757
  /**
35758
+ * Build the observation group a cycle adds, or undefined when retrieval is off. The group is
35759
+ * what the cycle wraps into the observations and what it indexes, so both carry the same ID.
35760
+ */
35761
+ createObservationGroup(observations, range) {
35762
+ if (!this.retrieval) return void 0;
35763
+ const content = this.scope === "resource" ? stripThreadTags(observations) : observations;
35764
+ return {
35765
+ id: generateAnchorId(),
35766
+ range,
35767
+ content: content.trim()
35768
+ };
35769
+ }
35770
+ /**
35759
35771
  * Create a message boundary delimiter with an ISO 8601 date.
35760
35772
  * Used to separate observation chunks for cache stability.
35761
35773
  */
@@ -35765,13 +35777,14 @@ var ObservationStrategy = class ObservationStrategy {
35765
35777
  /**
35766
35778
  * Wrap raw observations — in resource scope, wraps with thread tag and merges;
35767
35779
  * in thread scope, simply appends with a message boundary delimiter.
35780
+ * `group` is the cycle's new observation group when retrieval is on.
35768
35781
  */
35769
- wrapObservations(rawObservations, existingObservations, threadId, lastObservedAt, messageRange) {
35782
+ wrapObservations(rawObservations, existingObservations, threadId, lastObservedAt, group) {
35770
35783
  if (this.scope === "resource") return (async () => {
35771
- const threadSection = await this.wrapWithThreadTag(threadId, rawObservations, messageRange);
35784
+ const threadSection = await this.wrapWithThreadTag(threadId, rawObservations, group);
35772
35785
  return this.replaceOrAppendThreadSection(existingObservations, threadId, threadSection, lastObservedAt);
35773
35786
  })();
35774
- const grouped = this.retrieval && messageRange ? wrapInObservationGroup(rawObservations, messageRange) : rawObservations;
35787
+ const grouped = group ? wrapInObservationGroup(group.content, group.range, group.id) : rawObservations;
35775
35788
  if (!existingObservations) return grouped;
35776
35789
  return `${existingObservations}${lastObservedAt ? ObservationStrategy.createMessageBoundary(lastObservedAt) : "\n\n"}${grouped}`;
35777
35790
  }
@@ -35798,10 +35811,13 @@ var ObservationStrategy = class ObservationStrategy {
35798
35811
  }
35799
35812
  }
35800
35813
  if (existingSection) {
35801
- const dateLineEnd = newThreadSection.indexOf("\n", newThreadSection.indexOf("Date:"));
35814
+ const dateIdx = newThreadSection.indexOf("Date:");
35815
+ const dateLineEnd = newThreadSection.indexOf("\n", dateIdx);
35802
35816
  const newCloseIdx = newThreadSection.lastIndexOf(threadClose);
35817
+ const groupIdx = newThreadSection.indexOf("<observation-group ");
35818
+ const contentStart = groupIdx !== -1 && groupIdx < dateIdx ? groupIdx : dateLineEnd + 1;
35803
35819
  if (dateLineEnd !== -1 && newCloseIdx !== -1) {
35804
- const newObsContent = newThreadSection.slice(dateLineEnd + 1, newCloseIdx).trim();
35820
+ const newObsContent = newThreadSection.slice(contentStart, newCloseIdx).trim();
35805
35821
  if (newObsContent) {
35806
35822
  const merged = `${existingSection.slice(0, existingSection.length - 9).trimEnd()}\n${newObsContent}\n${threadClose}`;
35807
35823
  return existingObservations.slice(0, existingSectionStart) + merged + existingObservations.slice(existingSectionEnd);
@@ -35858,7 +35874,8 @@ var ObservationStrategy = class ObservationStrategy {
35858
35874
  processed: {
35859
35875
  ...processed,
35860
35876
  observations: target.activeObservations ?? "",
35861
- observationTokens: target.observationTokenCount
35877
+ observationTokens: target.observationTokenCount,
35878
+ newObservationGroup: void 0
35862
35879
  },
35863
35880
  record: target,
35864
35881
  alreadyCovered: true
@@ -35884,11 +35901,24 @@ var ObservationStrategy = class ObservationStrategy {
35884
35901
  return op();
35885
35902
  }, { abortSignal: this.opts.abortSignal });
35886
35903
  }
35904
+ /** Index every group parsed out of `observations` (used for buffered chunks, which carry no producer group). */
35887
35905
  async indexObservationGroups(observations, threadId, resourceId, observedAt, recordId) {
35888
- if (!resourceId || !this.deps.onIndexObservations) return;
35889
- const groups = parseObservationGroups(observations);
35890
- if (groups.length === 0) return;
35891
- await Promise.all(groups.map((group) => withRetry(() => this.deps.onIndexObservations({
35906
+ const groups = parseObservationGroups(observations).map((group) => ({
35907
+ group,
35908
+ threadId,
35909
+ observedAt
35910
+ }));
35911
+ await this.indexNewObservationGroups(groups, resourceId, recordId);
35912
+ }
35913
+ /**
35914
+ * Write the vectors for the groups a commit added, once. Call this right after the commit. The
35915
+ * observations are already committed, so a failed write is logged and must not fail the cycle:
35916
+ * that would skip its end markers and reflection.
35917
+ */
35918
+ async indexNewObservationGroups(groups, resourceId, recordId) {
35919
+ const onIndexObservations = this.deps.onIndexObservations;
35920
+ if (!resourceId || !onIndexObservations || groups.length === 0) return;
35921
+ const failures = (await Promise.allSettled(groups.map(({ group, threadId, observedAt }) => withRetry(() => onIndexObservations({
35892
35922
  text: group.content,
35893
35923
  groupId: group.id,
35894
35924
  range: group.range,
@@ -35899,7 +35929,12 @@ var ObservationStrategy = class ObservationStrategy {
35899
35929
  }), {
35900
35930
  label: "index-observations",
35901
35931
  abortSignal: this.opts.abortSignal
35902
- })));
35932
+ })))).filter((result) => result.status === "rejected");
35933
+ if (failures.length > 0) {
35934
+ const reason = failures[0].reason;
35935
+ omError("[OM] Indexing committed observation groups failed", reason);
35936
+ console.warn(`[observational-memory] Storing observation vectors failed for ${failures.length} of ${groups.length} group(s) (${reason instanceof Error ? reason.message : String(reason)}).`);
35937
+ }
35903
35938
  }
35904
35939
  /**
35905
35940
  * Messages this cycle observed for `threadId`, or undefined when the strategy does not
@@ -36229,8 +36264,8 @@ var SyncObservationStrategy = class extends ObservationStrategy {
36229
36264
  async compose(output, existingObservations, baseRecord) {
36230
36265
  const { threadId, messages } = this.opts;
36231
36266
  const lastObservedAt = this.getMaxMessageTimestamp(messages);
36232
- const messageRange = this.retrieval ? buildMessageRange(messages) : void 0;
36233
- const newObservations = await this.wrapObservations(output.observations, existingObservations, threadId, lastObservedAt, messageRange);
36267
+ const newObservationGroup = this.createObservationGroup(output.observations, buildMessageRange(messages));
36268
+ const newObservations = await this.wrapObservations(output.observations, existingObservations, threadId, lastObservedAt, newObservationGroup);
36234
36269
  const observationTokens = this.tokenCounter.countObservations(newObservations);
36235
36270
  const cycleObservationTokens = this.tokenCounter.countObservations(output.observations);
36236
36271
  const newMessageIds = messages.map((m) => m.id);
@@ -36241,6 +36276,7 @@ var SyncObservationStrategy = class extends ObservationStrategy {
36241
36276
  cycleObservationTokens,
36242
36277
  observedMessageIds: [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])],
36243
36278
  lastObservedAt,
36279
+ newObservationGroup,
36244
36280
  suggestedContinuation: output.suggestedContinuation,
36245
36281
  currentTask: output.currentTask,
36246
36282
  threadTitle: output.threadTitle,
@@ -36271,6 +36307,11 @@ var SyncObservationStrategy = class extends ObservationStrategy {
36271
36307
  reason: "the observational memory head kept changing during the commit"
36272
36308
  };
36273
36309
  processed = committed.processed;
36310
+ if (!committed.alreadyCovered && processed.newObservationGroup) await this.indexNewObservationGroups([{
36311
+ group: processed.newObservationGroup,
36312
+ threadId,
36313
+ observedAt: processed.lastObservedAt
36314
+ }], resourceId, committed.record.id);
36274
36315
  const thread = committed.alreadyCovered ? null : await this.storage.getThreadById({ threadId });
36275
36316
  if (thread) {
36276
36317
  const oldTitle = thread.title?.trim();
@@ -36301,7 +36342,6 @@ var SyncObservationStrategy = class extends ObservationStrategy {
36301
36342
  newTitle
36302
36343
  }));
36303
36344
  }
36304
- if (!committed.alreadyCovered) await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt, committed.record.id);
36305
36345
  return {
36306
36346
  status: "committed",
36307
36347
  processed,
@@ -36435,10 +36475,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
36435
36475
  lastObservedAt: /* @__PURE__ */ new Date()
36436
36476
  };
36437
36477
  }
36438
- const messageRange = this.retrieval ? buildMessageRange(messages) : void 0;
36478
+ const group = this.createObservationGroup(output.observations, buildMessageRange(messages));
36439
36479
  let newObservations;
36440
- if (this.scope === "resource") newObservations = await this.wrapWithThreadTag(threadId, output.observations, messageRange);
36441
- else newObservations = this.retrieval && messageRange ? wrapInObservationGroup(output.observations, messageRange) : output.observations;
36480
+ if (this.scope === "resource") newObservations = await this.wrapWithThreadTag(threadId, output.observations, group);
36481
+ else newObservations = group ? wrapInObservationGroup(group.content, group.range, group.id) : output.observations;
36442
36482
  const observationTokens = this.tokenCounter.countObservations(newObservations);
36443
36483
  const messageIds = messages.map((m) => m.id);
36444
36484
  const maxTs = this.getMaxMessageTimestamp(messages);
@@ -36531,7 +36571,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
36531
36571
  committedRecord = landedOn;
36532
36572
  }
36533
36573
  try {
36534
- await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt, this.persistedRecordId);
36574
+ await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt, committedRecord.id);
36535
36575
  const candidateTitle = processed.threadTitle?.trim();
36536
36576
  const hasValidThreadTitle = !!candidateTitle && candidateTitle.length >= 3;
36537
36577
  if (hasValidThreadTitle || processed.extractedValues) {
@@ -36644,6 +36684,8 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
36644
36684
  priorMetadataByThread = /* @__PURE__ */ new Map();
36645
36685
  /** Each observed thread's wrapped section from the last composition. */
36646
36686
  threadSections = /* @__PURE__ */ new Map();
36687
+ /** Each observed thread's new observation group, when retrieval is on. */
36688
+ newGroups = /* @__PURE__ */ new Map();
36647
36689
  /** Head text the processed observations were composed from (the commit's expected text). */
36648
36690
  composedFrom = "";
36649
36691
  constructor(deps, opts) {
@@ -36893,8 +36935,8 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
36893
36935
  const threadMetadataUpdates = [];
36894
36936
  for (const { threadId, threadMessages, result } of this.observationResults) {
36895
36937
  cycleObservationTokens += this.tokenCounter.countObservations(result.observations);
36896
- const messageRange = this.retrieval ? buildMessageRange(threadMessages) : void 0;
36897
- const threadSection = await this.wrapWithThreadTag(threadId, result.observations, messageRange);
36938
+ if (!this.newGroups.has(threadId)) this.newGroups.set(threadId, this.createObservationGroup(result.observations, buildMessageRange(threadMessages)));
36939
+ const threadSection = await this.wrapWithThreadTag(threadId, result.observations, this.newGroups.get(threadId));
36898
36940
  this.threadSections.set(threadId, threadSection);
36899
36941
  const threadLastObservedAt = this.getMaxMessageTimestamp(threadMessages);
36900
36942
  currentObservations = this.replaceOrAppendThreadSection(currentObservations, threadId, threadSection, threadLastObservedAt);
@@ -36944,6 +36986,17 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
36944
36986
  reason: "the observational memory head kept changing during the commit"
36945
36987
  };
36946
36988
  processed = committed.processed;
36989
+ if (!committed.alreadyCovered) {
36990
+ const added = this.observationResults.flatMap(({ threadId, threadMessages }) => {
36991
+ const group = this.newGroups.get(threadId);
36992
+ return group ? [{
36993
+ group,
36994
+ threadId,
36995
+ observedAt: this.getMaxMessageTimestamp(threadMessages)
36996
+ }] : [];
36997
+ });
36998
+ await this.indexNewObservationGroups(added, resourceId, committed.record.id);
36999
+ }
36947
37000
  const threadUpdateMarkers = [];
36948
37001
  if (processed.threadMetadataUpdates && !committed.alreadyCovered) for (const update of processed.threadMetadataUpdates) {
36949
37002
  const thread = await this.storage.getThreadById({ threadId: update.threadId });
@@ -36979,7 +37032,6 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
36979
37032
  }
36980
37033
  }
36981
37034
  for (const marker of threadUpdateMarkers) await this.streamMarker(marker);
36982
- if (resourceId && !committed.alreadyCovered) await Promise.all(this.observationResults.map(({ threadId, threadMessages, result }) => this.indexObservationGroups(result.observations, threadId, resourceId, this.getMaxMessageTimestamp(threadMessages), committed.record.id)));
36983
37035
  return {
36984
37036
  status: "committed",
36985
37037
  processed,
@@ -44282,7 +44334,8 @@ Notes:
44282
44334
  if (typeof this.vector === `undefined`) throw new Error(`Tried to create observation embedding index but no vector db is attached to this Memory instance.`);
44283
44335
  await this.vector.createIndex({
44284
44336
  indexName,
44285
- dimension: usedDimensions
44337
+ dimension: usedDimensions,
44338
+ metadataIndexes: ["resource_id", "thread_id"]
44286
44339
  });
44287
44340
  return { indexName };
44288
44341
  }
@@ -45692,4 +45745,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
45692
45745
  }
45693
45746
  });
45694
45747
 
45695
- //# sourceMappingURL=src-DZnFu-zL.cjs.map
45748
+ //# sourceMappingURL=src-CaxT6Rpu.cjs.map