@mastra/memory 1.5.2 → 1.6.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.
Files changed (30) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE.md +15 -0
  3. package/dist/{chunk-PVFLHAZX.cjs → chunk-5UYAHJVJ.cjs} +77 -73
  4. package/dist/chunk-5UYAHJVJ.cjs.map +1 -0
  5. package/dist/{chunk-HNPAIFCZ.js → chunk-A62BQK35.js} +77 -73
  6. package/dist/chunk-A62BQK35.js.map +1 -0
  7. package/dist/docs/SKILL.md +1 -1
  8. package/dist/docs/assets/SOURCE_MAP.json +16 -16
  9. package/dist/docs/references/docs-memory-observational-memory.md +3 -5
  10. package/dist/docs/references/reference-memory-cloneThread.md +13 -1
  11. package/dist/docs/references/reference-memory-observational-memory.md +4 -2
  12. package/dist/index.cjs +106 -7
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +106 -7
  17. package/dist/index.js.map +1 -1
  18. package/dist/{observational-memory-Q47HN5YL.cjs → observational-memory-MXI54VC7.cjs} +17 -17
  19. package/dist/{observational-memory-Q47HN5YL.cjs.map → observational-memory-MXI54VC7.cjs.map} +1 -1
  20. package/dist/{observational-memory-KAFD4QZK.js → observational-memory-SR6G4HN5.js} +3 -3
  21. package/dist/{observational-memory-KAFD4QZK.js.map → observational-memory-SR6G4HN5.js.map} +1 -1
  22. package/dist/processors/index.cjs +15 -15
  23. package/dist/processors/index.js +1 -1
  24. package/dist/processors/observational-memory/observational-memory.d.ts +3 -4
  25. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  26. package/dist/processors/observational-memory/types.d.ts +8 -2
  27. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  28. package/package.json +7 -7
  29. package/dist/chunk-HNPAIFCZ.js.map +0 -1
  30. package/dist/chunk-PVFLHAZX.cjs.map +0 -1
@@ -1615,6 +1615,11 @@ See https://mastra.ai/docs/memory/observational-memory#models for model recommen
1615
1615
  const messageTokens = config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens;
1616
1616
  const observationTokens = config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens;
1617
1617
  const isSharedBudget = config.shareTokenBudget ?? false;
1618
+ const isDefaultModelSelection = (model) => model === void 0 || model === "default";
1619
+ const observationSelectedModel = config.model ?? config.observation?.model ?? config.reflection?.model;
1620
+ const reflectionSelectedModel = config.model ?? config.reflection?.model ?? config.observation?.model;
1621
+ const observationDefaultMaxOutputTokens = config.observation?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(observationSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens : void 0);
1622
+ const reflectionDefaultMaxOutputTokens = config.reflection?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(reflectionSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens : void 0);
1618
1623
  const totalBudget = messageTokens + observationTokens;
1619
1624
  const userExplicitlyConfiguredAsync = config.observation?.bufferTokens !== void 0 || config.observation?.bufferActivation !== void 0 || config.reflection?.bufferActivation !== void 0;
1620
1625
  const asyncBufferingDisabled = config.observation?.bufferTokens === false || config.scope === "resource" && !userExplicitlyConfiguredAsync;
@@ -1646,7 +1651,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1646
1651
  shareTokenBudget: isSharedBudget,
1647
1652
  modelSettings: {
1648
1653
  temperature: config.observation?.modelSettings?.temperature ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.temperature,
1649
- maxOutputTokens: config.observation?.modelSettings?.maxOutputTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens
1654
+ ...observationDefaultMaxOutputTokens !== void 0 ? { maxOutputTokens: observationDefaultMaxOutputTokens } : {}
1650
1655
  },
1651
1656
  providerOptions: config.observation?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.providerOptions,
1652
1657
  maxTokensPerBatch: config.observation?.maxTokensPerBatch ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.maxTokensPerBatch,
@@ -1667,7 +1672,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
1667
1672
  shareTokenBudget: isSharedBudget,
