@mastra/server 1.72.0-alpha.0 → 1.72.0-alpha.10

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 (142) hide show
  1. package/dist/{a2a-CwxSk6H0.cjs → a2a-CAQVpVxH.cjs} +23 -3
  2. package/dist/a2a-CAQVpVxH.cjs.map +1 -0
  3. package/dist/{a2a-DQOqoU_q.js → a2a-D2BffGsK.js} +23 -3
  4. package/dist/a2a-D2BffGsK.js.map +1 -0
  5. package/dist/{agent-builder-BGTZux5y.js → agent-builder-BlD120ac.js} +2 -2
  6. package/dist/{agent-builder-BGTZux5y.js.map → agent-builder-BlD120ac.js.map} +1 -1
  7. package/dist/{agent-builder-D-LAj4SD.cjs → agent-builder-CvwtAD7k.cjs} +2 -2
  8. package/dist/{agent-builder-D-LAj4SD.cjs.map → agent-builder-CvwtAD7k.cjs.map} +1 -1
  9. package/dist/{api-schema-manifest-Maxewnw7.cjs → api-schema-manifest-CaZU5zVt.cjs} +2 -2
  10. package/dist/{api-schema-manifest-Maxewnw7.cjs.map → api-schema-manifest-CaZU5zVt.cjs.map} +1 -1
  11. package/dist/{api-schema-manifest-DJiUahwo.js → api-schema-manifest-wF1O8MCU.js} +2 -2
  12. package/dist/{api-schema-manifest-DJiUahwo.js.map → api-schema-manifest-wF1O8MCU.js.map} +1 -1
  13. package/dist/{channels-DMw4LEA-.cjs → channels-DJkXEpTr.cjs} +18 -7
  14. package/dist/channels-DJkXEpTr.cjs.map +1 -0
  15. package/dist/{channels-xqChW6Vw.js → channels-DzaCNtXe.js} +18 -7
  16. package/dist/channels-DzaCNtXe.js.map +1 -0
  17. package/dist/{dist-Cfr6t0cK.cjs → dist-CRMvLG_8.cjs} +746 -366
  18. package/dist/dist-CRMvLG_8.cjs.map +1 -0
  19. package/dist/{dist-ABdCIqbD.js → dist-CYk5xS3y.js} +746 -366
  20. package/dist/dist-CYk5xS3y.js.map +1 -0
  21. package/dist/docs/SKILL.md +1 -1
  22. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  23. package/dist/docs/references/reference-observability-tracing-trace-query.md +37 -16
  24. package/dist/{editor-builder-BrkDnCEd.js → editor-builder-DJMAKDR5.js} +3 -2
  25. package/dist/editor-builder-DJMAKDR5.js.map +1 -0
  26. package/dist/{editor-builder-DIpISGSI.cjs → editor-builder-DrDs-t9i.cjs} +3 -2
  27. package/dist/editor-builder-DrDs-t9i.cjs.map +1 -0
  28. package/dist/{observability-new-endpoints-Ct0CSLE4.cjs → observability-new-endpoints-7Zd6uEZf.cjs} +111 -3
  29. package/dist/observability-new-endpoints-7Zd6uEZf.cjs.map +1 -0
  30. package/dist/{observability-new-endpoints-DqS6dcR5.js → observability-new-endpoints-lNC3o66-.js} +107 -5
  31. package/dist/observability-new-endpoints-lNC3o66-.js.map +1 -0
  32. package/dist/route-builder-2yrD15TL.cjs.map +1 -1
  33. package/dist/route-builder-lBil8Cvo.js.map +1 -1
  34. package/dist/{routes-D6sAEgtj.js → routes-B6soWZap.js} +6 -5
  35. package/dist/routes-B6soWZap.js.map +1 -0
  36. package/dist/{routes-C9CmwhbC.cjs → routes-x4As7pym.cjs} +6 -5
  37. package/dist/routes-x4As7pym.cjs.map +1 -0
  38. package/dist/schema-e0VHpMt0-BS9KHnuT.cjs.map +1 -1
  39. package/dist/schema-e0VHpMt0-CvPvLS06.js.map +1 -1
  40. package/dist/server/handlers/a2a.cjs +1 -1
  41. package/dist/server/handlers/a2a.d.ts.map +1 -1
  42. package/dist/server/handlers/a2a.js +1 -1
  43. package/dist/server/handlers/agent-builder.cjs +3 -3
  44. package/dist/server/handlers/agent-builder.d.ts +4 -2
  45. package/dist/server/handlers/agent-builder.d.ts.map +1 -1
  46. package/dist/server/handlers/agent-builder.js +3 -3
  47. package/dist/server/handlers/agent-controller.cjs +2 -1
  48. package/dist/server/handlers/agent-controller.cjs.map +1 -1
  49. package/dist/server/handlers/agent-controller.d.ts.map +1 -1
  50. package/dist/server/handlers/agent-controller.js +2 -1
  51. package/dist/server/handlers/agent-controller.js.map +1 -1
  52. package/dist/server/handlers/agents.cjs +49 -20
  53. package/dist/server/handlers/agents.cjs.map +1 -1
  54. package/dist/server/handlers/agents.d.ts.map +1 -1
  55. package/dist/server/handlers/agents.js +49 -20
  56. package/dist/server/handlers/agents.js.map +1 -1
  57. package/dist/server/handlers/channels.cjs +1 -1
  58. package/dist/server/handlers/channels.d.ts.map +1 -1
  59. package/dist/server/handlers/channels.js +1 -1
  60. package/dist/server/handlers/editor-builder.cjs +1 -1
  61. package/dist/server/handlers/editor-builder.d.ts.map +1 -1
  62. package/dist/server/handlers/editor-builder.js +1 -1
  63. package/dist/server/handlers/favorites-enrichment.cjs +1 -1
  64. package/dist/server/handlers/favorites-enrichment.js +1 -1
  65. package/dist/server/handlers/observability-new-endpoints.cjs +2 -1
  66. package/dist/server/handlers/observability-new-endpoints.d.ts +100 -0
  67. package/dist/server/handlers/observability-new-endpoints.d.ts.map +1 -1
  68. package/dist/server/handlers/observability-new-endpoints.js +2 -2
  69. package/dist/server/handlers/observability-shared.cjs +46 -0
  70. package/dist/server/handlers/observability-shared.cjs.map +1 -1
  71. package/dist/server/handlers/observability-shared.d.ts +13 -0
  72. package/dist/server/handlers/observability-shared.d.ts.map +1 -1
  73. package/dist/server/handlers/observability-shared.js +42 -1
  74. package/dist/server/handlers/observability-shared.js.map +1 -1
  75. package/dist/server/handlers/observability.cjs +3 -1
  76. package/dist/server/handlers/observability.cjs.map +1 -1
  77. package/dist/server/handlers/observability.js +3 -2
  78. package/dist/server/handlers/observability.js.map +1 -1
  79. package/dist/server/handlers/stored-agent-favorites.cjs +1 -1
  80. package/dist/server/handlers/stored-agent-favorites.js +1 -1
  81. package/dist/server/handlers/stored-agents.cjs +6 -3
  82. package/dist/server/handlers/stored-agents.cjs.map +1 -1
  83. package/dist/server/handlers/stored-agents.d.ts.map +1 -1
  84. package/dist/server/handlers/stored-agents.js +6 -3
  85. package/dist/server/handlers/stored-agents.js.map +1 -1
  86. package/dist/server/handlers/stored-skill-favorites.cjs +1 -1
  87. package/dist/server/handlers/stored-skill-favorites.js +1 -1
  88. package/dist/server/handlers/stored-skills.cjs +1 -1
  89. package/dist/server/handlers/stored-skills.js +1 -1
  90. package/dist/server/handlers/system.cjs +2 -2
  91. package/dist/server/handlers/system.d.ts +2 -0
  92. package/dist/server/handlers/system.d.ts.map +1 -1
  93. package/dist/server/handlers/system.js +2 -2
  94. package/dist/server/handlers/workflows.cjs +28 -4
  95. package/dist/server/handlers/workflows.cjs.map +1 -1
  96. package/dist/server/handlers/workflows.d.ts +5 -2
  97. package/dist/server/handlers/workflows.d.ts.map +1 -1
  98. package/dist/server/handlers/workflows.js +28 -4
  99. package/dist/server/handlers/workflows.js.map +1 -1
  100. package/dist/server/handlers.cjs +1 -1
  101. package/dist/server/handlers.js +1 -1
  102. package/dist/server/schemas/index.cjs +3 -3
  103. package/dist/server/schemas/index.js +3 -3
  104. package/dist/server/schemas/system.d.ts +2 -0
  105. package/dist/server/schemas/system.d.ts.map +1 -1
  106. package/dist/server/schemas/workflows.d.ts +2 -1
  107. package/dist/server/schemas/workflows.d.ts.map +1 -1
  108. package/dist/server/server-adapter/index.cjs +1 -1
  109. package/dist/server/server-adapter/index.js +1 -1
  110. package/dist/server/server-adapter/routes/agent-builder.d.ts +4 -2
  111. package/dist/server/server-adapter/routes/agent-builder.d.ts.map +1 -1
  112. package/dist/server/server-adapter/routes/observability.d.ts +97 -0
  113. package/dist/server/server-adapter/routes/observability.d.ts.map +1 -1
  114. package/dist/server/server-adapter/routes/system.d.ts +2 -0
  115. package/dist/server/server-adapter/routes/system.d.ts.map +1 -1
  116. package/dist/server/server-adapter/routes/workflows.d.ts +4 -2
  117. package/dist/server/server-adapter/routes/workflows.d.ts.map +1 -1
  118. package/dist/{system-BE5tDMHq.js → system-6qFm79mm.js} +2 -1
  119. package/dist/system-6qFm79mm.js.map +1 -0
  120. package/dist/{system-BlGYb2fu.cjs → system-CskEzH1o.cjs} +2 -1
  121. package/dist/system-CskEzH1o.cjs.map +1 -0
  122. package/dist/{workflows-CvJ4c4_p.js → workflows-BEdLrZQZ.js} +3 -2
  123. package/dist/workflows-BEdLrZQZ.js.map +1 -0
  124. package/dist/{workflows-Cv6MGw9d.cjs → workflows-BdoErlkd.cjs} +3 -2
  125. package/dist/workflows-BdoErlkd.cjs.map +1 -0
  126. package/package.json +7 -7
  127. package/dist/a2a-CwxSk6H0.cjs.map +0 -1
  128. package/dist/a2a-DQOqoU_q.js.map +0 -1
  129. package/dist/channels-DMw4LEA-.cjs.map +0 -1
  130. package/dist/channels-xqChW6Vw.js.map +0 -1
  131. package/dist/dist-ABdCIqbD.js.map +0 -1
  132. package/dist/dist-Cfr6t0cK.cjs.map +0 -1
  133. package/dist/editor-builder-BrkDnCEd.js.map +0 -1
  134. package/dist/editor-builder-DIpISGSI.cjs.map +0 -1
  135. package/dist/observability-new-endpoints-Ct0CSLE4.cjs.map +0 -1
  136. package/dist/observability-new-endpoints-DqS6dcR5.js.map +0 -1
  137. package/dist/routes-C9CmwhbC.cjs.map +0 -1
  138. package/dist/routes-D6sAEgtj.js.map +0 -1
  139. package/dist/system-BE5tDMHq.js.map +0 -1
  140. package/dist/system-BlGYb2fu.cjs.map +0 -1
  141. package/dist/workflows-Cv6MGw9d.cjs.map +0 -1
  142. package/dist/workflows-CvJ4c4_p.js.map +0 -1
@@ -4638,7 +4638,7 @@ function splitLines(text) {
4638
4638
  return result;
4639
4639
  }
4640
4640
  //#endregion
4641
- //#region ../memory/dist/src-B-_pkR2t.js
4641
+ //#region ../memory/dist/src-BdfOygyn.js
4642
4642
  var __defProp$3 = Object.defineProperty;
4643
4643
  var __exportAll = (all, no_symbols) => {
4644
4644
  let target = {};
@@ -23224,9 +23224,224 @@ async function applyExtractorHooks(opts) {
23224
23224
  failures: mergeExtractionFailures(failures)
23225
23225
  };
23226
23226
  }
23227
+ /**
23228
+ * Retry knobs for the internal OM transport-error retry wrapper.
23229
+ * Exported as a mutable object so tests can shrink the backoff schedule
23230
+ * without changing public API.
23231
+ *
23232
+ * With the defaults the per-retry pre-jitter backoff schedule is:
23233
+ * 1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s (cap)
23234
+ * giving 8 retries / 9 total attempts and ~247s (~4 minutes) of waiting
23235
+ * before the final attempt fails. Designed to ride out short provider /
23236
+ * network blips without holding the actor turn for much longer than that.
23237
+ *
23238
+ * @internal
23239
+ */
23240
+ const RETRY_CONFIG = {
23241
+ /** Maximum number of retry *attempts* (total tries = maxRetries + 1). */
23242
+ maxRetries: 8,
23243
+ /** Initial backoff delay in milliseconds. */
23244
+ initialDelayMs: 1e3,
23245
+ /** Multiplier applied to the delay after each failed attempt. */
23246
+ backoffFactor: 2,
23247
+ /** Cap on per-attempt delay (ms). */
23248
+ maxDelayMs: 12e4,
23249
+ /** Random jitter as a fraction of the computed delay (e.g. 0.2 = ±20%). */
23250
+ jitter: .2
23251
+ };
23252
+ const TRANSIENT_MESSAGE_SUBSTRINGS = [
23253
+ "terminated",
23254
+ "fetch failed",
23255
+ "econnreset",
23256
+ "econnrefused",
23257
+ "enotfound",
23258
+ "eai_again",
23259
+ "socket hang up",
23260
+ "network error",
23261
+ "request timed out",
23262
+ "request timeout",
23263
+ "connection reset",
23264
+ "connection closed"
23265
+ ];
23227
23266
  function isRecord$5(value) {
23228
23267
  return typeof value === "object" && value !== null;
23229
23268
  }
