@mastra/memory 1.31.0-alpha.0 → 1.31.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.
Files changed (37) hide show
  1. package/dist/_types/@internal_ai-sdk-v4/package.json +1 -0
  2. package/dist/docs/SKILL.md +1 -1
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-memory-observational-memory.md +33 -0
  5. package/dist/docs/references/reference-memory-observational-memory.md +34 -4
  6. package/dist/hooks.cjs +158 -0
  7. package/dist/hooks.cjs.map +1 -0
  8. package/dist/hooks.d.ts +25 -0
  9. package/dist/hooks.d.ts.map +1 -0
  10. package/dist/hooks.js +156 -0
  11. package/dist/hooks.js.map +1 -0
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.d.ts +8 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/processors/index.cjs +1 -1
  17. package/dist/processors/index.js +1 -1
  18. package/dist/processors/observational-memory/hooks.d.ts +63 -0
  19. package/dist/processors/observational-memory/hooks.d.ts.map +1 -0
  20. package/dist/processors/observational-memory/observation-groups.d.ts.map +1 -1
  21. package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
  22. package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts.map +1 -1
  23. package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observation-strategies/thread-title.d.ts +15 -0
  25. package/dist/processors/observational-memory/observation-strategies/thread-title.d.ts.map +1 -0
  26. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  28. package/dist/processors/observational-memory/types.d.ts +6 -6
  29. package/dist/{src-Da7AKXPR.cjs → src-C4niho1P.cjs} +206 -54
  30. package/dist/src-C4niho1P.cjs.map +1 -0
  31. package/dist/{src-DqLGJfSb.js → src-DquR-jTR.js} +208 -56
  32. package/dist/src-DquR-jTR.js.map +1 -0
  33. package/dist/tools/om-tools.d.ts +4 -2
  34. package/dist/tools/om-tools.d.ts.map +1 -1
  35. package/package.json +15 -5
  36. package/dist/src-Da7AKXPR.cjs.map +0 -1
  37. package/dist/src-DqLGJfSb.js.map +0 -1
@@ -22800,13 +22800,13 @@ function renderFormattedParts(parts, timestamps, options) {
22800
22800
  tokenOffset: expandedTokens - options.maxTokens
22801
22801
  };
22802
22802
  }
