@mastra/memory 1.31.0-alpha.1 → 1.31.0-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.
Files changed (35) hide show
  1. package/dist/docs/SKILL.md +1 -1
  2. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  3. package/dist/docs/references/docs-memory-observational-memory.md +33 -0
  4. package/dist/docs/references/integrations-channels-github.md +6 -2
  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.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/processors/index.cjs +1 -1
  16. package/dist/processors/index.js +1 -1
  17. package/dist/processors/observational-memory/hooks.d.ts +63 -0
  18. package/dist/processors/observational-memory/hooks.d.ts.map +1 -0
  19. package/dist/processors/observational-memory/observation-groups.d.ts.map +1 -1
  20. package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
  21. package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -1
  22. package/dist/processors/observational-memory/observation-turn/turn.d.ts +2 -0
  23. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  26. package/dist/processors/observational-memory/types.d.ts +6 -6
  27. package/dist/{src-Ju0u9OWc.js → src-CSVmHSng.js} +256 -52
  28. package/dist/src-CSVmHSng.js.map +1 -0
  29. package/dist/{src-BCvV2aGL.cjs → src-QqTmOY5X.cjs} +271 -67
  30. package/dist/src-QqTmOY5X.cjs.map +1 -0
  31. package/dist/tools/om-tools.d.ts +4 -2
  32. package/dist/tools/om-tools.d.ts.map +1 -1
  33. package/package.json +15 -5
  34. package/dist/src-BCvV2aGL.cjs.map +0 -1
  35. package/dist/src-Ju0u9OWc.js.map +0 -1
@@ -18,8 +18,7 @@ import { appendFileSync, mkdirSync, writeFileSync } from "fs";
18
18
  import { join } from "path";
19
19
  import { createTool } from "@mastra/core/tools";
20
20
  import { MASTRA_THREAD_ID_KEY, RequestContext } from "@mastra/core/request-context";
21
- import { modelSupportsAttachments, resolveModelConfig } from "@mastra/core/llm";
22
- import { estimateTokenCount } from "tokenx";
21
+ import { modelSupportsAttachments, modelSupportsTemperature, resolveModelConfig } from "@mastra/core/llm";
23
22
  import { AsyncLocalStorage } from "async_hooks";
24
23
  import probeImageSizeSync from "probe-image-size/sync.js";
25
24
  import { isStandardSchemaWithJSON as isStandardSchemaWithJSON$1, toStandardSchema as toStandardSchema$1 } from "@mastra/core/schema";
@@ -18304,6 +18303,62 @@ function safeSlice(str, end) {
18304
18303
  return str.slice(0, safeEnd);
18305
18304
  }
18306
18305
  //#endregion