1668
1673
  modelSettings: {
1669
1674
  temperature: config.reflection?.modelSettings?.temperature ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.temperature,
1670
- maxOutputTokens: config.reflection?.modelSettings?.maxOutputTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens
1675
+ ...reflectionDefaultMaxOutputTokens !== void 0 ? { maxOutputTokens: reflectionDefaultMaxOutputTokens } : {}
1671
1676
  },
1672
1677
  providerOptions: config.reflection?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
1673
1678
  bufferActivation: asyncBufferingDisabled ? void 0 : config?.reflection?.bufferActivation ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.bufferActivation,
@@ -2409,18 +2414,17 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2409
2414
  const agent = this.getObserverAgent();
2410
2415
  const prompt = buildObserverPrompt(existingObservations, messagesToObserve, options);
2411
2416
  const doGenerate = async () => {
2412
- const result2 = await this.withAbortCheck(
2413
- () => agent.generate(prompt, {
2417
+ return this.withAbortCheck(async () => {
2418
+ const streamResult = await agent.stream(prompt, {
2414
2419
  modelSettings: {
2415
2420
  ...this.observationConfig.modelSettings
2416
2421
  },
2417
2422
  providerOptions: this.observationConfig.providerOptions,
2418
2423
  ...abortSignal ? { abortSignal } : {},
2419
2424
  ...options?.requestContext ? { requestContext: options.requestContext } : {}
2420
- }),
2421
- abortSignal
2422
- );
2423
- return result2;
2425
+ });
2426
+ return streamResult.getFullOutput();
2427
+ }, abortSignal);
2424
2428
  };
2425
2429
  let result = await doGenerate();
2426
2430
  let parsed = parseObserverOutput(result.text);
@@ -2467,17 +2471,17 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2467
2471
  this.observedMessageIds.add(msg.id);
2468
2472
  }
2469
2473
  const doGenerate = async () => {
2470
- return this.withAbortCheck(
2471
- () => agent.generate(prompt, {
2474
+ return this.withAbortCheck(async () => {
2475
+ const streamResult = await agent.stream(prompt, {
2472
2476
  modelSettings: {
2473
2477
  ...this.observationConfig.modelSettings
2474
2478
  },
2475
2479
  providerOptions: this.observationConfig.providerOptions,
2476
2480
  ...abortSignal ? { abortSignal } : {},
2477
2481
  ...requestContext ? { requestContext } : {}
2478
- }),
2479
- abortSignal
2480
- );
2482
+ });
2483
+ return streamResult.getFullOutput();
2484
+ }, abortSignal);
2481
2485
  };
2482
2486
  let result = await doGenerate();
2483
2487
  let parsed = parseMultiThreadObserverOutput(result.text);
@@ -2535,8 +2539,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2535
2539
  `[OM:callReflector] ${isRetry ? `retry #${attemptNumber - 1}` : "first attempt"}: level=${currentLevel}, originalTokens=${originalTokens}, targetThreshold=${targetThreshold}, promptLen=${prompt.length}, skipContinuationHints=${skipContinuationHints}`
2536
2540
  );
2537
2541
  let chunkCount = 0;
2538
- const result = await this.withAbortCheck(
2539
- () => agent.generate(prompt, {
2542
+ const result = await this.withAbortCheck(async () => {
2543
+ const streamResult = await agent.stream(prompt, {
2540
2544
  modelSettings: {
2541
2545
  ...this.reflectionConfig.modelSettings
2542
2546
  },
@@ -2563,9 +2567,9 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
2563
2567
  omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
2564
2568
  }
2565
2569
  } : {}
2566
- }),
2567
- abortSignal
2568
- );
2570
+ });
2571
+ return streamResult.getFullOutput();
2572
+ }, abortSignal);
2569
2573
  omDebug(
2570
2574
  `[OM:callReflector] attempt #${attemptNumber} returned: textLen=${result.text?.length}, textPreview="${result.text?.slice(0, 120)}...", inputTokens=${result.usage?.inputTokens ?? result.totalUsage?.inputTokens}, outputTokens=${result.usage?.outputTokens ?? result.totalUsage?.outputTokens}`
2571
2575
  );