22803
- async function recallPart({ memory, threadId, resourceId, cursor, partIndex, charOffset, threadScope, maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22803
+ async function recallPart({ memory, threadId, resourceId, cursor, partIndex, charOffset, threadScope, retrievalScope = "thread", maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22804
22804
  if (!memory || typeof memory.getMemoryStore !== "function") throw new Error("Memory instance is required for recall");
22805
22805
  if (!threadId) throw new Error("Thread ID is required for recall");
22806
22806
  const resolved = await resolveCursorMessage(memory, cursor, {
22807
22807
  resourceId,
22808
22808
  threadScope,
22809
- enforceThreadScope: false
22809
+ enforceThreadScope: retrievalScope !== "resource"
22810
22810
  });
22811
22811
  if ("hint" in resolved) throw new Error(resolved.hint);
22812
22812
  const allParts = formatMessageParts(resolved, "high");
@@ -22817,7 +22817,7 @@ async function recallPart({ memory, threadId, resourceId, cursor, partIndex, cha
22817
22817
  if (partIndex > Math.max(...allParts.map((p) => p.partIndex))) {
22818
22818
  const nextMessage = await getNextVisibleMessage({
22819
22819
  memory,
22820
- threadId,
22820
+ threadId: resolved.threadId ?? threadId,
22821
22821
  resourceId,
22822
22822
  after: resolved.createdAt
22823
22823
  });
@@ -22856,7 +22856,7 @@ async function recallPart({ memory, threadId, resourceId, cursor, partIndex, cha
22856
22856
  note
22857
22857
  };
22858
22858
  }
22859
- async function recallMessages({ memory, threadId, resourceId, cursor, page = 1, limit = 20, detail = "low", partType, toolName, threadScope, maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22859
+ async function recallMessages({ memory, threadId, resourceId, cursor, page = 1, limit = 20, detail = "low", partType, toolName, threadScope, retrievalScope = "thread", maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22860
22860
  if (!memory) throw new Error("Memory instance is required for recall");
22861
22861
  if (!threadId) throw new Error("Thread ID is required for recall");
22862
22862
  if (typeof memory.getMemoryStore !== "function") throw new Error("recall requires a Memory instance with storage access");
@@ -22867,7 +22867,7 @@ async function recallMessages({ memory, threadId, resourceId, cursor, page = 1,
22867
22867
  const resolved = await resolveCursorMessage(memory, cursor, {
22868
22868
  resourceId,
22869
22869
  threadScope,
22870
- enforceThreadScope: false
22870
+ enforceThreadScope: retrievalScope === "thread"
22871
22871
  });
22872
22872
  if ("hint" in resolved) return {
22873
22873
  messages: resolved.hint,
@@ -22884,7 +22884,7 @@ async function recallMessages({ memory, threadId, resourceId, cursor, page = 1,
22884
22884
  const anchor = resolved;
22885
22885
  const crossThreadId = anchor.threadId && anchor.threadId !== threadId ? anchor.threadId : void 0;
22886
22886
  if (crossThreadId && threadScope) return {
22887
- messages: `Cursor does not belong to the active thread. Expected thread "${threadId}" but cursor "${cursor}" belongs to "${anchor.threadId}". Pass threadId="${anchor.threadId}" to browse that thread, or omit threadId and use this cursor directly in resource scope.`,
22887
+ messages: `Cursor does not belong to the active thread. Expected thread "${threadId}" but cursor "${cursor}" belongs to "${anchor.threadId}". Pass threadId="${anchor.threadId}" to browse that thread.`,
22888
22888
  count: 0,
22889
22889
  cursor,
22890
22890
  page: normalizedPage,
@@ -23043,7 +23043,8 @@ async function recallThreadFromStart({ memory, threadId, resourceId, page = 1, l
23043
23043
  };
23044
23044
  }
23045
23045
  const recallTool = (_memoryConfig, options) => {
23046
- const isResourceScope = (options?.retrievalScope ?? "thread") === "resource";
23046
+ const retrievalScope = options?.retrievalScope ?? "thread";
23047
+ const isResourceScope = retrievalScope === "resource";
23047
23048
  const searchEnabled = options?.searchEnabled ?? true;
23048
23049
  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.`;
23049
23050
  const modeEnum = searchEnabled ? [
@@ -23254,23 +23255,25 @@ const recallTool = (_memoryConfig, options) => {
23254
23255
  if (partIndex !== void 0 && partIndex !== null) return recallPart({
23255
23256
  memory,
23256
23257
  threadId: targetThreadId,
23257
- resourceId: isResourceScope ? resourceId : void 0,
23258
+ resourceId,
23258
23259
  cursor,
23259
23260
  partIndex,
23260
23261
  charOffset,
23261
- threadScope
23262
+ threadScope,
23263
+ retrievalScope
23262
23264
  });
23263
23265
  return recallMessages({
23264
23266
  memory,
23265
23267
  threadId: targetThreadId,
23266
- resourceId: isResourceScope ? resourceId : void 0,
23268
+ resourceId,
23267
23269
  cursor,
23268
23270
  page,
23269
23271
  limit,
23270
23272
  detail: detail ?? "low",
23271
23273
  partType,
23272
23274
  toolName,
23273
- threadScope
23275
+ threadScope,
23276
+ retrievalScope
23274
23277
  });
23275
23278
  }
23276
23279
  });
@@ -24376,7 +24379,8 @@ function stripThreadTags(observations) {
24376
24379
  }
24377
24380
  //#endregion
24378
24381
  //#region src/processors/observational-memory/observation-groups.ts
24379
- const OBSERVATION_GROUP_PATTERN = /<observation-group\s([^>]*)>([\s\S]*?)<\/observation-group>/g;
24382
+ const OBSERVATION_GROUP_OPEN = "<observation-group";
24383
+ const OBSERVATION_GROUP_CLOSE = "</observation-group>";
24380
24384
  const ATTRIBUTE_PATTERN = /([\w][\w-]*)="([^"]*)"/g;
24381
24385
  const REFLECTION_GROUP_SPLIT_PATTERN = /^##\s+Group\s+/m;
24382
24386
  function parseObservationGroupAttributes(attributeString) {
@@ -24387,6 +24391,81 @@ function parseObservationGroupAttributes(attributeString) {
24387
24391
  }
24388
24392
  return attributes;
24389
24393
  }
24394
+ function isLineStart(observations, index) {
24395
+ return index === 0 || observations[index - 1] === "\n";
24396
+ }
24397
+ function scanObservationGroupTags(observations) {
24398
+ const tags = [];
24399
+ const incompleteOpenings = [];
24400
+ let cursor = 0;
24401
+ let closeFloor = 0;
24402
+ while (cursor < observations.length) {
24403
+ const start = observations.indexOf(OBSERVATION_GROUP_OPEN, cursor);
24404
+ if (start === -1) break;
24405
+ const attributesStart = start + 18;
24406
+ if (!/\s/.test(observations[attributesStart] ?? "")) {
24407
+ cursor = attributesStart;
24408
+ continue;
24409
+ }
24410
+ const openEnd = observations.indexOf(">", attributesStart);
24411
+ if (openEnd === -1) break;
24412
+ const closeStart = observations.indexOf(OBSERVATION_GROUP_CLOSE, Math.max(openEnd + 1, closeFloor));
24413
+ if (closeStart === -1) {
24414
+ if (isLineStart(observations, start)) incompleteOpenings.push({
24415
+ start,
24416
+ end: openEnd + 1
24417
+ });
24418
+ closeFloor = observations.length;
24419
+ cursor = openEnd + 1;
24420
+ continue;
24421
+ }
24422
+ const nestedStart = observations.indexOf(OBSERVATION_GROUP_OPEN, openEnd + 1);
24423
+ if (nestedStart !== -1 && nestedStart < closeStart && isLineStart(observations, nestedStart)) {
24424
+ incompleteOpenings.push({
24425
+ start,
24426
+ end: openEnd + 1
24427
+ });
24428
+ closeFloor = closeStart;
24429
+ cursor = nestedStart;
24430
+ continue;
24431
+ }
24432
+ tags.push({
24433
+ start,
24434
+ end: closeStart + 20,
24435
+ attributeString: observations.slice(attributesStart, openEnd),
24436
+ content: observations.slice(openEnd + 1, closeStart)
24437
+ });
24438
+ closeFloor = closeStart;
24439
+ cursor = closeStart + 20;
24440
+ }
24441
+ return {
24442
+ tags,
24443
+ incompleteOpenings
24444
+ };
24445
+ }
24446
+ function replaceObservationGroupTags(observations, replace) {
24447
+ const { tags, incompleteOpenings } = scanObservationGroupTags(observations);
24448
+ const parts = [];
24449
+ let cursor = 0;
24450
+ let openingIndex = 0;
24451
+ const skipIncompleteOpeningsBefore = (limit) => {
24452
+ while (openingIndex < incompleteOpenings.length && incompleteOpenings[openingIndex].start < limit) {
24453
+ const opening = incompleteOpenings[openingIndex];
24454
+ openingIndex++;
24455
+ if (opening.start < cursor) continue;
24456
+ parts.push(observations.slice(cursor, opening.start));
24457
+ cursor = opening.end;
24458
+ }
24459
+ };
24460
+ for (const tag of tags) {
24461
+ skipIncompleteOpeningsBefore(tag.start);
24462
+ parts.push(observations.slice(cursor, tag.start), replace(tag));
24463
+ cursor = tag.end;
24464
+ }
24465
+ skipIncompleteOpeningsBefore(Number.POSITIVE_INFINITY);
24466
+ parts.push(observations.slice(cursor));
24467
+ return parts.join("");
24468
+ }
24390
24469
  function parseReflectionObservationGroupSections(content) {
24391
24470
  const normalizedContent = content.trim();
24392
24471
  if (!normalizedContent || !REFLECTION_GROUP_SPLIT_PATTERN.test(normalizedContent)) return [];
@@ -24411,9 +24490,8 @@ function wrapInObservationGroup(observations, range, id = generateAnchorId(), _s
24411
24490
  function parseObservationGroups(observations) {
24412
24491
  if (!observations) return [];
24413
24492
  const groups = [];
24414
- let match;
24415
- while ((match = OBSERVATION_GROUP_PATTERN.exec(observations)) !== null) {
24416
- const attributes = parseObservationGroupAttributes(match[1] ?? "");
24493
+ for (const tag of scanObservationGroupTags(observations).tags) {
24494
+ const attributes = parseObservationGroupAttributes(tag.attributeString);
24417
24495
  const id = attributes.id;
24418
24496
  const range = attributes.range;
24419
24497
  if (!id || !range) continue;
@@ -24421,54 +24499,96 @@ function parseObservationGroups(observations) {
24421
24499
  id,
24422
24500
  range,
24423
24501
  kind: attributes.kind,
24424
- content: match[2].trim()
24502
+ content: tag.content.trim()
24425
24503
  });
24426
24504
  }
24427
24505
  return groups;
24428
24506
  }
24429
24507
  function stripObservationGroups(observations) {
24430
24508
  if (!observations) return observations;
24431
- return observations.replace(OBSERVATION_GROUP_PATTERN, (_match, _attributes, content) => content.trim()).replace(/\n{3,}/g, "\n\n").trim();
24509
+ return replaceObservationGroupTags(observations, (tag) => tag.content.trim()).replace(/\n{3,}/g, "\n\n").trim();
24432
24510
  }
24433
24511
  function getRangeSegments(range) {
24434
24512
  return range.split(",").map((segment) => segment.trim()).filter(Boolean);
24435
24513
  }
24514
+ function getRangeEndpoints(segment) {
24515
+ const parts = segment.split(":").map((part) => part.trim());
24516
+ if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
24517
+ return {
24518
+ start: parts[0],
24519
+ end: parts[1]
24520
+ };
24521
+ }
24522
+ /**
24523
+ * `buildMessageRange` produces opaque UUID endpoints, so those segments cannot be ordered by value.
24524
+ * Spanning from the first start to the last end keeps reflected metadata bounded — the full segment
24525
+ * list is deliberately not preserved (#14791). Segments that are not `start:end` pairs are left as
24526
+ * they are rather than being joined into a fabricated span.
24527
+ */
24528
+ function spanRangeSegments(segments) {
24529
+ const endpoints = segments.map(getRangeEndpoints);
24530
+ if (endpoints.some((endpoint) => endpoint === null)) return segments.join(",");
24531
+ const first = endpoints[0];
24532
+ const last = endpoints.at(-1);
24533
+ return `${first.start}:${last.end}`;
24534
+ }
24436
24535
  function combineObservationGroupRanges(groups) {
24437
- const segments = groups.flatMap((group) => getRangeSegments(group.range));
24536
+ const segments = Array.from(new Set(groups.flatMap((group) => getRangeSegments(group.range))));
24438
24537
  if (segments.length === 0) return "";
24439
- const firstSegment = segments[0];
24440
- const lastSegment = segments[segments.length - 1];
24441
- const firstStart = firstSegment?.split(":")[0]?.trim();
24442
- const lastEnd = lastSegment?.split(":").at(-1)?.trim();
24443
- if (firstStart && lastEnd) return `${firstStart}:${lastEnd}`;
24444
- return Array.from(new Set(segments)).join(",");
24538
+ const numericEndpoints = [];
24539
+ if (!segments.every((segment) => {
24540
+ const endpoints = getRangeEndpoints(segment);
24541
+ if (!endpoints || !/^\d+$/.test(endpoints.start) || !/^\d+$/.test(endpoints.end)) return false;
24542
+ const start = {
24543
+ label: endpoints.start,
24544
+ value: Number(endpoints.start)
24545
+ };
24546
+ const end = {
24547
+ label: endpoints.end,
24548
+ value: Number(endpoints.end)
24549
+ };
24550
+ if (!Number.isSafeInteger(start.value) || !Number.isSafeInteger(end.value)) return false;
24551
+ numericEndpoints.push(start.value <= end.value ? {
24552
+ start,
24553
+ end
24554
+ } : {
24555
+ start: end,
24556
+ end: start
24557
+ });
24558
+ return true;
24559
+ })) return spanRangeSegments(segments);
24560
+ const first = numericEndpoints.reduce((lowest, pair) => pair.start.value < lowest.start.value ? pair : lowest);
24561
+ const last = numericEndpoints.reduce((highest, pair) => pair.end.value > highest.end.value ? pair : highest);
24562
+ return `${first.start.label}:${last.end.label}`;
24445
24563
  }
24446
24564
  function renderObservationGroupsForReflection(observations) {
24447
- const groups = parseObservationGroups(observations);
24448
- if (groups.length === 0) return null;
24449
- const groupsByContent = new Map(groups.map((g) => [g.content.trim(), g]));
24450
- return observations.replace(OBSERVATION_GROUP_PATTERN, (_match, _attrs, content) => {
24451
- const group = groupsByContent.get(content.trim());
24452
- if (!group) return content.trim();
24453
- return `## Group \`${group.id}\`\n_range: \`${group.range}\`_\n\n${group.content}`;
24565
+ if (parseObservationGroups(observations).length === 0) return null;
24566
+ return replaceObservationGroupTags(observations, (tag) => {
24567
+ const attributes = parseObservationGroupAttributes(tag.attributeString);
24568
+ if (!attributes.id || !attributes.range) return tag.content.trim();
24569
+ return `## Group \`${attributes.id}\`\n_range: \`${attributes.range}\`_\n\n${tag.content.trim()}`;
24454
24570
  }).replace(/\n{3,}/g, "\n\n").trim();
24455
24571
  }
24456
24572
  function getCanonicalGroupId(sectionHeading, fallbackIndex) {
24457
24573
  return sectionHeading.match(/`([^`]+)`/)?.[1]?.trim() || `derived-group-${fallbackIndex + 1}`;
24458
24574
  }
24575
+ function getContentLines(content) {
24576
+ return content.split("\n").map((line) => line.trim()).filter(Boolean);
24577
+ }
24459
24578
  function deriveObservationGroupProvenance(content, groups) {
24460
24579
  const sections = parseReflectionObservationGroupSections(content);
24461
24580
  if (sections.length === 0 || groups.length === 0) return [];
24462
24581
  return sections.map((section, index) => {
24463
- const bodyLines = new Set(section.body.split("\n").map((line) => line.trim()).filter(Boolean));
24464
- const matchingGroups = groups.filter((group) => {
24465
- return group.content.split("\n").map((line) => line.trim()).filter(Boolean).some((line) => bodyLines.has(line));
24466
- });
24582
+ const canonicalGroupId = getCanonicalGroupId(section.heading, index);
24583
+ const identifiedGroup = groups.find((group) => group.id === canonicalGroupId);
24584
+ const bodyLines = new Set(getContentLines(section.body));
24585
+ const matchingGroups = groups.filter((group) => getContentLines(group.content).some((line) => bodyLines.has(line)));
24467
24586
  const fallbackGroup = groups[Math.min(index, groups.length - 1)];
24468
- const resolvedGroups = matchingGroups.length > 0 ? matchingGroups : fallbackGroup ? [fallbackGroup] : [];
24587
+ const identifiedLines = new Set(getContentLines(identifiedGroup?.content ?? ""));
24588
+ const contributingGroups = identifiedGroup ? matchingGroups.filter((group) => group.id !== identifiedGroup.id && getContentLines(group.content).some((line) => bodyLines.has(line) && !identifiedLines.has(line))) : [];
24469
24589
  return {
24470
- id: getCanonicalGroupId(section.heading, index),
24471
- range: combineObservationGroupRanges(resolvedGroups),
24590
+ id: canonicalGroupId,
24591
+ range: combineObservationGroupRanges(identifiedGroup ? groups.filter((group) => group === identifiedGroup || contributingGroups.includes(group)) : matchingGroups.length > 0 ? matchingGroups : fallbackGroup ? [fallbackGroup] : []),
24472
24592
  kind: "reflection",
24473
24593
  content: section.body
24474
24594
  };
@@ -24896,6 +25016,23 @@ var ObservationStrategy = class ObservationStrategy {
24896
25016
  }
24897
25017
  };
24898
25018
  //#endregion
25019
+ //#region src/processors/observational-memory/observation-strategies/thread-title.ts
25020
+ /**
25021
+ * Resolve the thread title an Observational Memory cycle should persist.
25022
+ *
25023
+ * Returns the trimmed candidate when it is a new, long-enough title, or
25024
+ * `undefined` when the current title must stay — including when the user
25025
+ * pinned it via `session.thread.rename()`, which sets the
25026
+ * `titlePinned` thread metadata flag.
25027
+ */
25028
+ function resolveThreadTitleUpdate(thread, generatedTitle) {
25029
+ const candidate = generatedTitle?.trim();
25030
+ if (!candidate || candidate.length < 3) return void 0;
25031
+ if ((0, _mastra_core_memory.isThreadTitlePinned)(thread?.metadata)) return void 0;
25032
+ if (candidate === thread?.title?.trim()) return void 0;
25033
+ return candidate;
25034
+ }
25035
+ //#endregion
24899
25036
  //#region src/processors/observational-memory/observation-strategies/sync.ts
24900
25037
  var SyncObservationStrategy = class extends ObservationStrategy {
24901
25038
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -25048,8 +25185,8 @@ var SyncObservationStrategy = class extends ObservationStrategy {
25048
25185
  let threadUpdateMarker;
25049
25186
  if (thread) {
25050
25187
  const oldTitle = thread.title?.trim();
25051
- const newTitle = processed.threadTitle?.trim();
25052
- const shouldUpdateThreadTitle = !!newTitle && newTitle.length >= 3 && newTitle !== oldTitle;
25188
+ const newTitle = resolveThreadTitleUpdate(thread, processed.threadTitle);
25189
+ const shouldUpdateThreadTitle = newTitle !== void 0;
25053
25190
  const previousOmMetadata = (0, _mastra_core_memory.getThreadOMMetadata)(thread.metadata);
25054
25191
  const metadataUpdate = buildThreadMetadataFromExtractedValues(processed.extractors ?? this.observationConfig.extractors, processed.extractedValues);
25055
25192
  const newMetadata = (0, _mastra_core_memory.setThreadOMMetadata)(thread.metadata, {
@@ -25248,13 +25385,14 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
25248
25385
  abortSignal: this.opts.abortSignal
25249
25386
  });
25250
25387
  await this.indexObservationGroups(processed.observations, threadId, resourceId, processed.lastObservedAt);
25251
- const newTitle = processed.threadTitle?.trim();
25252
- const hasValidThreadTitle = !!newTitle && newTitle.length >= 3;
25388
+ const candidateTitle = processed.threadTitle?.trim();
25389
+ const hasValidThreadTitle = !!candidateTitle && candidateTitle.length >= 3;
25253
25390
  if (hasValidThreadTitle || processed.extractedValues) {
25254
25391
  const thread = await this.storage.getThreadById({ threadId });
25255
25392
  if (thread) {
25256
25393
  const oldTitle = thread.title?.trim();
25257
- const shouldUpdateThreadTitle = hasValidThreadTitle && newTitle !== oldTitle;
25394
+ const newTitle = resolveThreadTitleUpdate(thread, candidateTitle);
25395
+ const shouldUpdateThreadTitle = newTitle !== void 0;
25258
25396
  const previousOmMetadata = (0, _mastra_core_memory.getThreadOMMetadata)(thread.metadata);
25259
25397
  const metadataUpdate = buildThreadMetadataFromExtractedValues(processed.extractors ?? this.observationConfig.extractors, processed.extractedValues);
25260
25398
  const newMetadata = (0, _mastra_core_memory.setThreadOMMetadata)(thread.metadata, {
@@ -25611,8 +25749,8 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
25611
25749
  const thread = await this.storage.getThreadById({ threadId: update.threadId });
25612
25750
  if (thread) {
25613
25751
  const oldTitle = thread.title?.trim();
25614
- const newTitle = update.threadTitle?.trim();
25615
- const shouldUpdateThreadTitle = !!newTitle && newTitle.length >= 3 && newTitle !== oldTitle;
25752
+ const newTitle = resolveThreadTitleUpdate(thread, update.threadTitle);
25753
+ const shouldUpdateThreadTitle = newTitle !== void 0;
25616
25754
  const previousOmMetadata = (0, _mastra_core_memory.getThreadOMMetadata)(thread.metadata);
25617
25755
  const metadataUpdate = buildThreadMetadataFromExtractedValues(update.extractors ?? this.observationConfig.extractors, update.extracted);
25618
25756
  const newMetadata = (0, _mastra_core_memory.setThreadOMMetadata)(thread.metadata, {
@@ -28045,11 +28183,19 @@ var ObservationalMemory = class ObservationalMemory {
28045
28183
  const messageTokens = config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens;
28046
28184
  const observationTokens = config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens;
28047
28185
  const isSharedBudget = config.shareTokenBudget ?? false;
28048
- const isDefaultModelSelection = (model) => model === void 0 || model === "default" || model instanceof ModelByInputTokens;
28186
+ const supportsDefaultTemperature = (model) => {
28187
+ if (model instanceof ModelByInputTokens || typeof model === "function") return false;
28188
+ if (typeof model === "string") return (0, _mastra_core_llm.modelSupportsTemperature)(model) === true;
28189
+ if (model && typeof model === "object" && "provider" in model && "modelId" in model && typeof model.provider === "string" && typeof model.modelId === "string") return (0, _mastra_core_llm.modelSupportsTemperature)(`${model.provider.split(".")[0]}/${model.modelId}`) === true;
28190
+ return false;
28191
+ };
28192
+ const usesDefaultOutputTokenBudget = (model) => model === void 0 || model === "default" || model instanceof ModelByInputTokens;
28049
28193
  const observationSelectedModel = topLevelModel ?? observationConfigModel ?? reflectionConfigModel;
28050
28194
  const reflectionSelectedModel = topLevelModel ?? reflectionConfigModel ?? observationConfigModel;
28051
- const observationDefaultMaxOutputTokens = config.observation?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(observationSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens : void 0);
28052
- const reflectionDefaultMaxOutputTokens = config.reflection?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(reflectionSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens : void 0);
28195
+ const observationTemperature = config.observation?.modelSettings?.temperature ?? (supportsDefaultTemperature(observationModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.temperature : void 0);
28196
+ const observationDefaultMaxOutputTokens = config.observation?.modelSettings?.maxOutputTokens ?? (usesDefaultOutputTokenBudget(observationSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens : void 0);
28197
+ const reflectionTemperature = config.reflection?.modelSettings?.temperature ?? (supportsDefaultTemperature(reflectionModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.temperature : void 0);
28198
+ const reflectionDefaultMaxOutputTokens = config.reflection?.modelSettings?.maxOutputTokens ?? (usesDefaultOutputTokenBudget(reflectionSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens : void 0);
28053
28199
  const totalBudget = messageTokens + observationTokens;
28054
28200
  const userExplicitlyConfiguredAsync = config.observation?.bufferTokens !== void 0 || config.observation?.bufferActivation !== void 0 || config.reflection?.bufferActivation !== void 0;
28055
28201
  const asyncBufferingDisabled = config.observation?.bufferTokens === false || config.scope === "resource" && !userExplicitlyConfiguredAsync;
@@ -28065,7 +28211,7 @@ var ObservationalMemory = class ObservationalMemory {
28065
28211
  } : messageTokens,
28066
28212
  shareTokenBudget: isSharedBudget,
28067
28213
  modelSettings: {
28068
- temperature: config.observation?.modelSettings?.temperature ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.temperature,
28214
+ ...observationTemperature !== void 0 ? { temperature: observationTemperature } : {},
28069
28215
  ...observationDefaultMaxOutputTokens !== void 0 ? { maxOutputTokens: observationDefaultMaxOutputTokens } : {}
28070
28216
  },
28071
28217
  providerOptions: config.observation?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.providerOptions,
@@ -28091,7 +28237,7 @@ var ObservationalMemory = class ObservationalMemory {
28091
28237
  observationTokens,
28092
28238
  shareTokenBudget: isSharedBudget,
28093
28239
  modelSettings: {
28094
- temperature: config.reflection?.modelSettings?.temperature ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.temperature,
28240
+ ...reflectionTemperature !== void 0 ? { temperature: reflectionTemperature } : {},
28095
28241
  ...reflectionDefaultMaxOutputTokens !== void 0 ? { maxOutputTokens: reflectionDefaultMaxOutputTokens } : {}
28096
28242
  },
28097
28243
  providerOptions: config.reflection?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
@@ -29815,9 +29961,8 @@ ${formattedMessages}
29815
29961
  currentTask: lastActivated.currentTask,
29816
29962
  threadTitle: chunkThreadTitle
29817
29963
  });
29818
- const oldTitle = thread.title?.trim();
29819
- const newTitle = chunkThreadTitle?.trim();
29820
- const shouldUpdateThreadTitle = !!newTitle && newTitle.length >= 3 && newTitle !== oldTitle;
29964
+ const newTitle = resolveThreadTitleUpdate(thread, chunkThreadTitle);
29965
+ const shouldUpdateThreadTitle = newTitle !== void 0;
29821
29966
  await this.storage.patchThread({
29822
29967
  id: threadId,
29823
29968
  ...shouldUpdateThreadTitle ? { title: newTitle } : {},
@@ -31209,6 +31354,13 @@ var Memory = class Memory extends _mastra_core_memory.MastraMemory {
31209
31354
  });
31210
31355
  return savedThread;
31211
31356
  }
31357
+ /**
31358
+ * Update a thread's title or metadata.
31359
+ *
31360
+ * Unlike `session.thread.rename()`, this does not pin the title, so
31361
+ * Observational Memory may still replace it with an extracted title. To
31362
+ * protect a manual rename, set `metadata.titlePinned` to `true`.
31363
+ */
31212
31364
  async updateThread({ id, title, metadata, memoryConfig }) {
31213
31365
  const updatedThread = await (await this.getMemoryStore()).patchThread({
31214
31366
  id,
@@ -33245,4 +33397,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
33245
33397
  }
33246
33398
  });
33247
33399
 
33248
- //# sourceMappingURL=src-Da7AKXPR.cjs.map
33400
+ //# sourceMappingURL=src-C4niho1P.cjs.map