@mastra/memory 1.18.3-alpha.0 → 1.19.0-alpha.1

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 (32) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/{chunk-BK3AYI7X.cjs → chunk-5IJQOXJM.cjs} +117 -24
  3. package/dist/chunk-5IJQOXJM.cjs.map +1 -0
  4. package/dist/{chunk-KLETR4RS.js → chunk-NZXH5WER.js} +117 -24
  5. package/dist/chunk-NZXH5WER.js.map +1 -0
  6. package/dist/docs/SKILL.md +2 -1
  7. package/dist/docs/assets/SOURCE_MAP.json +47 -47
  8. package/dist/docs/references/docs-evals-evals-with-memory.md +146 -0
  9. package/dist/docs/references/docs-memory-observational-memory.md +52 -17
  10. package/dist/docs/references/reference-memory-observational-memory.md +5 -3
  11. package/dist/index.cjs +13 -13
  12. package/dist/index.js +4 -4
  13. package/dist/{observational-memory-K5ES5KKQ.js → observational-memory-KFKHBTCB.js} +3 -3
  14. package/dist/{observational-memory-K5ES5KKQ.js.map → observational-memory-KFKHBTCB.js.map} +1 -1
  15. package/dist/{observational-memory-SRGNHILF.cjs → observational-memory-V2APY3TO.cjs} +26 -26
  16. package/dist/{observational-memory-SRGNHILF.cjs.map → observational-memory-V2APY3TO.cjs.map} +1 -1
  17. package/dist/processors/index.cjs +24 -24
  18. package/dist/processors/index.js +1 -1
  19. package/dist/processors/observational-memory/activation-ttl.d.ts +4 -0
  20. package/dist/processors/observational-memory/activation-ttl.d.ts.map +1 -0
  21. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  22. package/dist/processors/observational-memory/observer-agent.d.ts +13 -0
  23. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  26. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/tracing.d.ts.map +1 -1
  28. package/dist/processors/observational-memory/types.d.ts +30 -6
  29. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  30. package/package.json +4 -4
  31. package/dist/chunk-BK3AYI7X.cjs.map +0 -1
  32. package/dist/chunk-KLETR4RS.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.19.0-alpha.1
