@mastra/memory 1.18.2 → 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 (34) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/{chunk-4AQHFADP.cjs → chunk-5IJQOXJM.cjs} +141 -25
  3. package/dist/chunk-5IJQOXJM.cjs.map +1 -0
  4. package/dist/{chunk-LCALB7W6.js → chunk-NZXH5WER.js} +141 -25
  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-SYNXJVL4.js → observational-memory-KFKHBTCB.js} +3 -3
  14. package/dist/{observational-memory-SYNXJVL4.js.map → observational-memory-KFKHBTCB.js.map} +1 -1
  15. package/dist/{observational-memory-7M2T5EOV.cjs → observational-memory-V2APY3TO.cjs} +26 -26
  16. package/dist/{observational-memory-7M2T5EOV.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/observation-turn/turn.d.ts +6 -1
  22. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  23. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observer-agent.d.ts +13 -0
  25. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  26. package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  28. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  29. package/dist/processors/observational-memory/tracing.d.ts.map +1 -1
  30. package/dist/processors/observational-memory/types.d.ts +30 -6
  31. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  32. package/package.json +5 -5
  33. package/dist/chunk-4AQHFADP.cjs.map +0 -1
  34. package/dist/chunk-LCALB7W6.js.map +0 -1
@@ -14,6 +14,47 @@ import { inspect } from 'util';
14
14
  import { AsyncLocalStorage } from 'async_hooks';
15
15
  import imageSize from 'image-size';
16
16
 
17
+ // src/processors/observational-memory/activation-ttl.ts
18
+ var MINUTE = 6e4;
19
+ var HOUR = 60 * MINUTE;
20
+ var SHORT_TTL = 5 * MINUTE;
21
+ var OPENAI_EXTENDED_TTL = HOUR;
22
+ var GEMINI_TTL = 24 * HOUR;
23
+ var DEEPSEEK_TTL = HOUR;
24
+ var GROQ_TTL = 2 * HOUR;
25
+ function normalize(value) {
26
+ return value?.toLowerCase() ?? "";
27
+ }
28
+ function isOpenAIShortTtlModel(modelId) {
29
+ return /^gpt-4/.test(modelId) || /^gpt-5(?:$|-|\.([1-4])(?:$|-))/.test(modelId);
30
+ }
31
+ function getOpenAIPromptCacheRetention(providerOptions) {
32
+ const openaiOptions = providerOptions?.openai;
33
+ return typeof openaiOptions?.promptCacheRetention === "string" ? openaiOptions.promptCacheRetention.toLowerCase() : void 0;
34
+ }
35
+ function resolveActivationTTL(activateAfterIdle, modelContext) {
36
+ if (activateAfterIdle !== "auto") {
37
+ return activateAfterIdle;
38
+ }
39
+ return resolveAutoActivationTTL(modelContext);
40
+ }
41
+ function resolveAutoActivationTTL(modelContext) {
42
+ const provider = normalize(modelContext?.provider);
43
+ const modelId = normalize(modelContext?.modelId);
44
+ if (provider.includes("openai")) {
45
+ const promptCacheRetention = getOpenAIPromptCacheRetention(modelContext?.providerOptions);
46
+ if (promptCacheRetention === "24h") return OPENAI_EXTENDED_TTL;
47
+ if (promptCacheRetention === "in_memory") return SHORT_TTL;
48
+ return isOpenAIShortTtlModel(modelId) ? SHORT_TTL : OPENAI_EXTENDED_TTL;
49
+ }
50
+ if (provider.includes("google") || provider.includes("gemini")) return GEMINI_TTL;
51
+ if (provider.includes("deepseek")) return DEEPSEEK_TTL;
52
+ if (provider.includes("groq")) return GROQ_TTL;
53
+ if (provider.includes("anthropic")) return SHORT_TTL;
54
+ if (provider.includes("xai") || provider.includes("grok")) return SHORT_TTL;
55
+ if (provider.includes("openrouter")) return SHORT_TTL;
56
+ return SHORT_TTL;
57
+ }
17
58
  var OM_DEBUG_LOG = process.env.OM_DEBUG ? join(process.cwd(), "om-debug.log") : null;
18
59
  function omDebug(msg) {
19
60
  if (!OM_DEBUG_LOG) return;
@@ -2440,7 +2481,12 @@ var ObservationTurn = class {
2440
2481
  return this._currentStep;
2441
2482
  }
2442
2483
  /**
2443
- * Finalize the turn: save any remaining messages and return the latest record state.
2484
+ * Finalize the turn: save any remaining messages and return the current cached record.
2485
+ *
2486
+ * When async observation buffering is enabled and there are unobserved messages,
2487
+ * a background buffer operation is kicked off so that observations are computed
2488
+ * proactively while the agent is idle, rather than waiting for the next turn.
2489
+ * The returned record does not wait for that background buffering pass to finish.
2444
2490
  */
2445
2491
  async end() {
2446
2492
  if (this._ended) throw new Error("Turn already ended");
@@ -2451,6 +2497,24 @@ var ObservationTurn = class {
2451
2497
  if (unsavedMessages.length > 0) {
2452
2498
  await this.om.persistMessages(unsavedMessages, this.threadId, this.resourceId);
2453
2499
  }
2500
+ if (this.om.buffering.isAsyncObservationEnabled()) {
2501
+ const allMessages = this.messageList.get.all.db();
2502
+ const record = this._record;
2503
+ const unobservedMessages = this.om.getUnobservedMessages(allMessages, record);
2504
+ if (unobservedMessages.length > 0) {
2505
+ void this.om.buffer({
2506
+ threadId: this.threadId,
2507
+ resourceId: this.resourceId,
2508
+ messages: unobservedMessages,
2509
+ record,
2510
+ writer: this.writer,
2511
+ requestContext: this.requestContext,
2512
+ observabilityContext: this.observabilityContext
2513
+ }).catch((err) => {
2514
+ omDebug(`[OM:turn.end] idle buffer failed: ${err?.message}`);
2515
+ });
2516
+ }
2517
+ }
2454
2518
  return { record: this._record };
2455
2519
  }
2456
2520
  /**
@@ -3122,6 +3186,35 @@ function isImageLikeObserverFilePart(part) {
3122
3186
  }
3123
3187
  return hasObserverImageFilenameExtension(part.filename);
3124
3188
  }
3189
+ function resolveObserverAttachmentMimeType(part) {
3190
+ if (typeof part.mimeType === "string" && part.mimeType.length > 0) {
3191
+ return part.mimeType.toLowerCase();
3192
+ }
3193
+ if (part.type === "image") {
3194
+ return "image/*";
3195
+ }
3196
+ if (isImageLikeObserverFilePart(part)) {
3197
+ return "image/*";
3198
+ }
3199
+ return "application/octet-stream";
3200
+ }
3201
+ function matchObserverMimePattern(mimeType, pattern) {
3202
+ const normalized = pattern.trim().toLowerCase();
3203
+ if (!normalized) return false;
3204
+ if (normalized === "*" || normalized === "*/*") return true;
3205
+ if (normalized.endsWith("/*")) {
3206
+ const prefix = normalized.slice(0, normalized.length - 1);
3207
+ return mimeType.startsWith(prefix);
3208
+ }
3209
+ return mimeType === normalized;
3210
+ }
3211
+ function shouldIncludeObserverAttachment(part, filter) {
3212
+ if (filter === void 0 || filter === true) return true;
3213
+ if (filter === false) return false;
3214
+ if (!Array.isArray(filter) || filter.length === 0) return false;
3215
+ const mimeType = resolveObserverAttachmentMimeType(part);
3216
+ return filter.some((pattern) => matchObserverMimePattern(mimeType, pattern));
3217
+ }
3125
3218
  function toObserverInputAttachmentPart(part) {
3126
3219
  if (part.type === "image") {
3127
3220
  return {
@@ -3220,22 +3313,26 @@ function mapToolResultBlockToAttachment(block) {
3220
3313
  return void 0;
3221
3314
  }
3222
3315
  }
3223
- function extractToolResultAttachments(result, counter) {
3316
+ function extractToolResultAttachments(result, counter, attachmentFilter) {
3224
3317
  if (!isRecord(result) || result.type !== "content" || !Array.isArray(result.value)) {
3225
3318
  return { resultWithoutAttachments: result, attachments: [] };
3226
3319
  }
3227
3320
  const record = result;
3228
3321
  const attachments = [];
3322
+ let hadAttachmentBlocks = false;
3229
3323
  const newValue = record.value.map((block) => {
3230
3324
  const attachment = mapToolResultBlockToAttachment(block);
3231
3325
  if (!attachment) {
3232
3326
  return block;
3233
3327
  }
3234
- attachments.push(toObserverInputAttachmentPart(attachment));
3328
+ hadAttachmentBlocks = true;
3329
+ if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) {
3330
+ attachments.push(toObserverInputAttachmentPart(attachment));
3331
+ }
3235
3332
  const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
3236
3333
  return { type: isRecord(block) ? block.type : void 0, placeholder };
3237
3334
  });
3238
- if (attachments.length === 0) {
3335
+ if (!hadAttachmentBlocks) {
3239
3336
  return { resultWithoutAttachments: result, attachments };
3240
3337
  }
3241
3338
  return { resultWithoutAttachments: { ...record, value: newValue }, attachments };
@@ -3294,6 +3391,7 @@ function getTemporalGapMarkerText(msg) {
3294
3391
  function formatObserverMessage(msg, counter, options) {
3295
3392
  const maxLen = options?.maxPartLength;
3296
3393
  const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
3394
+ const attachmentFilter = options?.attachmentFilter;
3297
3395
  const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
3298
3396
  const attachments = [];
3299
3397
  const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
@@ -3331,7 +3429,8 @@ function formatObserverMessage(msg, counter, options) {
3331
3429
  );
3332
3430
  const { resultWithoutAttachments, attachments: extractedAttachments } = extractToolResultAttachments(
3333
3431
  resultForObserver,
3334
- counter
3432
+ counter,
3433
+ attachmentFilter
3335
3434
  );
3336
3435
  if (extractedAttachments.length > 0) {
3337
3436
  attachments.push(...extractedAttachments);
@@ -3360,9 +3459,11 @@ function formatObserverMessage(msg, counter, options) {
3360
3459
  }
3361
3460
  if (partType === "image" || partType === "file") {
3362
3461
  const attachment = part;
3363
- const inputAttachment = toObserverInputAttachmentPart(attachment);
3364
- if (inputAttachment) {
3365
- attachments.push(inputAttachment);
3462
+ if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) {
3463
+ const inputAttachment = toObserverInputAttachmentPart(attachment);
3464
+ if (inputAttachment) {
3465
+ attachments.push(inputAttachment);
3466
+ }
3366
3467
  }
3367
3468
  pushLine(
3368
3469
  partType === "image" ? "Image" : "File",
@@ -3950,13 +4051,11 @@ async function withOmTracingSpan({
3950
4051
  entityType: EntityType.OUTPUT_STEP_PROCESSOR,
3951
4052
  entityName: config.entityName,
3952
4053
  tracingContext: observabilityContext?.tracingContext ?? observabilityContext?.tracing,
3953
- attributes: {
3954
- metadata: {
3955
- omPhase: phase,
3956
- omInputTokens: inputTokens,
3957
- omSelectedModel: typeof model === "string" ? model : "(dynamic-model)",
3958
- ...metadata
3959
- }
4054
+ metadata: {
4055
+ omPhase: phase,
4056
+ omInputTokens: inputTokens,
4057
+ omSelectedModel: typeof model === "string" ? model : "(dynamic-model)",
4058
+ ...metadata
3960
4059
  },
3961
4060
  requestContext
3962
4061
  });
@@ -4027,7 +4126,9 @@ var ObserverRunner = class {
4027
4126
  includeThreadTitle: this.observationConfig.threadTitle
4028
4127
  })
4029
4128
  },
4030
- buildObserverHistoryMessage(messagesToObserve)
4129
+ buildObserverHistoryMessage(messagesToObserve, {
4130
+ attachmentFilter: this.observationConfig.observeAttachments
4131
+ })
4031
4132
  ];
4032
4133
  const doGenerate = async () => {
4033
4134
  return withRetry(
@@ -4124,7 +4225,9 @@ var ObserverRunner = class {
4124
4225
  this.observationConfig.threadTitle
4125
4226
  )
4126
4227
  },
4127
- buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder)
4228
+ buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder, {
4229
+ attachmentFilter: this.observationConfig.observeAttachments
4230
+ })
4128
4231
  ];
4129
4232
  for (const msgs of messagesByThread.values()) {
4130
4233
  for (const msg of msgs) {
@@ -4955,7 +5058,10 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
4955
5058
  ttlExpiredMs: activationMetadata?.ttlExpiredMs,
4956
5059
  previousModel: activationMetadata?.previousModel,
4957
5060
  currentModel: activationMetadata?.currentModel,
4958
- config: this.getObservationMarkerConfig(freshRecord)
5061
+ config: {
5062
+ ...this.getObservationMarkerConfig(freshRecord),
5063
+ activateAfterIdle: activationMetadata?.activateAfterIdle ?? this.reflectionConfig.activateAfterIdle
5064
+ }
4959
5065
  });
4960
5066
  void writer.custom({ ...activationMarker, transient: true }).catch(() => {
4961
5067
  });
@@ -5018,7 +5124,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5018
5124
  );
5019
5125
  }
5020
5126
  }
5021
- const activateAfterIdle = this.reflectionConfig.activateAfterIdle;
5127
+ const activateAfterIdle = resolveActivationTTL(this.reflectionConfig.activateAfterIdle, currentModel);
5022
5128
  const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
5023
5129
  const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
5024
5130
  const actorModel = getCurrentModel(currentModel);
@@ -5031,6 +5137,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
5031
5137
  const activationMetadata = {
5032
5138
  triggeredBy: activationTriggeredBy,
5033
5139
  lastActivityAt: activationTriggeredBy === "ttl" ? lastActivityAt : void 0,
5140
+ activateAfterIdle: activationTriggeredBy === "ttl" ? activateAfterIdle : void 0,
5034
5141
  ttlExpiredMs: activationTriggeredBy === "ttl" ? ttlExpiredMs : void 0,
5035
5142
  previousModel: activationTriggeredBy === "provider_change" ? lastModel : void 0,
5036
5143
  currentModel: activationTriggeredBy === "provider_change" ? actorModel : void 0
@@ -6871,6 +6978,9 @@ function parseActivationTTL(value, fieldPath) {
6871
6978
  if (value === void 0 || value === false) {
6872
6979
  return void 0;
6873
6980
  }
6981
+ if (value === "auto") {
6982
+ return value;
6983
+ }
6874
6984
  if (typeof value === "number") {
6875
6985
  if (!Number.isFinite(value) || value < 0) {
6876
6986
  throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
@@ -7043,7 +7153,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
7043
7153
  ),
7044
7154
  previousObserverTokens: config.observation?.previousObserverTokens ?? 2e3,
7045
7155
  instruction: config.observation?.instruction,
7046
- threadTitle: config.observation?.threadTitle ?? false
7156
+ threadTitle: config.observation?.threadTitle ?? false,
7157
+ observeAttachments: config.observation?.observeAttachments ?? true
7047
7158
  };
7048
7159
  this.reflectionConfig = {
7049
7160
  model: reflectionModel,
@@ -8966,6 +9077,7 @@ ${grouped}` : grouped;
8966
9077
  }
8967
9078
  let activationTriggeredBy = "threshold";
8968
9079
  let activationLastActivityAt;
9080
+ let activationActivateAfterIdle;
8969
9081
  let activateAfterIdleExpiredMs;
8970
9082
  let previousModel;
8971
9083
  let currentModel;
@@ -8975,7 +9087,7 @@ ${grouped}` : grouped;
8975
9087
  resourceId,
8976
9088
  record.lastObservedAt ? new Date(record.lastObservedAt) : void 0
8977
9089
  );
8978
- const activateAfterIdle = this.observationConfig.activateAfterIdle;
9090
+ const activateAfterIdle = resolveActivationTTL(this.observationConfig.activateAfterIdle, opts.currentModel);
8979
9091
  const lastActivityAt = getLastActivityFromMessages(thresholdMessages);
8980
9092
  const ttlExpiredMs = activateAfterIdle !== void 0 && lastActivityAt !== void 0 ? Date.now() - lastActivityAt : void 0;
8981
9093
  const ttlExpired = ttlExpiredMs !== void 0 && activateAfterIdle !== void 0 && ttlExpiredMs >= activateAfterIdle;
@@ -8989,6 +9101,7 @@ ${grouped}` : grouped;
8989
9101
  } else if (ttlExpired) {
8990
9102
  activationTriggeredBy = "ttl";
8991
9103
  activationLastActivityAt = lastActivityAt;
9104
+ activationActivateAfterIdle = activateAfterIdle;
8992
9105
  activateAfterIdleExpiredMs = ttlExpiredMs;
8993
9106
  } else {
8994
9107
  const status = await this.getStatus({ threadId, resourceId, messages: thresholdMessages });
@@ -9057,7 +9170,10 @@ ${grouped}` : grouped;
9057
9170
  ttlExpiredMs: activateAfterIdleExpiredMs,
9058
9171
  previousModel,
9059
9172
  currentModel,
9060
- config: this.getObservationMarkerConfig()
9173
+ config: {
9174
+ ...this.getObservationMarkerConfig(),
9175
+ activateAfterIdle: activationActivateAfterIdle ?? this.observationConfig.activateAfterIdle
9176
+ }
9061
9177
  });
9062
9178
  void opts.writer.custom({ ...activationMarker, transient: true }).catch(() => {
9063
9179
  });
@@ -9516,7 +9632,7 @@ var ObservationalMemoryProcessor = class {
9516
9632
  const { threadId, resourceId } = context;
9517
9633
  const memoryContext = parseMemoryRequestContext(requestContext);
9518
9634
  const readOnly = memoryContext?.memoryConfig?.readOnly;
9519
- const actorModelContext = model?.modelId ? { provider: model.provider, modelId: model.modelId } : void 0;
9635
+ const actorModelContext = model?.modelId ? { provider: model.provider, modelId: model.modelId, providerOptions: args.providerOptions } : void 0;
9520
9636
  state.__omActorModelContext = actorModelContext;
9521
9637
  return this.engine.getTokenCounter().runWithModelContext(actorModelContext, async () => {
9522
9638
  const reproCaptureEnabled = isOmReproCaptureEnabled();
@@ -9705,5 +9821,5 @@ function getObservationsAsOf(activeObservations, asOf) {
9705
9821
  }
9706
9822
 
9707
9823
  export { ModelByInputTokens, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, ObservationalMemoryProcessor, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, combineObservationGroupRanges, deriveObservationGroupProvenance, extractCurrentTask, formatMessagesForObserver, formatToolResultForObserver, getObservationsAsOf, hasCurrentTaskSection, injectAnchorIds, optimizeObservationsForContext, parseAnchorId, parseObservationGroups, parseObserverOutput, reconcileObservationGroupsFromReflection, renderObservationGroupsForReflection, resolveToolResultValue, stripEphemeralAnchorIds, stripObservationGroups, truncateStringByTokens, wrapInObservationGroup };
9708
- //# sourceMappingURL=chunk-LCALB7W6.js.map
9709
- //# sourceMappingURL=chunk-LCALB7W6.js.map
9824
+ //# sourceMappingURL=chunk-NZXH5WER.js.map
9825
+ //# sourceMappingURL=chunk-NZXH5WER.js.map