@mastra/memory 1.29.1-alpha.0 → 1.30.0-alpha.2

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.
@@ -30216,7 +30216,24 @@ function isSystemReminderMessage(message) {
30216
30216
  const firstTextPart = message.content.parts.find((part) => part.type === "text");
30217
30217
  return typeof firstTextPart?.text === "string" && firstTextPart.text.startsWith("<system-reminder");
30218
30218
  }
30219
- function filterSystemReminderMessages(messages, includeSystemReminders) {
30219
+ function isRecallSignalType(type) {
30220
+ return type === "user" || type === "state" || type === "reactive" || type === "notification" || type === "user-message" || type === "system-reminder";
30221
+ }
30222
+ function getRecallSignalType(message) {
30223
+ if (!isRecord(message.content)) return void 0;
30224
+ for (const part of message.content.parts) if ((part.type === "data-signal" || part.type === "data-user-message") && isRecord(part.data) && isRecallSignalType(part.data.type)) return part.data.type;
30225
+ const metadata = message.content.metadata;
30226
+ if (message.role === "signal" && isRecord(metadata) && isRecord(metadata.signal)) {
30227
+ if (isRecallSignalType(metadata.signal.type)) return metadata.signal.type;
30228
+ }
30229
+ return isSystemReminderMessage(message) ? "system-reminder" : void 0;
30230
+ }
30231
+ function filterSystemReminderMessages(messages, includeSystemReminders, hideSignals) {
30232
+ if (hideSignals === false) return messages;
30233
+ if (hideSignals !== void 0) return messages.filter((message) => {
30234
+ const type = getRecallSignalType(message);
30235
+ return type === void 0 || hideSignals !== true && !hideSignals.includes(type);
30236
+ });
30220
30237
  if (includeSystemReminders) return messages;
30221
30238
  return messages.filter((message) => !isSystemReminderMessage(message));
30222
30239
  }
@@ -30243,8 +30260,35 @@ const DEFAULT_TOP_K = 4;
30243
30260
  const VECTOR_DELETE_BATCH_SIZE = 100;
30244
30261
  const DEFAULT_EMBEDDING_CACHE_MAX_SIZE = 1e3;
30245
30262
  /**
30246
- * Concrete implementation of MastraMemory that adds support for thread configuration
30247
- * and message injection.
30263
+ * Gives Mastra agents conversation history, with optional working memory,
30264
+ * semantic recall, and observational memory.
30265
+ *
30266
+ * @remarks
30267
+ * Configure storage on this instance or its Mastra instance before use.
30268
+ * See the bundled docs for setup and conversation identifiers.
30269
+ *
30270
+ * @example
30271
+ * Attach memory to an agent; `yourModel` is your configured model.
30272
+ * ```typescript
30273
+ * import { Agent } from '@mastra/core/agent';
30274
+ * import { Memory } from '@mastra/memory';
30275
+ *
30276
+ * const agent = new Agent({
30277
+ * id: 'assistant',
30278
+ * name: 'Assistant',
30279
+ * instructions: 'You are a helpful assistant.',
30280
+ * model: yourModel,
30281
+ * memory: new Memory(),
30282
+ * });
30283
+ * ```
30284
+ *
30285
+ * @see For documentation bundled with your installed package, locate
30286
+ * `@mastra/memory/package.json` with your project's resolver or package-manager
30287
+ * tooling, then read `dist/docs/SKILL.md` from that package root and follow its
30288
+ * reference links. Use package-manager tools for virtual or archived packages.
30289
+ *
30290
+ * @see [Memory documentation](https://mastra.ai/docs/memory/overview)
30291
+ * if packaged docs are unavailable.
30248
30292
  */
30249
30293
  var Memory = class Memory extends MastraMemory {
30250
30294
  _omEngine;
@@ -30434,7 +30478,7 @@ var Memory = class Memory extends MastraMemory {
30434
30478
  });
30435
30479
  }
