@mastra/memory 1.31.0-alpha.2 → 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.
@@ -52,7 +52,6 @@ let path = require("path");
52
52
  let _mastra_core_tools = require("@mastra/core/tools");
53
53
  let _mastra_core_request_context = require("@mastra/core/request-context");
54
54
  let _mastra_core_llm = require("@mastra/core/llm");
55
- let tokenx = require("tokenx");
56
55
  let async_hooks = require("async_hooks");
57
56
  let probe_image_size_sync_js = require("probe-image-size/sync.js");
58
57
  probe_image_size_sync_js = __toESM$1(probe_image_size_sync_js, 1);
@@ -18326,6 +18325,62 @@ function safeSlice(str, end) {
18326
18325
  return str.slice(0, safeEnd);
18327
18326
  }
18328
18327
  //#endregion
18328
+ //#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/tokenx/1.3.0/09093cdaeebfe25c39ba9052d6277b45fc99abf5e14e859f60f338f5c5160afd/node_modules/tokenx/dist/index.mjs
18329
+ const PATTERNS = {
18330
+ whitespace: /^\s+$/,
18331
+ 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]/,
18332
+ numeric: /^\d+(?:[.,]\d+)*$/,
18333
+ punctuation: /[.,!?;(){}[\]<>:/\\|@#$%^&*+=`~_-]/,
18334
+ alphanumeric: /^[a-zA-Z0-9\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]+$/
18335
+ };
18336
+ const TOKEN_SPLIT_PATTERN = /* @__PURE__ */ new RegExp(`(\\s+|${PATTERNS.punctuation.source}+)`);
18337
+ const DEFAULT_CHARS_PER_TOKEN = 6;
18338
+ const SHORT_TOKEN_THRESHOLD = 3;
18339
+ const DEFAULT_LANGUAGE_CONFIGS = [
18340
+ {
18341
+ pattern: /[äöüßẞ]/i,
18342
+ averageCharsPerToken: 3
18343
+ },
18344
+ {
18345
+ pattern: /[éèêëàâîïôûùüÿçœæáíóúñ]/i,
18346
+ averageCharsPerToken: 3
18347
+ },
18348
+ {
18349
+ pattern: /[ąćęłńóśźżěščřžýůúďťň]/i,
18350
+ averageCharsPerToken: 3.5
18351
+ }
18352
+ ];
18353
+ /**
18354
+ * Estimates the number of tokens in a text string using heuristic rules.
18355
+ */
18356
+ function estimateTokenCount(text, options = {}) {
18357
+ if (!text) return 0;
18358
+ const { defaultCharsPerToken = DEFAULT_CHARS_PER_TOKEN, languageConfigs = DEFAULT_LANGUAGE_CONFIGS } = options;
18359
+ const segments = text.split(TOKEN_SPLIT_PATTERN).filter(Boolean);
18360
+ let tokenCount = 0;
18361
+ for (const segment of segments) tokenCount += estimateSegmentTokens(segment, languageConfigs, defaultCharsPerToken);
18362
+ return tokenCount;
18363
+ }
18364
+ function estimateSegmentTokens(segment, languageConfigs, defaultCharsPerToken) {
18365
+ if (PATTERNS.whitespace.test(segment)) return 0;
18366
+ if (PATTERNS.cjk.test(segment)) return getCharacterCount(segment);
18367
+ if (PATTERNS.numeric.test(segment)) return 1;
18368
+ if (segment.length <= SHORT_TOKEN_THRESHOLD) return 1;
18369
+ if (PATTERNS.punctuation.test(segment)) return segment.length > 1 ? Math.ceil(segment.length / 2) : 1;
18370
+ if (PATTERNS.alphanumeric.test(segment)) {
18371
+ const charsPerToken$1 = getLanguageSpecificCharsPerToken(segment, languageConfigs) ?? defaultCharsPerToken;
18372
+ return Math.ceil(segment.length / charsPerToken$1);
18373
+ }
18374
+ const charsPerToken = getLanguageSpecificCharsPerToken(segment, languageConfigs) ?? defaultCharsPerToken;
18375
+ return Math.ceil(segment.length / charsPerToken);
18376
+ }
18377
+ function getLanguageSpecificCharsPerToken(segment, languageConfigs) {
18378
+ for (const config of languageConfigs) if (config.pattern.test(segment)) return config.averageCharsPerToken;
18379
+ }
18380
+ function getCharacterCount(text) {
18381
+ return Array.from(text).length;
18382
+ }
18383
+ //#endregion
18329
18384
  //#region src/processors/observational-memory/tool-argument-helpers.ts
