@mastra/server 1.61.1-alpha.1 → 1.62.0-alpha.11

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 (60) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/_types/@internal_core/dist/storage/index.d.ts +8 -0
  3. package/dist/{api-schema-manifest-BxnLWV5l.js → api-schema-manifest-BVgosvca.js} +2 -2
  4. package/dist/{api-schema-manifest-BxnLWV5l.js.map → api-schema-manifest-BVgosvca.js.map} +1 -1
  5. package/dist/{api-schema-manifest-Cbonk0PF.cjs → api-schema-manifest-DrTLp1Fv.cjs} +2 -2
  6. package/dist/{api-schema-manifest-Cbonk0PF.cjs.map → api-schema-manifest-DrTLp1Fv.cjs.map} +1 -1
  7. package/dist/{datasets-DmN9V4rU.js → datasets-CInAtVdf.js} +3 -1
  8. package/dist/datasets-CInAtVdf.js.map +1 -0
  9. package/dist/{datasets-CNtKg_3d.cjs → datasets-DYhcB-od.cjs} +3 -1
  10. package/dist/datasets-DYhcB-od.cjs.map +1 -0
  11. package/dist/{dist-Cw-T1gX6.js → dist-L0EogwX_.js} +476 -311
  12. package/dist/dist-L0EogwX_.js.map +1 -0
  13. package/dist/{dist-DERatX43.cjs → dist-Xj9TvH1A.cjs} +476 -311
  14. package/dist/dist-Xj9TvH1A.cjs.map +1 -0
  15. package/dist/docs/SKILL.md +1 -1
  16. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  17. package/dist/docs/references/docs-server-middleware.md +30 -0
  18. package/dist/{observability-new-endpoints-Drx0C8uc.js → observability-new-endpoints-0RLfcyw6.js} +2 -1
  19. package/dist/observability-new-endpoints-0RLfcyw6.js.map +1 -0
  20. package/dist/{observability-new-endpoints-gRCFhnRr.cjs → observability-new-endpoints-4GcBP0uJ.cjs} +2 -1
  21. package/dist/observability-new-endpoints-4GcBP0uJ.cjs.map +1 -0
  22. package/dist/{routes-DGwUTyHr.cjs → routes-CDsvr7BB.cjs} +2 -2
  23. package/dist/{routes-DGwUTyHr.cjs.map → routes-CDsvr7BB.cjs.map} +1 -1
  24. package/dist/{routes-B6F4QBgN.js → routes-DCEudTA7.js} +2 -2
  25. package/dist/{routes-B6F4QBgN.js.map → routes-DCEudTA7.js.map} +1 -1
  26. package/dist/server/handlers/agent-builder.cjs +1 -1
  27. package/dist/server/handlers/agent-builder.js +1 -1
  28. package/dist/server/handlers/datasets.cjs +1 -1
  29. package/dist/server/handlers/datasets.d.ts +10 -0
  30. package/dist/server/handlers/datasets.d.ts.map +1 -1
  31. package/dist/server/handlers/datasets.js +1 -1
  32. package/dist/server/handlers/observability-new-endpoints.cjs +1 -1
  33. package/dist/server/handlers/observability-new-endpoints.d.ts +16 -0
  34. package/dist/server/handlers/observability-new-endpoints.d.ts.map +1 -1
  35. package/dist/server/handlers/observability-new-endpoints.js +1 -1
  36. package/dist/server/handlers/observability.cjs +1 -1
  37. package/dist/server/handlers/observability.js +1 -1
  38. package/dist/server/handlers/system.cjs +1 -1
  39. package/dist/server/handlers/system.js +1 -1
  40. package/dist/server/schemas/datasets.d.ts +4 -0
  41. package/dist/server/schemas/datasets.d.ts.map +1 -1
  42. package/dist/server/schemas/index.cjs +1 -1
  43. package/dist/server/schemas/index.js +1 -1
  44. package/dist/server/server-adapter/index.cjs +23 -3
  45. package/dist/server/server-adapter/index.cjs.map +1 -1
  46. package/dist/server/server-adapter/index.d.ts +14 -0
  47. package/dist/server/server-adapter/index.d.ts.map +1 -1
  48. package/dist/server/server-adapter/index.js +23 -4
  49. package/dist/server/server-adapter/index.js.map +1 -1
  50. package/dist/server/server-adapter/routes/datasets.d.ts +10 -0
  51. package/dist/server/server-adapter/routes/datasets.d.ts.map +1 -1
  52. package/dist/server/server-adapter/routes/observability.d.ts +8 -0
  53. package/dist/server/server-adapter/routes/observability.d.ts.map +1 -1
  54. package/package.json +6 -6
  55. package/dist/datasets-CNtKg_3d.cjs.map +0 -1
  56. package/dist/datasets-DmN9V4rU.js.map +0 -1
  57. package/dist/dist-Cw-T1gX6.js.map +0 -1
  58. package/dist/dist-DERatX43.cjs.map +0 -1
  59. package/dist/observability-new-endpoints-Drx0C8uc.js.map +0 -1
  60. package/dist/observability-new-endpoints-gRCFhnRr.cjs.map +0 -1
@@ -4693,7 +4693,7 @@ function splitLines(text) {
4693
4693
  return result;
4694
4694
  }
4695
4695
  //#endregion
4696
- //#region ../memory/dist/src-aTfbkxBl.js
4696
+ //#region ../memory/dist/src-BATJenuZ.js
4697
4697
  var __defProp$3 = Object.defineProperty;