30436
30480
  async recall(args) {
30437
- const { threadId, resourceId, perPage: perPageArg, page, orderBy, threadConfig, vectorSearchString, includeSystemReminders, filter, includeTotal } = args;
30481
+ const { threadId, resourceId, perPage: perPageArg, page, orderBy, threadConfig, vectorSearchString, includeSystemReminders, hideSignals, filter, includeTotal } = args;
30438
30482
  const config = this.getMergedThreadConfig(threadConfig || {});
30439
30483
  const semanticRecallEnabled = Boolean(config.semanticRecall);
30440
30484
  const span = this.createMemorySpan("recall", args.observabilityContext, {
@@ -30533,7 +30577,7 @@ var Memory = class Memory extends MastraMemory {
30533
30577
  const messages = filterSystemReminderMessages(new MessageList({
30534
30578
  threadId,
30535
30579
  resourceId
30536
- }).add(rawMessages, "memory").get.all.db(), includeSystemReminders);
30580
+ }).add(rawMessages, "memory").get.all.db(), includeSystemReminders, hideSignals);
30537
30581
  const { total, page: resultPage, perPage: resultPerPage, hasMore } = paginatedResult;
30538
30582
  const recallResult = {
30539
30583
  messages,
@@ -31843,10 +31887,40 @@ Notes:
31843
31887
  * ```
31844
31888
  */
31845
31889
  async cloneThread(args, memoryConfig) {
31890
+ const result = await this.copyThread(args, memoryConfig);
31891
+ const { messages } = await (await this.getMemoryStore()).listMessages({
31892
+ threadId: result.thread.id,
31893
+ resourceId: result.thread.resourceId,
31894
+ perPage: false,
31895
+ orderBy: {
31896
+ field: "createdAt",
31897
+ direction: "ASC"
31898
+ }
31899
+ });
31900
+ return {
31901
+ ...result,
31902
+ clonedMessages: messages
31903
+ };
31904
+ }
31905
+ /**
31906
+ * Copies a thread with all its messages to a new thread without returning the
31907
+ * message payloads. Working memory, observational memory, and semantic-recall
31908
+ * embeddings are carried over exactly as with `cloneThread`; the only difference
31909
+ * is that message content never has to be held in the Node heap at once.
31910
+ *
31911
+ * Use this instead of `cloneThread` when only the new thread id is needed
31912
+ * (e.g. forking a conversation for a subagent).
31913
+ *
31914
+ * @param args - Clone parameters, same as `cloneThread`
31915
+ * @param memoryConfig - Optional memory configuration override
31916
+ * @returns The newly created thread and the source→new message id map
31917
+ */
31918
+ async copyThread(args, memoryConfig) {
31846
31919
  const memoryStore = await this.getMemoryStore();
31847
- const result = await memoryStore.cloneThread(args);
31848
31920
  const config = this.getMergedThreadConfig(memoryConfig);
31921
+ const result = await memoryStore.copyThread(args);
31849
31922
  const sourceResourceId = (await this.getThreadById({ threadId: args.sourceThreadId }))?.resourceId;
31923
+ let priorDestinationResourceWm;
31850
31924
  if (config.workingMemory?.enabled) {
31851
31925
  const scope = config.workingMemory.scope || "resource";
31852
31926
  if (scope === "thread" || scope === "resource" && args.resourceId && args.resourceId !== sourceResourceId) {
@@ -31855,28 +31929,125 @@ Notes:
31855
31929
  resourceId: sourceResourceId,
31856
31930
  memoryConfig
31857
31931
  });
31858
- if (sourceWm) await this.updateWorkingMemory({
31859
- threadId: result.thread.id,
31860
- resourceId: result.thread.resourceId,
31861
- workingMemory: sourceWm,
31862
- memoryConfig
31863
- });
31932
+ if (sourceWm) {
31933
+ if (scope === "resource") priorDestinationResourceWm = (await memoryStore.getResourceById({ resourceId: result.thread.resourceId }))?.workingMemory ?? null;
31934
+ await this.updateWorkingMemory({
31935
+ threadId: result.thread.id,
31936
+ resourceId: result.thread.resourceId,
31937
+ workingMemory: sourceWm,
31938
+ memoryConfig
31939
+ });
31940
+ }
31864
31941
  }
31865
31942
  }
31866
31943
  if (memoryStore.supportsObservationalMemory && sourceResourceId) try {
31867
31944
  await this.cloneObservationalMemory(memoryStore, args.sourceThreadId, sourceResourceId, result);
31868
31945
  } catch (error) {
31869
- try {
31870
- await memoryStore.deleteThread({ threadId: result.thread.id });
31871
- } catch (rollbackError) {
31872
- this.logger.error("Failed to rollback cloned thread after OM clone failure", rollbackError);
31873
- }
31946
+ await this.rollbackCopiedThread(memoryStore, result.thread, "OM clone", false, priorDestinationResourceWm);
31947
+ throw error;
31948
+ }
31949
+ if (this.vector && this.embedder && config.semanticRecall) try {
31950
+ await this.embedCopiedMessagesInBatches(memoryStore, result, config);
31951
+ } catch (error) {
31952
+ await this.rollbackCopiedThread(memoryStore, result.thread, "embedding", true, priorDestinationResourceWm);
31874
31953
  throw error;
31875
31954
  }
31876
- if (this.vector && config.semanticRecall && result.clonedMessages.length > 0) await this.embedClonedMessages(result.clonedMessages, config);
31877
31955
  return result;
31878
31956
  }
31879
31957
  /**
31958
+ * Best-effort compensation when a later step of copyThread fails after the destination thread
31959
+ * was persisted. deleteThread removes the thread, its messages and thread-scoped working memory.
31960
+ * `priorDestinationResourceWm` is the destination resource's working memory before the copy
31961
+ * overwrote it (`null` = none existed); when defined it is restored. When `afterOmClone` is set the OM step already succeeded, so the thread-scoped OM record and
31962
+ * any vectors written so far are dropped too. Resource-scoped OM is left alone: it may be
31963
+ * shared with a pre-existing resource and isn't safe to clear blindly.
31964
+ */
31965
+ async rollbackCopiedThread(memoryStore, thread, failedStep, afterOmClone, priorDestinationResourceWm) {
31966
+ const threadId = thread.id;
31967
+ try {
31968
+ await memoryStore.deleteThread({ threadId });
31969
+ } catch (rollbackError) {
31970
+ this.logger.error(`Failed to rollback copied thread after ${failedStep} failure`, rollbackError);
31971
+ }
31972
+ if (priorDestinationResourceWm !== void 0) try {
31973
+ await memoryStore.updateResource({
31974
+ resourceId: thread.resourceId,
31975
+ workingMemory: priorDestinationResourceWm ?? ""
31976
+ });
31977
+ } catch (rollbackError) {
31978
+ this.logger.error(`Failed to restore resource working memory after ${failedStep} failure`, rollbackError);
31979
+ }
31980
+ if (!afterOmClone) return;
31981
+ if (memoryStore.supportsObservationalMemory) try {
31982
+ await memoryStore.clearObservationalMemory(threadId, thread.resourceId);
31983
+ } catch (rollbackError) {
31984
+ this.logger.error(`Failed to rollback copied thread OM after ${failedStep} failure`, rollbackError);
31985
+ }
31986
+ try {
31987
+ const messageIndexes = await this.getMemoryVectorIndexes([this.messageIndexPrefix]);
31988
+ await Promise.all(messageIndexes.map((indexName) => this.vector.deleteVectors({
31989
+ indexName,
31990
+ filter: { thread_id: threadId }
31991
+ })));
31992
+ } catch (rollbackError) {
31993
+ this.logger.error(`Failed to rollback copied thread vectors after ${failedStep} failure`, rollbackError);
31994
+ }
31995
+ }
31996
+ static CLONE_EMBED_PAGE_SIZE = 100;
31997
+ async embedCopiedMessagesInBatches(memoryStore, copied, config) {
31998
+ const size = Memory.CLONE_EMBED_PAGE_SIZE;
31999
+ const copiedIds = Object.values(copied.messageIdMap ?? {});
32000
+ if (copiedIds.length > 0) {
32001
+ for (let i = 0; i < copiedIds.length; i += size) {
32002
+ const { messages } = await memoryStore.listMessagesById({ messageIds: copiedIds.slice(i, i + size) });
32003
+ if (messages.length > 0) await this.embedClonedMessages(messages, config);
32004
+ }
32005
+ return;
32006
+ }
32007
+ const { messages } = await memoryStore.listMessages({
32008
+ threadId: copied.thread.id,
32009
+ resourceId: copied.thread.resourceId,
32010
+ perPage: false,
32011
+ includeTotal: false
32012
+ });
32013
+ for (let i = 0; i < messages.length; i += size) await this.embedClonedMessages(messages.slice(i, i + size), config);
32014
+ }
32015
+ async updateThreadResourceId({ threadId, resourceId, memoryConfig }) {
32016
+ const memoryStore = await this.getMemoryStore();
32017
+ const config = this.getMergedThreadConfig(memoryConfig);
32018
+ const migratesVectors = Boolean(this.vector && this.embedder && config.semanticRecall);
32019
+ if (!migratesVectors) {
32020
+ const existing = await memoryStore.getThreadById({ threadId });
32021
+ if (existing && existing.resourceId === resourceId) return existing;
32022
+ }
32023
+ const thread = await memoryStore.updateThreadResourceId({
32024
+ threadId,
32025
+ resourceId
32026
+ });
32027
+ if (migratesVectors) try {
32028
+ const { messages } = await memoryStore.listMessages({
32029
+ threadId,
32030
+ perPage: false
32031
+ });
32032
+ const messageIndexes = await this.getMemoryVectorIndexes([this.messageIndexPrefix]);
32033
+ await Promise.all(messageIndexes.map(async (indexName) => {
32034
+ await this.vector.deleteVectors({
32035
+ indexName,
32036
+ filter: { thread_id: threadId }
32037
+ });
32038
+ }));
32039
+ if (messages.length > 0) await this.embedClonedMessages(messages, config);
32040
+ } catch (error) {
32041
+ this.logger.error("Failed to migrate semantic-recall vectors during thread transfer", {
32042
+ threadId,
32043
+ resourceId,
32044
+ error: error instanceof Error ? error.message : String(error)
32045
+ });
32046
+ throw new Error(`Thread "${threadId}" was transferred to resource "${resourceId}", but migrating its semantic-recall vectors failed. The thread's messages may not surface under resource-scoped recall until the vectors are re-indexed. Cause: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
32047
+ }
32048
+ return thread;
32049
+ }
32050
+ /**
31880
32051
  * Clone observational memory records when cloning a thread.
31881
32052
  * Thread-scoped: always cloned to the new thread.
31882
32053
  * Resource-scoped: cloned only when the resourceId changes (same resourceId shares OM naturally).
@@ -32179,4 +32350,4 @@ Notes:
32179
32350
  //#endregion
32180
32351
  export { formatMessagesForObserver as A, OBSERVATION_CONTINUATION_HINT as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, ModelByInputTokens as G, KnowledgeSemanticIndexCoordinator as H, stripEphemeralAnchorIds as I, publishSubconsciousActivity as J, SUBCONSCIOUS_ACTIVITY_STATE_ID as K, OBSERVATIONAL_MEMORY_DEFAULTS as L, optimizeObservationsForContext as M, parseObserverOutput as N, buildObserverSystemPrompt as O, injectAnchorIds as P, OBSERVATION_CONTEXT_INSTRUCTIONS as R, WorkingMemoryExtractor as S, TokenCounter as T, StaleKnowledgeSemanticIndexError as U, Subconscious as V, SubconsciousRemindExtractor as W, Extractor as X, renderSubconsciousActivity as Y, wrapInObservationGroup as _, extractWorkingMemoryContent as a, WorkingMemoryStateProcessor as b, getObservationsAsOf as c, combineObservationGroupRanges as d, deriveObservationGroupProvenance as f, stripObservationGroups as g, renderObservationGroupsForReflection as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, ObservationalMemoryProcessor as l, reconcileObservationGroupsFromReflection as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, parseObservationGroups as p, buildSubconsciousActivitySnapshot as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, ObservationalMemory as u, WORKING_MEMORY_STATE_ID as v, summarizeConversation as w, deepMergeWorkingMemory as x, WORKING_MEMORY_STATE_PROCESSOR_ID as y, OBSERVATION_CONTEXT_PROMPT as z };
32181
32352
 
32182
- //# sourceMappingURL=src-C4x5Wx9t.js.map
32353
+ //# sourceMappingURL=src-TN3MwGgP.js.map