18330
18385
  const DEFAULT_OBSERVER_TOOL_ARGUMENT_MAX_TOKENS = 2e3;
18331
18386
  const OBSERVER_TOOL_ARGUMENT_INLINE_STRING_MAX_CHARS = 160;
@@ -18459,7 +18514,7 @@ function readEntries(value) {
18459
18514
  }
18460
18515
  }
18461
18516
  function fits(text, limits) {
18462
- return text.length <= limits.maxCharacters && (0, tokenx.estimateTokenCount)(text) <= limits.maxTokens;
18517
+ return text.length <= limits.maxCharacters && estimateTokenCount(text) <= limits.maxTokens;
18463
18518
  }
18464
18519
  function joinEntries(entries, marker) {
18465
18520
  const lines = [...entries].sort((a, b) => a.order - b.order).map((entry) => entry.text);
@@ -18473,7 +18528,7 @@ function selectOutlineEntries(entries, limits) {
18473
18528
  const selected = [];
18474
18529
  const candidates = [...entries].sort((a, b) => {
18475
18530
  if (a.depth !== b.depth) return a.depth - b.depth;
18476
- return (0, tokenx.estimateTokenCount)(a.text) - (0, tokenx.estimateTokenCount)(b.text) || a.text.length - b.text.length || a.order - b.order;
18531
+ return estimateTokenCount(a.text) - estimateTokenCount(b.text) || a.text.length - b.text.length || a.order - b.order;
18477
18532
  });
18478
18533
  for (const candidate of candidates) if (fits(joinEntries([...selected, candidate], marker), limits)) selected.push(candidate);
18479
18534
  const rendered = joinEntries(selected, marker);
@@ -18651,10 +18706,10 @@ function formatToolArgumentsForObserver(value, options) {
18651
18706
  let omittedPreviews = 0;
18652
18707
  for (let index = 0; index < previews.length; index++) {
18653
18708
  const remainingCount = previews.length - index;
18654
- const remainingTokens = Math.max(0, limits.maxTokens - (0, tokenx.estimateTokenCount)(rendered));
18709
+ const remainingTokens = Math.max(0, limits.maxTokens - estimateTokenCount(rendered));
18655
18710
  const remainingCharacters = Math.max(0, limits.maxCharacters - rendered.length - 1);
18656
18711
  const previewLimits = {
18657
- maxTokens: (0, tokenx.estimateTokenCount)(rendered) + Math.floor(remainingTokens / remainingCount),
18712
+ maxTokens: estimateTokenCount(rendered) + Math.floor(remainingTokens / remainingCount),
18658
18713
  maxCharacters: rendered.length + 1 + Math.floor(remainingCharacters / remainingCount)
18659
18714
  };
18660
18715
  const preview = formatPreview(previews[index].path, previews[index].value, previewLimits, rendered);
@@ -18736,11 +18791,11 @@ function resolveToolResultValue(part, invocationResult) {
18736
18791
  }
18737
18792
  function truncateStringByTokens(text, maxTokens) {
18738
18793
  if (!text || maxTokens <= 0) return "";
18739
- const totalTokens = (0, tokenx.estimateTokenCount)(text);
18794
+ const totalTokens = estimateTokenCount(text);
18740
18795
  if (totalTokens <= maxTokens) return text;
18741
18796
  const buildCandidate = (sliceEnd) => {
18742
18797
  const visible = safeSlice(text, sliceEnd);
18743
- return `${visible}\n... [truncated ~${totalTokens - (0, tokenx.estimateTokenCount)(visible)} tokens]`;
18798
+ return `${visible}\n... [truncated ~${totalTokens - estimateTokenCount(visible)} tokens]`;
18744
18799
  };
18745
18800
  let low = 0;
18746
18801
  let high = text.length;
@@ -18748,7 +18803,7 @@ function truncateStringByTokens(text, maxTokens) {
18748
18803
  while (low <= high) {
18749
18804
  const mid = Math.floor((low + high) / 2);
18750
18805
  const candidate = buildCandidate(mid);
18751
- if ((0, tokenx.estimateTokenCount)(candidate) <= maxTokens) {
18806
+ if (estimateTokenCount(candidate) <= maxTokens) {
18752
18807
  best = candidate;
18753
18808
  low = mid + 1;
18754
18809
  } else high = mid - 1;
@@ -21616,7 +21671,7 @@ var TokenCounter = class TokenCounter {
21616
21671
  */
21617
21672
  countString(text) {
21618
21673
  if (!text) return 0;
21619
- return (0, tokenx.estimateTokenCount)(text);
21674
+ return estimateTokenCount(text);
21620
21675
  }
21621
21676
  readOrPersistPartEstimate(part, kind, payload) {
21622
21677
  const key = buildEstimateKey(kind, payload);
@@ -22544,7 +22599,7 @@ function formatTimestamp(date) {
22544
22599
  return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
22545
22600
  }
22546
22601
  function truncateByTokens(text, maxTokens, hint) {
22547
- if ((0, tokenx.estimateTokenCount)(text) <= maxTokens) return {
22602
+ if (estimateTokenCount(text) <= maxTokens) return {
22548
22603
  text,
22549
22604
  wasTruncated: false
22550
22605
  };
@@ -22558,7 +22613,7 @@ function chunkTextByTokens(text, maxTokens, charOffset = 0) {
22558
22613
  const startCode = text.charCodeAt(startOffset);
22559
22614
  if (startCode >= 56320 && startCode <= 57343) startOffset += 1;
22560
22615
  const remaining = text.slice(startOffset);
22561
- if (!remaining || (0, tokenx.estimateTokenCount)(remaining) <= maxTokens) return {
22616
+ if (!remaining || estimateTokenCount(remaining) <= maxTokens) return {
22562
22617
  text: remaining,
22563
22618
  charOffset: startOffset,
22564
22619
  truncated: false
@@ -22569,7 +22624,7 @@ function chunkTextByTokens(text, maxTokens, charOffset = 0) {
22569
22624
  while (low <= high) {
22570
22625
  const mid = Math.floor((low + high) / 2);
22571
22626
  const candidate = safeSlice(remaining, mid);
22572
- const candidateTokens = (0, tokenx.estimateTokenCount)(candidate);
22627
+ const candidateTokens = estimateTokenCount(candidate);
22573
22628
  if (candidate && candidateTokens <= maxTokens) {
22574
22629
  best = candidate;
22575
22630
  low = mid + 1;
@@ -22745,7 +22800,7 @@ function expandPriority(part) {
22745
22800
  }
22746
22801
  function renderFormattedParts(parts, timestamps, options) {
22747
22802
  const text = buildRenderedText(parts, timestamps);
22748
- let totalTokens = (0, tokenx.estimateTokenCount)(text);
22803
+ let totalTokens = estimateTokenCount(text);
22749
22804
  if (totalTokens > options.maxTokens) return {
22750
22805
  text: truncateStringByTokens(text, options.maxTokens),
22751
22806
  truncated: true,
@@ -22764,8 +22819,8 @@ function renderFormattedParts(parts, timestamps, options) {
22764
22819
  for (const { part, index } of truncatedIndices) {
22765
22820
  if (remaining <= 0) break;
22766
22821
  const maxTokens = expandLimit(part);
22767
- const fullTokens = (0, tokenx.estimateTokenCount)(part.fullText);
22768
- const currentTokens = (0, tokenx.estimateTokenCount)(part.text);
22822
+ const fullTokens = estimateTokenCount(part.fullText);
22823
+ const currentTokens = estimateTokenCount(part.text);
22769
22824
  const targetTokens = Math.min(fullTokens, maxTokens);
22770
22825
  const delta = targetTokens - currentTokens;
22771
22826
  if (delta <= 0) continue;
@@ -22779,7 +22834,7 @@ function renderFormattedParts(parts, timestamps, options) {
22779
22834
  const expandedLimit = Math.min(currentTokens + remaining, maxTokens);
22780
22835
  const hint = `recall cursor="${part.messageId}" partIndex=${part.partIndex} detail="high"`;
22781
22836
  const { text: expanded } = truncateByTokens(part.fullText, expandedLimit, hint);
22782
- const expandedDelta = (0, tokenx.estimateTokenCount)(expanded) - currentTokens;
22837
+ const expandedDelta = estimateTokenCount(expanded) - currentTokens;
22783
22838
  parts[index] = {
22784
22839
  ...part,
22785
22840
  text: expanded
@@ -22788,7 +22843,7 @@ function renderFormattedParts(parts, timestamps, options) {
22788
22843
  }
22789
22844
  }
22790
22845
  const expanded = buildRenderedText(parts, timestamps);
22791
- const expandedTokens = (0, tokenx.estimateTokenCount)(expanded);
22846
+ const expandedTokens = estimateTokenCount(expanded);
22792
22847
  if (expandedTokens <= options.maxTokens) return {
22793
22848
  text: expanded,
22794
22849
  truncated: false,
@@ -25182,6 +25237,10 @@ var SyncObservationStrategy = class extends ObservationStrategy {
25182
25237
  async persist(processed) {
25183
25238
  const { record, threadId, resourceId, messages } = this.opts;
25184
25239
  const thread = await this.storage.getThreadById({ threadId });
25240
+ if (!await this.storage.getObservationalMemory(record.threadId, record.resourceId)) {
25241
+ omDebug(`[OM:sync-obs] skipping persist for thread ${threadId}: observational memory record is gone`);
25242
+ return;
25243
+ }
25185
25244
  let threadUpdateMarker;
25186
25245
  if (thread) {
25187
25246
  const oldTitle = thread.title?.trim();
@@ -25363,6 +25422,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
25363
25422
  async persist(processed) {
25364
25423
  if (!processed.observations) return;
25365
25424
  const { record, threadId, resourceId, messages } = this.opts;
25425
+ if (!await this.storage.getObservationalMemory(record.threadId, record.resourceId)) {
25426
+ omDebug(`[OM:asyncBuffer] skipping persist for thread ${threadId}: observational memory record is gone`);
25427
+ return;
25428
+ }
25366
25429
  const messageTokens = await this.tokenCounter.countMessagesAsync(messages);
25367
25430
  await withRetry(() => this.storage.updateBufferedObservations({
25368
25431
  id: record.id,
@@ -26356,6 +26419,10 @@ var ObservationTurn = class {
26356
26419
  if (!this._context) throw new Error("Turn not started — call start() first");
26357
26420
  return this._context;
26358
26421
  }
26422
+ /** Whether the turn has been ended and can no longer accept steps. */
26423
+ get ended() {
26424
+ return this._ended;
26425
+ }
26359
26426
  /** The current step, if one exists. */
26360
26427
  get currentStep() {
26361
26428
  return this._currentStep;
@@ -30092,9 +30159,11 @@ ${formattedMessages}
30092
30159
  let lifecycleError;
30093
30160
  let observationStarted = false;
30094
30161
  let generationBefore = -1;
30162
+ let recordInsideLock;
30095
30163
  try {
30096
30164
  await this.withLock(lockKey, async () => {
30097
30165
  const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
30166
+ recordInsideLock = freshRecord;
30098
30167
  generationBefore = freshRecord.generationCount;
30099
30168
  const unobservedMessages = messages ? this.getUnobservedMessages(messages, freshRecord) : await this.loadMessagesFromStorage(threadId, resourceId, freshRecord.lastObservedAt ? new Date(freshRecord.lastObservedAt) : void 0);
30100
30169
  if (!this.meetsObservationThreshold({
@@ -30137,7 +30206,7 @@ ${formattedMessages}
30137
30206
  omDebug(`[OM:hooks] onObservationEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
30138
30207
  }
30139
30208
  if (lifecycleError !== void 0) throw lifecycleError;
30140
- const record = await this.getOrCreateRecord(threadId, resourceId);
30209
+ const record = await this.getRecord(threadId, resourceId) ?? recordInsideLock;
30141
30210
  const reflected = record.generationCount > generationBefore && generationBefore >= 0;
30142
30211
  return {
30143
30212
  observed,
@@ -30618,6 +30687,10 @@ var ObservationalMemoryProcessor = class {
30618
30687
  if (this.turn === activeTurn) this.turn = void 0;
30619
30688
  state.__omTurn = void 0;
30620
30689
  }
30690
+ if (activeTurn?.ended || this.turn?.ended) {
30691
+ if (this.turn?.ended) this.turn = void 0;
30692
+ state.__omTurn = void 0;
30693
+ }
30621
30694
  if (!this.turn || !state.__omTurn) {
30622
30695
  if (this.turn && !state.__omTurn) await this.turn.end().catch(() => {});
30623
30696
  this.turn = this.engine.beginTurn({
@@ -30718,11 +30791,11 @@ var ObservationalMemoryProcessor = class {
30718
30791
  return this.engine.getTokenCounter().runWithModelContext(state.__omActorModelContext, async () => {
30719
30792
  if ((0, _mastra_core_memory.parseMemoryRequestContext)(requestContext)?.memoryConfig?.readOnly) return messageList;
30720
30793
  const turn = asLiveTurn(state.__omTurn) ?? this.turn;
30721
- if (turn) {
30722
- await turn.end();
30723
- this.turn = void 0;
30724
- state.__omTurn = void 0;
30725
- } else {
30794
+ const liveTurn = turn && !turn.ended ? turn : void 0;
30795
+ if (liveTurn) await liveTurn.end();
30796
+ this.turn = void 0;
30797
+ state.__omTurn = void 0;
30798
+ if (!liveTurn) {
30726
30799
  const newOutput = messageList.get.response.db();
30727
30800
  const messagesToSave = [...messageList.get.input.db(), ...newOutput];
30728
30801
  if (messagesToSave.length > 0 && context.threadId) await this.engine.persistMessages(messagesToSave, context.threadId, context.resourceId);
@@ -30932,6 +31005,7 @@ const DEFAULT_MESSAGE_RANGE = {
30932
31005
  };
30933
31006
  const DEFAULT_TOP_K = 4;
30934
31007
  const VECTOR_DELETE_BATCH_SIZE = 100;
31008
+ const OM_DELETE_DRAIN_TIMEOUT_MS = 1e4;
30935
31009
  const DEFAULT_EMBEDDING_CACHE_MAX_SIZE = 1e3;
30936
31010
  /**
30937
31011
  * Gives Mastra agents conversation history, with optional working memory,
@@ -31383,6 +31457,8 @@ var Memory = class Memory extends _mastra_core_memory.MastraMemory {
31383
31457
  await this.deleteStoredThread(memoryStore, threadId, thread?.resourceId);
31384
31458
  }
31385
31459
  async deleteStoredThread(memoryStore, threadId, resourceId) {
31460
+ const engine = this._omEngine ? await this._omEngine : this._omEngineInstance;
31461
+ if (engine && resourceId) await engine.waitForBuffering(threadId, resourceId, OM_DELETE_DRAIN_TIMEOUT_MS);
31386
31462
  await memoryStore.deleteThread({ threadId });
31387
31463
  if (resourceId && memoryStore.supportsObservationalMemory) await memoryStore.clearObservationalMemory(threadId, resourceId);
31388
31464
  if (this.vector) this.trackVectorCleanup(this.deleteThreadVectors(threadId));
@@ -33397,4 +33473,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
33397
33473
  }
33398
33474
  });
33399
33475
 
33400
- //# sourceMappingURL=src-C4niho1P.cjs.map
33476
+ //# sourceMappingURL=src-QqTmOY5X.cjs.map