18306
+ //#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/tokenx/1.3.0/09093cdaeebfe25c39ba9052d6277b45fc99abf5e14e859f60f338f5c5160afd/node_modules/tokenx/dist/index.mjs
18307
+ const PATTERNS = {
18308
+ whitespace: /^\s+$/,
18309
+ cjk: /[\u4E00-\u9FFF\u3400-\u4DBF\u3000-\u303F\uFF00-\uFFEF\u30A0-\u30FF\u2E80-\u2EFF\u31C0-\u31EF\u3200-\u32FF\u3300-\u33FF\uAC00-\uD7AF\u1100-\u11FF\u3130-\u318F\uA960-\uA97F\uD7B0-\uD7FF]/,
18310
+ numeric: /^\d+(?:[.,]\d+)*$/,
18311
+ punctuation: /[.,!?;(){}[\]<>:/\\|@#$%^&*+=`~_-]/,
18312
+ alphanumeric: /^[a-zA-Z0-9\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]+$/
18313
+ };
18314
+ const TOKEN_SPLIT_PATTERN = /* @__PURE__ */ new RegExp(`(\\s+|${PATTERNS.punctuation.source}+)`);
18315
+ const DEFAULT_CHARS_PER_TOKEN = 6;
18316
+ const SHORT_TOKEN_THRESHOLD = 3;
18317
+ const DEFAULT_LANGUAGE_CONFIGS = [
18318
+ {
18319
+ pattern: /[äöüßẞ]/i,
18320
+ averageCharsPerToken: 3
18321
+ },
18322
+ {
18323
+ pattern: /[éèêëàâîïôûùüÿçœæáíóúñ]/i,
18324
+ averageCharsPerToken: 3
18325
+ },
18326
+ {
18327
+ pattern: /[ąćęłńóśźżěščřžýůúďťň]/i,
18328
+ averageCharsPerToken: 3.5
18329
+ }
18330
+ ];
18331
+ /**
18332
+ * Estimates the number of tokens in a text string using heuristic rules.
18333
+ */
18334
+ function estimateTokenCount(text, options = {}) {
18335
+ if (!text) return 0;
18336
+ const { defaultCharsPerToken = DEFAULT_CHARS_PER_TOKEN, languageConfigs = DEFAULT_LANGUAGE_CONFIGS } = options;
18337
+ const segments = text.split(TOKEN_SPLIT_PATTERN).filter(Boolean);
18338
+ let tokenCount = 0;
18339
+ for (const segment of segments) tokenCount += estimateSegmentTokens(segment, languageConfigs, defaultCharsPerToken);
18340
+ return tokenCount;
18341
+ }
18342
+ function estimateSegmentTokens(segment, languageConfigs, defaultCharsPerToken) {
18343
+ if (PATTERNS.whitespace.test(segment)) return 0;
18344
+ if (PATTERNS.cjk.test(segment)) return getCharacterCount(segment);
18345
+ if (PATTERNS.numeric.test(segment)) return 1;
18346
+ if (segment.length <= SHORT_TOKEN_THRESHOLD) return 1;
18347
+ if (PATTERNS.punctuation.test(segment)) return segment.length > 1 ? Math.ceil(segment.length / 2) : 1;
18348
+ if (PATTERNS.alphanumeric.test(segment)) {
18349
+ const charsPerToken$1 = getLanguageSpecificCharsPerToken(segment, languageConfigs) ?? defaultCharsPerToken;
18350
+ return Math.ceil(segment.length / charsPerToken$1);
18351
+ }
18352
+ const charsPerToken = getLanguageSpecificCharsPerToken(segment, languageConfigs) ?? defaultCharsPerToken;
18353
+ return Math.ceil(segment.length / charsPerToken);
18354
+ }
18355
+ function getLanguageSpecificCharsPerToken(segment, languageConfigs) {
18356
+ for (const config of languageConfigs) if (config.pattern.test(segment)) return config.averageCharsPerToken;
18357
+ }
18358
+ function getCharacterCount(text) {
18359
+ return Array.from(text).length;
18360
+ }
18361
+ //#endregion
18307
18362
  //#region src/processors/observational-memory/tool-argument-helpers.ts
18308
18363
  const DEFAULT_OBSERVER_TOOL_ARGUMENT_MAX_TOKENS = 2e3;
18309
18364
  const OBSERVER_TOOL_ARGUMENT_INLINE_STRING_MAX_CHARS = 160;
@@ -22778,13 +22833,13 @@ function renderFormattedParts(parts, timestamps, options) {
22778
22833
  tokenOffset: expandedTokens - options.maxTokens
22779
22834
  };
22780
22835
  }