4
+
5
+ ### Minor Changes
6
+
7
+ - Added `activateAfterIdle: "auto"` for Observational Memory early activation. ([#16663](https://github.com/mastra-ai/mastra/pull/16663))
8
+
9
+ Mastra can now choose an idle activation timeout from the active model provider's prompt cache behavior. OpenAI also respects `providerOptions.openai.promptCacheRetention` when available.
10
+
11
+ ```ts
12
+ const memory = new Memory({
13
+ options: {
14
+ observationalMemory: {
15
+ model: 'google/gemini-2.5-flash',
16
+ activateAfterIdle: 'auto',
17
+ activateOnProviderChange: true,
18
+ },
19
+ },
20
+ });
21
+ ```
22
+
23
+ - Add `observeAttachments` to `ObservationConfig` for Observational Memory. Use it to control whether image/file parts on observed messages are forwarded to the Observer model alongside their placeholder text lines. ([#16671](https://github.com/mastra-ai/mastra/pull/16671))
24
+ - `true` (default) — forward all attachments (existing behavior).
25
+ - `false` — drop all attachments; placeholders still appear in the observer transcript.
26
+ - `string[]` — allowlist of mimeType patterns, e.g. `['image/*']` or `['application/pdf']`. Matching is case-insensitive and supports exact, `type/*`, and `*` patterns.
27
+
28
+ Useful when the Observer model is text-only (some DeepSeek endpoints, etc.) while the main agent uses a multimodal model. Tool-result attachments are filtered with the same rule.
29
+
30
+ ```ts
31
+ new Memory({
32
+ options: {
33
+ observationalMemory: {
34
+ observation: {
35
+ model: 'deepseek/deepseek-chat', // text-only observer
36
+ observeAttachments: false, // or e.g. ['image/*', 'application/pdf']
37
+ },
38
+ },
39
+ },
40
+ });
41
+ ```
42
+
43
+ ### Patch Changes
44
+
45
+ - Updated dependencies [[`c272d50`](https://github.com/mastra-ai/mastra/commit/c272d50610a54496b6b6d92ccd4d37b333a2613a), [`d8692af`](https://github.com/mastra-ai/mastra/commit/d8692afa253028e39cdce2aafa0ac414071a762e), [`841a222`](https://github.com/mastra-ai/mastra/commit/841a222560d8c19238f8213713f30535cdd82284)]:
46
+ - @mastra/core@1.36.0-alpha.4
47
+
3
48
  ## 1.18.3-alpha.0
4
49
 
5
50
  ### Patch Changes
@@ -21,6 +21,47 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
  var xxhash__default = /*#__PURE__*/_interopDefault(xxhash);
22
22
  var imageSize__default = /*#__PURE__*/_interopDefault(imageSize);
23
23
 
24
+ // src/processors/observational-memory/activation-ttl.ts
25
+ var MINUTE = 6e4;
26
+ var HOUR = 60 * MINUTE;
27
+ var SHORT_TTL = 5 * MINUTE;
28
+ var OPENAI_EXTENDED_TTL = HOUR;
29
+ var GEMINI_TTL = 24 * HOUR;
30
+ var DEEPSEEK_TTL = HOUR;
31
+ var GROQ_TTL = 2 * HOUR;
32
+ function normalize(value) {
33
+ return value?.toLowerCase() ?? "";
34
+ }
35
+ function isOpenAIShortTtlModel(modelId) {
36
+ return /^gpt-4/.test(modelId) || /^gpt-5(?:$|-|\.([1-4])(?:$|-))/.test(modelId);
37
+ }
38
+ function getOpenAIPromptCacheRetention(providerOptions) {
39
+ const openaiOptions = providerOptions?.openai;
40
+ return typeof openaiOptions?.promptCacheRetention === "string" ? openaiOptions.promptCacheRetention.toLowerCase() : void 0;
41
+ }
42
+ function resolveActivationTTL(activateAfterIdle, modelContext) {
43
+ if (activateAfterIdle !== "auto") {
44
+ return activateAfterIdle;
45
+ }
46
+ return resolveAutoActivationTTL(modelContext);
47
+ }
48
+ function resolveAutoActivationTTL(modelContext) {
49
+ const provider = normalize(modelContext?.provider);
50
+ const modelId = normalize(modelContext?.modelId);
51
+ if (provider.includes("openai")) {
52
+ const promptCacheRetention = getOpenAIPromptCacheRetention(modelContext?.providerOptions);
53
+ if (promptCacheRetention === "24h") return OPENAI_EXTENDED_TTL;
54
+ if (promptCacheRetention === "in_memory") return SHORT_TTL;
55
+ return isOpenAIShortTtlModel(modelId) ? SHORT_TTL : OPENAI_EXTENDED_TTL;
56
+ }
57
+ if (provider.includes("google") || provider.includes("gemini")) return GEMINI_TTL;
58
+ if (provider.includes("deepseek")) return DEEPSEEK_TTL;
59
+ if (provider.includes("groq")) return GROQ_TTL;
60
+ if (provider.includes("anthropic")) return SHORT_TTL;
61
+ if (provider.includes("xai") || provider.includes("grok")) return SHORT_TTL;
62
+ if (provider.includes("openrouter")) return SHORT_TTL;
63
+ return SHORT_TTL;
64
+ }
24
65
  var OM_DEBUG_LOG = process.env.OM_DEBUG ? path.join(process.cwd(), "om-debug.log") : null;
25
66
  function omDebug(msg) {
26
67
  if (!OM_DEBUG_LOG) return;
@@ -3152,6 +3193,35 @@ function isImageLikeObserverFilePart(part) {
3152
3193
  }
3153
3194
  return hasObserverImageFilenameExtension(part.filename);
3154
3195
  }
3196
+ function resolveObserverAttachmentMimeType(part) {
3197
+ if (typeof part.mimeType === "string" && part.mimeType.length > 0) {
3198
+ return part.mimeType.toLowerCase();
3199
+ }
3200
+ if (part.type === "image") {
3201
+ return "image/*";
3202
+ }
3203
+ if (isImageLikeObserverFilePart(part)) {
3204
+ return "image/*";
3205
+ }
3206
+ return "application/octet-stream";
3207
+ }
3208
+ function matchObserverMimePattern(mimeType, pattern) {
3209
+ const normalized = pattern.trim().toLowerCase();
3210
+ if (!normalized) return false;
3211
+ if (normalized === "*" || normalized === "*/*") return true;
3212
+ if (normalized.endsWith("/*")) {
3213
+ const prefix = normalized.slice(0, normalized.length - 1);
3214
+ return mimeType.startsWith(prefix);
3215
+ }
3216
+ return mimeType === normalized;
3217
+ }
3218
+ function shouldIncludeObserverAttachment(part, filter) {
3219
+ if (filter === void 0 || filter === true) return true;
3220
+ if (filter === false) return false;
3221
+ if (!Array.isArray(filter) || filter.length === 0) return false;
3222
+ const mimeType = resolveObserverAttachmentMimeType(part);
3223
+ return filter.some((pattern) => matchObserverMimePattern(mimeType, pattern));
3224
+ }
3155
3225
  function toObserverInputAttachmentPart(part) {
3156
3226
  if (part.type === "image") {
3157
3227
  return {
@@ -3250,22 +3320,26 @@ function mapToolResultBlockToAttachment(block) {
3250
3320
  return void 0;
3251
3321
  }
3252
3322
  }
3253
- function extractToolResultAttachments(result, counter) {
3323
+ function extractToolResultAttachments(result, counter, attachmentFilter) {
3254
3324
  if (!isRecord(result) || result.type !== "content" || !Array.isArray(result.value)) {
3255
3325
  return { resultWithoutAttachments: result, attachments: [] };
3256
3326
  }
3257
3327
  const record = result;
3258
3328
  const attachments = [];
3329
+ let hadAttachmentBlocks = false;
3259
3330
  const newValue = record.value.map((block) => {
3260
3331
  const attachment = mapToolResultBlockToAttachment(block);
3261
3332
  if (!attachment) {
3262
3333
  return block;
3263
3334
  }
3264
- attachments.push(toObserverInputAttachmentPart(attachment));
3335
+ hadAttachmentBlocks = true;
3336
+ if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) {
3337
+ attachments.push(toObserverInputAttachmentPart(attachment));
3338
+ }
3265
3339
  const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
3266
3340
  return { type: isRecord(block) ? block.type : void 0, placeholder };
3267
3341
  });
3268
- if (attachments.length === 0) {
3342
+ if (!hadAttachmentBlocks) {
3269
3343
  return { resultWithoutAttachments: result, attachments };
3270
3344
  }
3271
3345
  return { resultWithoutAttachments: { ...record, value: newValue }, attachments };
@@ -3324,6 +3398,7 @@ function getTemporalGapMarkerText(msg) {
3324
3398
  function formatObserverMessage(msg, counter, options) {
3325
3399
  const maxLen = options?.maxPartLength;
3326
3400
  const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
3401
+ const attachmentFilter = options?.attachmentFilter;
3327
3402
  const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
3328
3403
  const attachments = [];
3329
3404
  const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
@@ -3361,7 +3436,8 @@ function formatObserverMessage(msg, counter, options) {
3361
3436
  );
3362
3437
  const { resultWithoutAttachments, attachments: extractedAttachments } = extractToolResultAttachments(
3363
3438
  resultForObserver,
3364
- counter
3439
+ counter,
3440
+ attachmentFilter
3365
3441
  );
3366
3442
  if (extractedAttachments.length > 0) {
3367
3443
  attachments.push(...extractedAttachments);
@@ -3390,9 +3466,11 @@ function formatObserverMessage(msg, counter, options) {
3390
3466
  }
3391
3467
  if (partType === "image" || partType === "file") {
3392
3468
  const attachment = part;
3393
- const inputAttachment = toObserverInputAttachmentPart(attachment);
3394
- if (inputAttachment) {
3395
- attachments.push(inputAttachment);
3469
+ if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) {
3470
+ const inputAttachment = toObserverInputAttachmentPart(attachment);
3471
+ if (inputAttachment) {
3472
+ attachments.push(inputAttachment);
3473
+ }
3396
3474
  }
3397
3475
  pushLine(
3398
3476
  partType === "image" ? "Image" : "File",
@@ -3980,13 +4058,11 @@ async function withOmTracingSpan({
3980
4058
  entityType: observability.EntityType.OUTPUT_STEP_PROCESSOR,
3981
4059
  entityName: config.entityName,
3982
4060
  tracingContext: observabilityContext?.tracingContext ?? observabilityContext?.tracing,
3983
- attributes: {
3984
- metadata: {
3985
- omPhase: phase,
3986
- omInputTokens: inputTokens,
3987
- omSelectedModel: typeof model === "string" ? model : "(dynamic-model)",
3988
- ...metadata
3989
- }
4061
+ metadata: {
4062
+ omPhase: phase,
4063
+ omInputTokens: inputTokens,
4064
+ omSelectedModel: typeof model === "string" ? model : "(dynamic-model)",
4065
+ ...metadata
3990
4066
  },
3991
4067
  requestContext
3992
4068
  });
@@ -4057,7 +4133,9 @@ var ObserverRunner = class {
4057
4133
  includeThreadTitle: this.observationConfig.threadTitle
4058
4134
  })
4059
4135
  },
4060
- buildObserverHistoryMessage(messagesToObserve)
4136
+ buildObserverHistoryMessage(messagesToObserve, {
4137
+ attachmentFilter: this.observationConfig.observeAttachments
4138
+ })
4061
4139
  ];
4062
4140
  const doGenerate = async () => {
4063
4141
  return withRetry(
@@ -4154,7 +4232,9 @@ var ObserverRunner = class {
4154
4232
  this.observationConfig.threadTitle
4155
4233
  )
4156
4234
  },
4157
- buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder)
4235
+ buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder, {
4236
+ attachmentFilter: this.observationConfig.observeAttachments
4237
+ })
4158
4238
  ];
4159
4239
  for (const msgs of messagesByThread.values()) {
4160
4240
  for (const msg of msgs) {
@@ -4985,7 +5065,10 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4985
5065
  ttlExpiredMs: activationMetadata?.ttlExpiredMs,
4986
5066
  previousModel: activationMetadata?.previousModel,
4987
5067
  currentModel: activationMetadata?.currentModel,
4988
- config: this.getObservationMarkerConfig(freshRecord)
5068
+ config: {
5069
+ ...this.getObservationMarkerConfig(freshRecord),
5070
+ activateAfterIdle: activationMetadata?.activateAfterIdle ?? this.reflectionConfig.activateAfterIdle
5071
+ }
4989
5072
  });
4990
5073
  void writer.custom({ ...activationMarker, transient: true }).catch(() => {
4991
5074
  });
@@ -5048,7 +5131,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5048
5131
  );
5049
5132
  }
5050
5133
  }
5051
- const activateAfterIdle = this.reflectionConfig.activateAfterIdle;
5134
+ const activateAfterIdle = resolveActivationTTL(this.reflectionConfig.activateAfterIdle, currentModel);
5052
5135
  const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
5053
5136
  const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
5054
5137
  const actorModel = getCurrentModel(currentModel);
@@ -5061,6 +5144,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5061
5144
  const activationMetadata = {
5062
5145
  triggeredBy: activationTriggeredBy,
5063
5146
  lastActivityAt: activationTriggeredBy === "ttl" ? lastActivityAt : void 0,
5147
+ activateAfterIdle: activationTriggeredBy === "ttl" ? activateAfterIdle : void 0,
5064
5148
  ttlExpiredMs: activationTriggeredBy === "ttl" ? ttlExpiredMs : void 0,
5065
5149
  previousModel: activationTriggeredBy === "provider_change" ? lastModel : void 0,
5066
5150
  currentModel: activationTriggeredBy === "provider_change" ? actorModel : void 0
@@ -6901,6 +6985,9 @@ function parseActivationTTL(value, fieldPath) {
6901
6985
  if (value === void 0 || value === false) {
6902
6986
  return void 0;
6903
6987
  }
6988
+ if (value === "auto") {
6989
+ return value;
6990
+ }
6904
6991
  if (typeof value === "number") {
6905
6992
  if (!Number.isFinite(value) || value < 0) {
6906
6993
  throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
@@ -7073,7 +7160,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
7073
7160
  ),
7074
7161
  previousObserverTokens: config.observation?.previousObserverTokens ?? 2e3,
7075
7162
  instruction: config.observation?.instruction,
7076
- threadTitle: config.observation?.threadTitle ?? false
7163
+ threadTitle: config.observation?.threadTitle ?? false,
7164
+ observeAttachments: config.observation?.observeAttachments ?? true
7077
7165
  };
7078
7166
  this.reflectionConfig = {
7079
7167
  model: reflectionModel,
@@ -8996,6 +9084,7 @@ ${grouped}` : grouped;
8996
9084
  }
8997
9085
  let activationTriggeredBy = "threshold";
8998
9086
  let activationLastActivityAt;
9087
+ let activationActivateAfterIdle;
8999
9088
  let activateAfterIdleExpiredMs;
9000
9089
  let previousModel;
9001
9090
  let currentModel;
@@ -9005,7 +9094,7 @@ ${grouped}` : grouped;
9005
9094
  resourceId,
9006
9095
  record.lastObservedAt ? new Date(record.lastObservedAt) : void 0
9007
9096
  );
9008
- const activateAfterIdle = this.observationConfig.activateAfterIdle;
9097
+ const activateAfterIdle = resolveActivationTTL(this.observationConfig.activateAfterIdle, opts.currentModel);
9009
9098
  const lastActivityAt = getLastActivityFromMessages(thresholdMessages);
9010
9099
  const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
9011
9100
  const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
@@ -9019,6 +9108,7 @@ ${grouped}` : grouped;
9019
9108
  } else if (ttlExpired) {
9020
9109
  activationTriggeredBy = "ttl";
9021
9110
  activationLastActivityAt = lastActivityAt;
9111
+ activationActivateAfterIdle = activateAfterIdle;
9022
9112
  activateAfterIdleExpiredMs = ttlExpiredMs;
9023
9113
  } else {
9024
9114
  const status = await this.getStatus({ threadId, resourceId, messages: thresholdMessages });
@@ -9087,7 +9177,10 @@ ${grouped}` : grouped;
9087
9177
  ttlExpiredMs: activateAfterIdleExpiredMs,
9088
9178
  previousModel,
9089
9179
  currentModel,
9090
- config: this.getObservationMarkerConfig()
9180
+ config: {
9181
+ ...this.getObservationMarkerConfig(),
9182
+ activateAfterIdle: activationActivateAfterIdle ?? this.observationConfig.activateAfterIdle
9183
+ }
9091
9184
  });
9092
9185
  void opts.writer.custom({ ...activationMarker, transient: true }).catch(() => {
9093
9186
  });
@@ -9546,7 +9639,7 @@ var ObservationalMemoryProcessor = class {
9546
9639
  const { threadId, resourceId } = context;
9547
9640
  const memoryContext = memory.parseMemoryRequestContext(requestContext);
9548
9641
  const readOnly = memoryContext?.memoryConfig?.readOnly;
9549
- const actorModelContext = model?.modelId ? { provider: model.provider, modelId: model.modelId } : void 0;
9642
+ const actorModelContext = model?.modelId ? { provider: model.provider, modelId: model.modelId, providerOptions: args.providerOptions } : void 0;
9550
9643
  state.__omActorModelContext = actorModelContext;
9551
9644
  return this.engine.getTokenCounter().runWithModelContext(actorModelContext, async () => {
9552
9645
  const reproCaptureEnabled = isOmReproCaptureEnabled();
@@ -9760,5 +9853,5 @@ exports.stripEphemeralAnchorIds = stripEphemeralAnchorIds;
9760
9853
  exports.stripObservationGroups = stripObservationGroups;
9761
9854
  exports.truncateStringByTokens = truncateStringByTokens;
9762
9855
  exports.wrapInObservationGroup = wrapInObservationGroup;
9763
- //# sourceMappingURL=chunk-BK3AYI7X.cjs.map
9764
- //# sourceMappingURL=chunk-BK3AYI7X.cjs.map
9856
+ //# sourceMappingURL=chunk-5IJQOXJM.cjs.map
9857
+ //# sourceMappingURL=chunk-5IJQOXJM.cjs.map