4698
4698
  var __exportAll = (all, no_symbols) => {
4699
4699
  let target = {};
@@ -8927,6 +8927,7 @@ async function cancelResponseBody$1(response) {
8927
8927
  var initialGlobalFetch$1 = globalThis.fetch;
8928
8928
  isNodeDefaultFetch$1(initialGlobalFetch$1);
8929
8929
  function isNodeDefaultFetch$1(fetch) {
8930
+ if (typeof fetch !== "function") return false;
8930
8931
  const source = Function.prototype.toString.call(fetch);
8931
8932
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
8932
8933
  }
@@ -13818,6 +13819,7 @@ var DownloadError$2 = class extends (_b$2$1 = AISDKError$3, _a$2$1 = symbol$2$1,
13818
13819
  var initialGlobalFetch$2 = globalThis.fetch;
13819
13820
  isNodeDefaultFetch$2(initialGlobalFetch$2);
13820
13821
  function isNodeDefaultFetch$2(fetch) {
13822
+ if (typeof fetch !== "function") return false;
13821
13823
  const source = Function.prototype.toString.call(fetch);
13822
13824
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
13823
13825
  }
@@ -23480,6 +23482,12 @@ function extractListItemsOnly(content) {
23480
23482
  */
23481
23483
  const MAX_OBSERVATION_LINE_CHARS = 1e4;
23482
23484
  /**
23485
+ * Minimum trimmed line length considered by the duplicate-line degenerate
23486
+ * check. Short lines (blank lines, separators, terse bullets) legitimately
23487
+ * repeat; long identical lines almost never do.
23488
+ */
23489
+ const MIN_DUPLICATE_LINE_CHARS = 24;
23490
+ /**
23483
23491
  * Truncate individual observation lines that exceed the maximum length.
23484
23492
  */
23485
23493
  function sanitizeObservationLines(observations) {
@@ -23517,6 +23525,18 @@ function detectDegenerateRepetition(text) {
23517
23525
  if (totalWindows > 5 && duplicateWindows / totalWindows > .4) return true;
23518
23526
  const lines = text.split("\n");
23519
23527
  for (const line of lines) if (line.length > 5e4) return true;
23528
+ const seenLines = /* @__PURE__ */ new Map();
23529
+ let duplicateLines = 0;
23530
+ let totalCountedLines = 0;
23531
+ for (const line of lines) {
23532
+ const trimmed = line.trim();
23533
+ if (trimmed.length < MIN_DUPLICATE_LINE_CHARS) continue;
23534
+ totalCountedLines++;
23535
+ const count = (seenLines.get(trimmed) ?? 0) + 1;
23536
+ seenLines.set(trimmed, count);
23537
+ if (count > 1) duplicateLines++;
23538
+ }
23539
+ if (totalCountedLines >= 20 && duplicateLines / totalCountedLines > .5) return true;
23520
23540
  return false;
23521
23541
  }
23522
23542
  /**
@@ -23551,12 +23571,26 @@ function describeDegenerateOutput(text, snippetChars = 400) {
23551
23571
  topWindow = window;
23552
23572
  }
23553
23573
  let longestLine = 0;
23554
- for (const line of text.split("\n")) if (line.length > longestLine) longestLine = line.length;
23574
+ const seenLines = /* @__PURE__ */ new Map();
23575
+ let duplicateLines = 0;
23576
+ let totalCountedLines = 0;
23577
+ for (const line of text.split("\n")) {
23578
+ if (line.length > longestLine) longestLine = line.length;
23579
+ const trimmed = line.trim();
23580
+ if (trimmed.length < MIN_DUPLICATE_LINE_CHARS) continue;
23581
+ totalCountedLines++;
23582
+ const count = (seenLines.get(trimmed) ?? 0) + 1;
23583
+ seenLines.set(trimmed, count);
23584
+ if (count > 1) duplicateLines++;
23585
+ }
23555
23586
  const duplicateRatio = totalWindows > 0 ? (duplicateWindows / totalWindows).toFixed(2) : "n/a";
23587
+ const duplicateLineRatio = totalCountedLines > 0 ? (duplicateLines / totalCountedLines).toFixed(2) : "n/a";
23556
23588
  const parts = [
23557
23589
  `length=${text.length}`,
23558
23590
  `sampledWindows=${totalWindows}`,
23559
23591
  `duplicateRatio=${duplicateRatio}`,
23592
+ `duplicateLineRatio=${duplicateLineRatio}`,
23593
+ `countedLines=${totalCountedLines}`,
23560
23594
  `longestLine=${longestLine}`,
23561
23595
  `topWindowCount=${topCount}`
23562
23596
  ];
@@ -23824,7 +23858,19 @@ async function withOmTracingSpan({ phase, model, inputTokens, requestContext, ob
23824
23858
  });
23825
23859
  const childObservabilityContext = createObservabilityContext({ currentSpan: span });
23826
23860
  if (!span) return callback(childObservabilityContext);
23827
- return span.executeInContext(() => callback(childObservabilityContext));
23861
+ return span.executeInContext(async () => {
23862
+ try {
23863
+ const result = await callback(childObservabilityContext);
23864
+ span.end();
23865
+ return result;
23866
+ } catch (error) {
23867
+ span.error({
23868
+ error,
23869
+ endSpan: true
23870
+ });
23871
+ throw error;
23872
+ }
23873
+ });
23828
23874
  }
23829
23875
  function filterObserverExtractors(extractors, skipContinuationHints) {
23830
23876
  const configuredExtractors = extractors ?? [];
@@ -26923,6 +26969,149 @@ function createWorkingMemoryTool(config, options = {}) {
26923
26969
  tool
26924
26970
  };
26925
26971
  }
26972
+ /**
26973
+ * WorkingMemoryStateProcessor
26974
+ *
26975
+ * Experimental: delivers working memory to the model as a state signal instead
26976
+ * of folding it into the system message. Storage and the `setWorkingMemory`
26977
+ * tool are unchanged — this processor only changes the delivery path.
26978
+ *
26979
+ * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:
26980
+ * - `stateId` namespaces the state lane on the thread.
26981
+ * - `cacheKey` is derived from the rendered payload so dedup is automatic.
26982
+ * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the
26983
+ * current snapshot after older messages drop out of the window.
26984
+ *
26985
+ * Delta emission (markdown mode only): when a prior snapshot exists in the
26986
+ * context window, the processor emits a unified-diff delta against that
26987
+ * snapshot's contents. Schema mode and the snapshot fallback always emit a
26988
+ * full snapshot.
26989
+ *
26990
+ * @example
26991
+ * ```ts
26992
+ * new Memory({
26993
+ * options: {
26994
+ * workingMemory: {
26995
+ * enabled: true,
26996
+ * template: '...',
26997
+ * useStateSignals: true, // auto-attaches this processor
26998
+ * },
26999
+ * },
27000
+ * });
27001
+ * ```
27002
+ */
27003
+ const WORKING_MEMORY_STATE_ID = "working-memory";
27004
+ const WORKING_MEMORY_STATE_PROCESSOR_ID = "working-memory-state";
27005
+ var WorkingMemoryStateProcessor = class {
27006
+ memory;
27007
+ memoryConfig;
27008
+ id = WORKING_MEMORY_STATE_PROCESSOR_ID;
27009
+ stateId = WORKING_MEMORY_STATE_ID;
27010
+ constructor(memory, memoryConfig) {
27011
+ this.memory = memory;
27012
+ this.memoryConfig = memoryConfig;
27013
+ }
27014
+ async computeStateSignal(args) {
27015
+ const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });
27016
+ if (!template) return;
27017
+ const contents = (await this.memory.getWorkingMemory({
27018
+ threadId: args.threadId,
27019
+ resourceId: args.resourceId,
27020
+ memoryConfig: this.memoryConfig
27021
+ }))?.trim();
27022
+ if (!contents) return;
27023
+ const cacheKey = stableWorkingMemoryCacheKey({
27024
+ format: template.format,
27025
+ data: contents
27026
+ });
27027
+ const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;
27028
+ if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;
27029
+ const scope = this.memory.getMergedThreadConfig(this.memoryConfig).workingMemory?.scope ?? "resource";
27030
+ const deltaCandidate = template.format === "markdown" && !shouldMakeSnapshot ? buildMarkdownDelta({
27031
+ lastSnapshot: args.lastSnapshot,
27032
+ deltasSinceSnapshot: args.deltasSinceSnapshot,
27033
+ nextContents: contents
27034
+ }) : void 0;
27035
+ if (deltaCandidate) return {
27036
+ id: WORKING_MEMORY_STATE_ID,
27037
+ mode: "delta",
27038
+ cacheKey,
27039
+ tagName: "working-memory",
27040
+ contents: deltaCandidate.contents,
27041
+ delta: deltaCandidate.contents,
27042
+ value: contents,
27043
+ attributes: {
27044
+ format: template.format,
27045
+ scope,
27046
+ patch: "unified-diff"
27047
+ }
27048
+ };
27049
+ return {
27050
+ id: WORKING_MEMORY_STATE_ID,
27051
+ mode: "snapshot",
27052
+ cacheKey,
27053
+ tagName: "working-memory",
27054
+ contents,
27055
+ value: contents,
27056
+ attributes: {
27057
+ format: template.format,
27058
+ scope
27059
+ }
27060
+ };
27061
+ }
27062
+ };
27063
+ /**
27064
+ * Stable cache key for the rendered working memory payload. Returns a SHA-256
27065
+ * digest so dedup metadata stays compact regardless of payload size (working
27066
+ * memory blobs can grow arbitrarily long).
27067
+ */
27068
+ function stableWorkingMemoryCacheKey(input) {
27069
+ const hash = createHash("sha256");
27070
+ hash.update(input.format);
27071
+ hash.update("\0");
27072
+ hash.update(input.data ?? "");
27073
+ return `sha256:${hash.digest("hex")}`;
27074
+ }
27075
+ /**
27076
+ * Build a unified-diff delta against the most recently emitted state. Prefers
27077
+ * the latest delta's `value` (the post-edit full text) when available, falling
27078
+ * back to the snapshot's `value` and finally the snapshot's `contents`. This
27079
+ * keeps deltas incremental (B→C) instead of cumulative against a stale
27080
+ * snapshot (A→C), which matters when many small edits land between snapshots.
27081
+ *
27082
+ * Returns undefined when:
27083
+ * - there's no prior state to diff against
27084
+ * - the prior state isn't a plain string (multimodal signal)
27085
+ *
27086
+ * In either case the caller falls back to emitting a full snapshot.
27087
+ */
27088
+ function buildMarkdownDelta(args) {
27089
+ const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;
27090
+ const prior = pickStringValue(readSignalValue(deltasSinceSnapshot.at(-1))) ?? pickStringValue(readSignalValue(lastSnapshot)) ?? (typeof lastSnapshot?.contents === "string" ? lastSnapshot.contents : void 0);
27091
+ if (!prior) return;
27092
+ return { contents: renderHunksOnly(prior, nextContents) };
27093
+ }
27094
+ function pickStringValue(value) {
27095
+ return typeof value === "string" ? value : void 0;
27096
+ }
27097
+ function readSignalValue(signal) {
27098
+ return (signal?.metadata)?.value;
27099
+ }
27100
+ /**
27101
+ * Render a unified-diff-style patch body containing only `@@` hunks and their
27102
+ * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)
27103
+ * that `createPatch` emits and the `` trailer.
27104
+ * The preamble exists for tooling like `patch -p1` to know which file to
27105
+ * apply to; we only ever diff a single working-memory blob. The newline
27106
+ * trailer is semantically meaningless to the model and adds noise to the
27107
+ * state signal.
27108
+ */
27109
+ function renderHunksOnly(prior, next) {
27110
+ const { hunks } = structuredPatch("", "", prior, next, "", "", { context: 0 });
27111
+ return hunks.map((hunk) => {
27112
+ return [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`, ...hunk.lines.filter((line) => !line.startsWith("\"))].join("\n");
27113
+ }).join("\n");
27114
+ }
26926
27115
  const MINUTE = 6e4;
26927
27116
  const HOUR = 60 * MINUTE;
26928
27117
  const SHORT_TTL = 5 * MINUTE;
@@ -28917,10 +29106,11 @@ ObservationStrategy.create = ((om, opts) => {
28917
29106
  if (deps.scope === "resource" && opts.resourceId) return new ResourceScopedObservationStrategy(deps, opts);
28918
29107
  return new SyncObservationStrategy(deps, opts);
28919
29108
  });
28920
- async function loadMemoryContextMessages({ memory, messageList, threadId, resourceId }) {
29109
+ async function loadMemoryContextMessages({ memory, messageList, threadId, resourceId, runState }) {
28921
29110
  const ctx = await memory.getContext({
28922
29111
  threadId,
28923
- resourceId
29112
+ resourceId,
29113
+ runState
28924
29114
  });
28925
29115
  for (const msg of ctx.messages) if (msg.role !== "system") messageList.add(msg, "memory");
28926
29116
  return ctx;
@@ -29374,10 +29564,11 @@ var ObservationTurn = class {
29374
29564
  * If a MemoryContextProvider is passed, loads historical messages and adds
29375
29565
  * them to the MessageList. Without a provider, only fetches/caches the record.
29376
29566
  */
29377
- async start(memory) {
29567
+ async start(memory, runState) {
29378
29568
  if (this._started) throw new Error("Turn already started");
29379
29569
  this._started = true;
29380
29570
  this._record = await this.om.getOrCreateRecord(this.threadId, this.resourceId);
29571
+ runState?.set(`observational-memory:record:${this.threadId}:${this.resourceId ?? ""}`, this._record);
29381
29572
  this._generationCountAtStart = this._record.generationCount;
29382
29573
  this.memory = memory;
29383
29574
  if (memory) {
@@ -29385,7 +29576,8 @@ var ObservationTurn = class {
29385
29576
  memory,
29386
29577
  messageList: this.messageList,
29387
29578
  threadId: this.threadId,
29388
- resourceId: this.resourceId
29579
+ resourceId: this.resourceId,
29580
+ runState
29389
29581
  });
29390
29582
  this._context = {
29391
29583
  messages: ctx.messages,
@@ -29861,6 +30053,21 @@ var ReflectorRunner = class {
29861
30053
  memory;
29862
30054
  onReflectionCommitted;
29863
30055
  mastra;
30056
+ /**
30057
+ * Per-lock-key observation-token count of the last unproductive synchronous
30058
+ * reflection (failed, or committed while still over the reflection
30059
+ * threshold). `call()` already runs the full bounded compression ladder for
30060
+ * a given input, so re-running it against unchanged observations cannot
30061
+ * succeed — threshold-triggered sync reflection is skipped while the count
30062
+ * is unchanged and retried as soon as the input differs.
30063
+ * Keyed by lock key (thread/resource), which is stable across generations —
30064
+ * record ids change every time a generation is created.
30065
+ *
30066
+ * Lifecycle: an entry is deleted when a sync reflection finishes under
30067
+ * threshold and replaced on each unproductive attempt. Entries are one
30068
+ * number per active thread/resource key and live for the runner's lifetime.
30069
+ */
30070
+ syncReflectionSuppression = /* @__PURE__ */ new Map();
29864
30071
  constructor(opts) {
29865
30072
  this.reflectionConfig = opts.reflectionConfig;
29866
30073
  this.observationConfig = opts.observationConfig;
@@ -29943,8 +30150,8 @@ var ReflectorRunner = class {
29943
30150
  };
29944
30151
  let reflectedTokens = 0;
29945
30152
  let attemptNumber = 0;
29946
- /** Observations from the previous attempt, used to detect a no-progress ladder. */
29947
- let previousObservations;
30153
+ /** True when the latest attempt returned an empty block for non-empty input. */
30154
+ let emptyOutput = false;
29948
30155
  while (currentLevel <= maxLevel) {
29949
30156
  attemptNumber++;
29950
30157
  const isRetry = attemptNumber > 1;
@@ -30008,21 +30215,20 @@ var ReflectorRunner = class {
30008
30215
  }
30009
30216
  finalProviderMetadata = result.providerMetadata ?? finalProviderMetadata;
30010
30217
  parsed = parseReflectorOutput(result.text, observations, activeExtractors);
30218
+ emptyOutput = !parsed.degenerate && observations.trim().length > 0 && parsed.observations.trim().length === 0;
30011
30219
  if (parsed.degenerate) {
30012
30220
  omDebug(`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure. ${describeDegenerateOutput(result.text, 2e3)}`);
30013
30221
  reflectedTokens = originalTokens;
30222
+ } else if (emptyOutput) {
30223
+ omDebug(`[OM:callReflector] attempt #${attemptNumber}: empty observations block for non-empty input, treating as compression failure`);
30224
+ reflectedTokens = originalTokens;
30014
30225
  } else reflectedTokens = this.tokenCounter.countObservations(parsed.observations);
30015
30226
  omDebug(`[OM:callReflector] attempt #${attemptNumber} parsed: reflectedTokens=${reflectedTokens}, targetThreshold=${targetThreshold}, compressionValid=${validateCompression(reflectedTokens, targetThreshold)}, parsedObsLen=${parsed.observations?.length}, degenerate=${parsed.degenerate ?? false}`);
30016
- if (!parsed.degenerate && (validateCompression(reflectedTokens, targetThreshold) || currentLevel >= maxLevel)) break;
30017
- if (parsed.degenerate && currentLevel >= maxLevel) {
30018
- omDebug(`[OM:callReflector] degenerate output persists at maxLevel=${maxLevel}, breaking`);
30019
- break;
30020
- }
30021
- if (!parsed.degenerate && previousObservations !== void 0 && parsed.observations === previousObservations) {
30022
- omDebug(`[OM:callReflector] attempt #${attemptNumber} returned output identical to the previous attempt; escalating cannot help, stopping the ladder`);
30227
+ if (!parsed.degenerate && !emptyOutput && (validateCompression(reflectedTokens, targetThreshold) || currentLevel >= maxLevel)) break;
30228
+ if ((parsed.degenerate || emptyOutput) && currentLevel >= maxLevel) {
30229
+ omDebug(`[OM:callReflector] ${parsed.degenerate ? "degenerate" : "empty"} output persists at maxLevel=${maxLevel}, breaking`);
30023
30230
  break;
30024
30231
  }
30025
- previousObservations = parsed.observations;
30026
30232
  if (streamContext?.writer) {
30027
30233
  const failedMarker = createObservationFailedMarker({
30028
30234
  cycleId: streamContext.cycleId,
@@ -30058,6 +30264,7 @@ var ReflectorRunner = class {
30058
30264
  }
30059
30265
  currentLevel = Math.min(currentLevel + 1, maxLevel);
30060
30266
  }
30267
+ if (observations.trim().length > 0 && parsed.observations.trim().length === 0) throw new Error(`Reflector produced empty output after ${attemptNumber} attempt(s)${parsed.degenerate ? " (degenerate repetition)" : ""} — refusing to commit an empty reflection over ${originalTokens} observation tokens`);
30061
30268
  const structuredExtraction = await extractStructuredValues({
30062
30269
  agent,
30063
30270
  source: "reflector",
@@ -30106,52 +30313,51 @@ var ReflectorRunner = class {
30106
30313
  this.storage.setBufferingReflectionFlag(record.id, true).catch((err) => {
30107
30314
  omError("[OM] Failed to set buffering reflection flag", err);
30108
30315
  });
30109
- reflectionHooks?.onReflectionStart?.();
30110
- const asyncOp = this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext, priorExtractedValues, mainAgent, sendSignal).then((outcome) => {
30111
- try {
30112
- reflectionHooks?.onReflectionEnd?.({
30113
- usage: outcome?.usage,
30114
- ...outcome?.providerMetadata ? { providerMetadata: outcome.providerMetadata } : {}
30115
- });
30116
- } catch (hookError) {
30117
- omError("[OM] onReflectionEnd hook failed after async buffered reflection", hookError);
30118
- }
30119
- }, async (error) => {
30120
- if (writer) try {
30121
- const failedMarker = createBufferingFailedMarker({
30122
- cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
30123
- operationType: "reflection",
30124
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
30125
- tokensAttempted: observationTokens,
30126
- error: error instanceof Error ? error.message : String(error),
30127
- recordId: record.id,
30128
- threadId: record.threadId ?? ""
30129
- });
30130
- writer.custom({
30131
- ...failedMarker,
30132
- transient: true
30133
- }).catch(() => {});
30134
- await this.persistMarkerToStorage(failedMarker, record.threadId ?? "", record.resourceId ?? void 0);
30135
- } catch (markerError) {
30136
- omError("[OM] Failed to persist buffering-failed marker after async buffered reflection failure", markerError);
30137
- }
30138
- omError("[OM] Async buffered reflection failed", error);
30316
+ const asyncOp = (async () => {
30317
+ let outcome;
30318
+ let reflectionError;
30139
30319
  try {
30140
- reflectionHooks?.onReflectionEnd?.({
30141
- usage: void 0,
30142
- error: error instanceof Error ? error : new Error(String(error))
30320
+ await reflectionHooks?.onReflectionStart?.();
30321
+ outcome = await this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext, priorExtractedValues, mainAgent, sendSignal);
30322
+ } catch (error) {
30323
+ reflectionError = error instanceof Error ? error : new Error(String(error));
30324
+ if (writer) try {
30325
+ const failedMarker = createBufferingFailedMarker({
30326
+ cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
30327
+ operationType: "reflection",
30328
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
30329
+ tokensAttempted: observationTokens,
30330
+ error: reflectionError.message,
30331
+ recordId: record.id,
30332
+ threadId: record.threadId ?? ""
30333
+ });
30334
+ writer.custom({
30335
+ ...failedMarker,
30336
+ transient: true
30337
+ }).catch(() => {});
30338
+ await this.persistMarkerToStorage(failedMarker, record.threadId ?? "", record.resourceId ?? void 0);
30339
+ } catch (markerError) {
30340
+ omError("[OM] Failed to persist buffering-failed marker after async buffered reflection failure", markerError);
30341
+ }
30342
+ omError("[OM] Async buffered reflection failed", error);
30343
+ BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
30344
+ } finally {
30345
+ try {
30346
+ await reflectionHooks?.onReflectionEnd?.({
30347
+ usage: outcome?.usage,
30348
+ error: reflectionError,
30349
+ ...outcome?.providerMetadata ? { providerMetadata: outcome.providerMetadata } : {}
30350
+ });
30351
+ } catch (hookError) {
30352
+ omError("[OM] onReflectionEnd hook failed after async buffered reflection", hookError);
30353
+ }
30354
+ BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
30355
+ unregisterOp(record.id, "bufferingReflection");
30356
+ this.storage.setBufferingReflectionFlag(record.id, false).catch((err) => {
30357
+ omError("[OM] Failed to clear buffering reflection flag", err);
30143
30358
  });
30144
- } catch (hookError) {
30145
- omError("[OM] onReflectionEnd hook failed after async buffered reflection failure", hookError);
30146
30359
  }
30147
- BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
30148
- }).finally(() => {
30149
- BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
30150
- unregisterOp(record.id, "bufferingReflection");
30151
- this.storage.setBufferingReflectionFlag(record.id, false).catch((err) => {
30152
- omError("[OM] Failed to clear buffering reflection flag", err);
30153
- });
30154
- });
30360
+ })();
30155
30361
  BufferingCoordinator.asyncBufferingOps.set(bufferKey, asyncOp);
30156
30362
  }
30157
30363
  /**
@@ -30412,6 +30618,13 @@ var ReflectorRunner = class {
30412
30618
  return;
30413
30619
  }
30414
30620
  }
30621
+ if (activationTriggeredBy === "threshold") {
30622
+ const suppressedAtTokens = this.syncReflectionSuppression.get(lockKey);
30623
+ if (suppressedAtTokens !== void 0 && observationTokens === suppressedAtTokens) {
30624
+ omDebug(`[OM:reflect] skipping sync reflection — observations unchanged at ${observationTokens} tokens since the last unproductive attempt; retrying once they change`);
30625
+ return;
30626
+ }
30627
+ }
30415
30628
  await this.storage.setReflectingFlag(record.id, true);
30416
30629
  registerOp(record.id, "reflecting");
30417
30630
  const cycleId = crypto.randomUUID();
@@ -30452,8 +30665,15 @@ var ReflectorRunner = class {
30452
30665
  let reflectionUsage;
30453
30666
  let reflectionProviderMetadata;
30454
30667
  let reflectionError;
30455
- reflectionHooks?.onReflectionStart?.();
30668
+ let lifecycleError;
30456
30669
  try {
30670
+ try {
30671
+ await reflectionHooks?.onReflectionStart?.();
30672
+ } catch (error) {
30673
+ lifecycleError = error;
30674
+ reflectionError = error instanceof Error ? error : new Error(String(error));
30675
+ throw error;
30676
+ }
30457
30677
  const compressionStartLevel = await this.getCompressionStartLevel(requestContext);
30458
30678
  const reflectResult = await this.call(record.activeObservations, void 0, streamContext, reflectThreshold, abortSignal, void 0, compressionStartLevel, requestContext, priorExtractedValues, observabilityContext, void 0, mainAgent, sendSignal);
30459
30679
  reflectionUsage = reflectResult.usage;
@@ -30465,6 +30685,8 @@ var ReflectorRunner = class {
30465
30685
  reflection: reflectResult.observations,
30466
30686
  tokenCount: reflectionTokenCount
30467
30687
  });
30688
+ if (reflectionTokenCount >= reflectThreshold) this.syncReflectionSuppression.set(lockKey, reflectionTokenCount);
30689
+ else this.syncReflectionSuppression.delete(lockKey);
30468
30690
  await this.notifyReflectionCommitted({
30469
30691
  parentThreadId: requestedThreadId ?? record.threadId ?? "",
30470
30692
  resourceId: record.resourceId ?? "",
@@ -30523,18 +30745,28 @@ var ReflectorRunner = class {
30523
30745
  await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
30524
30746
  }
30525
30747
  reflectionError = error instanceof Error ? error : new Error(String(error));
30526
- if (abortSignal?.aborted) throw error;
30748
+ if (lifecycleError !== void 0 || abortSignal?.aborted) throw error;
30749
+ this.syncReflectionSuppression.set(lockKey, observationTokens);
30527
30750
  omError("[OM] Reflection failed", error);
30528
30751
  } finally {
30529
- await this.storage.setReflectingFlag(record.id, false);
30530
30752
  try {
30531
- reflectionHooks?.onReflectionEnd?.({
30753
+ await this.storage.setReflectingFlag(record.id, false);
30754
+ } finally {
30755
+ unregisterOp(record.id, "reflecting");
30756
+ }
30757
+ let endHookError;
30758
+ try {
30759
+ await reflectionHooks?.onReflectionEnd?.({
30532
30760
  usage: reflectionUsage,
30533
30761
  error: reflectionError,
30534
30762
  ...reflectionProviderMetadata ? { providerMetadata: reflectionProviderMetadata } : {}
30535
30763
  });
30536
- } finally {
30537
- unregisterOp(record.id, "reflecting");
30764
+ } catch (error) {
30765
+ endHookError = error;
30766
+ }
30767
+ if (endHookError !== void 0) {
30768
+ if (lifecycleError === void 0 && !abortSignal?.aborted) throw endHookError;
30769
+ omDebug(`[OM:hooks] onReflectionEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
30538
30770
  }
30539
30771
  }
30540
30772
  }
@@ -30857,10 +31089,24 @@ function getLatestStepParts(parts) {
30857
31089
  return parts;
30858
31090
  }
30859
31091
  /**
30860
- * Returns true when a message contains at least one part with visible user/assistant
30861
- * content (text, tool-invocation, reasoning, image, file). Messages that only carry
30862
- * internal `data-*` parts (buffering markers, observation markers, etc.) return false.
31092
+ * Returns true for persisted Working Memory state signals (`role: 'signal'`,
31093
+ * `signal.type: 'state'`, `state.id: 'working-memory'`). OM must not observe these:
31094
+ * they mirror memory OM itself manages, and re-observing them creates a self-feedback
31095
+ * loop that can overwrite stored working memory (#21961). Only the working-memory lane
31096
+ * is filtered — other state lanes are outside OM's write path, so re-observing them
31097
+ * cannot corrupt OM-managed state; excluding them broadly is a separate decision.
30863
31098
  */
31099
+ function isWorkingMemoryStateSignal(message) {
31100
+ if (message.role !== "signal") return false;
31101
+ const signal = message.content.metadata?.signal;
31102
+ if (!signal || typeof signal !== "object" || Array.isArray(signal)) return false;
31103
+ const signalRecord = signal;
31104
+ if ((signalRecord.type ?? message.type) !== "state") return false;
31105
+ const metadata = signalRecord.metadata;
31106
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return false;
31107
+ const state = metadata.state;
31108
+ return !!state && typeof state === "object" && !Array.isArray(state) && state.id === "working-memory";
31109
+ }
30864
31110
  function messageHasVisibleContent(msg) {
30865
31111
  const content = msg.content;
30866
31112
  if (content?.parts && Array.isArray(content.parts)) return content.parts.some((p) => {
@@ -30991,6 +31237,8 @@ var ObservationalMemory = class ObservationalMemory {
30991
31237
  onIndexObservations;
30992
31238
  /** Config-level lifecycle hooks fired for every observation/reflection cycle. */
30993
31239
  hooks;
31240
+ /** Execution policy for config-level hooks on manual and turn-synchronous cycles. */
31241
+ hookExecution;
30994
31242
  /** Observer agent runner — handles LLM calls for extracting observations. */
30995
31243
  observer;
30996
31244
  /** Reflector agent runner — handles LLM calls for compressing observations. */
@@ -31097,6 +31345,7 @@ var ObservationalMemory = class ObservationalMemory {
31097
31345
  this.retrievalSearch = typeof config.retrieval === "object" && Boolean(config.retrieval.vector);
31098
31346
  this.onIndexObservations = config.onIndexObservations;
31099
31347
  this.hooks = config.hooks;
31348
+ this.hookExecution = config.hookExecution ?? "non-blocking";
31100
31349
  this.mastra = config.mastra;
31101
31350
  this.memory = config.memory;
31102
31351
  this.curationCadence = config.curationCadence;
@@ -31628,7 +31877,7 @@ var ObservationalMemory = class ObservationalMemory {
31628
31877
  }
31629
31878
  const result = [];
31630
31879
  for (const msg of allMessages) {
31631
- if (msg.role === "system") continue;
31880
+ if (msg.role === "system" || isWorkingMemoryStateSignal(msg)) continue;
31632
31881
  if (observedMessageIds?.has(msg.id)) continue;
31633
31882
  const endMarkerIndex = findLastCompletedObservationBoundary(msg);
31634
31883
  if (this.hasInProgressObservation(msg)) result.push(msg);
@@ -31850,7 +32099,7 @@ var ObservationalMemory = class ObservationalMemory {
31850
32099
  },
31851
32100
  filter: startDate ? { dateRange: { start: startDate } } : void 0
31852
32101
  });
31853
- return result.messages.filter((msg) => msg.role !== "system");
32102
+ return result.messages.filter((msg) => msg.role !== "system" && !isWorkingMemoryStateSignal(msg));
31854
32103
  }
31855
32104
  /**
31856
32105
  * Format unobserved messages from other threads as <unobserved-context> blocks.
@@ -32309,7 +32558,7 @@ ${formattedMessages}
32309
32558
  direction: "ASC"
32310
32559
  },
32311
32560
  filter: startDate ? { dateRange: { start: startDate } } : void 0
32312
- })).messages.filter((m) => !this.observedMessageIds.has(m.id));
32561
+ })).messages.filter((message) => !this.observedMessageIds.has(message.id) && !isWorkingMemoryStateSignal(message));
32313
32562
  if (filtered.length > 0) messagesByThread.set(thread.id, filtered);
32314
32563
  }
32315
32564
  if (messagesByThread.size === 0) return void 0;
@@ -32889,57 +33138,54 @@ ${formattedMessages}
32889
33138
  };
32890
33139
  }
32891
33140
  /**
32892
- * Compose the config-level hooks with optional per-call hooks into a single
32893
- * `ObserveHooks` object, binding call context onto the config-level
32894
- * callbacks. Config-level hooks are guarded so a throwing consumer hook can
32895
- * never fail an observation/reflection cycle; per-call hooks keep their
32896
- * existing payloads and propagation semantics. Returns undefined when
32897
- * neither is configured, so call sites stay zero-cost.
32898
- *
32899
- * Used internally by every pipeline path (manual observe/reflect,
32900
- * turn-engine sync observation, async buffering); public only so the
32901
- * observation turn engine can thread config-level reflection hooks into
32902
- * reflector calls.
33141
+ * Compose config-level hooks with optional per-call hooks and bind context to
33142
+ * config-level callbacks. The configured policy applies unless a background
33143
+ * path explicitly requests non-blocking invocation.
32903
33144
  *
32904
33145
  * @internal
32905
33146
  */
32906
- composeHooks(callHooks, context) {
33147
+ composeHooks(callHooks, context, execution = "configured") {
32907
33148
  const configHooks = this.hooks;
32908
33149
  if (!configHooks) return callHooks;
32909
33150
  if (!callHooks && !Object.keys(configHooks).length) return void 0;
32910
- const fireConfigHook = (name, arg) => {
33151
+ const shouldAwaitConfig = execution === "configured" && this.hookExecution === "await";
33152
+ const invokeConfigHook = async (name, arg) => {
32911
33153
  const hook = configHooks[name];
32912
33154
  if (!hook) return;
33155
+ if (shouldAwaitConfig) {
33156
+ await hook(arg);
33157
+ return;
33158
+ }
32913
33159
  const logHookFailure = (error) => omDebug(`[OM:hooks] config-level ${name} hook failed: ${error instanceof Error ? error.message : String(error)}`);
32914
33160
  try {
32915
- const out = hook(arg);
32916
- if (out && typeof out.then === "function") out.then(void 0, logHookFailure);
33161
+ const result = hook(arg);
33162
+ if (result && typeof result.then === "function") result.then(void 0, logHookFailure);
32917
33163
  } catch (error) {
32918
33164
  logHookFailure(error);
32919
33165
  }
32920
33166
  };
32921
33167
  return {
32922
- onObservationStart: () => {
32923
- fireConfigHook("onObservationStart", context);
32924
- callHooks?.onObservationStart?.();
33168
+ onObservationStart: async () => {
33169
+ await invokeConfigHook("onObservationStart", context);
33170
+ await callHooks?.onObservationStart?.();
32925
33171
  },
32926
- onObservationEnd: (result) => {
32927
- fireConfigHook("onObservationEnd", {
33172
+ onObservationEnd: async (result) => {
33173
+ await invokeConfigHook("onObservationEnd", {
32928
33174
  ...context,
32929
33175
  ...result
32930
33176
  });
32931
- callHooks?.onObservationEnd?.(result);
33177
+ await callHooks?.onObservationEnd?.(result);
32932
33178
  },
32933
- onReflectionStart: () => {
32934
- fireConfigHook("onReflectionStart", context);
32935
- callHooks?.onReflectionStart?.();
33179
+ onReflectionStart: async () => {
33180
+ await invokeConfigHook("onReflectionStart", context);
33181
+ await callHooks?.onReflectionStart?.();
32936
33182
  },
32937
- onReflectionEnd: (result) => {
32938
- fireConfigHook("onReflectionEnd", {
33183
+ onReflectionEnd: async (result) => {
33184
+ await invokeConfigHook("onReflectionEnd", {
32939
33185
  ...context,
32940
33186
  ...result
32941
33187
  });
32942
- callHooks?.onReflectionEnd?.(result);
33188
+ await callHooks?.onReflectionEnd?.(result);
32943
33189
  }
32944
33190
  };
32945
33191
  }
@@ -32952,22 +33198,29 @@ ${formattedMessages}
32952
33198
  * end hook reports `error` for failed cycles even though nothing throws.
32953
33199
  */
32954
33200
  async runBufferedObservationCycle(context, run) {
32955
- const hooks = this.composeHooks(void 0, context);
32956
- hooks?.onObservationStart?.();
33201
+ const hooks = this.composeHooks(void 0, context, "non-blocking");
32957
33202
  let runResult;
32958
33203
  let runError;
33204
+ let lifecycleError;
32959
33205
  try {
33206
+ await hooks?.onObservationStart?.();
32960
33207
  runResult = await run();
32961
33208
  return runResult;
32962
33209
  } catch (error) {
33210
+ lifecycleError = error;
32963
33211
  runError = error instanceof Error ? error : new Error(String(error));
32964
33212
  throw error;
32965
33213
  } finally {
32966
- hooks?.onObservationEnd?.({
32967
- usage: runResult?.usage,
32968
- error: runError ?? runResult?.error,
32969
- ...runResult?.providerMetadata ? { providerMetadata: runResult.providerMetadata } : {}
32970
- });
33214
+ try {
33215
+ await hooks?.onObservationEnd?.({
33216
+ usage: runResult?.usage,
33217
+ error: runError ?? runResult?.error,
33218
+ ...runResult?.providerMetadata ? { providerMetadata: runResult.providerMetadata } : {}
33219
+ });
33220
+ } catch (endHookError) {
33221
+ if (lifecycleError === void 0) throw endHookError;
33222
+ omDebug(`[OM:hooks] async-buffer onObservationEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
33223
+ }
32971
33224
  }
32972
33225
  }
32973
33226
  /**
@@ -32995,18 +33248,21 @@ ${formattedMessages}
32995
33248
  let observed = false;
32996
33249
  let observationUsage;
32997
33250
  let observationProviderMetadata;
33251
+ let observationError;
33252
+ let lifecycleError;
33253
+ let observationStarted = false;
32998
33254
  let generationBefore = -1;
32999
- await this.withLock(lockKey, async () => {
33000
- const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
33001
- generationBefore = freshRecord.generationCount;
33002
- const unobservedMessages = messages ? this.getUnobservedMessages(messages, freshRecord) : await this.loadMessagesFromStorage(threadId, resourceId, freshRecord.lastObservedAt ? new Date(freshRecord.lastObservedAt) : void 0);
33003
- if (!this.meetsObservationThreshold({
33004
- record: freshRecord,
33005
- unobservedTokens: await this.tokenCounter.countMessagesAsync(unobservedMessages)
33006
- })) return;
33007
- hooks?.onObservationStart?.();
33008
- let observationError;
33009
- try {
33255
+ try {
33256
+ await this.withLock(lockKey, async () => {
33257
+ const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
33258
+ generationBefore = freshRecord.generationCount;
33259
+ const unobservedMessages = messages ? this.getUnobservedMessages(messages, freshRecord) : await this.loadMessagesFromStorage(threadId, resourceId, freshRecord.lastObservedAt ? new Date(freshRecord.lastObservedAt) : void 0);
33260
+ if (!this.meetsObservationThreshold({
33261
+ record: freshRecord,
33262
+ unobservedTokens: await this.tokenCounter.countMessagesAsync(unobservedMessages)
33263
+ })) return;
33264
+ observationStarted = true;
33265
+ await hooks?.onObservationStart?.();
33010
33266
  const result = await ObservationStrategy.create(this, {
33011
33267
  record: freshRecord,
33012
33268
  threadId,
@@ -33024,17 +33280,22 @@ ${formattedMessages}
33024
33280
  observed = result.observed;
33025
33281
  observationUsage = result.usage;
33026
33282
  observationProviderMetadata = result.providerMetadata;
33027
- } catch (error) {
33028
- observationError = error instanceof Error ? error : new Error(String(error));
33029
- throw error;
33030
- } finally {
33031
- hooks?.onObservationEnd?.({
33032
- usage: observationUsage,
33033
- error: observationError,
33034
- ...observationProviderMetadata ? { providerMetadata: observationProviderMetadata } : {}
33035
- });
33036
- }
33037
- });
33283
+ });
33284
+ } catch (error) {
33285
+ lifecycleError = error;
33286
+ observationError = error instanceof Error ? error : new Error(String(error));
33287
+ }
33288
+ if (observationStarted) try {
33289
+ await hooks?.onObservationEnd?.({
33290
+ usage: observationUsage,
33291
+ error: observationError,
33292
+ ...observationProviderMetadata ? { providerMetadata: observationProviderMetadata } : {}
33293
+ });
33294
+ } catch (endHookError) {
33295
+ if (lifecycleError === void 0) throw endHookError;
33296
+ omDebug(`[OM:hooks] onObservationEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
33297
+ }
33298
+ if (lifecycleError !== void 0) throw lifecycleError;
33038
33299
  const record = await this.getOrCreateRecord(threadId, resourceId);
33039
33300
  const reflected = record.generationCount > generationBefore && generationBefore >= 0;
33040
33301
  if (observed) this.maybeTriggerCadenceCuration(threadId, resourceId, record, requestContext).catch((error) => {
@@ -33108,11 +33369,18 @@ ${formattedMessages}
33108
33369
  resourceId,
33109
33370
  trigger: "manual"
33110
33371
  });
33111
- hooks?.onReflectionStart?.();
33112
33372
  let reflectionUsage;
33113
33373
  let reflectionProviderMetadata;
33114
33374
  let reflectionError;
33375
+ let lifecycleError;
33115
33376
  try {
33377
+ try {
33378
+ await hooks?.onReflectionStart?.();
33379
+ } catch (error) {
33380
+ lifecycleError = error;
33381
+ reflectionError = error instanceof Error ? error : new Error(String(error));
33382
+ throw error;
33383
+ }
33116
33384
  const thread = await this.storage.getThreadById({ threadId });
33117
33385
  const previousOmMetadata = getThreadOMMetadata(thread?.metadata);
33118
33386
  const priorExtractedValues = getPriorExtractedValues(previousOmMetadata, this.reflectionConfig.extractors);
@@ -33150,6 +33418,7 @@ ${formattedMessages}
33150
33418
  };
33151
33419
  } catch (error) {
33152
33420
  reflectionError = error instanceof Error ? error : new Error(String(error));
33421
+ if (lifecycleError !== void 0) throw error;
33153
33422
  omError("[OM] reflect() failed", error);
33154
33423
  return {
33155
33424
  reflected: false,
@@ -33157,13 +33426,25 @@ ${formattedMessages}
33157
33426
  usage: void 0
33158
33427
  };
33159
33428
  } finally {
33160
- hooks?.onReflectionEnd?.({
33161
- usage: reflectionUsage,
33162
- error: reflectionError,
33163
- ...reflectionProviderMetadata ? { providerMetadata: reflectionProviderMetadata } : {}
33164
- });
33165
- await this.storage.setReflectingFlag(record.id, false);
33166
- unregisterOp(record.id, "reflecting");
33429
+ try {
33430
+ await this.storage.setReflectingFlag(record.id, false);
33431
+ } finally {
33432
+ unregisterOp(record.id, "reflecting");
33433
+ }
33434
+ let endHookError;
33435
+ try {
33436
+ await hooks?.onReflectionEnd?.({
33437
+ usage: reflectionUsage,
33438
+ error: reflectionError,
33439
+ ...reflectionProviderMetadata ? { providerMetadata: reflectionProviderMetadata } : {}
33440
+ });
33441
+ } catch (error) {
33442
+ endHookError = error;
33443
+ }
33444
+ if (endHookError !== void 0) {
33445
+ if (lifecycleError === void 0) throw endHookError;
33446
+ omDebug(`[OM:hooks] onReflectionEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
33447
+ }
33167
33448
  }
33168
33449
  }
33169
33450
  /**
@@ -33455,7 +33736,9 @@ var ObservationalMemoryProcessor = class {
33455
33736
  return messageList;
33456
33737
  }
33457
33738
  const { threadId, resourceId } = context;
33458
- const readOnly = parseMemoryRequestContext(requestContext)?.memoryConfig?.readOnly;
33739
+ const memoryContext = parseMemoryRequestContext(requestContext);
33740
+ const runState = memoryContext?.runState?.();
33741
+ const readOnly = memoryContext?.memoryConfig?.readOnly;
33459
33742
  const actorModelContext = model?.modelId ? {
33460
33743
  provider: model.provider,
33461
33744
  modelId: model.modelId,
@@ -33472,7 +33755,8 @@ var ObservationalMemoryProcessor = class {
33472
33755
  memory: this.memory,
33473
33756
  messageList,
33474
33757
  threadId,
33475
- resourceId
33758
+ resourceId,
33759
+ runState
33476
33760
  });
33477
33761
  const systemMessages = ctx.omRecord ? await this.engine.buildContextSystemMessages({
33478
33762
  threadId,
@@ -33513,7 +33797,7 @@ var ObservationalMemoryProcessor = class {
33513
33797
  this.turn.sendStateSignal = args.sendStateSignal;
33514
33798
  this.turn.agent = args.agent;
33515
33799
  this.turn.requestContext = requestContext;
33516
- await this.turn.start(this.memory);
33800
+ await this.turn.start(this.memory, runState);
33517
33801
  if (stepNumber === 0 && this.temporalMarkers) await insertTemporalGapMarkers({
33518
33802
  messageList,
33519
33803
  sendSignal: args.sendSignal
@@ -33691,149 +33975,6 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
33691
33975
  summarizeConversation: () => summarizeConversation,
33692
33976
  wrapInObservationGroup: () => wrapInObservationGroup
33693
33977
  });
33694
- /**
33695
- * WorkingMemoryStateProcessor
33696
- *
33697
- * Experimental: delivers working memory to the model as a state signal instead
33698
- * of folding it into the system message. Storage and the `setWorkingMemory`
33699
- * tool are unchanged — this processor only changes the delivery path.
33700
- *
33701
- * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:
33702
- * - `stateId` namespaces the state lane on the thread.
33703
- * - `cacheKey` is derived from the rendered payload so dedup is automatic.
33704
- * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the
33705
- * current snapshot after older messages drop out of the window.
33706
- *
33707
- * Delta emission (markdown mode only): when a prior snapshot exists in the
33708
- * context window, the processor emits a unified-diff delta against that
33709
- * snapshot's contents. Schema mode and the snapshot fallback always emit a
33710
- * full snapshot.
33711
- *
33712
- * @example
33713
- * ```ts
33714
- * new Memory({
33715
- * options: {
33716
- * workingMemory: {
33717
- * enabled: true,
33718
- * template: '...',
33719
- * useStateSignals: true, // auto-attaches this processor
33720
- * },
33721
- * },
33722
- * });
33723
- * ```
33724
- */
33725
- const WORKING_MEMORY_STATE_ID = "working-memory";
33726
- const WORKING_MEMORY_STATE_PROCESSOR_ID = "working-memory-state";
33727
- var WorkingMemoryStateProcessor = class {
33728
- memory;
33729
- memoryConfig;
33730
- id = WORKING_MEMORY_STATE_PROCESSOR_ID;
33731
- stateId = WORKING_MEMORY_STATE_ID;
33732
- constructor(memory, memoryConfig) {
33733
- this.memory = memory;
33734
- this.memoryConfig = memoryConfig;
33735
- }
33736
- async computeStateSignal(args) {
33737
- const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });
33738
- if (!template) return;
33739
- const contents = (await this.memory.getWorkingMemory({
33740
- threadId: args.threadId,
33741
- resourceId: args.resourceId,
33742
- memoryConfig: this.memoryConfig
33743
- }))?.trim();
33744
- if (!contents) return;
33745
- const cacheKey = stableWorkingMemoryCacheKey({
33746
- format: template.format,
33747
- data: contents
33748
- });
33749
- const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;
33750
- if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;
33751
- const scope = this.memory.getMergedThreadConfig(this.memoryConfig).workingMemory?.scope ?? "resource";
33752
- const deltaCandidate = template.format === "markdown" && !shouldMakeSnapshot ? buildMarkdownDelta({
33753
- lastSnapshot: args.lastSnapshot,
33754
- deltasSinceSnapshot: args.deltasSinceSnapshot,
33755
- nextContents: contents
33756
- }) : void 0;
33757
- if (deltaCandidate) return {
33758
- id: WORKING_MEMORY_STATE_ID,
33759
- mode: "delta",
33760
- cacheKey,
33761
- tagName: "working-memory",
33762
- contents: deltaCandidate.contents,
33763
- delta: deltaCandidate.contents,
33764
- value: contents,
33765
- attributes: {
33766
- format: template.format,
33767
- scope,
33768
- patch: "unified-diff"
33769
- }
33770
- };
33771
- return {
33772
- id: WORKING_MEMORY_STATE_ID,
33773
- mode: "snapshot",
33774
- cacheKey,
33775
- tagName: "working-memory",
33776
- contents,
33777
- value: contents,
33778
- attributes: {
33779
- format: template.format,
33780
- scope
33781
- }
33782
- };
33783
- }
33784
- };
33785
- /**
33786
- * Stable cache key for the rendered working memory payload. Returns a SHA-256
33787
- * digest so dedup metadata stays compact regardless of payload size (working
33788
- * memory blobs can grow arbitrarily long).
33789
- */
33790
- function stableWorkingMemoryCacheKey(input) {
33791
- const hash = createHash("sha256");
33792
- hash.update(input.format);
33793
- hash.update("\0");
33794
- hash.update(input.data ?? "");
33795
- return `sha256:${hash.digest("hex")}`;
33796
- }
33797
- /**
33798
- * Build a unified-diff delta against the most recently emitted state. Prefers
33799
- * the latest delta's `value` (the post-edit full text) when available, falling
33800
- * back to the snapshot's `value` and finally the snapshot's `contents`. This
33801
- * keeps deltas incremental (B→C) instead of cumulative against a stale
33802
- * snapshot (A→C), which matters when many small edits land between snapshots.
33803
- *
33804
- * Returns undefined when:
33805
- * - there's no prior state to diff against
33806
- * - the prior state isn't a plain string (multimodal signal)
33807
- *
33808
- * In either case the caller falls back to emitting a full snapshot.
33809
- */
33810
- function buildMarkdownDelta(args) {
33811
- const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;
33812
- const prior = pickStringValue(readSignalValue(deltasSinceSnapshot.at(-1))) ?? pickStringValue(readSignalValue(lastSnapshot)) ?? (typeof lastSnapshot?.contents === "string" ? lastSnapshot.contents : void 0);
33813
- if (!prior) return;
33814
- return { contents: renderHunksOnly(prior, nextContents) };
33815
- }
33816
- function pickStringValue(value) {
33817
- return typeof value === "string" ? value : void 0;
33818
- }
33819
- function readSignalValue(signal) {
33820
- return (signal?.metadata)?.value;
33821
- }
33822
- /**
33823
- * Render a unified-diff-style patch body containing only `@@` hunks and their
33824
- * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)
33825
- * that `createPatch` emits and the `` trailer.
33826
- * The preamble exists for tooling like `patch -p1` to know which file to
33827
- * apply to; we only ever diff a single working-memory blob. The newline
33828
- * trailer is semantically meaningless to the model and adds noise to the
33829
- * state signal.
33830
- */
33831
- function renderHunksOnly(prior, next) {
33832
- const { hunks } = structuredPatch("", "", prior, next, "", "", { context: 0 });
33833
- return hunks.map((hunk) => {
33834
- return [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`, ...hunk.lines.filter((line) => !line.startsWith("\"))].join("\n");
33835
- }).join("\n");
33836
- }
33837
33978
  var working_memory_state_exports = /* @__PURE__ */ __exportAll({
33838
33979
  WORKING_MEMORY_STATE_ID: () => WORKING_MEMORY_STATE_ID,
33839
33980
  WORKING_MEMORY_STATE_PROCESSOR_ID: () => WORKING_MEMORY_STATE_PROCESSOR_ID,
@@ -34660,14 +34801,18 @@ ${workingMemory}`;
34660
34801
  if (!this.threadConfig.workingMemory?.enabled) return null;
34661
34802
  return extractWorkingMemoryContent(text)?.trim() ?? null;
34662
34803
  }
34663
- async getWorkingMemory({ threadId, resourceId, memoryConfig }) {
34804
+ async getWorkingMemory({ threadId, resourceId, memoryConfig, runState }) {
34664
34805
  const config = this.getMergedThreadConfig(memoryConfig || {});
34665
34806
  if (!config.workingMemory?.enabled) return null;
34666
34807
  const scope = config.workingMemory.scope || "resource";
34667
34808
  let workingMemoryData = null;
34668
34809
  if (scope === "resource" && !resourceId) throw new Error("Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.");
34669
- if (scope === "resource" && resourceId) workingMemoryData = (await (await this.getMemoryStore()).getResourceById({ resourceId }))?.workingMemory || null;
34670
- else workingMemoryData = (await this.getThreadById({ threadId }))?.metadata?.workingMemory;
34810
+ if (scope === "resource" && resourceId) {
34811
+ const loadWorkingMemory = async () => {
34812
+ return (await (await this.getMemoryStore()).getResourceById({ resourceId }))?.workingMemory || null;
34813
+ };
34814
+ workingMemoryData = runState ? await runState.load(`working-memory:resource:${resourceId}`, loadWorkingMemory) : await loadWorkingMemory();
34815
+ } else workingMemoryData = (runState?.threadLoaded ? runState.thread : await this.getThreadById({ threadId }))?.metadata?.workingMemory;
34671
34816
  if (!workingMemoryData) return null;
34672
34817
  return workingMemoryData;
34673
34818
  }
@@ -34699,16 +34844,18 @@ ${workingMemory}`;
34699
34844
  content: (config.workingMemory.template || this.defaultWorkingMemoryTemplate).trim()
34700
34845
  };
34701
34846
  }
34702
- async getSystemMessage({ threadId, resourceId, memoryConfig }) {
34847
+ async getSystemMessage({ threadId, resourceId, memoryConfig, runState }) {
34703
34848
  const config = this.getMergedThreadConfig(memoryConfig);
34704
34849
  this.assertWorkingMemoryStateSignalsCompatibility(config);
34705
34850
  if (!config.workingMemory?.enabled) return null;
34706
34851
  if (config.workingMemory?.useStateSignals) return null;
34707
- const workingMemoryTemplate = await this.getWorkingMemoryTemplate({ memoryConfig });
34852
+ const loadTemplate = () => this.getWorkingMemoryTemplate({ memoryConfig });
34853
+ const workingMemoryTemplate = runState ? await runState.load("working-memory:template", loadTemplate) : await loadTemplate();
34708
34854
  const workingMemoryData = await this.getWorkingMemory({
34709
34855
  threadId,
34710
34856
  resourceId,
34711
- memoryConfig: config
34857
+ memoryConfig: config,
34858
+ runState
34712
34859
  });
34713
34860
  if (!workingMemoryTemplate) return null;
34714
34861
  const workingMemoryConfig = config.workingMemory;
@@ -34741,7 +34888,7 @@ ${workingMemory}`;
34741
34888
  * ```
34742
34889
  */
34743
34890
  async getContext(opts) {
34744
- const { threadId, resourceId, memoryConfig } = opts;
34891
+ const { threadId, resourceId, memoryConfig, runState } = opts;
34745
34892
  const config = this.getMergedThreadConfig(memoryConfig);
34746
34893
  const memoryStore = await this.getMemoryStore();
34747
34894
  const systemParts = [];
@@ -34751,10 +34898,14 @@ ${workingMemory}`;
34751
34898
  let otherThreadsContext;
34752
34899
  const omEngine = await this.omEngine;
34753
34900
  if (omEngine) {
34754
- omRecord = await omEngine.getRecord(threadId, resourceId);
34901
+ const loadOmRecord = () => omEngine.getRecord(threadId, resourceId);
34902
+ omRecord = runState ? await runState.load(`observational-memory:record:${threadId}:${resourceId ?? ""}`, loadOmRecord) : await loadOmRecord();
34755
34903
  if (omRecord?.activeObservations) {
34756
34904
  hasObservations = true;
34757
- if (omEngine.scope === "resource" && resourceId) otherThreadsContext = await omEngine.getOtherThreadsContext(resourceId, threadId);
34905
+ if (omEngine.scope === "resource" && resourceId) {
34906
+ const loadOtherThreadsContext = () => omEngine.getOtherThreadsContext(resourceId, threadId);
34907
+ otherThreadsContext = runState ? await runState.load(`observational-memory:other-threads:${resourceId}:${threadId}:${omRecord.lastObservedAt ?? ""}`, loadOtherThreadsContext) : await loadOtherThreadsContext();
34908
+ }
34758
34909
  const obsSystemMessage = await omEngine.buildContextSystemMessage({
34759
34910
  threadId,
34760
34911
  resourceId,
@@ -34782,30 +34933,41 @@ ${workingMemory}`;
34782
34933
  const workingMemoryMessage = await this.getSystemMessage({
34783
34934
  threadId,
34784
34935
  resourceId,
34785
- memoryConfig: config
34936
+ memoryConfig: config,
34937
+ runState
34786
34938
  });
34787
34939
  if (workingMemoryMessage) systemParts.push(workingMemoryMessage);
34788
34940
  let messages;
34789
34941
  if (omEngine && omRecord) {
34790
34942
  const dateFilter = omRecord.lastObservedAt ? { dateRange: { start: new Date(new Date(omRecord.lastObservedAt).getTime() + 1) } } : void 0;
34791
- if (omEngine.scope === "resource" && resourceId) messages = (await memoryStore.listMessagesByResourceId({
34792
- resourceId,
34793
- orderBy: {
34794
- field: "createdAt",
34795
- direction: "ASC"
34796
- },
34797
- perPage: false,
34798
- filter: dateFilter
34799
- })).messages;
34800
- else messages = (await memoryStore.listMessages({
34801
- threadId,
34802
- orderBy: {
34803
- field: "createdAt",
34804
- direction: "ASC"
34805
- },
34806
- perPage: false,
34807
- filter: dateFilter
34808
- })).messages;
34943
+ const boundary = omRecord.lastObservedAt ? new Date(omRecord.lastObservedAt).toISOString() : "";
34944
+ if (omEngine.scope === "resource" && resourceId) {
34945
+ const loadMessages = async () => {
34946
+ return (await memoryStore.listMessagesByResourceId({
34947
+ resourceId,
34948
+ orderBy: {
34949
+ field: "createdAt",
34950
+ direction: "ASC"
34951
+ },
34952
+ perPage: false,
34953
+ filter: dateFilter
34954
+ })).messages;
34955
+ };
34956
+ messages = runState ? await runState.load(`observational-memory:messages:resource:${resourceId}:${boundary}`, loadMessages) : await loadMessages();
34957
+ } else {
34958
+ const loadMessages = async () => {
34959
+ return (await memoryStore.listMessages({
34960
+ threadId,
34961
+ orderBy: {
34962
+ field: "createdAt",
34963
+ direction: "ASC"
34964
+ },
34965
+ perPage: false,
34966
+ filter: dateFilter
34967
+ })).messages;
34968
+ };
34969
+ messages = runState ? await runState.load(`observational-memory:messages:thread:${threadId}:${boundary}`, loadMessages) : await loadMessages();
34970
+ }
34809
34971
  } else {
34810
34972
  const lastMessages = config.lastMessages;
34811
34973
  if (lastMessages === false) messages = [];
@@ -34893,6 +35055,7 @@ ${workingMemory}`;
34893
35055
  instruction: omConfig.observation.instruction,
34894
35056
  threadTitle: omConfig.observation.threadTitle,
34895
35057
  observeAttachments: omConfig.observation.observeAttachments,
35058
+ continuationHints: omConfig.observation.continuationHints,
34896
35059
  extract: omConfig.observation.extract
34897
35060
  } : void 0,
34898
35061
  reflection: omConfig.reflection ? {
@@ -34903,6 +35066,7 @@ ${workingMemory}`;
34903
35066
  bufferActivation: omConfig.reflection.bufferActivation,
34904
35067
  blockAfter: omConfig.reflection.blockAfter,
34905
35068
  instruction: omConfig.reflection.instruction,
35069
+ continuationHints: omConfig.reflection.continuationHints,
34906
35070
  extract: omConfig.reflection.extract
34907
35071
  } : void 0
34908
35072
  });
@@ -41070,6 +41234,7 @@ async function cancelResponseBody(response) {
41070
41234
  var initialGlobalFetch = globalThis.fetch;
41071
41235
  isNodeDefaultFetch(initialGlobalFetch);
41072
41236
  function isNodeDefaultFetch(fetch) {
41237
+ if (typeof fetch !== "function") return false;
41073
41238
  const source = Function.prototype.toString.call(fetch);
41074
41239
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
41075
41240
  }
@@ -46196,4 +46361,4 @@ const agentBuilderWorkflows = {
46196
46361
  //#endregion
46197
46362
  export { agentBuilderWorkflows };
46198
46363
 
46199
- //# sourceMappingURL=dist-Cw-T1gX6.js.map
46364
+ //# sourceMappingURL=dist-L0EogwX_.js.map