22781
- async function recallPart({ memory, threadId, resourceId, cursor, partIndex, charOffset, threadScope, maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22836
+ async function recallPart({ memory, threadId, resourceId, cursor, partIndex, charOffset, threadScope, retrievalScope = "thread", maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22782
22837
  if (!memory || typeof memory.getMemoryStore !== "function") throw new Error("Memory instance is required for recall");
22783
22838
  if (!threadId) throw new Error("Thread ID is required for recall");
22784
22839
  const resolved = await resolveCursorMessage(memory, cursor, {
22785
22840
  resourceId,
22786
22841
  threadScope,
22787
- enforceThreadScope: false
22842
+ enforceThreadScope: retrievalScope !== "resource"
22788
22843
  });
22789
22844
  if ("hint" in resolved) throw new Error(resolved.hint);
22790
22845
  const allParts = formatMessageParts(resolved, "high");
@@ -22795,7 +22850,7 @@ async function recallPart({ memory, threadId, resourceId, cursor, partIndex, cha
22795
22850
  if (partIndex > Math.max(...allParts.map((p) => p.partIndex))) {
22796
22851
  const nextMessage = await getNextVisibleMessage({
22797
22852
  memory,
22798
- threadId,
22853
+ threadId: resolved.threadId ?? threadId,
22799
22854
  resourceId,
22800
22855
  after: resolved.createdAt
22801
22856
  });
@@ -22834,7 +22889,7 @@ async function recallPart({ memory, threadId, resourceId, cursor, partIndex, cha
22834
22889
  note
22835
22890
  };
22836
22891
  }
22837
- async function recallMessages({ memory, threadId, resourceId, cursor, page = 1, limit = 20, detail = "low", partType, toolName, threadScope, maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22892
+ async function recallMessages({ memory, threadId, resourceId, cursor, page = 1, limit = 20, detail = "low", partType, toolName, threadScope, retrievalScope = "thread", maxTokens = DEFAULT_MAX_RESULT_TOKENS }) {
22838
22893
  if (!memory) throw new Error("Memory instance is required for recall");
22839
22894
  if (!threadId) throw new Error("Thread ID is required for recall");
22840
22895
  if (typeof memory.getMemoryStore !== "function") throw new Error("recall requires a Memory instance with storage access");
@@ -22845,7 +22900,7 @@ async function recallMessages({ memory, threadId, resourceId, cursor, page = 1,
22845
22900
  const resolved = await resolveCursorMessage(memory, cursor, {
22846
22901
  resourceId,
22847
22902
  threadScope,
22848
- enforceThreadScope: false
22903
+ enforceThreadScope: retrievalScope === "thread"
22849
22904
  });
22850
22905
  if ("hint" in resolved) return {
22851
22906
  messages: resolved.hint,
@@ -22862,7 +22917,7 @@ async function recallMessages({ memory, threadId, resourceId, cursor, page = 1,
22862
22917
  const anchor = resolved;
22863
22918
  const crossThreadId = anchor.threadId && anchor.threadId !== threadId ? anchor.threadId : void 0;
22864
22919
  if (crossThreadId && threadScope) return {
22865
- 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.`,
22920
+ 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.`,
22866
22921
  count: 0,
22867
22922
  cursor,
22868
22923
  page: normalizedPage,
@@ -23021,7 +23076,8 @@ async function recallThreadFromStart({ memory, threadId, resourceId, page = 1, l
23021
23076
  };
23022
23077
  }
23023
23078
  const recallTool = (_memoryConfig, options) => {
23024
- const isResourceScope = (options?.retrievalScope ?? "thread") === "resource";
23079
+ const retrievalScope = options?.retrievalScope ?? "thread";
23080
+ const isResourceScope = retrievalScope === "resource";
23025
23081
  const searchEnabled = options?.searchEnabled ?? true;
23026
23082
  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.`;
23027
23083
  const modeEnum = searchEnabled ? [
@@ -23232,23 +23288,25 @@ const recallTool = (_memoryConfig, options) => {
23232
23288
  if (partIndex !== void 0 && partIndex !== null) return recallPart({
23233
23289
  memory,
23234
23290
  threadId: targetThreadId,
23235
- resourceId: isResourceScope ? resourceId : void 0,
23291
+ resourceId,
23236
23292
  cursor,
23237
23293
  partIndex,
23238
23294
  charOffset,
23239
- threadScope
23295
+ threadScope,
23296
+ retrievalScope
23240
23297
  });
23241
23298
  return recallMessages({
23242
23299
  memory,
23243
23300
  threadId: targetThreadId,
23244
- resourceId: isResourceScope ? resourceId : void 0,
23301
+ resourceId,
23245
23302
  cursor,
23246
23303
  page,
23247
23304
  limit,
23248
23305
  detail: detail ?? "low",
23249
23306
  partType,
23250
23307
  toolName,
23251
- threadScope
23308
+ threadScope,
23309
+ retrievalScope
23252
23310
  });
23253
23311
  }
23254
23312
  });
@@ -24354,7 +24412,8 @@ function stripThreadTags(observations) {
24354
24412
  }
24355
24413
  //#endregion
24356
24414
  //#region src/processors/observational-memory/observation-groups.ts
24357
- const OBSERVATION_GROUP_PATTERN = /<observation-group\s([^>]*)>([\s\S]*?)<\/observation-group>/g;
24415
+ const OBSERVATION_GROUP_OPEN = "<observation-group";
24416
+ const OBSERVATION_GROUP_CLOSE = "</observation-group>";
24358
24417
  const ATTRIBUTE_PATTERN = /([\w][\w-]*)="([^"]*)"/g;
24359
24418
  const REFLECTION_GROUP_SPLIT_PATTERN = /^##\s+Group\s+/m;
24360
24419
  function parseObservationGroupAttributes(attributeString) {
@@ -24365,6 +24424,81 @@ function parseObservationGroupAttributes(attributeString) {
24365
24424
  }
24366
24425
  return attributes;
24367
24426
  }
24427
+ function isLineStart(observations, index) {
24428
+ return index === 0 || observations[index - 1] === "\n";
24429
+ }
24430
+ function scanObservationGroupTags(observations) {
24431
+ const tags = [];
24432
+ const incompleteOpenings = [];
24433
+ let cursor = 0;
24434
+ let closeFloor = 0;
24435
+ while (cursor < observations.length) {
24436
+ const start = observations.indexOf(OBSERVATION_GROUP_OPEN, cursor);
24437
+ if (start === -1) break;
24438
+ const attributesStart = start + 18;
24439
+ if (!/\s/.test(observations[attributesStart] ?? "")) {
24440
+ cursor = attributesStart;
24441
+ continue;
24442
+ }
24443
+ const openEnd = observations.indexOf(">", attributesStart);
24444
+ if (openEnd === -1) break;
24445
+ const closeStart = observations.indexOf(OBSERVATION_GROUP_CLOSE, Math.max(openEnd + 1, closeFloor));
24446
+ if (closeStart === -1) {
24447
+ if (isLineStart(observations, start)) incompleteOpenings.push({
24448
+ start,
24449
+ end: openEnd + 1
24450
+ });
24451
+ closeFloor = observations.length;
24452
+ cursor = openEnd + 1;
24453
+ continue;
24454
+ }
24455
+ const nestedStart = observations.indexOf(OBSERVATION_GROUP_OPEN, openEnd + 1);
24456
+ if (nestedStart !== -1 && nestedStart < closeStart && isLineStart(observations, nestedStart)) {
24457
+ incompleteOpenings.push({
24458
+ start,
24459
+ end: openEnd + 1
24460
+ });
24461
+ closeFloor = closeStart;
24462
+ cursor = nestedStart;
24463
+ continue;
24464
+ }
24465
+ tags.push({
24466
+ start,
24467
+ end: closeStart + 20,
24468
+ attributeString: observations.slice(attributesStart, openEnd),
24469
+ content: observations.slice(openEnd + 1, closeStart)
24470
+ });
24471
+ closeFloor = closeStart;
24472
+ cursor = closeStart + 20;
24473
+ }
24474
+ return {
24475
+ tags,
24476
+ incompleteOpenings
24477
+ };
24478
+ }
24479
+ function replaceObservationGroupTags(observations, replace) {
24480
+ const { tags, incompleteOpenings } = scanObservationGroupTags(observations);
24481
+ const parts = [];
24482
+ let cursor = 0;
24483
+ let openingIndex = 0;
24484
+ const skipIncompleteOpeningsBefore = (limit) => {
24485
+ while (openingIndex < incompleteOpenings.length && incompleteOpenings[openingIndex].start < limit) {
24486
+ const opening = incompleteOpenings[openingIndex];
24487
+ openingIndex++;
24488
+ if (opening.start < cursor) continue;
24489
+ parts.push(observations.slice(cursor, opening.start));
24490
+ cursor = opening.end;
24491
+ }
24492
+ };
24493
+ for (const tag of tags) {
24494
+ skipIncompleteOpeningsBefore(tag.start);
24495
+ parts.push(observations.slice(cursor, tag.start), replace(tag));
24496
+ cursor = tag.end;
24497
+ }
24498
+ skipIncompleteOpeningsBefore(Number.POSITIVE_INFINITY);
24499
+ parts.push(observations.slice(cursor));
24500
+ return parts.join("");
24501
+ }
24368
24502
  function parseReflectionObservationGroupSections(content) {
24369
24503
  const normalizedContent = content.trim();
24370
24504
  if (!normalizedContent || !REFLECTION_GROUP_SPLIT_PATTERN.test(normalizedContent)) return [];
@@ -24389,9 +24523,8 @@ function wrapInObservationGroup(observations, range, id = generateAnchorId(), _s
24389
24523
  function parseObservationGroups(observations) {
24390
24524
  if (!observations) return [];
24391
24525
  const groups = [];
24392
- let match;
24393
- while ((match = OBSERVATION_GROUP_PATTERN.exec(observations)) !== null) {
24394
- const attributes = parseObservationGroupAttributes(match[1] ?? "");
24526
+ for (const tag of scanObservationGroupTags(observations).tags) {
24527
+ const attributes = parseObservationGroupAttributes(tag.attributeString);
24395
24528
  const id = attributes.id;
24396
24529
  const range = attributes.range;
24397
24530
  if (!id || !range) continue;
@@ -24399,54 +24532,96 @@ function parseObservationGroups(observations) {
24399
24532
  id,
24400
24533
  range,
24401
24534
  kind: attributes.kind,
24402
- content: match[2].trim()
24535
+ content: tag.content.trim()
24403
24536
  });
24404
24537
  }
24405
24538
  return groups;
24406
24539
  }
24407
24540
  function stripObservationGroups(observations) {
24408
24541
  if (!observations) return observations;
24409
- return observations.replace(OBSERVATION_GROUP_PATTERN, (_match, _attributes, content) => content.trim()).replace(/\n{3,}/g, "\n\n").trim();
24542
+ return replaceObservationGroupTags(observations, (tag) => tag.content.trim()).replace(/\n{3,}/g, "\n\n").trim();
24410
24543
  }
24411
24544
  function getRangeSegments(range) {
24412
24545
  return range.split(",").map((segment) => segment.trim()).filter(Boolean);
24413
24546
  }
24547
+ function getRangeEndpoints(segment) {
24548
+ const parts = segment.split(":").map((part) => part.trim());
24549
+ if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
24550
+ return {
24551
+ start: parts[0],
24552
+ end: parts[1]
24553
+ };
24554
+ }
24555
+ /**
24556
+ * `buildMessageRange` produces opaque UUID endpoints, so those segments cannot be ordered by value.
24557
+ * Spanning from the first start to the last end keeps reflected metadata bounded — the full segment
24558
+ * list is deliberately not preserved (#14791). Segments that are not `start:end` pairs are left as
24559
+ * they are rather than being joined into a fabricated span.
24560
+ */
24561
+ function spanRangeSegments(segments) {
24562
+ const endpoints = segments.map(getRangeEndpoints);
24563
+ if (endpoints.some((endpoint) => endpoint === null)) return segments.join(",");
24564
+ const first = endpoints[0];
24565
+ const last = endpoints.at(-1);
24566
+ return `${first.start}:${last.end}`;
24567
+ }
24414
24568
  function combineObservationGroupRanges(groups) {
24415
- const segments = groups.flatMap((group) => getRangeSegments(group.range));
24569
+ const segments = Array.from(new Set(groups.flatMap((group) => getRangeSegments(group.range))));
24416
24570
  if (segments.length === 0) return "";
24417
- const firstSegment = segments[0];
24418
- const lastSegment = segments[segments.length - 1];
24419
- const firstStart = firstSegment?.split(":")[0]?.trim();
24420
- const lastEnd = lastSegment?.split(":").at(-1)?.trim();
24421
- if (firstStart && lastEnd) return `${firstStart}:${lastEnd}`;
24422
- return Array.from(new Set(segments)).join(",");
24571
+ const numericEndpoints = [];
24572
+ if (!segments.every((segment) => {
24573
+ const endpoints = getRangeEndpoints(segment);
24574
+ if (!endpoints || !/^\d+$/.test(endpoints.start) || !/^\d+$/.test(endpoints.end)) return false;
24575
+ const start = {
24576
+ label: endpoints.start,
24577
+ value: Number(endpoints.start)
24578
+ };
24579
+ const end = {
24580
+ label: endpoints.end,
24581
+ value: Number(endpoints.end)
24582
+ };
24583
+ if (!Number.isSafeInteger(start.value) || !Number.isSafeInteger(end.value)) return false;
24584
+ numericEndpoints.push(start.value <= end.value ? {
24585
+ start,
24586
+ end
24587
+ } : {
24588
+ start: end,
24589
+ end: start
24590
+ });
24591
+ return true;
24592
+ })) return spanRangeSegments(segments);
24593
+ const first = numericEndpoints.reduce((lowest, pair) => pair.start.value < lowest.start.value ? pair : lowest);
24594
+ const last = numericEndpoints.reduce((highest, pair) => pair.end.value > highest.end.value ? pair : highest);
24595
+ return `${first.start.label}:${last.end.label}`;
24423
24596
  }
24424
24597
  function renderObservationGroupsForReflection(observations) {
24425
- const groups = parseObservationGroups(observations);
24426
- if (groups.length === 0) return null;
24427
- const groupsByContent = new Map(groups.map((g) => [g.content.trim(), g]));
24428
- return observations.replace(OBSERVATION_GROUP_PATTERN, (_match, _attrs, content) => {
24429
- const group = groupsByContent.get(content.trim());
24430
- if (!group) return content.trim();
24431
- return `## Group \`${group.id}\`\n_range: \`${group.range}\`_\n\n${group.content}`;
24598
+ if (parseObservationGroups(observations).length === 0) return null;
24599
+ return replaceObservationGroupTags(observations, (tag) => {
24600
+ const attributes = parseObservationGroupAttributes(tag.attributeString);
24601
+ if (!attributes.id || !attributes.range) return tag.content.trim();
24602
+ return `## Group \`${attributes.id}\`\n_range: \`${attributes.range}\`_\n\n${tag.content.trim()}`;
24432
24603
  }).replace(/\n{3,}/g, "\n\n").trim();
24433
24604
  }
24434
24605
  function getCanonicalGroupId(sectionHeading, fallbackIndex) {
24435
24606
  return sectionHeading.match(/`([^`]+)`/)?.[1]?.trim() || `derived-group-${fallbackIndex + 1}`;
24436
24607
  }
24608
+ function getContentLines(content) {
24609
+ return content.split("\n").map((line) => line.trim()).filter(Boolean);
24610
+ }
24437
24611
  function deriveObservationGroupProvenance(content, groups) {
24438
24612
  const sections = parseReflectionObservationGroupSections(content);
24439
24613
  if (sections.length === 0 || groups.length === 0) return [];
24440
24614
  return sections.map((section, index) => {
24441
- const bodyLines = new Set(section.body.split("\n").map((line) => line.trim()).filter(Boolean));
24442
- const matchingGroups = groups.filter((group) => {
24443
- return group.content.split("\n").map((line) => line.trim()).filter(Boolean).some((line) => bodyLines.has(line));
24444
- });
24615
+ const canonicalGroupId = getCanonicalGroupId(section.heading, index);
24616
+ const identifiedGroup = groups.find((group) => group.id === canonicalGroupId);
24617
+ const bodyLines = new Set(getContentLines(section.body));
24618
+ const matchingGroups = groups.filter((group) => getContentLines(group.content).some((line) => bodyLines.has(line)));
24445
24619
  const fallbackGroup = groups[Math.min(index, groups.length - 1)];
24446
- const resolvedGroups = matchingGroups.length > 0 ? matchingGroups : fallbackGroup ? [fallbackGroup] : [];
24620
+ const identifiedLines = new Set(getContentLines(identifiedGroup?.content ?? ""));
24621
+ const contributingGroups = identifiedGroup ? matchingGroups.filter((group) => group.id !== identifiedGroup.id && getContentLines(group.content).some((line) => bodyLines.has(line) && !identifiedLines.has(line))) : [];
24447
24622
  return {
24448
- id: getCanonicalGroupId(section.heading, index),
24449
- range: combineObservationGroupRanges(resolvedGroups),
24623
+ id: canonicalGroupId,
24624
+ range: combineObservationGroupRanges(identifiedGroup ? groups.filter((group) => group === identifiedGroup || contributingGroups.includes(group)) : matchingGroups.length > 0 ? matchingGroups : fallbackGroup ? [fallbackGroup] : []),
24450
24625
  kind: "reflection",
24451
24626
  content: section.body
24452
24627
  };
@@ -25040,6 +25215,10 @@ var SyncObservationStrategy = class extends ObservationStrategy {
25040
25215
  async persist(processed) {
25041
25216
  const { record, threadId, resourceId, messages } = this.opts;
25042
25217
  const thread = await this.storage.getThreadById({ threadId });
25218
+ if (!await this.storage.getObservationalMemory(record.threadId, record.resourceId)) {
25219
+ omDebug(`[OM:sync-obs] skipping persist for thread ${threadId}: observational memory record is gone`);
25220
+ return;
25221
+ }
25043
25222
  let threadUpdateMarker;
25044
25223
  if (thread) {
25045
25224
  const oldTitle = thread.title?.trim();
@@ -25221,6 +25400,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
25221
25400
  async persist(processed) {
25222
25401
  if (!processed.observations) return;
25223
25402
  const { record, threadId, resourceId, messages } = this.opts;
25403
+ if (!await this.storage.getObservationalMemory(record.threadId, record.resourceId)) {
25404
+ omDebug(`[OM:asyncBuffer] skipping persist for thread ${threadId}: observational memory record is gone`);
25405
+ return;
25406
+ }
25224
25407
  const messageTokens = await this.tokenCounter.countMessagesAsync(messages);
25225
25408
  await withRetry(() => this.storage.updateBufferedObservations({
25226
25409
  id: record.id,
@@ -26214,6 +26397,10 @@ var ObservationTurn = class {
26214
26397
  if (!this._context) throw new Error("Turn not started — call start() first");
26215
26398
  return this._context;
26216
26399
  }
26400
+ /** Whether the turn has been ended and can no longer accept steps. */
26401
+ get ended() {
26402
+ return this._ended;
26403
+ }
26217
26404
  /** The current step, if one exists. */
26218
26405
  get currentStep() {
26219
26406
  return this._currentStep;
@@ -28041,11 +28228,19 @@ var ObservationalMemory = class ObservationalMemory {
28041
28228
  const messageTokens = config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens;
28042
28229
  const observationTokens = config.reflection?.observationTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.observationTokens;
28043
28230
  const isSharedBudget = config.shareTokenBudget ?? false;
28044
- const isDefaultModelSelection = (model) => model === void 0 || model === "default" || model instanceof ModelByInputTokens;
28231
+ const supportsDefaultTemperature = (model) => {
28232
+ if (model instanceof ModelByInputTokens || typeof model === "function") return false;
28233
+ if (typeof model === "string") return modelSupportsTemperature(model) === true;
28234
+ if (model && typeof model === "object" && "provider" in model && "modelId" in model && typeof model.provider === "string" && typeof model.modelId === "string") return modelSupportsTemperature(`${model.provider.split(".")[0]}/${model.modelId}`) === true;
28235
+ return false;
28236
+ };
28237
+ const usesDefaultOutputTokenBudget = (model) => model === void 0 || model === "default" || model instanceof ModelByInputTokens;
28045
28238
  const observationSelectedModel = topLevelModel ?? observationConfigModel ?? reflectionConfigModel;
28046
28239
  const reflectionSelectedModel = topLevelModel ?? reflectionConfigModel ?? observationConfigModel;
28047
- const observationDefaultMaxOutputTokens = config.observation?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(observationSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens : void 0);
28048
- const reflectionDefaultMaxOutputTokens = config.reflection?.modelSettings?.maxOutputTokens ?? (isDefaultModelSelection(reflectionSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens : void 0);
28240
+ const observationTemperature = config.observation?.modelSettings?.temperature ?? (supportsDefaultTemperature(observationModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.temperature : void 0);
28241
+ const observationDefaultMaxOutputTokens = config.observation?.modelSettings?.maxOutputTokens ?? (usesDefaultOutputTokenBudget(observationSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.maxOutputTokens : void 0);
28242
+ const reflectionTemperature = config.reflection?.modelSettings?.temperature ?? (supportsDefaultTemperature(reflectionModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.temperature : void 0);
28243
+ const reflectionDefaultMaxOutputTokens = config.reflection?.modelSettings?.maxOutputTokens ?? (usesDefaultOutputTokenBudget(reflectionSelectedModel) ? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.maxOutputTokens : void 0);
28049
28244
  const totalBudget = messageTokens + observationTokens;
28050
28245
  const userExplicitlyConfiguredAsync = config.observation?.bufferTokens !== void 0 || config.observation?.bufferActivation !== void 0 || config.reflection?.bufferActivation !== void 0;
28051
28246
  const asyncBufferingDisabled = config.observation?.bufferTokens === false || config.scope === "resource" && !userExplicitlyConfiguredAsync;
@@ -28061,7 +28256,7 @@ var ObservationalMemory = class ObservationalMemory {
28061
28256
  } : messageTokens,
28062
28257
  shareTokenBudget: isSharedBudget,
28063
28258
  modelSettings: {
28064
- temperature: config.observation?.modelSettings?.temperature ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings.temperature,
28259
+ ...observationTemperature !== void 0 ? { temperature: observationTemperature } : {},
28065
28260
  ...observationDefaultMaxOutputTokens !== void 0 ? { maxOutputTokens: observationDefaultMaxOutputTokens } : {}
28066
28261
  },
28067
28262
  providerOptions: config.observation?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.providerOptions,
@@ -28087,7 +28282,7 @@ var ObservationalMemory = class ObservationalMemory {
28087
28282
  observationTokens,
28088
28283
  shareTokenBudget: isSharedBudget,
28089
28284
  modelSettings: {
28090
- temperature: config.reflection?.modelSettings?.temperature ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.modelSettings.temperature,
28285
+ ...reflectionTemperature !== void 0 ? { temperature: reflectionTemperature } : {},
28091
28286
  ...reflectionDefaultMaxOutputTokens !== void 0 ? { maxOutputTokens: reflectionDefaultMaxOutputTokens } : {}
28092
28287
  },
28093
28288
  providerOptions: config.reflection?.providerOptions ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.providerOptions,
@@ -29942,9 +30137,11 @@ ${formattedMessages}
29942
30137
  let lifecycleError;
29943
30138
  let observationStarted = false;
29944
30139
  let generationBefore = -1;
30140
+ let recordInsideLock;
29945
30141
  try {
29946
30142
  await this.withLock(lockKey, async () => {
29947
30143
  const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
30144
+ recordInsideLock = freshRecord;
29948
30145
  generationBefore = freshRecord.generationCount;
29949
30146
  const unobservedMessages = messages ? this.getUnobservedMessages(messages, freshRecord) : await this.loadMessagesFromStorage(threadId, resourceId, freshRecord.lastObservedAt ? new Date(freshRecord.lastObservedAt) : void 0);
29950
30147
  if (!this.meetsObservationThreshold({
@@ -29987,7 +30184,7 @@ ${formattedMessages}
29987
30184
  omDebug(`[OM:hooks] onObservationEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
29988
30185
  }
29989
30186
  if (lifecycleError !== void 0) throw lifecycleError;
29990
- const record = await this.getOrCreateRecord(threadId, resourceId);
30187
+ const record = await this.getRecord(threadId, resourceId) ?? recordInsideLock;
29991
30188
  const reflected = record.generationCount > generationBefore && generationBefore >= 0;
29992
30189
  return {
29993
30190
  observed,
@@ -30468,6 +30665,10 @@ var ObservationalMemoryProcessor = class {
30468
30665
  if (this.turn === activeTurn) this.turn = void 0;
30469
30666
  state.__omTurn = void 0;
30470
30667
  }
30668
+ if (activeTurn?.ended || this.turn?.ended) {
30669
+ if (this.turn?.ended) this.turn = void 0;
30670
+ state.__omTurn = void 0;
30671
+ }
30471
30672
  if (!this.turn || !state.__omTurn) {
30472
30673
  if (this.turn && !state.__omTurn) await this.turn.end().catch(() => {});
30473
30674
  this.turn = this.engine.beginTurn({
@@ -30568,11 +30769,11 @@ var ObservationalMemoryProcessor = class {
30568
30769
  return this.engine.getTokenCounter().runWithModelContext(state.__omActorModelContext, async () => {
30569
30770
  if (parseMemoryRequestContext(requestContext)?.memoryConfig?.readOnly) return messageList;
30570
30771
  const turn = asLiveTurn(state.__omTurn) ?? this.turn;
30571
- if (turn) {
30572
- await turn.end();
30573
- this.turn = void 0;
30574
- state.__omTurn = void 0;
30575
- } else {
30772
+ const liveTurn = turn && !turn.ended ? turn : void 0;
30773
+ if (liveTurn) await liveTurn.end();
30774
+ this.turn = void 0;
30775
+ state.__omTurn = void 0;
30776
+ if (!liveTurn) {
30576
30777
  const newOutput = messageList.get.response.db();
30577
30778
  const messagesToSave = [...messageList.get.input.db(), ...newOutput];
30578
30779
  if (messagesToSave.length > 0 && context.threadId) await this.engine.persistMessages(messagesToSave, context.threadId, context.resourceId);
@@ -30782,6 +30983,7 @@ const DEFAULT_MESSAGE_RANGE = {
30782
30983
  };
30783
30984
  const DEFAULT_TOP_K = 4;
30784
30985
  const VECTOR_DELETE_BATCH_SIZE = 100;
30986
+ const OM_DELETE_DRAIN_TIMEOUT_MS = 1e4;
30785
30987
  const DEFAULT_EMBEDDING_CACHE_MAX_SIZE = 1e3;
30786
30988
  /**
30787
30989
  * Gives Mastra agents conversation history, with optional working memory,
@@ -31233,6 +31435,8 @@ var Memory = class Memory extends MastraMemory {
31233
31435
  await this.deleteStoredThread(memoryStore, threadId, thread?.resourceId);
31234
31436
  }
31235
31437
  async deleteStoredThread(memoryStore, threadId, resourceId) {
31438
+ const engine = this._omEngine ? await this._omEngine : this._omEngineInstance;
31439
+ if (engine && resourceId) await engine.waitForBuffering(threadId, resourceId, OM_DELETE_DRAIN_TIMEOUT_MS);
31236
31440
  await memoryStore.deleteThread({ threadId });
31237
31441
  if (resourceId && memoryStore.supportsObservationalMemory) await memoryStore.clearObservationalMemory(threadId, resourceId);
31238
31442
  if (this.vector) this.trackVectorCleanup(this.deleteThreadVectors(threadId));
@@ -32966,4 +33170,4 @@ Notes:
32966
33170
  //#endregion
32967
33171
  export { formatMessagesForObserver as A, OBSERVATION_CONTINUATION_HINT as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, ModelByInputTokens as G, KnowledgeSemanticIndexCoordinator as H, stripEphemeralAnchorIds as I, publishSubconsciousActivity as J, SUBCONSCIOUS_ACTIVITY_STATE_ID as K, OBSERVATIONAL_MEMORY_DEFAULTS as L, optimizeObservationsForContext as M, parseObserverOutput as N, buildObserverSystemPrompt as O, injectAnchorIds as P, OBSERVATION_CONTEXT_INSTRUCTIONS as R, WorkingMemoryExtractor as S, TokenCounter as T, StaleKnowledgeSemanticIndexError as U, Subconscious as V, SubconsciousRemindExtractor as W, Extractor as X, renderSubconsciousActivity as Y, wrapInObservationGroup as _, extractWorkingMemoryContent as a, WorkingMemoryStateProcessor as b, getObservationsAsOf as c, combineObservationGroupRanges as d, deriveObservationGroupProvenance as f, stripObservationGroups as g, renderObservationGroupsForReflection as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, ObservationalMemoryProcessor as l, reconcileObservationGroupsFromReflection as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, parseObservationGroups as p, buildSubconsciousActivitySnapshot as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, ObservationalMemory as u, WORKING_MEMORY_STATE_ID as v, summarizeConversation as w, deepMergeWorkingMemory as x, WORKING_MEMORY_STATE_PROCESSOR_ID as y, OBSERVATION_CONTEXT_PROMPT as z };
32968
33172
 
32969
- //# sourceMappingURL=src-Ju0u9OWc.js.map
33173
+ //# sourceMappingURL=src-CSVmHSng.js.map