23269
+ function isAbortError$1(error) {
23270
+ if (!isRecord$5(error)) return false;
23271
+ if (error.name === "AbortError") return true;
23272
+ if (typeof error.code === "string" && error.code === "ABORT_ERR") return true;
23273
+ return false;
23274
+ }
23275
+ function hasTransientMessage(value) {
23276
+ if (!isRecord$5(value)) return false;
23277
+ const message = typeof value.message === "string" ? value.message.toLowerCase() : "";
23278
+ if (message && TRANSIENT_MESSAGE_SUBSTRINGS.some((sub) => message.includes(sub))) return true;
23279
+ if (typeof value.code === "string" && value.code.toUpperCase().startsWith("UND_ERR_")) return true;
23280
+ return false;
23281
+ }
23282
+ function isRetryableHttpStatus(status) {
23283
+ if (status === 408 || status === 425 || status === 429) return true;
23284
+ return status >= 500 && status <= 599;
23285
+ }
23286
+ function hasRetryableHttpStatus(value) {
23287
+ if (!isRecord$5(value)) return false;
23288
+ if (typeof value.statusCode === "number" && isRetryableHttpStatus(value.statusCode)) return true;
23289
+ if (typeof value.code === "number" && isRetryableHttpStatus(value.code)) return true;
23290
+ return false;
23291
+ }
23292
+ function hasIsRetryableFlag(value) {
23293
+ if (!isRecord$5(value)) return false;
23294
+ return value.isRetryable === true;
23295
+ }
23296
+ /**
23297
+ * Returns true when a user-initiated cancellation appears anywhere in the
23298
+ * error's `cause`/`error` wrapper chain, so a wrapped abort can never be
23299
+ * mistaken for a retryable or survivable provider failure.
23300
+ *
23301
+ * @internal
23302
+ */
23303
+ function hasAbortInChain(error) {
23304
+ const seen = /* @__PURE__ */ new Set();
23305
+ function visit(candidate) {
23306
+ if (isAbortError$1(candidate)) return true;
23307
+ if (!isRecord$5(candidate)) return false;
23308
+ if (seen.has(candidate)) return false;
23309
+ seen.add(candidate);
23310
+ return visit(candidate.cause) || visit(candidate.error);
23311
+ }
23312
+ return visit(error);
23313
+ }
23314
+ /**
23315
+ * Returns true when the given error looks like a transient transport-class
23316
+ * failure that's worth retrying — undici `terminated`, `fetch failed`,
23317
+ * `UND_ERR_*` codes, AI SDK `APICallError` with `isRetryable: true`, and
23318
+ * common HTTP 408/425/429/5xx statuses. Walks the `error.cause` chain so
23319
+ * wrapper errors don't hide the real failure.
23320
+ *
23321
+ * Never retries on user-initiated aborts.
23322
+ *
23323
+ * @internal
23324
+ */
23325
+ function isTransientLLMError(error) {
23326
+ if (hasAbortInChain(error)) return false;
23327
+ const visited = /* @__PURE__ */ new WeakSet();
23328
+ function visit(candidate) {
23329
+ if (isRecord$5(candidate)) {
23330
+ if (visited.has(candidate)) return false;
23331
+ visited.add(candidate);
23332
+ }
23333
+ if (hasTransientMessage(candidate)) return true;
23334
+ if (hasRetryableHttpStatus(candidate)) return true;
23335
+ if (hasIsRetryableFlag(candidate)) return true;
23336
+ if (isRecord$5(candidate)) {
23337
+ if (visit(candidate.cause)) return true;
23338
+ if (visit(candidate.error)) return true;
23339
+ }
23340
+ return false;
23341
+ }
23342
+ return visit(error);
23343
+ }
23344
+ /**
23345
+ * Compute the backoff delay (ms) for the Nth retry (0-indexed).
23346
+ *
23347
+ * Exponential growth (`initialDelayMs * backoffFactor^attempt`) capped at
23348
+ * `maxDelayMs`, then nudged by ±`jitter` (fractional). Exported for unit
23349
+ * tests that lock the schedule against drift.
23350
+ *
23351
+ * @internal
23352
+ */
23353
+ function computeDelay(attempt) {
23354
+ const base = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, attempt);
23355
+ const capped = Math.min(base, RETRY_CONFIG.maxDelayMs);
23356
+ if (RETRY_CONFIG.jitter <= 0) return capped;
23357
+ const jitterRange = capped * RETRY_CONFIG.jitter;
23358
+ const offset = (Math.random() * 2 - 1) * jitterRange;
23359
+ return Math.max(0, Math.round(capped + offset));
23360
+ }
23361
+ function sleep(ms, abortSignal) {
23362
+ if (ms <= 0) return Promise.resolve();
23363
+ return new Promise((resolve, reject) => {
23364
+ if (abortSignal?.aborted) {
23365
+ reject(/* @__PURE__ */ new Error("The operation was aborted."));
23366
+ return;
23367
+ }
23368
+ const timer = setTimeout(() => {
23369
+ abortSignal?.removeEventListener("abort", onAbort);
23370
+ resolve();
23371
+ }, ms);
23372
+ const onAbort = () => {
23373
+ clearTimeout(timer);
23374
+ abortSignal?.removeEventListener("abort", onAbort);
23375
+ reject(/* @__PURE__ */ new Error("The operation was aborted."));
23376
+ };
23377
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
23378
+ });
23379
+ }
23380
+ /**
23381
+ * Run `fn` with retries on transient transport-class errors.
23382
+ *
23383
+ * Non-transient errors (auth, validation, schema, etc.) are rethrown
23384
+ * immediately. User-initiated aborts are rethrown without delay.
23385
+ *
23386
+ * @internal
23387
+ */
23388
+ async function withRetry(fn, opts) {
23389
+ const { label, abortSignal, maxRetries = RETRY_CONFIG.maxRetries } = opts;
23390
+ let attempt = 0;
23391
+ while (true) {
23392
+ if (abortSignal?.aborted) throw new Error("The operation was aborted.");
23393
+ try {
23394
+ return await fn();
23395
+ } catch (error) {
23396
+ if (hasAbortInChain(error) || abortSignal?.aborted) throw error;
23397
+ if (attempt >= maxRetries || !isTransientLLMError(error)) {
23398
+ if (attempt > 0) omDebug(`[OM:retry:${label}] giving up after ${attempt} retry/retries: ${error instanceof Error ? error.message : String(error)}`);
23399
+ throw error;
23400
+ }
23401
+ const delay = computeDelay(attempt);
23402
+ attempt++;
23403
+ omDebug(`[OM:retry:${label}] transient error on attempt ${attempt}, retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`);
23404
+ await sleep(delay, abortSignal);
23405
+ }
23406
+ }
23407
+ }
23408
+ const AI_API_CALL_ERROR_MARKER = Symbol.for("vercel.ai.error.AI_APICallError");
23409
+ function isRecord$4(value) {
23410
+ return typeof value === "object" && value !== null;
23411
+ }
23412
+ function isOmModelExecutionFailure(error) {
23413
+ if (hasAbortInChain(error)) return false;
23414
+ if (isTransientLLMError(error)) return true;
23415
+ const visited = /* @__PURE__ */ new Set();
23416
+ let current = error;
23417
+ while (isRecord$4(current) && !visited.has(current)) {
23418
+ visited.add(current);
23419
+ if (Object.prototype.hasOwnProperty.call(current, AI_API_CALL_ERROR_MARKER)) return Reflect.get(current, AI_API_CALL_ERROR_MARKER) === true;
23420
+ current = current.cause ?? current.error;
23421
+ }
23422
+ return false;
23423
+ }
23424
+ var OmModelExecutionError = class extends Error {
23425
+ failureKind;
23426
+ constructor(failureKind, cause) {
23427
+ super(formatOmError(cause), { cause });
23428
+ this.failureKind = failureKind;
23429
+ this.name = "OmModelExecutionError";
23430
+ }
23431
+ };
23432
+ function isOmModelExecutionError(error) {
23433
+ try {
23434
+ return error instanceof OmModelExecutionError;
23435
+ } catch {
23436
+ return false;
23437
+ }
23438
+ }
23439
+ function getOmFailureMetadata(error, failurePolicy) {
23440
+ return {
23441
+ failurePolicy,
23442
+ ...isOmModelExecutionError(error) ? { failureKind: error.failureKind } : {}
23443
+ };
23444
+ }
23230
23445
  /** Keep provider diagnostics in streamed/persisted markers, not whole API request/response objects. */