@@ -2904,19 +2908,17 @@ ${suggestedResponse}
2904
2908
  omDebug(
2905
2909
  `[OM:threshold] activation succeeded, obsTokens=${updatedRecord.observationTokenCount}, activeObsLen=${updatedRecord.activeObservations?.length}`
2906
2910
  );
2907
- if (activationResult.suggestedContinuation || activationResult.currentTask) {
2908
- const thread = await this.storage.getThreadById({ threadId });
2909
- if (thread) {
2910
- const newMetadata = setThreadOMMetadata(thread.metadata, {
2911
- suggestedResponse: activationResult.suggestedContinuation,
2912
- currentTask: activationResult.currentTask
2913
- });
2914
- await this.storage.updateThread({
2915
- id: threadId,
2916
- title: thread.title ?? "",
2917
- metadata: newMetadata
2918
- });
2919
- }
2911
+ const thread = await this.storage.getThreadById({ threadId });
2912
+ if (thread) {
2913
+ const newMetadata = setThreadOMMetadata(thread.metadata, {
2914
+ suggestedResponse: activationResult.suggestedContinuation,
2915
+ currentTask: activationResult.currentTask
2916
+ });
2917
+ await this.storage.updateThread({
2918
+ id: threadId,
2919
+ title: thread.title ?? "",
2920
+ metadata: newMetadata
2921
+ });
2920
2922
  }
2921
2923
  await this.maybeAsyncReflect(
2922
2924
  updatedRecord,
@@ -3272,19 +3274,17 @@ ${suggestedResponse}
3272
3274
  _ObservationalMemory.lastBufferedBoundary.set(bufKey, 0);
3273
3275
  this.storage.setBufferingObservationFlag(record.id, false, 0).catch(() => {
3274
3276
  });
3275
- if (activationResult.suggestedContinuation || activationResult.currentTask) {
3276
- const thread = await this.storage.getThreadById({ threadId });
3277
- if (thread) {
3278
- const newMetadata = setThreadOMMetadata(thread.metadata, {
3279
- suggestedResponse: activationResult.suggestedContinuation,
3280
- currentTask: activationResult.currentTask
3281
- });
3282
- await this.storage.updateThread({
3283
- id: threadId,
3284
- title: thread.title ?? "",
3285
- metadata: newMetadata
3286
- });
3287
- }
3277
+ const thread = await this.storage.getThreadById({ threadId });
3278
+ if (thread) {
3279
+ const newMetadata = setThreadOMMetadata(thread.metadata, {
3280
+ suggestedResponse: activationResult.suggestedContinuation,
3281
+ currentTask: activationResult.currentTask
3282
+ });
3283
+ await this.storage.updateThread({
3284
+ id: threadId,
3285
+ title: thread.title ?? "",
3286
+ metadata: newMetadata
3287
+ });
3288
3288
  }
3289
3289
  await this.maybeReflect({
3290
3290
  record,
@@ -3503,24 +3503,30 @@ ${suggestedResponse}
3503
3503
  return messageList;
3504
3504
  }
3505
3505
  /**
3506
- * Save messages to storage, regenerating IDs for any messages that were
3507
- * previously saved with observation markers (sealed).
3506
+ * Save messages to storage while preventing duplicate inserts for sealed messages.
3508
3507
  *
3509
- * After saving, tracks which messages now have observation markers
3510
- * so their IDs won't be reused in future save cycles.
3508
+ * Sealed messages that do not yet contain a completed observation boundary are
3509
+ * skipped because async buffering already persisted them.
3511
3510
  */
3512
3511
  async saveMessagesWithSealedIdTracking(messagesToSave, sealedIds, threadId, resourceId, state) {
3512
+ const filteredMessages = [];
3513
3513
  for (const msg of messagesToSave) {
3514
3514
  if (sealedIds.has(msg.id)) {
3515
- msg.id = crypto.randomUUID();
3515
+ if (this.findLastCompletedObservationBoundary(msg) !== -1) {
3516
+ filteredMessages.push(msg);
3517
+ }
3518
+ } else {
3519
+ filteredMessages.push(msg);
3516
3520
  }
3517
3521
  }
3518
- await this.messageHistory.persistMessages({
3519
- messages: messagesToSave,
3520
- threadId,
3521
- resourceId
3522
- });
3523
- for (const msg of messagesToSave) {
3522
+ if (filteredMessages.length > 0) {
3523
+ await this.messageHistory.persistMessages({
3524
+ messages: filteredMessages,
3525
+ threadId,
3526
+ resourceId
3527
+ });
3528
+ }
3529
+ for (const msg of filteredMessages) {
3524
3530
  if (this.findLastCompletedObservationBoundary(msg) !== -1) {
3525
3531
  sealedIds.add(msg.id);
3526
3532
  }
@@ -3803,19 +3809,17 @@ ${result.observations}` : result.observations;
3803
3809
  const newMessageIds = messagesToObserve.map((m) => m.id);
3804
3810
  const existingIds = freshRecord?.observedMessageIds ?? record.observedMessageIds ?? [];
3805
3811
  const allObservedIds = [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])];
3806
- if (result.suggestedContinuation || result.currentTask) {
3807
- const thread = await this.storage.getThreadById({ threadId });
3808
- if (thread) {
3809
- const newMetadata = setThreadOMMetadata(thread.metadata, {
3810
- suggestedResponse: result.suggestedContinuation,
3811
- currentTask: result.currentTask
3812
- });
3813
- await this.storage.updateThread({
3814
- id: threadId,
3815
- title: thread.title ?? "",
3816
- metadata: newMetadata
3817
- });
3818
- }
3812
+ const thread = await this.storage.getThreadById({ threadId });
3813
+ if (thread) {
3814
+ const newMetadata = setThreadOMMetadata(thread.metadata, {
3815
+ suggestedResponse: result.suggestedContinuation,
3816
+ currentTask: result.currentTask
3817
+ });
3818
+ await this.storage.updateThread({
3819
+ id: threadId,
3820
+ title: thread.title ?? "",
3821
+ metadata: newMetadata
3822
+ });
3819
3823
  }
3820
3824
  await this.storage.updateActiveObservations({
3821
3825
  id: record.id,
@@ -4051,7 +4055,7 @@ ${result.observations}` : result.observations;
4051
4055
  messagesToBuffer,
4052
4056
  void 0,
4053
4057
  // No abort signal for background ops
4054
- { requestContext }
4058
+ { skipContinuationHints: true, requestContext }
4055
4059
  );
4056
4060
  if (!result.observations) {
4057
4061
  omDebug(`[OM:doAsyncBufferedObservation] empty observations returned, skipping buffer storage`);
@@ -4669,8 +4673,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4669
4673
  if (thread) {
4670
4674
  const newMetadata = setThreadOMMetadata(thread.metadata, {
4671
4675
  lastObservedAt: threadLastObservedAt.toISOString(),
4672
- ...result.suggestedContinuation && { suggestedResponse: result.suggestedContinuation },
4673
- ...result.currentTask && { currentTask: result.currentTask }
4676
+ suggestedResponse: result.suggestedContinuation,
4677
+ currentTask: result.currentTask
4674
4678
  });
4675
4679
  await this.storage.updateThread({
4676
4680
  id: threadId,
@@ -5108,5 +5112,5 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5108
5112
  };
5109
5113
 
5110
5114
  export { OBSERVATIONAL_MEMORY_DEFAULTS, OBSERVATION_CONTEXT_INSTRUCTIONS, OBSERVATION_CONTEXT_PROMPT, OBSERVATION_CONTINUATION_HINT, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, extractCurrentTask, formatMessagesForObserver, hasCurrentTaskSection, optimizeObservationsForContext, parseObserverOutput };
5111
- //# sourceMappingURL=chunk-HNPAIFCZ.js.map
5112
- //# sourceMappingURL=chunk-HNPAIFCZ.js.map
5115
+ //# sourceMappingURL=chunk-A62BQK35.js.map
5116
+ //# sourceMappingURL=chunk-A62BQK35.js.map