@mastra/memory 1.26.1-alpha.0 → 1.26.1-alpha.3

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.
@@ -18521,33 +18521,62 @@ var TokenCounter = class TokenCounter {
18521
18521
  if (isImageAttachment) await resolveImageDimensionsAsync(part);
18522
18522
  return this.countAttachmentPartSync(part);
18523
18523
  }
18524
+ /**
18525
+ * Count the name and the arguments of a tool call. Every state before the tool produces an
18526
+ * output holds the same call signature in the context window, so all of those states share
18527
+ * these cache kinds. `buildEstimateKey` hashes the text, so a shared kind stays correct and
18528
+ * keeps the estimate warm while the invocation moves from one state to the next.
18529
+ */
18530
+ countToolCallSignature(part, invocation) {
18531
+ let tokens = 0;
18532
+ let overheadDelta = 0;
18533
+ if (invocation.toolName) tokens += this.readOrPersistPartEstimate(part, "tool-call-name", invocation.toolName);
18534
+ if (invocation.args) if (typeof invocation.args === "string") tokens += this.readOrPersistPartEstimate(part, "tool-call-args", invocation.args);
18535
+ else {
18536
+ const argsJson = JSON.stringify(invocation.args);
18537
+ tokens += this.readOrPersistPartEstimate(part, "tool-call-args-json", argsJson);
18538
+ overheadDelta -= 12;
18539
+ }
18540
+ return {
18541
+ tokens,
18542
+ overheadDelta
18543
+ };
18544
+ }
18524
18545
  countNonAttachmentPart(part) {
18525
18546
  let overheadDelta = 0;
18526
- let toolResultDelta = 0;
18547
+ let extraMessageDelta = 0;
18527
18548
  if (part.type === "text") return {
18528
18549
  tokens: this.readOrPersistPartEstimate(part, "text", part.text),
18529
18550
  overheadDelta,
18530
- toolResultDelta
18551
+ extraMessageDelta
18531
18552
  };
18532
18553
  if (part.type === "tool-invocation") {
18533
18554
  const invocation = part.toolInvocation;
18555
+ const state = invocation.state;
18534
18556
  let tokens = 0;
18535
- if (invocation.state === "call" || invocation.state === "partial-call") {
18536
- if (invocation.toolName) tokens += this.readOrPersistPartEstimate(part, `tool-${invocation.state}-name`, invocation.toolName);
18537
- if (invocation.args) if (typeof invocation.args === "string") tokens += this.readOrPersistPartEstimate(part, `tool-${invocation.state}-args`, invocation.args);
18538
- else {
18539
- const argsJson = JSON.stringify(invocation.args);
18540
- tokens += this.readOrPersistPartEstimate(part, `tool-${invocation.state}-args-json`, argsJson);
18541
- overheadDelta -= 12;
18542
- }
18557
+ if (state === "call" || state === "partial-call" || state === "approval-requested") {
18558
+ const signature = this.countToolCallSignature(part, invocation);
18559
+ return {
18560
+ tokens: signature.tokens,
18561
+ overheadDelta: overheadDelta + signature.overheadDelta,
18562
+ extraMessageDelta
18563
+ };
18564
+ }
18565
+ if (state === "approval-responded") {
18566
+ extraMessageDelta++;
18567
+ const signature = this.countToolCallSignature(part, invocation);
18568
+ tokens += signature.tokens;
18569
+ overheadDelta += signature.overheadDelta;
18570
+ const reason = invocation.approval?.reason;
18571
+ if (reason) tokens += this.readOrPersistPartEstimate(part, "tool-approval-reason", reason);
18543
18572
  return {
18544
18573
  tokens,
18545
18574
  overheadDelta,
18546
- toolResultDelta
18575
+ extraMessageDelta
18547
18576
  };
18548
18577
  }
18549
- if (invocation.state === "result") {
18550
- toolResultDelta++;
18578
+ if (state === "result") {
18579
+ extraMessageDelta++;
18551
18580
  const { value: resultForCounting, usingStoredModelOutput } = this.resolveToolResultForTokenCounting(part, invocation.result);
18552
18581
  if (resultForCounting !== void 0) {
18553
18582
  const contentTokens = this.countMultimodalToolResultContent(part, resultForCounting);
@@ -18561,47 +18590,45 @@ var TokenCounter = class TokenCounter {
18561
18590
  return {
18562
18591
  tokens,
18563
18592
  overheadDelta,
18564
- toolResultDelta
18593
+ extraMessageDelta
18565
18594
  };
18566
18595
  }
18567
- if (invocation.state === "output-denied") {
18568
- toolResultDelta++;
18596
+ if (state === "output-denied") {
18597
+ extraMessageDelta++;
18569
18598
  const reason = invocation.approval?.reason ?? "Tool call was not approved by the user";
18570
18599
  tokens += this.readOrPersistPartEstimate(part, "tool-result-denied", reason);
18571
18600
  return {
18572
18601
  tokens,
18573
18602
  overheadDelta,
18574
- toolResultDelta
18603
+ extraMessageDelta
18575
18604
  };
18576
18605
  }
18577
- if (invocation.state === "output-error") {
18578
- toolResultDelta++;
18579
- const errorText = invocation.errorText;
18580
- const errorMessage = typeof errorText === "string" ? errorText : "Tool execution failed";
18606
+ if (state === "output-error") {
18607
+ extraMessageDelta++;
18608
+ const errorMessage = typeof invocation.errorText === "string" ? invocation.errorText : "Tool execution failed";
18581
18609
  tokens += this.readOrPersistPartEstimate(part, "tool-result-error", errorMessage);
18582
18610
  return {
18583
18611
  tokens,
18584
18612
  overheadDelta,
18585
- toolResultDelta
18613
+ extraMessageDelta
18586
18614
  };
18587
18615
  }
18588
- throw new Error(`Unhandled tool-invocation state '${part.toolInvocation?.state}' in token counting for part type '${part.type}'`);
18589
18616
  }
18590
18617
  if (typeof part.type === "string" && part.type.startsWith("data-")) return {
18591
18618
  tokens: 0,
18592
18619
  overheadDelta,
18593
- toolResultDelta
18620
+ extraMessageDelta
18594
18621
  };
18595
18622
  if (part.type === "reasoning") return {
18596
18623
  tokens: 0,
18597
18624
  overheadDelta,
18598
- toolResultDelta
18625
+ extraMessageDelta
18599
18626
  };
18600
18627
  const serialized = serializePartForTokenCounting(part);
18601
18628
  return {
18602
18629
  tokens: this.readOrPersistPartEstimate(part, `part-${part.type}`, serialized),
18603
18630
  overheadDelta,
18604
- toolResultDelta
18631
+ extraMessageDelta
18605
18632
  };
18606
18633
  }
18607
18634
  /**
@@ -18610,7 +18637,7 @@ var TokenCounter = class TokenCounter {
18610
18637
  countMessage(message) {
18611
18638
  let payloadTokens = this.countString(message.role);
18612
18639
  let overhead = TokenCounter.TOKENS_PER_MESSAGE;
18613
- let toolResultCount = 0;
18640
+ let extraMessageCount = 0;
18614
18641
  if (typeof message.content === "string") payloadTokens += this.readOrPersistMessageEstimate(message, "message-content", message.content);
18615
18642
  else if (message.content && typeof message.content === "object") {
18616
18643
  if (message.content.content && !Array.isArray(message.content.parts)) payloadTokens += this.readOrPersistMessageEstimate(message, "content-content", message.content.content);
@@ -18623,16 +18650,16 @@ var TokenCounter = class TokenCounter {
18623
18650
  const result = this.countNonAttachmentPart(part);
18624
18651
  payloadTokens += result.tokens;
18625
18652
  overhead += result.overheadDelta;
18626
- toolResultCount += result.toolResultDelta;
18653
+ extraMessageCount += result.extraMessageDelta;
18627
18654
  }
18628
18655
  }
18629
- if (toolResultCount > 0) overhead += toolResultCount * TokenCounter.TOKENS_PER_MESSAGE;
18656
+ if (extraMessageCount > 0) overhead += extraMessageCount * TokenCounter.TOKENS_PER_MESSAGE;
18630
18657
  return Math.round(payloadTokens + overhead);
18631
18658
  }
18632
18659
  async countMessageAsync(message) {
18633
18660
  let payloadTokens = this.countString(message.role);
18634
18661
  let overhead = TokenCounter.TOKENS_PER_MESSAGE;
18635
- let toolResultCount = 0;
18662
+ let extraMessageCount = 0;
18636
18663
  if (typeof message.content === "string") payloadTokens += this.readOrPersistMessageEstimate(message, "message-content", message.content);
18637
18664
  else if (message.content && typeof message.content === "object") {
18638
18665
  if (message.content.content && !Array.isArray(message.content.parts)) payloadTokens += this.readOrPersistMessageEstimate(message, "content-content", message.content.content);
@@ -18645,10 +18672,10 @@ var TokenCounter = class TokenCounter {
18645
18672
  const result = this.countNonAttachmentPart(part);
18646
18673
  payloadTokens += result.tokens;
18647
18674
  overhead += result.overheadDelta;
18648
- toolResultCount += result.toolResultDelta;
18675
+ extraMessageCount += result.extraMessageDelta;
18649
18676
  }
18650
18677
  }
18651
- if (toolResultCount > 0) overhead += toolResultCount * TokenCounter.TOKENS_PER_MESSAGE;
18678
+ if (extraMessageCount > 0) overhead += extraMessageCount * TokenCounter.TOKENS_PER_MESSAGE;
18652
18679
  return Math.round(payloadTokens + overhead);
18653
18680
  }
18654
18681
  /**
@@ -18959,9 +18986,10 @@ async function listThreadsForResource({ memory, resourceId, currentThreadId, pag
18959
18986
  hasMore
18960
18987
  };
18961
18988
  }
18989
+ const SEARCH_NOT_CONFIGURED_MESSAGE = "Search is not configured. Enable it with `retrieval: { vector: true }` and configure a vector store and embedder on your Memory instance.";
18962
18990
  async function searchMessagesForResource({ memory, resourceId, currentThreadId, query, topK = 10, maxTokens = DEFAULT_MAX_RESULT_TOKENS, before, after, threadScope }) {
18963
18991
  if (!memory.searchMessages) return {
18964
- results: "Search is not configured. Enable it with `retrieval: { vector: true }` and configure a vector store and embedder on your Memory instance.",
18992
+ results: SEARCH_NOT_CONFIGURED_MESSAGE,
18965
18993
  count: 0
18966
18994
  };
18967
18995
  const MAX_TOPK = 20;
@@ -19507,9 +19535,16 @@ async function recallThreadFromStart({ memory, threadId, resourceId, page = 1, l
19507
19535
  }
19508
19536
  const recallTool = (_memoryConfig, options) => {
19509
19537
  const isResourceScope = (options?.retrievalScope ?? "thread") === "resource";
19538
+ const searchEnabled = options?.searchEnabled ?? true;
19539
+ const description = isResourceScope ? `Browse conversation history. Use mode="threads" to list all threads for the current user. Use mode="messages" (default) to browse messages in the current thread or pass threadId to browse another thread in the active resource. When mode="messages" has no cursor or threadId, it defaults to the current thread and says so at the top of the result. If you pass only a cursor, it must belong to the current thread.${searchEnabled ? " Use mode=\"search\" to find messages by content across all threads." : ""}` : `Browse conversation history in the current thread. Use mode="messages" (default) to page through messages near a cursor.${searchEnabled ? " Use mode=\"search\" to find messages by content in this thread." : ""} Use mode="threads" to get the current thread's ID and title.`;
19540
+ const modeEnum = searchEnabled ? [
19541
+ "messages",
19542
+ "threads",
19543
+ "search"
19544
+ ] : ["messages", "threads"];
19510
19545
  return createTool({
19511
19546
  id: "recall",
19512
- description: isResourceScope ? "Browse conversation history. Use mode=\"threads\" to list all threads for the current user. Use mode=\"messages\" (default) to browse messages in the current thread or pass threadId to browse another thread in the active resource. When mode=\"messages\" has no cursor or threadId, it defaults to the current thread and says so at the top of the result. If you pass only a cursor, it must belong to the current thread. Use mode=\"search\" to find messages by content across all threads." : "Browse conversation history in the current thread. Use mode=\"messages\" (default) to page through messages near a cursor. Use mode=\"search\" to find messages by content in this thread. Use mode=\"threads\" to get the current thread's ID and title.",
19547
+ description,
19513
19548
  inputSchema: {
19514
19549
  $schema: "http://json-schema.org/draft-07/schema#",
19515
19550
  type: "object",
@@ -19517,12 +19552,8 @@ const recallTool = (_memoryConfig, options) => {
19517
19552
  ...isResourceScope ? {
19518
19553
  mode: {
19519
19554
  type: "string",
19520
- enum: [
19521
- "messages",
19522
- "threads",
19523
- "search"
19524
- ],
19525
- description: "What to retrieve. \"messages\" (default) pages through message history. \"threads\" lists all threads for the current user. \"search\" finds messages by semantic similarity across all threads."
19555
+ enum: modeEnum,
19556
+ description: `What to retrieve. "messages" (default) pages through message history. "threads" lists all threads for the current user.${searchEnabled ? " \"search\" finds messages by semantic similarity across all threads." : ""}`
19526
19557
  },
19527
19558
  threadId: {
19528
19559
  type: "string",
@@ -19539,18 +19570,14 @@ const recallTool = (_memoryConfig, options) => {
19539
19570
  }
19540
19571
  } : { mode: {
19541
19572
  type: "string",
19542
- enum: [
19543
- "messages",
19544
- "threads",
19545
- "search"
19546
- ],
19547
- description: "What to retrieve. \"messages\" (default) pages through message history. \"threads\" returns info about the current thread. \"search\" finds messages by semantic similarity in this thread."
19573
+ enum: modeEnum,
19574
+ description: `What to retrieve. "messages" (default) pages through message history. "threads" returns info about the current thread.${searchEnabled ? " \"search\" finds messages by semantic similarity in this thread." : ""}`
19548
19575
  } },
19549
- query: {
19576
+ ...searchEnabled ? { query: {
19550
19577
  type: "string",
19551
19578
  minLength: 1,
19552
19579
  description: "Search query for mode=\"search\". Finds messages semantically similar to this text."
19553
- },
19580
+ } } : {},
19554
19581
  cursor: {
19555
19582
  type: "string",
19556
19583
  minLength: 1,
@@ -19611,6 +19638,10 @@ const recallTool = (_memoryConfig, options) => {
19611
19638
  if (!memory) throw new Error("Memory instance is required for recall");
19612
19639
  if (explicitThreadId === "current" && !currentThreadId) throw new Error("Could not resolve current thread.");
19613
19640
  if (mode === "search") {
19641
+ if (!searchEnabled) return {
19642
+ results: SEARCH_NOT_CONFIGURED_MESSAGE,
19643
+ count: 0
19644
+ };
19614
19645
  if (!query) throw new Error("query is required for mode=\"search\"");
19615
19646
  if (!resourceId) throw new Error("Resource ID is required for recall");
19616
19647
  return searchMessagesForResource({
@@ -19747,6 +19778,7 @@ function deepMergeWorkingMemory(existing, update) {
19747
19778
  for (const key of Object.keys(update)) {
19748
19779
  const updateValue = update[key];
19749
19780
  const existingValue = result[key];
19781
+ if (updateValue === void 0) continue;
19750
19782
  if (updateValue === null) delete result[key];
19751
19783
  else if (Array.isArray(updateValue)) result[key] = updateValue;
19752
19784
  else if (typeof updateValue === "object" && updateValue !== null && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) result[key] = deepMergeWorkingMemory(existingValue, updateValue);
@@ -19799,7 +19831,7 @@ const updateWorkingMemoryTool = (memoryConfig) => {
19799
19831
  version: 1,
19800
19832
  vendor: "mastra",
19801
19833
  validate: (value) => {
19802
- const memoryValue = !!value && typeof value === "object" && !Array.isArray(value) && "memory" in value ? value.memory : stripNullsFromOptional(value, jsonSchema);
19834
+ const memoryValue = stripNullsFromOptional(!!value && typeof value === "object" && !Array.isArray(value) && "memory" in value ? value.memory : value, jsonSchema);
19803
19835
  const result = validateMemory(memoryValue);
19804
19836
  return result instanceof Promise ? result.then(toWrappedResult) : toWrappedResult(result);
19805
19837
  },
@@ -19816,6 +19848,7 @@ const updateWorkingMemoryTool = (memoryConfig) => {
19816
19848
  id: "update-working-memory",
19817
19849
  description: schema ? useStateSignals ? `${stateSignalsPreamble} Data is merged with existing memory — only include fields you want to add or update.` : `Update the working memory with new information. Data is merged with existing memory - only include fields you want to add or update. To preserve existing data, omit the field entirely. Arrays are replaced entirely when provided, so pass the complete array or omit it to keep the existing values.` : useStateSignals ? `${stateSignalsPreamble} Pass the full updated Markdown blob as a string in the memory field.` : `Update the working memory with new information. Any data not included will be overwritten. Always pass data as string to the memory field. Never pass an object.`,
19818
19850
  inputSchema,
19851
+ ...usesMergeSemantics ? { strict: false } : {},
19819
19852
  execute: async (inputData, context) => {
19820
19853
  const workingMemoryInput = inputData;
19821
19854
  const threadId = context?.agent?.threadId;
@@ -20572,6 +20605,24 @@ function getUnobservedPartsPreservingToolCallPairs(message) {
20572
20605
  return preservedCalls.length > 0 ? [...preservedCalls, ...unobservedParts] : unobservedParts;
20573
20606
  }
20574
20607
  /**
20608
+ * Get the messages Observational Memory is allowed to work with.
20609
+ *
20610
+ * Messages supplied through the `context` option are per-run ephemeral input. Core's
20611
+ * persistence contract already treats them as never-persist: `MessageStateManager` routes
20612
+ * them into `userContextMessages`, and `drainUnsavedMessages` only drains input/response.
20613
+ *
20614
+ * OM builds its windows from `get.all.db()`, which includes context messages, and then
20615
+ * seals and persists candidates directly — turning ephemeral context into durable user
20616
+ * messages. Excluding them here keeps OM's window, sealing, persistence and token
20617
+ * accounting consistent with that contract.
20618
+ */
20619
+ function getObservableMessages(messageList) {
20620
+ const allMessages = messageList.get.all.db();
20621
+ const contextMessageIds = messageList.makeMessageSourceChecker().context;
20622
+ if (contextMessageIds.size === 0) return allMessages;
20623
+ return allMessages.filter((message) => !contextMessageIds.has(message.id));
20624
+ }
20625
+ /**
20575
20626
  * Safely extract buffered observation chunks from a record.
20576
20627
  * Handles both array and JSON-string formats, returning empty array if malformed.
20577
20628
  */
@@ -20584,7 +20635,7 @@ function getUnobservedPartsPreservingToolCallPairs(message) {
20584
20635
  */
20585
20636
  function filterObservedMessages(opts) {
20586
20637
  const { messageList, record } = opts;
20587
- const allMessages = messageList.get.all.db();
20638
+ const allMessages = getObservableMessages(messageList);
20588
20639
  const useMarkerBoundaryPruning = opts.useMarkerBoundaryPruning ?? true;
20589
20640
  const preserveMessageIds = opts.preserveMessageIds ?? /* @__PURE__ */ new Set();
20590
20641
  const observedIds = new Set(Array.isArray(record?.observedMessageIds) ? record.observedMessageIds : []);
@@ -21137,7 +21188,7 @@ var ObservationStrategy = class ObservationStrategy {
21137
21188
  */
21138
21189
  async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
21139
21190
  if (!messageList) return false;
21140
- const allMsgs = messageList.get.all.db();
21191
+ const allMsgs = getObservableMessages(messageList);
21141
21192
  for (let i = allMsgs.length - 1; i >= 0; i--) {
21142
21193
  const msg = allMsgs[i];
21143
21194
  if (msg?.role === "assistant" && msg.content?.parts && Array.isArray(msg.content.parts)) {
@@ -22016,7 +22067,7 @@ var ObservationStep = class {
22016
22067
  let didThresholdCleanup = false;
22017
22068
  let observerExchange;
22018
22069
  if (this.stepNumber === 0) {
22019
- const step0Messages = messageList.get.all.db();
22070
+ const step0Messages = getObservableMessages(messageList);
22020
22071
  const activation = await om.activate({
22021
22072
  threadId,
22022
22073
  resourceId,
@@ -22048,7 +22099,7 @@ var ObservationStep = class {
22048
22099
  currentModel: this.turn.actorModelContext,
22049
22100
  requestContext: this.turn.requestContext,
22050
22101
  observabilityContext: this.turn.observabilityContext,
22051
- lastActivityAt: getLastActivityFromMessages(messageList.get.all.db()),
22102
+ lastActivityAt: getLastActivityFromMessages(getObservableMessages(messageList)),
22052
22103
  reflectionHooks: om.composeHooks(void 0, {
22053
22104
  threadId,
22054
22105
  resourceId,
@@ -22058,7 +22109,7 @@ var ObservationStep = class {
22058
22109
  await this.turn.refreshRecord();
22059
22110
  if (this.turn.record.generationCount > preReflectGeneration) reflected = true;
22060
22111
  }
22061
- const allMsgsForToolCheck = messageList.get.all.db();
22112
+ const allMsgsForToolCheck = getObservableMessages(messageList);
22062
22113
  const lastMessage = allMsgsForToolCheck[allMsgsForToolCheck.length - 1];
22063
22114
  const pendingStepMessages = [...messageList.get.input.db(), ...messageList.get.response.db()];
22064
22115
  const latestStepParts = [...getLatestStepParts(lastMessage?.content?.parts ?? []), ...pendingStepMessages.flatMap((msg) => getLatestStepParts(msg.content?.parts ?? []))];
@@ -22067,10 +22118,10 @@ var ObservationStep = class {
22067
22118
  let statusSnapshot = await om.getStatus({
22068
22119
  threadId,
22069
22120
  resourceId,
22070
- messages: messageList.get.all.db()
22121
+ messages: getObservableMessages(messageList)
22071
22122
  });
22072
22123
  if (statusSnapshot.shouldBuffer && !hasIncompleteToolCalls) {
22073
- const allMessages = messageList.get.all.db();
22124
+ const allMessages = getObservableMessages(messageList);
22074
22125
  const unobservedMessages = om.getUnobservedMessages(allMessages, statusSnapshot.record);
22075
22126
  const candidates = om.getUnobservedMessages(unobservedMessages, statusSnapshot.record, { excludeBuffered: true });
22076
22127
  if (candidates.length > 0) {
@@ -22162,7 +22213,7 @@ var ObservationStep = class {
22162
22213
  statusSnapshot = await om.getStatus({
22163
22214
  threadId,
22164
22215
  resourceId,
22165
- messages: messageList.get.all.db()
22216
+ messages: getObservableMessages(messageList)
22166
22217
  });
22167
22218
  }
22168
22219
  const otherThreadsContext = await this.turn.refreshOtherThreadsContext();
@@ -22211,7 +22262,7 @@ var ObservationStep = class {
22211
22262
  const { threadId, resourceId, messageList } = this.turn;
22212
22263
  const om = this.turn.om;
22213
22264
  await om.waitForBuffering(threadId, resourceId);
22214
- const observableMessages = this.seededResponseMessage ? messageList.get.all.db().filter((msg) => msg.id !== this.turn.responseMessageId) : messageList.get.all.db();
22265
+ const observableMessages = this.seededResponseMessage ? getObservableMessages(messageList).filter((msg) => msg.id !== this.turn.responseMessageId) : getObservableMessages(messageList);
22215
22266
  const freshStatus = await om.getStatus({
22216
22267
  threadId,
22217
22268
  resourceId,
@@ -22241,7 +22292,7 @@ var ObservationStep = class {
22241
22292
  currentModel: this.turn.actorModelContext,
22242
22293
  requestContext: this.turn.requestContext,
22243
22294
  observabilityContext: this.turn.observabilityContext,
22244
- lastActivityAt: getLastActivityFromMessages(messageList.get.all.db()),
22295
+ lastActivityAt: getLastActivityFromMessages(getObservableMessages(messageList)),
22245
22296
  reflectionHooks: om.composeHooks(void 0, {
22246
22297
  threadId,
22247
22298
  resourceId,
@@ -22267,7 +22318,7 @@ var ObservationStep = class {
22267
22318
  });
22268
22319
  if (obsResult.observed) {
22269
22320
  const observedMessageIds = new Set(obsResult.record.observedMessageIds ?? []);
22270
- const liveMessages = messageList.get.all.db();
22321
+ const liveMessages = getObservableMessages(messageList);
22271
22322
  let latestObservedIndex = -1;
22272
22323
  for (let i = liveMessages.length - 1; i >= 0; i--) {
22273
22324
  const message = liveMessages[i];
@@ -22442,7 +22493,7 @@ var ObservationTurn = class {
22442
22493
  const asyncObservationEnabled = this.om.buffering.isAsyncObservationEnabled();
22443
22494
  const bufferOnIdle = this.om.getObservationConfig().bufferOnIdle;
22444
22495
  if (asyncObservationEnabled && bufferOnIdle) {
22445
- const allMessages = this.messageList.get.all.db();
22496
+ const allMessages = getObservableMessages(this.messageList);
22446
22497
  const record = this._record;
22447
22498
  const unobservedMessages = this.om.getUnobservedMessages(allMessages, record);
22448
22499
  if (unobservedMessages.length > 0) this.om.buffer({
@@ -22812,7 +22863,7 @@ function getCurrentModel$1(model) {
22812
22863
  return formatModelContext$1(model?.provider, model?.modelId);
22813
22864
  }
22814
22865
  function getLastModelFromMessageList(messageList) {
22815
- const messages = messageList?.get.all.db();
22866
+ const messages = messageList ? getObservableMessages(messageList) : void 0;
22816
22867
  if (!messages) return void 0;
22817
22868
  for (let i = messages.length - 1; i >= 0; i--) {
22818
22869
  const message = messages[i];
@@ -24396,7 +24447,7 @@ var ObservationalMemory = class ObservationalMemory {
24396
24447
  */
24397
24448
  async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
24398
24449
  if (!messageList) return;
24399
- const allMsgs = messageList.get.all.db();
24450
+ const allMsgs = getObservableMessages(messageList);
24400
24451
  for (let i = allMsgs.length - 1; i >= 0; i--) {
24401
24452
  const msg = allMsgs[i];
24402
24453
  if (msg?.role === "assistant" && msg.content?.parts && Array.isArray(msg.content.parts)) {
@@ -25074,7 +25125,7 @@ ${formattedMessages}
25074
25125
  async cleanupMessages(opts) {
25075
25126
  const { threadId, resourceId, observedMessageIds, retentionFloor, preserveMessageIds } = opts;
25076
25127
  const messageList = this.isMessageList(opts.messages) ? opts.messages : void 0;
25077
- const allMsgs = messageList ? messageList.get.all.db() : opts.messages;
25128
+ const allMsgs = messageList ? getObservableMessages(messageList) : opts.messages;
25078
25129
  let markerIdx = -1;
25079
25130
  let markerMsg = null;
25080
25131
  for (let i = allMsgs.length - 1; i >= 0; i--) {
@@ -26190,7 +26241,7 @@ function isTemporalGapMarkerForMessage(message, targetMessageId) {
26190
26241
  async function insertTemporalGapMarkers({ messageList, sendSignal }) {
26191
26242
  const latestInputMessage = messageList.get.input.db().filter((message) => Boolean(message)).at(-1);
26192
26243
  if (!latestInputMessage || isTemporalGapMarker(latestInputMessage)) return;
26193
- const allMessages = messageList.get.all.db().filter((message) => Boolean(message));
26244
+ const allMessages = getObservableMessages(messageList).filter((message) => Boolean(message));
26194
26245
  const latestInputIndex = allMessages.findIndex((message) => message.id === latestInputMessage.id);
26195
26246
  if (latestInputIndex <= 0) return;
26196
26247
  if (allMessages.some((message) => isTemporalGapMarkerForMessage(message, latestInputMessage.id))) return;
@@ -26414,7 +26465,7 @@ var ObservationalMemoryProcessor = class {
26414
26465
  threadId,
26415
26466
  resourceId
26416
26467
  });
26417
- const allDbMsgs = messageList.get.all.db();
26468
+ const allDbMsgs = getObservableMessages(messageList);
26418
26469
  const tokenCounter = this.engine.getTokenCounter();
26419
26470
  const contextTokens = await tokenCounter.countMessagesAsync(allDbMsgs);
26420
26471
  const otherThreadsContext = this.turn.context.otherThreadsContext;
@@ -26784,6 +26835,18 @@ var Memory = class extends MastraMemory {
26784
26835
  _omEngine;
26785
26836
  _omEngineInstance;
26786
26837
  _mastraInstance;
26838
+ /**
26839
+ * Every vector cleanup that deleteThread or deleteMessages started in the background.
26840
+ * Callers do not wait for the cleanup, so this handle is the only join point.
26841
+ */
26842
+ pendingVectorCleanup = Promise.resolve();
26843
+ /**
26844
+ * Adds a background vector cleanup to the join handle.
26845
+ * The handle keeps the earlier cleanups, so it settles only after all of them end.
26846
+ */
26847
+ trackVectorCleanup(cleanup) {
26848
+ this.pendingVectorCleanup = Promise.allSettled([this.pendingVectorCleanup, cleanup]).then(() => void 0);
26849
+ }
26787
26850
  /** The shared ObservationalMemory engine. Lazily created on first access. */
26788
26851
  get omEngine() {
26789
26852
  if (!this._omEngine) this._omEngine = this._initOMEngine().then((engine) => {
@@ -27048,26 +27111,40 @@ var Memory = class extends MastraMemory {
27048
27111
  const thread = await memoryStore.getThreadById({ threadId });
27049
27112
  await memoryStore.deleteThread({ threadId });
27050
27113
  if (thread?.resourceId && memoryStore.supportsObservationalMemory) await memoryStore.clearObservationalMemory(threadId, thread.resourceId);
27051
- if (this.vector) this.deleteThreadVectors(threadId);
27114
+ if (this.vector) this.trackVectorCleanup(this.deleteThreadVectors(threadId));
27115
+ }
27116
+ /**
27117
+ * Prefix shared by every message index. The index for the default embedding
27118
+ * dimension is named with the bare prefix; other dimensions add a suffix.
27119
+ */
27120
+ get messageIndexPrefix() {
27121
+ return this.getEmbeddingIndexName();
27052
27122
  }
27053
27123
  /**
27054
- * Lists all vector indexes that match the memory messages prefix.
27055
- * Handles separator differences across vector store backends (e.g. '_' vs '-').
27124
+ * Prefix shared by every observation index. Each observation index adds a dimension suffix.
27056
27125
  */
27057
- async getMemoryVectorIndexes() {
27126
+ get observationIndexPrefix() {
27127
+ return `memory${this.vector?.indexSeparator ?? "_"}observations`;
27128
+ }
27129
+ /**
27130
+ * Lists the vector indexes whose name starts with one of the given prefixes.
27131
+ * Index names can carry a dimension suffix, so discovery matches on the prefix.
27132
+ */
27133
+ async getMemoryVectorIndexes(prefixes) {
27058
27134
  if (!this.vector) return [];
27059
- const prefix = `memory${this.vector.indexSeparator ?? "_"}messages`;
27060
- return (await this.vector.listIndexes()).filter((name) => name.startsWith(prefix));
27135
+ return (await this.vector.listIndexes()).filter((name) => prefixes.some((prefix) => name.startsWith(prefix)));
27061
27136
  }
27062
27137
  /**
27063
27138
  * Deletes all vector embeddings associated with a thread.
27064
27139
  * This is called internally by deleteThread to clean up orphaned vectors.
27140
+ * Both message and observation vectors are removed, so no text of the deleted
27141
+ * thread stays reachable through resource-scoped retrieval.
27065
27142
  *
27066
27143
  * @param threadId - The ID of the thread whose vectors should be deleted
27067
27144
  */
27068
27145
  async deleteThreadVectors(threadId) {
27069
27146
  try {
27070
- const memoryIndexes = await this.getMemoryVectorIndexes();
27147
+ const memoryIndexes = await this.getMemoryVectorIndexes([this.messageIndexPrefix, this.observationIndexPrefix]);
27071
27148
  await Promise.all(memoryIndexes.map(async (indexName) => {
27072
27149
  try {
27073
27150
  await this.vector.deleteVectors({
@@ -27075,14 +27152,14 @@ var Memory = class extends MastraMemory {
27075
27152
  filter: { thread_id: threadId }
27076
27153
  });
27077
27154
  } catch {
27078
- this.logger.debug("Failed to delete vectors for thread, skipping", {
27155
+ this.logger.warn("Failed to delete vectors of the deleted thread from index", {
27079
27156
  threadId,
27080
27157
  indexName
27081
27158
  });
27082
27159
  }
27083
27160
  }));
27084
27161
  } catch {
27085
- this.logger.debug("Failed to clean up vectors for thread", { threadId });
27162
+ this.logger.warn("Failed to clean up vectors of the deleted thread", { threadId });
27086
27163
  }
27087
27164
  }
27088
27165
  async updateWorkingMemory({ threadId, resourceId, workingMemory, memoryConfig, observabilityContext }) {
@@ -27755,7 +27832,7 @@ Notes:
27755
27832
  getObservationEmbeddingIndexName(dimensions) {
27756
27833
  const usedDimensions = dimensions ?? 384;
27757
27834
  const separator = this.vector?.indexSeparator ?? "_";
27758
- return `memory${separator}observations${separator}${usedDimensions}`;
27835
+ return `${this.observationIndexPrefix}${separator}${usedDimensions}`;
27759
27836
  }
27760
27837
  async createObservationEmbeddingIndex(dimensions) {
27761
27838
  const usedDimensions = dimensions ?? 384;
@@ -28001,7 +28078,10 @@ Notes:
28001
28078
  tools[name] = tool;
28002
28079
  }
28003
28080
  const omConfig = normalizeObservationalMemoryConfig(mergedConfig.observationalMemory);
28004
- if (omConfig?.retrieval) tools.recall = recallTool(mergedConfig, { retrievalScope: typeof omConfig.retrieval === "object" ? omConfig.retrieval.scope ?? "resource" : "resource" });
28081
+ if (omConfig?.retrieval) tools.recall = recallTool(mergedConfig, {
28082
+ retrievalScope: typeof omConfig.retrieval === "object" ? omConfig.retrieval.scope ?? "resource" : "resource",
28083
+ searchEnabled: this.hasRetrievalSearch(omConfig.retrieval)
28084
+ });
28005
28085
  return tools;
28006
28086
  }
28007
28087
  /**
@@ -28057,7 +28137,7 @@ Notes:
28057
28137
  }));
28058
28138
  const messageIdsNeedingDeletion = /* @__PURE__ */ new Set([...messageIdsWithClearedContent, ...messageIdsWithNewEmbeddings]);
28059
28139
  if (messageIdsNeedingDeletion.size > 0) try {
28060
- const memoryIndexes = await this.getMemoryVectorIndexes();
28140
+ const memoryIndexes = await this.getMemoryVectorIndexes([this.messageIndexPrefix]);
28061
28141
  const idsToDelete = [...messageIdsNeedingDeletion];
28062
28142
  await Promise.all(memoryIndexes.map(async (indexName) => {
28063
28143
  for (let i = 0; i < idsToDelete.length; i += VECTOR_DELETE_BATCH_SIZE) {
@@ -28116,7 +28196,7 @@ Notes:
28116
28196
  const span = this.createMemorySpan("delete", observabilityContext, void 0, { messageCount: messageIds.length });
28117
28197
  try {
28118
28198
  await (await this.getMemoryStore()).deleteMessages(messageIds);
28119
- if (this.vector) this.deleteMessageVectors(messageIds);
28199
+ if (this.vector) this.trackVectorCleanup(this.deleteMessageVectors(messageIds));
28120
28200
  span?.end({
28121
28201
  output: { success: true },
28122
28202
  attributes: { messageCount: messageIds.length }
@@ -28132,12 +28212,14 @@ Notes:
28132
28212
  /**
28133
28213
  * Deletes vector embeddings for specific messages.
28134
28214
  * This is called internally by deleteMessages to clean up orphaned vectors.
28215
+ * Only the message indexes are touched, because observation vectors can hold
28216
+ * text of other messages of the thread.
28135
28217
  *
28136
28218
  * @param messageIds - The IDs of the messages whose vectors should be deleted
28137
28219
  */
28138
28220
  async deleteMessageVectors(messageIds) {
28139
28221
  try {
28140
- const memoryIndexes = await this.getMemoryVectorIndexes();
28222
+ const memoryIndexes = await this.getMemoryVectorIndexes([this.messageIndexPrefix]);
28141
28223
  await Promise.all(memoryIndexes.map(async (indexName) => {
28142
28224
  for (let i = 0; i < messageIds.length; i += VECTOR_DELETE_BATCH_SIZE) {
28143
28225
  const batch = messageIds.slice(i, i + VECTOR_DELETE_BATCH_SIZE);
@@ -28531,4 +28613,4 @@ Notes:
28531
28613
  //#endregion
28532
28614
  export { extractCurrentTask as A, OBSERVATION_CONTEXT_INSTRUCTIONS as B, WorkingMemoryExtractor as C, OBSERVER_SYSTEM_PROMPT as D, TokenCounter as E, injectAnchorIds as F, OBSERVATION_CONTINUATION_HINT as H, parseAnchorId as I, stripEphemeralAnchorIds as L, hasCurrentTaskSection as M, optimizeObservationsForContext as N, buildObserverPrompt as O, parseObserverOutput as P, Extractor as R, deepMergeWorkingMemory as S, summarizeConversation as T, OBSERVATION_CONTEXT_PROMPT as V, reconcileObservationGroupsFromReflection as _, extractWorkingMemoryContent as a, wrapInObservationGroup as b, WORKING_MEMORY_STATE_ID as c, getObservationsAsOf as d, ObservationalMemoryProcessor as f, parseObservationGroups as g, deriveObservationGroupProvenance as h, WorkingMemory as i, formatMessagesForObserver as j, buildObserverSystemPrompt as k, WORKING_MEMORY_STATE_PROCESSOR_ID as l, combineObservationGroupRanges as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, ObservationalMemory as p, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, WorkingMemoryStateProcessor as u, renderObservationGroupsForReflection as v, SUMMARIZE_THREAD_DEFAULTS as w, ModelByInputTokens as x, stripObservationGroups as y, OBSERVATIONAL_MEMORY_DEFAULTS as z };
28533
28615
 
28534
- //# sourceMappingURL=src-Iw-V5CfD.js.map
28616
+ //# sourceMappingURL=src-MScpLVRh.js.map