23231
23446
  function formatOmError(error) {
23232
23447
  const details = /* @__PURE__ */ new Set();
@@ -23236,20 +23451,21 @@ function formatOmError(error) {
23236
23451
  };
23237
23452
  function collectErrorDetails(value, depth) {
23238
23453
  if (depth > 5) return;
23239
- if (!isRecord$5(value)) {
23454
+ if (!isRecord$4(value)) {
23240
23455
  if (value !== void 0) add(String(value));
23241
23456
  return;
23242
23457
  }
23243
23458
  if (visited.has(value)) return;
23244
23459
  visited.add(value);
23460
+ if (typeof value.id === "string" && typeof value.domain === "string" && typeof value.category === "string") add(value.id);
23245
23461
  add(value.message);
23246
23462
  if (typeof value.statusCode === "number") add(`HTTP ${value.statusCode}`);
23247
23463
  if (typeof value.responseBody === "string") try {
23248
23464
  const body = JSON.parse(value.responseBody);
23249
- if (isRecord$5(body)) {
23465
+ if (isRecord$4(body)) {
23250
23466
  add(body.message);
23251
23467
  add(body.detail);
23252
- if (isRecord$5(body.error)) add(body.error.message);
23468
+ if (isRecord$4(body.error)) add(body.error.message);
23253
23469
  else add(body.error);
23254
23470
  }
23255
23471
  } catch {}
@@ -23262,8 +23478,8 @@ function formatOmError(error) {
23262
23478
  const message = [...details].join(": ") || "Unknown error";
23263
23479
  return message.length > 2e3 ? `${message.slice(0, 1997)}...` : message;
23264
23480
  }
23265
- function isAbortError$1(error, abortSignal) {
23266
- return abortSignal?.aborted === true || error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
23481
+ function isAbortError$5(error, abortSignal) {
23482
+ return abortSignal?.aborted === true || hasAbortInChain(error);
23267
23483
  }
23268
23484
  function shouldRetryEmptyStructuredObject(object, extractors) {
23269
23485
  return Object.keys(object).length === 0 && extractors.some((extractor) => extractor.retryStructuredExtractionOnEmptyObject);
@@ -23289,7 +23505,12 @@ If a prior value is still applicable and carry-forward is enabled, return that p
23289
23505
  ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values\n\n${priorLines.join("\n\n")}` : ""}`;
23290
23506
  const values = {};
23291
23507
  const failures = [];
23292
- const streamWithStructuredOutput = async (jsonPromptInjection) => {
23508
+ const streamWithStructuredOutput = async (jsonPromptInjection) => withRetry(() => streamOnce(jsonPromptInjection), {
23509
+ label: `om-${opts.source}-structured-extraction`,
23510
+ abortSignal: opts.abortSignal,
23511
+ maxRetries: 1
23512
+ });
23513
+ const streamOnce = async (jsonPromptInjection) => {
23293
23514
  const object = await (await opts.agent.stream(prompt, {
23294
23515
  structuredOutput: {
23295
23516
  schema,
@@ -23309,11 +23530,21 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
23309
23530
  object = await streamWithStructuredOutput();
23310
23531
  retryEmptyObject = shouldRetryEmptyStructuredObject(object, structuredExtractors);
23311
23532
  } catch (error) {
23312
- if (isAbortError$1(error, opts.abortSignal)) throw error;
23533
+ if (isAbortError$5(error, opts.abortSignal)) throw error;
23534
+ if (isTransientLLMError(error)) {
23535
+ const message = error instanceof Error ? error.message : String(error);
23536
+ return {
23537
+ values,
23538
+ failures: structuredExtractors.map((extractor) => ({
23539
+ slug: extractor.slug,
23540
+ error: message
23541
+ }))
23542
+ };
23543
+ }
23313
23544
  try {
23314
23545
  object = await streamWithStructuredOutput(_mastra_core_features.coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
23315
23546
  } catch (fallbackError) {
23316
- if (isAbortError$1(fallbackError, opts.abortSignal)) throw fallbackError;
23547
+ if (isAbortError$5(fallbackError, opts.abortSignal)) throw fallbackError;
23317
23548
  const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
23318
23549
  return {
23319
23550
  values,
@@ -23327,7 +23558,7 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
23327
23558
  if (retryEmptyObject) try {
23328
23559
  object = await streamWithStructuredOutput(_mastra_core_features.coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
23329
23560
  } catch (fallbackError) {
23330
- if (isAbortError$1(fallbackError, opts.abortSignal)) throw fallbackError;
23561
+ if (isAbortError$5(fallbackError, opts.abortSignal)) throw fallbackError;
23331
23562
  const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
23332
23563
  return {
23333
23564
  values,
@@ -23353,15 +23584,15 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
23353
23584
  };
23354
23585
  }
23355
23586
  const LEGACY_SYSTEM_REMINDER_METADATA_KEY = "dynamicAgentsMdReminder";
23356
- function isRecord$4(value) {
23587
+ function isRecord$3(value) {
23357
23588
  return typeof value === "object" && value !== null;
23358
23589
  }
23359
23590
  function isSystemReminderMessage(message) {
23360
- if (!isRecord$4(message.content)) return false;
23591
+ if (!isRecord$3(message.content)) return false;
23361
23592
  const metadata = message.content.metadata;
23362
- if (message.role === "signal") return isRecord$4(metadata) && isRecord$4(metadata.signal) && (metadata.signal.type === "system-reminder" || metadata.signal.type === "reactive");
23593
+ if (message.role === "signal") return isRecord$3(metadata) && isRecord$3(metadata.signal) && (metadata.signal.type === "system-reminder" || metadata.signal.type === "reactive");
23363
23594
  if (message.role !== "user") return false;
23364
- if (isRecord$4(metadata) && (isRecord$4(metadata.systemReminder) || LEGACY_SYSTEM_REMINDER_METADATA_KEY in metadata)) return true;
23595
+ if (isRecord$3(metadata) && (isRecord$3(metadata.systemReminder) || LEGACY_SYSTEM_REMINDER_METADATA_KEY in metadata)) return true;
23365
23596
  const firstTextPart = message.content.parts.find((part) => part.type === "text");
23366
23597
  return typeof firstTextPart?.text === "string" && firstTextPart.text.startsWith("<system-reminder");
23367
23598
  }
@@ -23420,12 +23651,41 @@ function stripEphemeralAnchorIds(observations) {
23420
23651
  * Date/time utility functions for Observational Memory.
23421
23652
  * Pure functions for formatting relative timestamps and annotating observations.
23422
23653
  */
23423
- /**
23424
- * Format a relative time string like "5 days ago", "2 weeks ago", "today", etc.
23425
- */
23426
- function formatRelativeTime(date, currentDate) {
23427
- const diffMs = currentDate.getTime() - date.getTime();
23428
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
23654
+ const DAY_MS = 1440 * 60 * 1e3;
23655
+ /** Day number of the calendar date a parsed date names. Parsed dates are built from local-time fields. */
23656
+ function calendarDay(date) {
23657
+ return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / DAY_MS;
23658
+ }
23659
+ const knownTimeZones = /* @__PURE__ */ new Map();
23660
+ /** `timeZone` when this runtime recognises it; otherwise undefined, which means the process time zone. */
23661
+ function resolveTimeZone(timeZone) {
23662
+ if (!timeZone) return void 0;
23663
+ let known = knownTimeZones.get(timeZone);
23664
+ if (known === void 0) {
23665
+ try {
23666
+ new Intl.DateTimeFormat("en-US", { timeZone });
23667
+ known = true;
23668
+ } catch {
23669
+ known = false;
23670
+ }
23671
+ knownTimeZones.set(timeZone, known);
23672
+ }
23673
+ return known ? timeZone : void 0;
23674
+ }
23675
+ /** Day number of the calendar date `instant` falls on in `timeZone` (the process time zone when unset). */
23676
+ function calendarDayIn(instant, timeZone) {
23677
+ const zone = resolveTimeZone(timeZone);
23678
+ if (!zone) return calendarDay(instant);
23679
+ const parts = new Intl.DateTimeFormat("en-US", {
23680
+ timeZone: zone,
23681
+ year: "numeric",
23682
+ month: "numeric",
23683
+ day: "numeric"
23684
+ }).formatToParts(instant);
23685
+ const part = (type) => Number(parts.find((p) => p.type === type)?.value);
23686
+ return Date.UTC(part("year"), part("month") - 1, part("day")) / DAY_MS;
23687
+ }
23688
+ function formatRelativeDays(diffDays) {
23429
23689
  if (diffDays < 0) {
23430
23690
  const futureDays = Math.abs(diffDays);
23431
23691
  if (futureDays === 1) return "tomorrow";
@@ -23451,8 +23711,7 @@ function formatRelativeTime(date, currentDate) {
23451
23711
  * Returns null for consecutive days (no gap marker needed).
23452
23712
  */
23453
23713
  function formatGapBetweenDates(prevDate, currDate) {
23454
- const diffMs = currDate.getTime() - prevDate.getTime();
23455
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
23714
+ const diffDays = calendarDay(currDate) - calendarDay(prevDate);
23456
23715
  if (diffDays <= 1) return null;
23457
23716
  else if (diffDays < 7) return `[${diffDays} days later]`;
23458
23717
  else if (diffDays < 14) return `[1 week later]`;
@@ -23460,45 +23719,156 @@ function formatGapBetweenDates(prevDate, currDate) {
23460
23719
  else if (diffDays < 60) return `[1 month later]`;
23461
23720
  else return `[${Math.floor(diffDays / 30)} months later]`;
23462
23721
  }
23722
+ const MONTH_INDEX = {
23723
+ jan: 0,
23724
+ feb: 1,
23725
+ mar: 2,
23726
+ apr: 3,
23727
+ may: 4,
23728
+ jun: 5,
23729
+ jul: 6,
23730
+ aug: 7,
23731
+ sep: 8,
23732
+ oct: 9,
23733
+ nov: 10,
23734
+ dec: 11
23735
+ };
23736
+ const MONTH = String.raw`(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\.?`;
23737
+ const DAY = String.raw`\d{1,2}(?:st|nd|rd|th)?`;
23738
+ const YEAR = String.raw`(?:19|20|21)\d{2}(?!\d)`;
23739
+ const MERIDIEM = String.raw`(?:[AaPp]\.[Mm]\.|[AaPp][Mm]\b)`;
23740
+ const TIME = String.raw`(?:,?\s+(?:at\s+)?\d{1,2}:\d{2}(?:\s*${MERIDIEM})?|\s+at\s+\d{1,2}\s*${MERIDIEM})`;
23741
+ const QUALIFIER = String.raw`(?:(?:early|mid|late)[- ](?:to[- ](?:early|mid|late)[- ])?)`;
23742
+ const RANGE_SEP = String.raw`(?:\s*[–—]\s*|\s*-\s*|\s+(?:to|through|until)\s+)`;
23743
+ /**
23744
+ * Dates written into observation text that carry their own year, longest forms first:
23745
+ * "Mar 1 - Mar 18, 2025", "May 27-28, 2023", "January 10, 2024 at 3:00 PM",
23746
+ * "June–July 2022", "Dec 2021 – June 2023", "late April 2023".
23747
+ * Bare years, dates without a year, and ISO dates (often part of model IDs, API versions,
23748
+ * paths and branch names) are deliberately not matched.
23749
+ */
23750
+ const FREE_TEXT_DATE = new RegExp([
23751
+ String.raw`\b${MONTH}\s+${DAY}(?:,?\s+${YEAR})?${RANGE_SEP}${MONTH}\s+${DAY},?\s+${YEAR}${TIME}?`,
23752
+ String.raw`\b${MONTH}\s+${DAY}${RANGE_SEP}${DAY},?\s+${YEAR}`,
23753
+ String.raw`\b${MONTH}\s+${DAY},?\s+${YEAR}${TIME}?`,
23754
+ String.raw`\b${QUALIFIER}?${MONTH}(?:\s+${YEAR})?${RANGE_SEP}${QUALIFIER}?${MONTH}\s+${YEAR}`,
23755
+ String.raw`\b${QUALIFIER}?${MONTH}\s+${YEAR}`
23756
+ ].join("|"), "g");
23757
+ const ENDPOINT = new RegExp(String.raw`^(?:(early|mid|late)\s*)?(?:(${MONTH})\s*)?(?:(\d{1,2})(?:st|nd|rd|th)?)?,?\s*(?:(${YEAR}))?$`, "i");
23758
+ function parseEndpoint(text) {
23759
+ const match = ENDPOINT.exec(text.trim());
23760
+ if (!match || !match[0]) return null;
23761
+ const [, qualifier, month, day, year] = match;
23762
+ return {
23763
+ ...qualifier ? { qualifier: qualifier.toLowerCase() } : {},
23764
+ ...month ? { month: MONTH_INDEX[month.slice(0, 3).toLowerCase()] } : {},
23765
+ ...day ? { day: Number(day) } : {},
23766
+ ...year ? { year: Number(year) } : {}
23767
+ };
23768
+ }
23769
+ const QUALIFIER_DAY = {
23770
+ early: 7,
23771
+ mid: 15,
23772
+ late: 23
23773
+ };
23774
+ const QUALIFIER_MONTH = {
23775
+ early: [1, 15],
23776
+ mid: [6, 1],
23777
+ late: [10, 15]
23778
+ };
23779
+ /** Earliest and latest local day an endpoint can mean; null when it has no year or is not a real date. */
23780
+ function endpointBounds(date) {
23781
+ const { qualifier, month, day, year } = date;
23782
+ if (year === void 0) return null;
23783
+ if (month === void 0) {
23784
+ if (day !== void 0) return null;
23785
+ if (qualifier) {
23786
+ const [m, d] = QUALIFIER_MONTH[qualifier];
23787
+ return [new Date(year, m, d), new Date(year, m, d)];
23788
+ }
23789
+ return [new Date(year, 0, 1), new Date(year, 11, 31)];
23790
+ }
23791
+ if (day !== void 0) {
23792
+ const point = new Date(year, month, day);
23793
+ if (point.getMonth() !== month) return null;
23794
+ return [point, point];
23795
+ }
23796
+ if (qualifier) {
23797
+ const point = new Date(year, month, QUALIFIER_DAY[qualifier]);
23798
+ return [point, point];
23799
+ }
23800
+ return [new Date(year, month, 1), new Date(year, month + 1, 0)];
23801
+ }
23802
+ /** Parses one alternative: a single date or a two-ended range, filling each end's missing parts from the other. */
23803
+ function parseSpanAlternative(text) {
23804
+ const ends = text.split(new RegExp(`${RANGE_SEP}(?=\\S)`)).filter(Boolean);
23805
+ if (ends.length === 0 || ends.length > 2) return null;
23806
+ const parsed = ends.map(parseEndpoint);
23807
+ if (parsed.some((end) => end === null)) return null;
23808
+ const [first, second = first] = parsed;
23809
+ const from = { ...first };
23810
+ const to = { ...second };
23811
+ const fromYearInferred = from.year === void 0;
23812
+ const toYearInferred = to.year === void 0;
23813
+ from.year ??= to.year;
23814
+ to.year ??= from.year;
23815
+ if (from.month === void 0 && (from.day !== void 0 || from.qualifier) && to.month !== void 0) from.month = to.month;
23816
+ if (to.month === void 0 && to.day !== void 0 && from.month !== void 0) to.month = from.month;
23817
+ if (from.month !== void 0 && to.month !== void 0 && from.month > to.month && from.year === to.year) {
23818
+ if (fromYearInferred) from.year -= 1;
23819
+ else if (toYearInferred) to.year += 1;
23820
+ }
23821
+ const fromBounds = endpointBounds(from);
23822
+ const toBounds = endpointBounds(to);
23823
+ if (!fromBounds || !toBounds) return null;
23824
+ const span = {
23825
+ start: fromBounds[0],
23826
+ end: toBounds[1]
23827
+ };
23828
+ return span.start <= span.end ? span : null;
23829
+ }
23830
+ function normalizeDateText(text) {
23831
+ return text.replace(/\s+/g, " ").trim().replace(/^(?:approx(?:\.|imately)?|about|around|circa|c\.|~)\s*/i, "").replace(/^(?:by|in|until|before|after)\s+/i, "").replace(/\b(?:the\s+)?end\s+of\s+/gi, "late ").replace(/\b(?:the\s+)?(?:start|beginning)\s+of\s+/gi, "early ").replace(/\b(\d{4})-(\d{2})-(\d{2})\b/g, (whole, y, m, d) => {
23832
+ const month = Object.keys(MONTH_INDEX)[Number(m) - 1];
23833
+ return month ? `${month} ${Number(d)}, ${y}` : whole;
23834
+ }).replace(new RegExp(TIME, "g"), "").replace(/\b(early|mid|late|to)-/gi, "$1 ").replace(/ {2,}/g, " ").trim();
23835
+ }
23463
23836
  /**
23464
- * Parses a date string like "May 30, 2023", "May 27-28, 2023", "late April 2023", etc.
23465
- * Returns the parsed Date or null if unparseable.
23837
+ * Parses the whole of `text` as a date or date range. Accepts "May 30, 2023",
23838
+ * "Mar 22, 2025 at 18:08", "May 27-28, 2023", "Dec 27–31, 2021", "Mar 1 - Mar 18, 2025",
23839
+ * "Aug 1, 2024 - Feb 28, 2025", "August 2024", "June–July 2022", "late April 2023",
23840
+ * "mid-to-late May 2023", "late 2023", "2035", "2021–2026", an "approx." prefix, and
23841
+ * "A or B" alternatives (their union). Returns null when no year is known.
23466
23842
  */
23467
- function parseDateFromContent(dateContent) {
23468
- let targetDate = null;
23469
- const simpleDateMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})/);
23470
- if (simpleDateMatch) {
23471
- const parsed = /* @__PURE__ */ new Date(`${simpleDateMatch[1]} ${simpleDateMatch[2]}, ${simpleDateMatch[3]}`);
23472
- if (!isNaN(parsed.getTime())) targetDate = parsed;
23473
- }
23474
- if (!targetDate) {
23475
- const rangeMatch = dateContent.match(/([A-Z][a-z]+)\s+(\d{1,2})-\d{1,2},?\s+(\d{4})/);
23476
- if (rangeMatch) {
23477
- const parsed = /* @__PURE__ */ new Date(`${rangeMatch[1]} ${rangeMatch[2]}, ${rangeMatch[3]}`);
23478
- if (!isNaN(parsed.getTime())) targetDate = parsed;
23479
- }
23480
- }
23481
- if (!targetDate) {
23482
- const vagueMatch = dateContent.match(/(late|early|mid)[- ]?(?:to[- ]?(?:late|early|mid)[- ]?)?([A-Z][a-z]+)\s+(\d{4})/i);
23483
- if (vagueMatch) {
23484
- const month = vagueMatch[2];
23485
- const year = vagueMatch[3];
23486
- const modifier = vagueMatch[1].toLowerCase();
23487
- let day = 15;
23488
- if (modifier === "early") day = 7;
23489
- if (modifier === "late") day = 23;
23490
- const parsed = /* @__PURE__ */ new Date(`${month} ${day}, ${year}`);
23491
- if (!isNaN(parsed.getTime())) targetDate = parsed;
23492
- }
23493
- }
23494
- if (!targetDate) {
23495
- const crossMonthMatch = dateContent.match(/([A-Z][a-z]+)\s+to\s+(?:early\s+)?([A-Z][a-z]+)\s+(\d{4})/i);
23496
- if (crossMonthMatch) {
23497
- const parsed = /* @__PURE__ */ new Date(`${crossMonthMatch[2]} 1, ${crossMonthMatch[3]}`);
23498
- if (!isNaN(parsed.getTime())) targetDate = parsed;
23499
- }
23500
- }
23501
- return targetDate;
23843
+ function parseDateSpan(text) {
23844
+ const alternatives = normalizeDateText(text).split(/\s+or\s+/i).map(parseSpanAlternative);
23845
+ if (alternatives.length === 0 || alternatives.some((span) => span === null)) return null;
23846
+ const spans = alternatives;
23847
+ return {
23848
+ start: new Date(Math.min(...spans.map((span) => span.start.getTime()))),
23849
+ end: new Date(Math.max(...spans.map((span) => span.end.getTime())))
23850
+ };
23851
+ }
23852
+ const QUALIFIED_YEAR = new RegExp(String.raw`\b(?:by|in|until|before|after|(?:the\s+)?(?:early|mid|late|end\s+of|start\s+of|beginning\s+of))\s+${YEAR}`, "gi");
23853
+ /**
23854
+ * Like `parseDateSpan`, but falls back to the first dated expression inside longer text,
23855
+ * then to a year on its own ("by late 2027", "$500 in 2024"). Only for text known to be a date.
23856
+ */
23857
+ function findDateSpan(text) {
23858
+ const whole = parseDateSpan(text);
23859
+ if (whole) return whole;
23860
+ for (const pattern of [FREE_TEXT_DATE, QUALIFIED_YEAR]) for (const match of text.matchAll(pattern)) {
23861
+ const span = parseDateSpan(match[0]);
23862
+ if (span) return span;
23863
+ }
23864
+ return null;
23865
+ }
23866
+ function relativeSpan(span, today) {
23867
+ const start = formatRelativeDays(today - calendarDay(span.start));
23868
+ const end = formatRelativeDays(today - calendarDay(span.end));
23869
+ if (start === end) return start;
23870
+ if (!end.startsWith("in ")) return `${start} to ${end}`;
23871
+ return start.startsWith("in ") ? `${start} to ${end.slice(3)}` : `${start} to ${end.slice(3)} from now`;
23502
23872
  }
23503
23873
  /**
23504
23874
  * Detects if an observation line indicates future intent (will do, plans to, looking forward to, etc.)
@@ -23518,40 +23888,59 @@ function isFutureIntentObservation(line) {
23518
23888
  }
23519
23889
  /**
23520
23890
  * Expand inline estimated dates with relative time.
23521
- * Matches patterns like "(estimated May 27-28, 2023)" or "(meaning May 30, 2023)"
23522
- * and expands them to "(meaning May 30, 2023 - which was 3 weeks ago)"
23891
+ * Matches patterns like "(estimated May 27-28, 2023)" or "(meaning May 30, 2023 at 3:00 PM)"
23892
+ * and expands them to "(meaning May 30, 2023 - 3 weeks ago)". Notes without a year are left alone.
23523
23893
  */
23524
- function expandInlineEstimatedDates(observations, currentDate) {
23525
- return observations.replace(/\((estimated|meaning)\s+([^)]+\d{4})\)/gi, (match, prefix, dateContent, offset) => {
23526
- const targetDate = parseDateFromContent(dateContent);
23527
- if (targetDate) {
23528
- const relative = formatRelativeTime(targetDate, currentDate);
23529
- const lineStart = observations.lastIndexOf("\n", offset) + 1;
23530
- const lineBeforeDate = observations.slice(lineStart, offset);
23531
- const isPastDate = targetDate < currentDate;
23532
- const isFutureIntent = isFutureIntentObservation(lineBeforeDate);
23533
- if (isPastDate && isFutureIntent) return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
23534
- return `(${prefix} ${dateContent} - ${relative})`;
23535
- }
23536
- return match;
23894
+ function expandInlineEstimatedDates(observations, currentDate, timeZone) {
23895
+ const inlineDateRegex = /\((estimated|meaning)\s([^()]*)\)/gi;
23896
+ const today = calendarDayIn(currentDate, timeZone);
23897
+ return observations.replace(inlineDateRegex, (match, prefix, noteText, offset) => {
23898
+ const dateContent = noteText.trimStart();
23899
+ const span = findDateSpan(dateContent);
23900
+ if (!span) return match;
23901
+ const relative = relativeSpan(span, today);
23902
+ const lineStart = observations.lastIndexOf("\n", offset) + 1;
23903
+ const lineBeforeDate = observations.slice(lineStart, offset);
23904
+ if (calendarDay(span.end) < today && isFutureIntentObservation(lineBeforeDate)) return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
23905
+ return `(${prefix} ${dateContent} - ${relative})`;
23906
+ });
23907
+ }
23908
+ /**
23909
+ * Annotates dates written into observation text, e.g. "exam on January 10, 2024" becomes
23910
+ * "exam on January 10, 2024 (5 months ago)". Only dates that state their year are annotated;
23911
+ * "Date:" headers and "(meaning/estimated …)" notes are handled separately and skipped here.
23912
+ */
23913
+ function annotateObservationTextDates(observations, currentDate, timeZone) {
23914
+ const regex = new RegExp(String.raw`^Date:.*$|\((?:[Ee]stimated|[Mm]eaning)\b[^()]*\)|${FREE_TEXT_DATE.source}`, "gm");
23915
+ const today = calendarDayIn(currentDate, timeZone);
23916
+ return observations.replace(regex, (match, offset) => {
23917
+ if (match.startsWith("Date:") || match.startsWith("(")) return match;
23918
+ const span = parseDateSpan(match);
23919
+ if (!span) return match;
23920
+ const relative = relativeSpan(span, today);
23921
+ return observations[offset + match.length] === ")" ? `${match} - ${relative}` : `${match} (${relative})`;
23537
23922
  });
23538
23923
  }
23539
23924
  /**
23540
23925
  * Add relative time annotations to observations.
23541
- * Transforms "Date: May 15, 2023" headers to "Date: May 15, 2023 (5 days ago)"
23542
- * and expands inline estimated dates with relative time context.
23926
+ * Transforms "Date: May 15, 2023" headers to "Date: May 15, 2023 (5 days ago)", range headers such as
23927
+ * "Date: Aug 1, 2024 - Feb 28, 2025" to "(7 months ago to 4 weeks ago)", and annotates inline dates.
23928
+ *
23929
+ * `timeZone` is the zone the Observer wrote its dates in (the record's `observedTimezone`). "Today" is
23930
+ * `currentDate`'s calendar date in that zone, so the result doesn't depend on the zone of the process rendering it.
23543
23931
  */
23544
- function addRelativeTimeToObservations(observations, currentDate) {
23545
- const withInlineDates = expandInlineEstimatedDates(observations, currentDate);
23546
- const dateHeaderRegex = /^(Date:\s*)([A-Z][a-z]+ \d{1,2}, \d{4})$/gm;
23932
+ function addRelativeTimeToObservations(observations, currentDate, timeZone) {
23933
+ const today = calendarDayIn(currentDate, timeZone);
23934
+ const withInlineDates = annotateObservationTextDates(expandInlineEstimatedDates(observations, currentDate, timeZone), currentDate, timeZone);
23935
+ const dateHeaderRegex = /^(Date:[ \t]*)(.*)$/gm;
23547
23936
  const dates = [];
23548
23937
  let regexMatch;
23549
23938
  while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
23550
- const dateStr = regexMatch[2];
23551
- const parsed = new Date(dateStr);
23552
- if (!isNaN(parsed.getTime())) dates.push({
23939
+ const dateStr = regexMatch[2].trimEnd();
23940
+ const span = parseDateSpan(dateStr);
23941
+ if (span) dates.push({
23553
23942
  index: regexMatch.index,
23554
- date: parsed,
23943
+ span,
23555
23944
  match: regexMatch[0],
23556
23945
  prefix: regexMatch[1],
23557
23946
  dateStr
@@ -23565,11 +23954,10 @@ function addRelativeTimeToObservations(observations, currentDate) {
23565
23954
  const prev = i > 0 ? dates[i - 1] : null;
23566
23955
  result += withInlineDates.slice(lastIndex, curr.index);
23567
23956
  if (prev) {
23568
- const gap = formatGapBetweenDates(prev.date, curr.date);
23957
+ const gap = formatGapBetweenDates(prev.span.end, curr.span.start);
23569
23958
  if (gap) result += `\n${gap}\n\n`;
23570
23959
  }
23571
- const relative = formatRelativeTime(curr.date, currentDate);
23572
- result += `${curr.prefix}${curr.dateStr} (${relative})`;
23960
+ result += `${curr.prefix}${curr.dateStr} (${relativeSpan(curr.span, today)})`;
23573
23961
  lastIndex = curr.index + curr.match.length;
23574
23962
  }
23575
23963
  result += withInlineDates.slice(lastIndex);
@@ -24574,11 +24962,20 @@ const OBSERVER_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
24574
24962
  "heif",
24575
24963
  "avif"
24576
24964
  ]);
24577
- function formatObserverDate(createdAt) {
24578
- return createdAt ? `${createdAt.toLocaleDateString("en-US", { month: "short" })} ${createdAt.getDate()} ${createdAt.getFullYear()}` : "";
24965
+ function formatObserverDate(createdAt, timeZone) {
24966
+ if (!createdAt) return "";
24967
+ const parts = new Intl.DateTimeFormat("en-US", {
24968
+ timeZone,
24969
+ year: "numeric",
24970
+ month: "short",
24971
+ day: "numeric"
24972
+ }).formatToParts(createdAt);
24973
+ const part = (type) => parts.find((p) => p.type === type)?.value;
24974
+ return `${part("month")} ${part("day")} ${part("year")}`;
24579
24975
  }
24580
- function formatObserverTime(createdAt) {
24976
+ function formatObserverTime(createdAt, timeZone) {
24581
24977
  return createdAt ? createdAt.toLocaleTimeString("en-US", {
24978
+ timeZone,
24582
24979
  hour: "numeric",
24583
24980
  minute: "2-digit",
24584
24981
  hour12: true
@@ -24668,11 +25065,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
24668
25065
  const label = resolveObserverAttachmentLabel(part);
24669
25066
  return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
24670
25067
  }
24671
- function isRecord$3(value) {
25068
+ function isRecord$2(value) {
24672
25069
  return !!value && typeof value === "object";
24673
25070
  }
24674
25071
  function mapToolResultBlockToAttachment(block) {
24675
- if (!isRecord$3(block) || typeof block.type !== "string") return;
25072
+ if (!isRecord$2(block) || typeof block.type !== "string") return;
24676
25073
  const mediaType = typeof block.mediaType === "string" ? block.mediaType : void 0;
24677
25074
  const filename = typeof block.filename === "string" ? block.filename : void 0;
24678
25075
  switch (block.type) {
@@ -24733,7 +25130,7 @@ function mapToolResultBlockToAttachment(block) {
24733
25130
  }
24734
25131
  }
24735
25132
  function extractToolResultAttachments(result, counter, attachmentFilter) {
24736
- if (!isRecord$3(result) || result.type !== "content" || !Array.isArray(result.value)) return {
25133
+ if (!isRecord$2(result) || result.type !== "content" || !Array.isArray(result.value)) return {
24737
25134
  resultWithoutAttachments: result,
24738
25135
  attachments: []
24739
25136
  };
@@ -24747,7 +25144,7 @@ function extractToolResultAttachments(result, counter, attachmentFilter) {
24747
25144
  if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) attachments.push(toObserverInputAttachmentPart(attachment));
24748
25145
  const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
24749
25146
  return {
24750
- type: isRecord$3(block) ? block.type : void 0,
25147
+ type: isRecord$2(block) ? block.type : void 0,
24751
25148
  placeholder
24752
25149
  };
24753
25150
  });
@@ -24864,6 +25261,7 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
24864
25261
  const maxLen = options?.maxPartLength;
24865
25262
  const maxToolResultTokens = options?.maxToolResultTokens ?? 5e3;
24866
25263
  const attachmentFilter = options?.attachmentFilter;
25264
+ const timeZone = resolveTimeZone(options?.timeZone);
24867
25265
  const role = getObserverMessageLabel(msg);
24868
25266
  const attachments = [];
24869
25267
  const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
@@ -24873,8 +25271,8 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
24873
25271
  if (!body) return;
24874
25272
  const normalizedCreatedAt = normalizeObserverCreatedAt(createdAt) ?? messageCreatedAt;
24875
25273
  lines.push({
24876
- date: formatObserverDate(normalizedCreatedAt),
24877
- time: formatObserverTime(normalizedCreatedAt),
25274
+ date: formatObserverDate(normalizedCreatedAt, timeZone),
25275
+ time: formatObserverTime(normalizedCreatedAt, timeZone),
24878
25276
  title,
24879
25277
  body
24880
25278
  });
@@ -25427,169 +25825,6 @@ function optimizeObservationsForContext(observations) {
25427
25825
  optimized = optimized.replace(/\n{3,}/g, "\n\n");
25428
25826
  return optimized.trim();
25429
25827
  }
25430
- /**
25431
- * Retry knobs for the internal OM transport-error retry wrapper.
25432
- * Exported as a mutable object so tests can shrink the backoff schedule
25433
- * without changing public API.
25434
- *
25435
- * With the defaults the per-retry pre-jitter backoff schedule is:
25436
- * 1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s (cap)
25437
- * giving 8 retries / 9 total attempts and ~247s (~4 minutes) of waiting
25438
- * before the final attempt fails. Designed to ride out short provider /
25439
- * network blips without holding the actor turn for much longer than that.
25440
- *
25441
- * @internal
25442
- */
25443
- const RETRY_CONFIG = {
25444
- /** Maximum number of retry *attempts* (total tries = maxRetries + 1). */
25445
- maxRetries: 8,
25446
- /** Initial backoff delay in milliseconds. */
25447
- initialDelayMs: 1e3,
25448
- /** Multiplier applied to the delay after each failed attempt. */
25449
- backoffFactor: 2,
25450
- /** Cap on per-attempt delay (ms). */
25451
- maxDelayMs: 12e4,
25452
- /** Random jitter as a fraction of the computed delay (e.g. 0.2 = ±20%). */
25453
- jitter: .2
25454
- };
25455
- const TRANSIENT_MESSAGE_SUBSTRINGS = [
25456
- "terminated",
25457
- "fetch failed",
25458
- "econnreset",
25459
- "econnrefused",
25460
- "enotfound",
25461
- "eai_again",
25462
- "socket hang up",
25463
- "network error",
25464
- "request timed out",
25465
- "request timeout",
25466
- "connection reset",
25467
- "connection closed"
25468
- ];
25469
- function isRecord$2(value) {
25470
- return typeof value === "object" && value !== null;
25471
- }
25472
- function isAbortError$5(error) {
25473
- if (!isRecord$2(error)) return false;
25474
- if (error.name === "AbortError") return true;
25475
- if (typeof error.code === "string" && error.code === "ABORT_ERR") return true;
25476
- return false;
25477
- }
25478
- function hasTransientMessage(value) {
25479
- if (!isRecord$2(value)) return false;
25480
- const message = typeof value.message === "string" ? value.message.toLowerCase() : "";
25481
- if (message && TRANSIENT_MESSAGE_SUBSTRINGS.some((sub) => message.includes(sub))) return true;
25482
- if (typeof value.code === "string" && value.code.toUpperCase().startsWith("UND_ERR_")) return true;
25483
- return false;
25484
- }
25485
- function isRetryableHttpStatus(status) {
25486
- if (status === 408 || status === 425 || status === 429) return true;
25487
- return status >= 500 && status <= 599;
25488
- }
25489
- function hasRetryableHttpStatus(value) {
25490
- if (!isRecord$2(value)) return false;
25491
- if (typeof value.statusCode === "number" && isRetryableHttpStatus(value.statusCode)) return true;
25492
- if (typeof value.code === "number" && isRetryableHttpStatus(value.code)) return true;
25493
- return false;
25494
- }
25495
- function hasIsRetryableFlag(value) {
25496
- if (!isRecord$2(value)) return false;
25497
- return value.isRetryable === true;
25498
- }
25499
- /**
25500
- * Returns true when the given error looks like a transient transport-class
25501
- * failure that's worth retrying — undici `terminated`, `fetch failed`,
25502
- * `UND_ERR_*` codes, AI SDK `APICallError` with `isRetryable: true`, and
25503
- * common HTTP 408/425/429/5xx statuses. Walks the `error.cause` chain so
25504
- * wrapper errors don't hide the real failure.
25505
- *
25506
- * Never retries on user-initiated aborts.
25507
- *
25508
- * @internal
25509
- */
25510
- function isTransientLLMError(error) {
25511
- if (isAbortError$5(error)) return false;
25512
- const visited = /* @__PURE__ */ new WeakSet();
25513
- function visit(candidate) {
25514
- if (isRecord$2(candidate)) {
25515
- if (visited.has(candidate)) return false;
25516
- visited.add(candidate);
25517
- }
25518
- if (hasTransientMessage(candidate)) return true;
25519
- if (hasRetryableHttpStatus(candidate)) return true;
25520
- if (hasIsRetryableFlag(candidate)) return true;
25521
- if (isRecord$2(candidate)) {
25522
- if (visit(candidate.cause)) return true;
25523
- if (visit(candidate.error)) return true;
25524
- }
25525
- return false;
25526
- }
25527
- return visit(error);
25528
- }
25529
- /**
25530
- * Compute the backoff delay (ms) for the Nth retry (0-indexed).
25531
- *
25532
- * Exponential growth (`initialDelayMs * backoffFactor^attempt`) capped at
25533
- * `maxDelayMs`, then nudged by ±`jitter` (fractional). Exported for unit
25534
- * tests that lock the schedule against drift.
25535
- *
25536
- * @internal
25537
- */
25538
- function computeDelay(attempt) {
25539
- const base = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, attempt);
25540
- const capped = Math.min(base, RETRY_CONFIG.maxDelayMs);
25541
- if (RETRY_CONFIG.jitter <= 0) return capped;
25542
- const jitterRange = capped * RETRY_CONFIG.jitter;
25543
- const offset = (Math.random() * 2 - 1) * jitterRange;
25544
- return Math.max(0, Math.round(capped + offset));
25545
- }
25546
- function sleep(ms, abortSignal) {
25547
- if (ms <= 0) return Promise.resolve();
25548
- return new Promise((resolve, reject) => {
25549
- if (abortSignal?.aborted) {
25550
- reject(/* @__PURE__ */ new Error("The operation was aborted."));
25551
- return;
25552
- }
25553
- const timer = setTimeout(() => {
25554
- abortSignal?.removeEventListener("abort", onAbort);
25555
- resolve();
25556
- }, ms);
25557
- const onAbort = () => {
25558
- clearTimeout(timer);
25559
- abortSignal?.removeEventListener("abort", onAbort);
25560
- reject(/* @__PURE__ */ new Error("The operation was aborted."));
25561
- };
25562
- abortSignal?.addEventListener("abort", onAbort, { once: true });
25563
- });
25564
- }
25565
- /**
25566
- * Run `fn` with retries on transient transport-class errors.
25567
- *
25568
- * Non-transient errors (auth, validation, schema, etc.) are rethrown
25569
- * immediately. User-initiated aborts are rethrown without delay.
25570
- *
25571
- * @internal
25572
- */
25573
- async function withRetry(fn, opts) {
25574
- const { label, abortSignal } = opts;
25575
- let attempt = 0;
25576
- while (true) {
25577
- if (abortSignal?.aborted) throw new Error("The operation was aborted.");
25578
- try {
25579
- return await fn();
25580
- } catch (error) {
25581
- if (isAbortError$5(error) || abortSignal?.aborted) throw error;
25582
- if (attempt >= RETRY_CONFIG.maxRetries || !isTransientLLMError(error)) {
25583
- if (attempt > 0) omDebug(`[OM:retry:${label}] giving up after ${attempt} retry/retries: ${error instanceof Error ? error.message : String(error)}`);
25584
- throw error;
25585
- }
25586
- const delay = computeDelay(attempt);
25587
- attempt++;
25588
- omDebug(`[OM:retry:${label}] transient error on attempt ${attempt}, retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`);
25589
- await sleep(delay, abortSignal);
25590
- }
25591
- }
25592
- }
25593
25828
  function createTemporaryOmMemoryContext(prefix) {
25594
25829
  const options = {
25595
25830
  thread: `${prefix}-${(0, crypto$1.randomUUID)()}`,
@@ -25724,11 +25959,27 @@ var ObserverRunner = class {
25724
25959
  this.mastra = mastra;
25725
25960
  }
25726
25961
  createAgent(model, isMultiThread = false, memory, extractors = this.observationConfig.extractors ?? []) {
25962
+ let agentModel = model;
25963
+ if (Array.isArray(agentModel)) agentModel = agentModel.map((fallback) => ({
25964
+ ...fallback,
25965
+ maxRetries: 0
25966
+ }));
25967
+ else if (typeof agentModel === "function") {
25968
+ const resolveDynamicModel = agentModel;
25969
+ agentModel = (async (args) => {
25970
+ const resolvedModel = await resolveDynamicModel(args);
25971
+ return Array.isArray(resolvedModel) ? resolvedModel.map((fallback) => ({
25972
+ ...fallback,
25973
+ maxRetries: 0
25974
+ })) : resolvedModel;
25975
+ });
25976
+ }
25727
25977
  return new _mastra_core_agent.Agent({
25728
25978
  id: isMultiThread ? "multi-thread-observer" : "observational-memory-observer",
25729
25979
  name: isMultiThread ? "multi-thread-observer" : "Observer",
25980
+ maxRetries: 0,
25730
25981
  instructions: buildObserverSystemPrompt(isMultiThread, this.observationConfig.instruction, this.observationConfig.threadTitle, extractors),
25731
- model,
25982
+ model: agentModel,
25732
25983
  ...memory ? { memory } : {},
25733
25984
  ...this.mastra ? { mastra: this.mastra } : {}
25734
25985
  });
@@ -25820,7 +26071,10 @@ var ObserverRunner = class {
25820
26071
  ...options,
25821
26072
  includeThreadTitle: this.observationConfig.threadTitle,
25822
26073
  extractors: activeExtractors
25823
- }, { attachmentFilter })];
26074
+ }, {
26075
+ attachmentFilter,
26076
+ timeZone: options?.timeZone
26077
+ })];
25824
26078
  const doGenerate = async () => {
25825
26079
  return withRetry(() => withOmTracingSpan({
25826
26080
  phase: "observer",
@@ -25858,12 +26112,14 @@ var ObserverRunner = class {
25858
26112
  hasRequestContext: Boolean(internalRequestContext),
25859
26113
  aborted: abortSignal?.aborted ?? false
25860
26114
  });
25861
- throw error;
26115
+ if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
26116
+ throw new OmModelExecutionError("observer-model", error);
25862
26117
  }
25863
26118
  }, abortSignal)
25864
26119
  }), {
25865
26120
  label: "observer",
25866
- abortSignal
26121
+ abortSignal,
26122
+ maxRetries: this.observationConfig.maxRetries
25867
26123
  });
25868
26124
  };
25869
26125
  let result = await doGenerate();
@@ -25931,7 +26187,7 @@ var ObserverRunner = class {
25931
26187
  /**
25932
26188
  * Call the Observer agent for multiple threads in a single batched request.
25933
26189
  */
25934
- async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext) {
26190
+ async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext, timeZone) {
25935
26191
  const contextFor = (threadId) => ({
25936
26192
  threadId,
25937
26193
  ...hookContext
@@ -25942,7 +26198,7 @@ var ObserverRunner = class {
25942
26198
  if (filtered.length > 0) messagesByThread.set(threadId, filtered);
25943
26199
  }
25944
26200
  const threadOrder = allThreadOrder.filter((threadId) => messagesByThread.has(threadId));
25945
- const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model);
26201
+ const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone);
25946
26202
  for (const threadId of allThreadOrder) {
25947
26203
  const threadResult = output.results.get(threadId);
25948
26204
  if (!threadResult) {
@@ -25953,7 +26209,7 @@ var ObserverRunner = class {
25953
26209
  }
25954
26210
  return output;
25955
26211
  }
25956
- async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model) {
26212
+ async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone) {
25957
26213
  if (threadOrder.length === 0) return { results: /* @__PURE__ */ new Map() };
25958
26214
  const inputTokens = Array.from(messagesByThread.values()).reduce((total, messages) => total + this.tokenCounter.countMessages(messages), 0);
25959
26215
  const resolvedModel = (() => {
@@ -25995,7 +26251,8 @@ var ObserverRunner = class {
25995
26251
  priorSuggestedResponse: priorMetadataByThread?.get(threadId)?.suggestedResponse,
25996
26252
  priorThreadTitle: priorMetadataByThread?.get(threadId)?.threadTitle,
25997
26253
  priorExtractedValues: priorMetadataByThread?.get(threadId)?.extracted,
25998
- model: resolvedModel.model
26254
+ model: resolvedModel.model,
26255
+ timeZone
25999
26256
  });
26000
26257
  results.set(threadId, {
26001
26258
  observations: threadResult.observations,
@@ -26012,6 +26269,7 @@ var ObserverRunner = class {
26012
26269
  totalUsage.totalTokens += threadResult.usage.totalTokens ?? 0;
26013
26270
  }
26014
26271
  }
26272
+ for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
26015
26273
  return {
26016
26274
  results,
26017
26275
  usage: totalUsage
@@ -26020,8 +26278,10 @@ var ObserverRunner = class {
26020
26278
  const agent = this.createAgent(resolvedModel.model, true, void 0, activeExtractors);
26021
26279
  const internalRequestContext = withOmInternalThreadId(requestContext, agent.id);
26022
26280
  const multiThreadAttachmentFilter = this.resolveAttachmentFilter(resolvedModel.model, requestContext);
26023
- const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, void 0, this.observationConfig.threadTitle, activeExtractors, { attachmentFilter: multiThreadAttachmentFilter })];
26024
- for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
26281
+ const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, void 0, this.observationConfig.threadTitle, activeExtractors, {
26282
+ attachmentFilter: multiThreadAttachmentFilter,
26283
+ timeZone
26284
+ })];
26025
26285
  const doGenerate = async () => {
26026
26286
  return withRetry(() => withOmTracingSpan({
26027
26287
  phase: "observer-multi-thread",
@@ -26059,12 +26319,14 @@ var ObserverRunner = class {
26059
26319
  hasRequestContext: Boolean(internalRequestContext),
26060
26320
  aborted: abortSignal?.aborted ?? false
26061
26321
  });
26062
- throw error;
26322
+ if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
26323
+ throw new OmModelExecutionError("observer-model", error);
26063
26324
  }
26064
26325
  }, abortSignal)
26065
26326
  }), {
26066
26327
  label: "observer-multi-thread",
26067
- abortSignal
26328
+ abortSignal,
26329
+ maxRetries: this.observationConfig.maxRetries
26068
26330
  });
26069
26331
  };
26070
26332
  let result = await doGenerate();
@@ -26120,6 +26382,7 @@ var ObserverRunner = class {
26120
26382
  });
26121
26383
  }
26122
26384
  for (const threadId of threadOrder) if (!results.has(threadId)) results.set(threadId, { observations: "" });
26385
+ for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
26123
26386
  const usage = result.totalUsage ?? result.usage;
26124
26387
  return {
26125
26388
  results,
@@ -27607,6 +27870,8 @@ async function summarizeConversation(opts) {
27607
27870
  const runner = new ObserverRunner({
27608
27871
  observationConfig: {
27609
27872
  model: opts.model,
27873
+ maxRetries: 8,
27874
+ failurePolicy: "abort",
27610
27875
  messageTokens: OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens,
27611
27876
  shareTokenBudget: false,
27612
27877
  modelSettings: { ...OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings },
@@ -29417,6 +29682,7 @@ function createObservationFailedMarker(params) {
29417
29682
  durationMs,
29418
29683
  tokensAttempted: params.tokensAttempted,
29419
29684
  error: formatOmError(params.error),
29685
+ ...getOmFailureMetadata(params.error, params.failurePolicy ?? "abort"),
29420
29686
  recordId: params.recordId,
29421
29687
  threadId: params.threadId
29422
29688
  }
@@ -29478,6 +29744,7 @@ function createBufferingFailedMarker(params) {
29478
29744
  durationMs,
29479
29745
  tokensAttempted: params.tokensAttempted,
29480
29746
  error: formatOmError(params.error),
29747
+ ...getOmFailureMetadata(params.error, params.failurePolicy ?? "abort"),
29481
29748
  recordId: params.recordId,
29482
29749
  threadId: params.threadId
29483
29750
  }
@@ -29532,7 +29799,10 @@ function createThreadUpdateMarker(params) {
29532
29799
  function findLastCompletedObservationBoundary(message) {
29533
29800
  const parts = message.content?.parts;
29534
29801
  if (!parts || !Array.isArray(parts)) return -1;
29535
- for (let i = parts.length - 1; i >= 0; i--) if (parts[i]?.type === "data-om-observation-end") return i;
29802
+ for (let i = parts.length - 1; i >= 0; i--) {
29803
+ const part = parts[i];
29804
+ if (part?.type === "data-om-observation-end" && part.data?.operationType !== "reflection") return i;
29805
+ }
29536
29806
  return -1;
29537
29807
  }
29538
29808
  /**
@@ -29782,6 +30052,7 @@ function scanObservationGroupTags(observations) {
29782
30052
  const incompleteOpenings = [];
29783
30053
  let cursor = 0;
29784
30054
  let closeFloor = 0;
30055
+ let nestedFloor = -1;
29785
30056
  while (cursor < observations.length) {
29786
30057
  const start = observations.indexOf(OBSERVATION_GROUP_OPEN, cursor);
29787
30058
  if (start === -1) break;
@@ -29802,8 +30073,12 @@ function scanObservationGroupTags(observations) {
29802
30073
  cursor = openEnd + 1;
29803
30074
  continue;
29804
30075
  }
29805
- const nestedStart = observations.indexOf(OBSERVATION_GROUP_OPEN, openEnd + 1);
29806
- if (nestedStart !== -1 && nestedStart < closeStart && isLineStart(observations, nestedStart)) {
30076
+ if (nestedFloor <= openEnd) {
30077
+ const lineOpen = observations.indexOf(`\n${OBSERVATION_GROUP_OPEN}`, openEnd);
30078
+ nestedFloor = lineOpen === -1 ? observations.length : lineOpen + 1;
30079
+ }
30080
+ const nestedStart = nestedFloor;
30081
+ if (nestedStart < closeStart) {
29807
30082
  incompleteOpenings.push({
29808
30083
  start,
29809
30084
  end: openEnd + 1
@@ -30186,6 +30461,7 @@ var ObservationStrategy = class ObservationStrategy {
30186
30461
  observationTokens: processed.observationTokens,
30187
30462
  threadId,
30188
30463
  writer,
30464
+ messageList: this.opts.messageList,
30189
30465
  abortSignal,
30190
30466
  mainAgent: this.opts.agent,
30191
30467
  sendSignal: this.opts.sendSignal,
@@ -30210,6 +30486,7 @@ var ObservationStrategy = class ObservationStrategy {
30210
30486
  operationType: "observation",
30211
30487
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
30212
30488
  error: formatOmError(error),
30489
+ ...getOmFailureMetadata(error, this.observationConfig.failurePolicy),
30213
30490
  recordId: record.id,
30214
30491
  threadId
30215
30492
  }
@@ -30223,6 +30500,10 @@ var ObservationStrategy = class ObservationStrategy {
30223
30500
  };
30224
30501
  }
30225
30502
  omError("[OM] Observation failed", error);
30503
+ if (this.observationConfig.failurePolicy === "continue" && isOmModelExecutionError(error) && error.failureKind === "observer-model") return {
30504
+ observed: false,
30505
+ error
30506
+ };
30226
30507
  throw error;
30227
30508
  }
30228
30509
  }
@@ -30488,7 +30769,8 @@ var SyncObservationStrategy = class extends ObservationStrategy {
30488
30769
  threadId: this.opts.threadId,
30489
30770
  resourceId: this.opts.resourceId,
30490
30771
  trigger: this.opts.trigger,
30491
- mainAgent: this.opts.agent
30772
+ mainAgent: this.opts.agent,
30773
+ timeZone: this.opts.record.observedTimezone
30492
30774
  });
30493
30775
  const hookedValues = await applyExtractorHooks({
30494
30776
  source: "observer",
@@ -30497,7 +30779,10 @@ var SyncObservationStrategy = class extends ObservationStrategy {
30497
30779
  failures: result.extractionFailures,
30498
30780
  previousValues: this.priorExtractedValues,
30499
30781
  rawObservations: result.observations,
30500
- recentMessages: formatMessagesForObserver(this.opts.messages, { maxPartLength: 500 }),
30782
+ recentMessages: formatMessagesForObserver(this.opts.messages, {
30783
+ maxPartLength: 500,
30784
+ timeZone: this.opts.record.observedTimezone
30785
+ }),
30501
30786
  threadId: this.opts.threadId,
30502
30787
  resourceId: this.opts.resourceId,
30503
30788
  mainAgent: this.opts.agent,
@@ -30628,6 +30913,7 @@ var SyncObservationStrategy = class extends ObservationStrategy {
30628
30913
  startedAt: this.startedAt,
30629
30914
  tokensAttempted: this.tokensToObserve,
30630
30915
  error,
30916
+ failurePolicy: this.observationConfig.failurePolicy,
30631
30917
  recordId: this.opts.record.id,
30632
30918
  threadId: this.opts.threadId
30633
30919
  });
@@ -30677,7 +30963,8 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
30677
30963
  threadId: this.opts.threadId,
30678
30964
  resourceId: this.opts.resourceId,
30679
30965
  trigger: this.opts.trigger,
30680
- mainAgent: this.opts.agent
30966
+ mainAgent: this.opts.agent,
30967
+ timeZone: this.opts.record.observedTimezone
30681
30968
  });
30682
30969
  const hookedValues = await applyExtractorHooks({
30683
30970
  source: "observer",
@@ -30686,7 +30973,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
30686
30973
  failures: result.extractionFailures,
30687
30974
  previousValues: this.priorExtractedValues,
30688
30975
  rawObservations: result.observations,
30689
- recentMessages: formatMessagesForObserver(messages, { maxPartLength: 500 }),
30976
+ recentMessages: formatMessagesForObserver(messages, {
30977
+ maxPartLength: 500,
30978
+ timeZone: this.opts.record.observedTimezone
30979
+ }),
30690
30980
  threadId: this.opts.threadId,
30691
30981
  resourceId: this.opts.resourceId,
30692
30982
  mainAgent: this.opts.agent,
@@ -30832,6 +31122,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
30832
31122
  startedAt: this.startedAt,
30833
31123
  tokensAttempted,
30834
31124
  error,
31125
+ failurePolicy: this.observationConfig.failurePolicy,
30835
31126
  recordId: record.id,
30836
31127
  threadId
30837
31128
  });
@@ -31006,7 +31297,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
31006
31297
  return this.deps.observer.callMultiThread(_existingObservations, batch.threadMap, batch.threadIds, this.opts.abortSignal, this.opts.requestContext, this.priorMetadataByThread, this.opts.observabilityContext, void 0, {
31007
31298
  resourceId: this.opts.resourceId,
31008
31299
  trigger: this.opts.trigger
31009
- });
31300
+ }, this.opts.record.observedTimezone);
31010
31301
  }));
31011
31302
  for (const batchResult of batchResults) {
31012
31303
  for (const [threadId, result] of batchResult.results) this.multiThreadResults.set(threadId, result);
@@ -31039,7 +31330,10 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
31039
31330
  failures: result.extractionFailures,
31040
31331
  previousValues,
31041
31332
  rawObservations: result.observations,
31042
- recentMessages: formatMessagesForObserver(threadMessages, { maxPartLength: 500 }),
31333
+ recentMessages: formatMessagesForObserver(threadMessages, {
31334
+ maxPartLength: 500,
31335
+ timeZone: this.opts.record.observedTimezone
31336
+ }),
31043
31337
  threadId,
31044
31338
  resourceId: this.resourceId,
31045
31339
  mainAgent: this.opts.agent,
@@ -31198,6 +31492,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
31198
31492
  startedAt: this.startedAt,
31199
31493
  tokensAttempted,
31200
31494
  error,
31495
+ failurePolicy: this.observationConfig.failurePolicy,
31201
31496
  recordId: this.opts.record.id,
31202
31497
  threadId
31203
31498
  });
@@ -31604,19 +31899,28 @@ var ObservationStep = class {
31604
31899
  };
31605
31900
  }
31606
31901
  }
31607
- const obsResult = await om.observe({
31608
- threadId,
31609
- resourceId,
31610
- messages: observableMessages,
31611
- messageList,
31612
- trigger: "turn-sync",
31613
- agent: this.turn.agent,
31614
- sendSignal: this.turn.sendSignal,
31615
- sendStateSignal: this.turn.sendStateSignal,
31616
- requestContext: this.turn.requestContext,
31617
- writer: this.turn.writer,
31618
- observabilityContext: this.turn.observabilityContext
31619
- });
31902
+ let obsResult;
31903
+ try {
31904
+ obsResult = await om.observe({
31905
+ threadId,
31906
+ resourceId,
31907
+ messages: observableMessages,
31908
+ messageList,
31909
+ trigger: "turn-sync",
31910
+ agent: this.turn.agent,
31911
+ sendSignal: this.turn.sendSignal,
31912
+ sendStateSignal: this.turn.sendStateSignal,
31913
+ requestContext: this.turn.requestContext,
31914
+ writer: this.turn.writer,
31915
+ observabilityContext: this.turn.observabilityContext
31916
+ });
31917
+ } catch (error) {
31918
+ if (om.config.observation.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "observer-model") throw error;
31919
+ return {
31920
+ succeeded: false,
31921
+ record: freshStatus.record
31922
+ };
31923
+ }
31620
31924
  if (obsResult.observed) {
31621
31925
  const observedMessageIds = new Set(obsResult.record.observedMessageIds ?? []);
31622
31926
  const liveMessages = getObservableMessages(messageList);
@@ -32268,11 +32572,27 @@ var ReflectorRunner = class {
32268
32572
  this.mastra = mastra;
32269
32573
  }
32270
32574
  createAgent(model, memory, extractors = this.reflectionConfig.extractors) {
32575
+ let agentModel = model;
32576
+ if (Array.isArray(agentModel)) agentModel = agentModel.map((fallback) => ({
32577
+ ...fallback,
32578
+ maxRetries: 0
32579
+ }));
32580
+ else if (typeof agentModel === "function") {
32581
+ const resolveDynamicModel = agentModel;
32582
+ agentModel = (async (args) => {
32583
+ const resolvedModel = await resolveDynamicModel(args);
32584
+ return Array.isArray(resolvedModel) ? resolvedModel.map((fallback) => ({
32585
+ ...fallback,
32586
+ maxRetries: 0
32587
+ })) : resolvedModel;
32588
+ });
32589
+ }
32271
32590
  return new _mastra_core_agent.Agent({
32272
32591
  id: "observational-memory-reflector",
32273
32592
  name: "Reflector",
32274
32593
  instructions: buildReflectorSystemPrompt(this.reflectionConfig.instruction, extractors),
32275
- model,
32594
+ model: agentModel,
32595
+ maxRetries: 0,
32276
32596
  ...memory ? { memory } : {},
32277
32597
  ...this.mastra ? { mastra: this.mastra } : {}
32278
32598
  });
@@ -32354,36 +32674,42 @@ var ReflectorRunner = class {
32354
32674
  },
32355
32675
  callback: (childObservabilityContext) => withAbortCheck(async () => {
32356
32676
  chunkCount = 0;
32357
- return (await agent.stream(prompt, {
32358
- modelSettings: { ...this.reflectionConfig.modelSettings },
32359
- providerOptions: this.reflectionConfig.providerOptions,
32360
- ...temporaryMemory ? { memory: temporaryMemory.options } : {},
32361
- ...abortSignal ? { abortSignal } : {},
32362
- ...internalRequestContext ? { requestContext: internalRequestContext } : {},
32363
- ...childObservabilityContext,
32364
- ...attemptNumber === 1 ? {
32365
- onChunk(chunk) {
32366
- chunkCount++;
32367
- if (chunkCount === 1 || chunkCount % 50 === 0) {
32368
- const preview = chunk.type === "text-delta" ? ` text="${chunk.textDelta?.slice(0, 80)}..."` : chunk.type === "tool-call" ? ` tool=${chunk.toolName}` : "";
32369
- omDebug(`[OM:callReflector] chunk#${chunkCount}: type=${chunk.type}${preview}`);
32677
+ try {
32678
+ return await (await agent.stream(prompt, {
32679
+ modelSettings: { ...this.reflectionConfig.modelSettings },
32680
+ providerOptions: this.reflectionConfig.providerOptions,
32681
+ ...temporaryMemory ? { memory: temporaryMemory.options } : {},
32682
+ ...abortSignal ? { abortSignal } : {},
32683
+ ...internalRequestContext ? { requestContext: internalRequestContext } : {},
32684
+ ...childObservabilityContext,
32685
+ ...attemptNumber === 1 ? {
32686
+ onChunk(chunk) {
32687
+ chunkCount++;
32688
+ if (chunkCount === 1 || chunkCount % 50 === 0) {
32689
+ const preview = chunk.type === "text-delta" ? ` text="${chunk.textDelta?.slice(0, 80)}..."` : chunk.type === "tool-call" ? ` tool=${chunk.toolName}` : "";
32690
+ omDebug(`[OM:callReflector] chunk#${chunkCount}: type=${chunk.type}${preview}`);
32691
+ }
32692
+ },
32693
+ onFinish(event) {
32694
+ omDebug(`[OM:callReflector] onFinish: chunks=${chunkCount}, finishReason=${event.finishReason}, inputTokens=${event.usage?.inputTokens}, outputTokens=${event.usage?.outputTokens}, textLen=${event.text?.length}`);
32695
+ },
32696
+ onAbort(event) {
32697
+ omDebug(`[OM:callReflector] onAbort: chunks=${chunkCount}, reason=${event?.reason ?? "unknown"}`);
32698
+ },
32699
+ onError({ error }) {
32700
+ omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
32370
32701
  }
32371
- },
32372
- onFinish(event) {
32373
- omDebug(`[OM:callReflector] onFinish: chunks=${chunkCount}, finishReason=${event.finishReason}, inputTokens=${event.usage?.inputTokens}, outputTokens=${event.usage?.outputTokens}, textLen=${event.text?.length}`);
32374
- },
32375
- onAbort(event) {
32376
- omDebug(`[OM:callReflector] onAbort: chunks=${chunkCount}, reason=${event?.reason ?? "unknown"}`);
32377
- },
32378
- onError({ error }) {
32379
- omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
32380
- }
32381
- } : {}
32382
- })).getFullOutput();
32702
+ } : {}
32703
+ })).getFullOutput();
32704
+ } catch (error) {
32705
+ if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
32706
+ throw new OmModelExecutionError("reflector-model", error);
32707
+ }
32383
32708
  }, abortSignal)
32384
32709
  }), {
32385
32710
  label: "reflector",
32386
- abortSignal
32711
+ abortSignal,
32712
+ maxRetries: this.reflectionConfig.maxRetries
32387
32713
  });
32388
32714
  omDebug(`[OM:callReflector] attempt #${attemptNumber} returned: textLen=${result.text?.length}, textPreview="${result.text?.slice(0, 120)}...", inputTokens=${result.usage?.inputTokens ?? result.totalUsage?.inputTokens}, outputTokens=${result.usage?.outputTokens ?? result.totalUsage?.outputTokens}`);
32389
32715
  const usage = result.totalUsage ?? result.usage;
@@ -32507,6 +32833,7 @@ var ReflectorRunner = class {
32507
32833
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
32508
32834
  tokensAttempted: observationTokens,
32509
32835
  error,
32836
+ failurePolicy: this.reflectionConfig.failurePolicy,
32510
32837
  recordId: record.id,
32511
32838
  threadId: record.threadId ?? ""
32512
32839
  });
@@ -32919,24 +33246,43 @@ var ReflectorRunner = class {
32919
33246
  usage: reflectResult.usage
32920
33247
  });
32921
33248
  } catch (error) {
32922
- if (writer && streamContext) {
32923
- const failedMarker = createObservationFailedMarker({
32924
- cycleId: streamContext.cycleId,
32925
- operationType: "reflection",
32926
- startedAt: streamContext.startedAt,
32927
- tokensAttempted: observationTokens,
32928
- error,
32929
- recordId: record.id,
32930
- threadId
32931
- });
32932
- await writer.custom({
32933
- ...failedMarker,
32934
- transient: true
32935
- }).catch(() => {});
33249
+ reflectionError = error instanceof Error ? error : new Error(String(error));
33250
+ const failedMarker = createObservationFailedMarker({
33251
+ cycleId: streamContext?.cycleId ?? cycleId,
33252
+ operationType: "reflection",
33253
+ startedAt: streamContext?.startedAt ?? startedAt,
33254
+ tokensAttempted: observationTokens,
33255
+ error,
33256
+ failurePolicy: this.reflectionConfig.failurePolicy,
33257
+ recordId: record.id,
33258
+ threadId
33259
+ });
33260
+ if (writer) await writer.custom({
33261
+ ...failedMarker,
33262
+ transient: true
33263
+ }).catch(() => {});
33264
+ let persistedToList = false;
33265
+ try {
33266
+ persistedToList = await this.persistMarkerToMessage(failedMarker, messageList, threadId, record.resourceId ?? void 0);
33267
+ } catch {
33268
+ persistedToList = false;
33269
+ }
33270
+ if (!persistedToList) try {
32936
33271
  await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
33272
+ } catch (markerError) {
33273
+ omError("[OM] Failed to persist reflection-failed marker", markerError);
32937
33274
  }
32938
- reflectionError = error instanceof Error ? error : new Error(String(error));
32939
- if (lifecycleError !== void 0 || abortSignal?.aborted) throw error;
33275
+ this.emitDebugEvent({
33276
+ type: "reflection_failed",
33277
+ timestamp: /* @__PURE__ */ new Date(),
33278
+ threadId,
33279
+ resourceId: record.resourceId ?? "",
33280
+ inputTokens: observationTokens,
33281
+ failurePolicy: this.reflectionConfig.failurePolicy,
33282
+ failureKind: failedMarker.data.failureKind,
33283
+ error: reflectionError.message
33284
+ });
33285
+ if (lifecycleError !== void 0 || abortSignal?.aborted || this.reflectionConfig.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "reflector-model") throw error;
32940
33286
  this.syncReflectionSuppression.set(lockKey, observationTokens);
32941
33287
  omError("[OM] Reflection failed", error);
32942
33288
  } finally {
@@ -33570,6 +33916,8 @@ var ObservationalMemory = class ObservationalMemory {
33570
33916
  const observationActivateAfterIdlePath = config.observation?.activateAfterIdle !== void 0 ? "observation.activateAfterIdle" : "activateAfterIdle";
33571
33917
  this.observationConfig = {
33572
33918
  model: observationModel,
33919
+ maxRetries: config.observation?.maxRetries ?? RETRY_CONFIG.maxRetries,
33920
+ failurePolicy: config.observation?.failurePolicy ?? "abort",
33573
33921
  messageTokens: isSharedBudget ? {
33574
33922
  min: messageTokens,
33575
33923
  max: totalBudget
@@ -33599,6 +33947,8 @@ var ObservationalMemory = class ObservationalMemory {
33599
33947
  };
33600
33948
  this.reflectionConfig = {
33601
33949
  model: reflectionModel,
33950
+ maxRetries: config.reflection?.maxRetries ?? RETRY_CONFIG.maxRetries,
33951
+ failurePolicy: config.reflection?.failurePolicy ?? "abort",
33602
33952
  observationTokens,
33603
33953
  shareTokenBudget: isSharedBudget,
33604
33954
  modelSettings: {
@@ -33672,9 +34022,15 @@ var ObservationalMemory = class ObservationalMemory {
33672
34022
  retrieval: this.retrieval,
33673
34023
  observation: {
33674
34024
  messageTokens: this.observationConfig.messageTokens,
33675
- previousObserverTokens: this.observationConfig.previousObserverTokens
34025
+ previousObserverTokens: this.observationConfig.previousObserverTokens,
34026
+ maxRetries: this.observationConfig.maxRetries,
34027
+ failurePolicy: this.observationConfig.failurePolicy
33676
34028
  },
33677
- reflection: { observationTokens: this.reflectionConfig.observationTokens }
34029
+ reflection: {
34030
+ observationTokens: this.reflectionConfig.observationTokens,
34031
+ maxRetries: this.reflectionConfig.maxRetries,
34032
+ failurePolicy: this.reflectionConfig.failurePolicy
34033
+ }
33678
34034
  };
33679
34035
  }
33680
34036
  /**
@@ -33778,11 +34134,15 @@ var ObservationalMemory = class ObservationalMemory {
33778
34134
  messageTokens: this.observationConfig.messageTokens,
33779
34135
  model: observationResolved.model,
33780
34136
  previousObserverTokens: this.observationConfig.previousObserverTokens,
34137
+ maxRetries: this.observationConfig.maxRetries,
34138
+ failurePolicy: this.observationConfig.failurePolicy,
33781
34139
  routing: observationResolved.routing
33782
34140
  },
33783
34141
  reflection: {
33784
34142
  observationTokens: this.reflectionConfig.observationTokens,
33785
34143
  model: reflectionResolved.model,
34144
+ maxRetries: this.reflectionConfig.maxRetries,
34145
+ failurePolicy: this.reflectionConfig.failurePolicy,
33786
34146
  routing: reflectionResolved.routing
33787
34147
  }
33788
34148
  };
@@ -33799,6 +34159,7 @@ var ObservationalMemory = class ObservationalMemory {
33799
34159
  * Ensures bufferTokens is less than the threshold and bufferActivation is valid.
33800
34160
  */
33801
34161
  validateBufferConfig() {
34162
+ for (const [path$8, value] of [["observation.maxRetries", this.observationConfig.maxRetries], ["reflection.maxRetries", this.reflectionConfig.maxRetries]]) if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${path$8} must be a finite non-negative integer, got ${value}`);
33802
34163
  if ((this.observationConfig.bufferTokens !== void 0 || this.observationConfig.bufferActivation !== void 0 || this.reflectionConfig.bufferActivation !== void 0) && this.scope === "resource") throw new Error("Async buffering is not yet supported with scope: 'resource'. Use scope: 'thread', or set observation: { bufferTokens: false } to disable async buffering.");
33803
34164
  const observationThreshold = getMaxThreshold(this.observationConfig.messageTokens);
33804
34165
  if (this.observationConfig.bufferTokens !== void 0) {
@@ -33926,18 +34287,19 @@ var ObservationalMemory = class ObservationalMemory {
33926
34287
  };
33927
34288
  }
33928
34289
  /**
33929
- * Persist a data-om-* marker part on the last assistant message in messageList
33930
- * AND save the updated message to the DB so it survives page reload.
33931
- * (data-* parts are filtered out before sending to the LLM, so they don't affect model calls.)
34290
+ * Persist a data-om-* marker part on the message that owns the operation in
34291
+ * messageList and save it to the DB. Observation markers belong to the latest
34292
+ * assistant message; reflection markers belong to the current user input.
33932
34293
  * @internal Used by ReflectorRunner. Do not call directly.
33933
34294
  */
33934
34295
  async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
33935
- if (!messageList) return;
33936
- const allMsgs = getObservableMessages(messageList);
34296
+ if (!messageList) return false;
34297
+ const allMsgs = messageList.get.all.db();
34298
+ const markerData = marker.data;
34299
+ const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
33937
34300
  for (let i = allMsgs.length - 1; i >= 0; i--) {
33938
34301
  const msg = allMsgs[i];
33939
- if (msg?.role === "assistant" && msg.content?.parts && Array.isArray(msg.content.parts)) {
33940
- const markerData = marker.data;
34302
+ if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
33941
34303
  if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
33942
34304
  try {
33943
34305
  await this.messageHistory.persistMessages({
@@ -33947,16 +34309,19 @@ var ObservationalMemory = class ObservationalMemory {
33947
34309
  });
33948
34310
  } catch (e) {
33949
34311
  omDebug(`[OM:persistMarker] failed to save marker to DB: ${e}`);
34312
+ return false;
33950
34313
  }
33951
- return;
34314
+ return true;
33952
34315
  }
33953
34316
  }
34317
+ return false;
33954
34318
  }
33955
34319
  /**
33956
- * Persist a marker to the last assistant message in storage.
33957
- * Unlike persistMarkerToMessage, this fetches messages directly from the DB
33958
- * so it works even when no MessageList is available (e.g. async buffering ops).
33959
- * @internal Used by observation strategies. Do not call directly.
34320
+ * Persist a marker to the message that owns the operation in storage.
34321
+ * Observation markers belong to the latest assistant message. Reflection runs
34322
+ * before the current assistant reply exists, so its failure marker belongs to
34323
+ * the latest user input that triggered it.
34324
+ * @internal Used by observation strategies and the reflector. Do not call directly.
33960
34325
  */
33961
34326
  async persistMarkerToStorage(marker, threadId, resourceId) {
33962
34327
  try {
@@ -33968,8 +34333,9 @@ var ObservationalMemory = class ObservationalMemory {
33968
34333
  direction: "DESC"
33969
34334
  }
33970
34335
  }))?.messages ?? [];
33971
- for (const msg of messages) if (msg?.role === "assistant" && msg.content?.parts && Array.isArray(msg.content.parts)) {
33972
- const markerData = marker.data;
34336
+ const markerData = marker.data;
34337
+ const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
34338
+ for (const msg of messages) if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
33973
34339
  if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
33974
34340
  await this.messageHistory.persistMessages({
33975
34341
  messages: [msg],
@@ -34211,9 +34577,9 @@ var ObservationalMemory = class ObservationalMemory {
34211
34577
  * In resource scope mode, filters continuity messages to only show
34212
34578
  * the message for the current thread.
34213
34579
  */
34214
- formatObservationsForContext(observations, currentTask, suggestedResponse, extractedValues, unobservedContextBlocks, currentDate, retrieval = false) {
34580
+ formatObservationsForContext(observations, currentTask, suggestedResponse, extractedValues, unobservedContextBlocks, currentDate, retrieval = false, timeZone) {
34215
34581
  let optimized = retrieval ? renderObservationGroupsForReflection(observations) ?? optimizeObservationsForContext(observations) : optimizeObservationsForContext(observations);
34216
- if (currentDate) optimized = addRelativeTimeToObservations(optimized, currentDate);
34582
+ if (currentDate) optimized = addRelativeTimeToObservations(optimized, currentDate, timeZone);
34217
34583
  const messages = [`${getObservationContextPrompt(this.scope)}\n\n${OBSERVATION_CONTEXT_INSTRUCTIONS}${retrieval ? `\n\n${getRetrievalInstructions(this.retrievalScope, this.retrievalInstructions, this.retrievalSearch)}` : ""}`];
34218
34584
  if (unobservedContextBlocks) messages.push(`The following content is from OTHER conversations different from the current conversation, they're here for reference, but they're not necessarily your focus:\nSTART_OTHER_CONVERSATIONS_BLOCK\n${unobservedContextBlocks}\nEND_OTHER_CONVERSATIONS_BLOCK`);
34219
34585
  const observationChunks = this.splitObservationContextChunks(optimized);
@@ -34313,12 +34679,15 @@ var ObservationalMemory = class ObservationalMemory {
34313
34679
  * These are injected into the Actor's context so it has awareness of activity
34314
34680
  * in other threads for the same resource.
34315
34681
  */
34316
- async formatUnobservedContextBlocks(messagesByThread, currentThreadId) {
34682
+ async formatUnobservedContextBlocks(messagesByThread, currentThreadId, timeZone) {
34317
34683
  const blocks = [];
34318
34684
  for (const [threadId, messages] of messagesByThread) {
34319
34685
  if (threadId === currentThreadId) continue;
34320
34686
  if (messages.length === 0) continue;
34321
- const formattedMessages = formatMessagesForObserver(messages, { maxPartLength: 500 });
34687
+ const formattedMessages = formatMessagesForObserver(messages, {
34688
+ maxPartLength: 500,
34689
+ timeZone
34690
+ });
34322
34691
  if (formattedMessages) {
34323
34692
  const obscuredId = await this.representThreadIDInContext(threadId);
34324
34693
  blocks.push(`<other-conversation id="${obscuredId}">
@@ -34495,7 +34864,7 @@ ${formattedMessages}
34495
34864
  transient: true
34496
34865
  }).catch(() => {});
34497
34866
  omDebug(`[OM:bufferInput] cycleId=${cycleId}, msgCount=${messagesToBuffer.length}, msgTokens=${tokensToBuffer}, ids=${messagesToBuffer.map((m) => `${m.id?.slice(0, 8)}@${m.createdAt ? new Date(m.createdAt).toISOString() : "none"}`).join(",")}`);
34498
- await this.runBufferedObservationCycle({
34867
+ if ((await this.runBufferedObservationCycle({
34499
34868
  threadId,
34500
34869
  resourceId: freshRecord.resourceId ?? void 0,
34501
34870
  trigger: "async-buffer"
@@ -34510,10 +34879,11 @@ ${formattedMessages}
34510
34879
  requestContext,
34511
34880
  observabilityContext,
34512
34881
  trigger: "async-buffer"
34513
- }).run());
34514
- const maxTs = this.getMaxMessageTimestamp(messagesToBuffer);
34515
- const cursor = new Date(maxTs.getTime() + 1);
34516
- BufferingCoordinator.lastBufferedAtTime.set(bufferKey, cursor);
34882
+ }).run()))?.observed) {
34883
+ const maxTs = this.getMaxMessageTimestamp(messagesToBuffer);
34884
+ const cursor = new Date(maxTs.getTime() + 1);
34885
+ BufferingCoordinator.lastBufferedAtTime.set(bufferKey, cursor);
34886
+ }
34517
34887
  }
34518
34888
  /**
34519
34889
  * Trigger async buffered observation if the token count has crossed a new interval.
@@ -34741,7 +35111,7 @@ ${formattedMessages}
34741
35111
  const currentTask = activeExtractors.some((extractor) => extractor.slug === "current-task") ? omMetadata?.currentTask : void 0;
34742
35112
  const suggestedResponse = activeExtractors.some((extractor) => extractor.slug === "suggested-response") ? omMetadata?.suggestedResponse : void 0;
34743
35113
  const currentDate = opts.currentDate ?? /* @__PURE__ */ new Date();
34744
- return this.formatObservationsForContext(record.activeObservations, currentTask, suggestedResponse, omMetadata?.extracted, unobservedContextBlocks, currentDate, this.retrieval);
35114
+ return this.formatObservationsForContext(record.activeObservations, currentTask, suggestedResponse, omMetadata?.extracted, unobservedContextBlocks, currentDate, this.retrieval, record.observedTimezone);
34745
35115
  }
34746
35116
  /**
34747
35117
  * Get unobserved messages from other threads for resource-scoped observation.
@@ -34753,7 +35123,8 @@ ${formattedMessages}
34753
35123
  async getOtherThreadsContext(resourceId, currentThreadId) {
34754
35124
  const { threads: allThreads } = await this.storage.listThreads({ filter: { resourceId } });
34755
35125
  const messagesByThread = /* @__PURE__ */ new Map();
34756
- const recordLastObservedAt = (await this.getRecord(currentThreadId, resourceId))?.lastObservedAt;
35126
+ const record = await this.getRecord(currentThreadId, resourceId);
35127
+ const recordLastObservedAt = record?.lastObservedAt;
34757
35128
  for (const thread of allThreads) {
34758
35129
  if (thread.id === currentThreadId) continue;
34759
35130
  const threadLastObservedAt = (0, _mastra_core_memory.getThreadOMMetadata)(thread.metadata)?.lastObservedAt ?? recordLastObservedAt;
@@ -34770,7 +35141,7 @@ ${formattedMessages}
34770
35141
  if (filtered.length > 0) messagesByThread.set(thread.id, filtered);
34771
35142
  }
34772
35143
  if (messagesByThread.size === 0) return void 0;
34773
- return await this.formatUnobservedContextBlocks(messagesByThread, currentThreadId) || void 0;
35144
+ return await this.formatUnobservedContextBlocks(messagesByThread, currentThreadId, record?.observedTimezone) || void 0;
34774
35145
  }
34775
35146
  /**
34776
35147
  * Emit debug event and stream progress for UI feedback.
@@ -35113,7 +35484,7 @@ ${formattedMessages}
35113
35484
  ...startMarker,
35114
35485
  transient: true
35115
35486
  }).catch(() => {});
35116
- await this.runBufferedObservationCycle({
35487
+ if (!(await this.runBufferedObservationCycle({
35117
35488
  threadId,
35118
35489
  resourceId: record.resourceId ?? resourceId,
35119
35490
  trigger: "async-buffer"
@@ -35132,7 +35503,10 @@ ${formattedMessages}
35132
35503
  currentModel: opts.currentModel,
35133
35504
  observabilityContext,
35134
35505
  trigger: "async-buffer"
35135
- }).run());
35506
+ }).run()))?.observed) return {
35507
+ buffered: false,
35508
+ record
35509
+ };
35136
35510
  if (isOmReproCaptureEnabled()) writeObserverExchangeReproCapture({
35137
35511
  threadId,
35138
35512
  resourceId: record.resourceId ?? void 0,
@@ -35492,6 +35866,7 @@ ${formattedMessages}
35492
35866
  observed = result.observed;
35493
35867
  observationUsage = result.usage;
35494
35868
  observationProviderMetadata = result.providerMetadata;
35869
+ observationError = result.error;
35495
35870
  });
35496
35871
  } catch (error) {
35497
35872
  lifecycleError = error;
@@ -37402,6 +37777,7 @@ ${workingMemory}`;
37402
37777
  model: omConfig.model,
37403
37778
  mastra: this._mastraInstance,
37404
37779
  onIndexObservations,
37780
+ onDebugEvent: omConfig.onDebugEvent,
37405
37781
  hooks: omConfig.hooks,
37406
37782
  observation: omConfig.observation ? {
37407
37783
  model: omConfig.observation.model,
@@ -37418,10 +37794,14 @@ ${workingMemory}`;
37418
37794
  threadTitle: omConfig.observation.threadTitle,
37419
37795
  observeAttachments: omConfig.observation.observeAttachments,
37420
37796
  continuationHints: omConfig.observation.continuationHints,
37797
+ maxRetries: omConfig.observation.maxRetries,
37798
+ failurePolicy: omConfig.observation.failurePolicy,
37421
37799
  extract: omConfig.observation.extract
37422
37800
  } : void 0,
37423
37801
  reflection: omConfig.reflection ? {
37424
37802
  model: omConfig.reflection.model,
37803
+ maxRetries: omConfig.reflection.maxRetries,
37804
+ failurePolicy: omConfig.reflection.failurePolicy,
37425
37805
  observationTokens: omConfig.reflection.observationTokens,
37426
37806
  modelSettings: omConfig.reflection.modelSettings,
37427
37807
  providerOptions: omConfig.reflection.providerOptions,
@@ -49274,4 +49654,4 @@ const agentBuilderWorkflows = {
49274
49654
  //#endregion
49275
49655
  exports.agentBuilderWorkflows = agentBuilderWorkflows;
49276
49656
 
49277
- //# sourceMappingURL=dist-Cfr6t0cK.cjs.map
49657
+ //# sourceMappingURL=dist-CRMvLG_8.cjs.map