@mastra/server 1.72.0-alpha.2 → 1.72.0-alpha.4
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.
- package/dist/{dist-ABdCIqbD.js → dist-Bo9o3JNW.js} +745 -366
- package/dist/dist-Bo9o3JNW.js.map +1 -0
- package/dist/{dist-Cfr6t0cK.cjs → dist-_n0Mp6PP.cjs} +745 -366
- package/dist/dist-_n0Mp6PP.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/server/handlers/agent-builder.cjs +1 -1
- package/dist/server/handlers/agent-builder.js +1 -1
- package/dist/server/handlers/agents.cjs +5 -2
- package/dist/server/handlers/agents.cjs.map +1 -1
- package/dist/server/handlers/agents.d.ts.map +1 -1
- package/dist/server/handlers/agents.js +5 -2
- package/dist/server/handlers/agents.js.map +1 -1
- package/package.json +6 -6
- package/dist/dist-ABdCIqbD.js.map +0 -1
- package/dist/dist-Cfr6t0cK.cjs.map +0 -1
|
@@ -4638,7 +4638,7 @@ function splitLines(text) {
|
|
|
4638
4638
|
return result;
|
|
4639
4639
|
}
|
|
4640
4640
|
//#endregion
|
|
4641
|
-
//#region ../memory/dist/src-
|
|
4641
|
+
//#region ../memory/dist/src-BikfoIdS.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,7 +23451,7 @@ function formatOmError(error) {
|
|
|
23236
23451
|
};
|
|
23237
23452
|
function collectErrorDetails(value, depth) {
|
|
23238
23453
|
if (depth > 5) return;
|
|
23239
|
-
if (!isRecord$
|
|
23454
|
+
if (!isRecord$4(value)) {
|
|
23240
23455
|
if (value !== void 0) add(String(value));
|
|
23241
23456
|
return;
|
|
23242
23457
|
}
|
|
@@ -23246,10 +23461,10 @@ function formatOmError(error) {
|
|
|
23246
23461
|
if (typeof value.statusCode === "number") add(`HTTP ${value.statusCode}`);
|
|
23247
23462
|
if (typeof value.responseBody === "string") try {
|
|
23248
23463
|
const body = JSON.parse(value.responseBody);
|
|
23249
|
-
if (isRecord$
|
|
23464
|
+
if (isRecord$4(body)) {
|
|
23250
23465
|
add(body.message);
|
|
23251
23466
|
add(body.detail);
|
|
23252
|
-
if (isRecord$
|
|
23467
|
+
if (isRecord$4(body.error)) add(body.error.message);
|
|
23253
23468
|
else add(body.error);
|
|
23254
23469
|
}
|
|
23255
23470
|
} catch {}
|
|
@@ -23262,8 +23477,8 @@ function formatOmError(error) {
|
|
|
23262
23477
|
const message = [...details].join(": ") || "Unknown error";
|
|
23263
23478
|
return message.length > 2e3 ? `${message.slice(0, 1997)}...` : message;
|
|
23264
23479
|
}
|
|
23265
|
-
function isAbortError$
|
|
23266
|
-
return abortSignal?.aborted === true || error
|
|
23480
|
+
function isAbortError$5(error, abortSignal) {
|
|
23481
|
+
return abortSignal?.aborted === true || hasAbortInChain(error);
|
|
23267
23482
|
}
|
|
23268
23483
|
function shouldRetryEmptyStructuredObject(object, extractors) {
|
|
23269
23484
|
return Object.keys(object).length === 0 && extractors.some((extractor) => extractor.retryStructuredExtractionOnEmptyObject);
|
|
@@ -23289,7 +23504,12 @@ If a prior value is still applicable and carry-forward is enabled, return that p
|
|
|
23289
23504
|
${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values\n\n${priorLines.join("\n\n")}` : ""}`;
|
|
23290
23505
|
const values = {};
|
|
23291
23506
|
const failures = [];
|
|
23292
|
-
const streamWithStructuredOutput = async (jsonPromptInjection) => {
|
|
23507
|
+
const streamWithStructuredOutput = async (jsonPromptInjection) => withRetry(() => streamOnce(jsonPromptInjection), {
|
|
23508
|
+
label: `om-${opts.source}-structured-extraction`,
|
|
23509
|
+
abortSignal: opts.abortSignal,
|
|
23510
|
+
maxRetries: 1
|
|
23511
|
+
});
|
|
23512
|
+
const streamOnce = async (jsonPromptInjection) => {
|
|
23293
23513
|
const object = await (await opts.agent.stream(prompt, {
|
|
23294
23514
|
structuredOutput: {
|
|
23295
23515
|
schema,
|
|
@@ -23309,11 +23529,21 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
23309
23529
|
object = await streamWithStructuredOutput();
|
|
23310
23530
|
retryEmptyObject = shouldRetryEmptyStructuredObject(object, structuredExtractors);
|
|
23311
23531
|
} catch (error) {
|
|
23312
|
-
if (isAbortError$
|
|
23532
|
+
if (isAbortError$5(error, opts.abortSignal)) throw error;
|
|
23533
|
+
if (isTransientLLMError(error)) {
|
|
23534
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23535
|
+
return {
|
|
23536
|
+
values,
|
|
23537
|
+
failures: structuredExtractors.map((extractor) => ({
|
|
23538
|
+
slug: extractor.slug,
|
|
23539
|
+
error: message
|
|
23540
|
+
}))
|
|
23541
|
+
};
|
|
23542
|
+
}
|
|
23313
23543
|
try {
|
|
23314
23544
|
object = await streamWithStructuredOutput(_mastra_core_features.coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
|
|
23315
23545
|
} catch (fallbackError) {
|
|
23316
|
-
if (isAbortError$
|
|
23546
|
+
if (isAbortError$5(fallbackError, opts.abortSignal)) throw fallbackError;
|
|
23317
23547
|
const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
23318
23548
|
return {
|
|
23319
23549
|
values,
|
|
@@ -23327,7 +23557,7 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
23327
23557
|
if (retryEmptyObject) try {
|
|
23328
23558
|
object = await streamWithStructuredOutput(_mastra_core_features.coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
|
|
23329
23559
|
} catch (fallbackError) {
|
|
23330
|
-
if (isAbortError$
|
|
23560
|
+
if (isAbortError$5(fallbackError, opts.abortSignal)) throw fallbackError;
|
|
23331
23561
|
const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
23332
23562
|
return {
|
|
23333
23563
|
values,
|
|
@@ -23353,15 +23583,15 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
23353
23583
|
};
|
|
23354
23584
|
}
|
|
23355
23585
|
const LEGACY_SYSTEM_REMINDER_METADATA_KEY = "dynamicAgentsMdReminder";
|
|
23356
|
-
function isRecord$
|
|
23586
|
+
function isRecord$3(value) {
|
|
23357
23587
|
return typeof value === "object" && value !== null;
|
|
23358
23588
|
}
|
|
23359
23589
|
function isSystemReminderMessage(message) {
|
|
23360
|
-
if (!isRecord$
|
|
23590
|
+
if (!isRecord$3(message.content)) return false;
|
|
23361
23591
|
const metadata = message.content.metadata;
|
|
23362
|
-
if (message.role === "signal") return isRecord$
|
|
23592
|
+
if (message.role === "signal") return isRecord$3(metadata) && isRecord$3(metadata.signal) && (metadata.signal.type === "system-reminder" || metadata.signal.type === "reactive");
|
|
23363
23593
|
if (message.role !== "user") return false;
|
|
23364
|
-
if (isRecord$
|
|
23594
|
+
if (isRecord$3(metadata) && (isRecord$3(metadata.systemReminder) || LEGACY_SYSTEM_REMINDER_METADATA_KEY in metadata)) return true;
|
|
23365
23595
|
const firstTextPart = message.content.parts.find((part) => part.type === "text");
|
|
23366
23596
|
return typeof firstTextPart?.text === "string" && firstTextPart.text.startsWith("<system-reminder");
|
|
23367
23597
|
}
|
|
@@ -23420,12 +23650,41 @@ function stripEphemeralAnchorIds(observations) {
|
|
|
23420
23650
|
* Date/time utility functions for Observational Memory.
|
|
23421
23651
|
* Pure functions for formatting relative timestamps and annotating observations.
|
|
23422
23652
|
*/
|
|
23423
|
-
|
|
23424
|
-
|
|
23425
|
-
|
|
23426
|
-
|
|
23427
|
-
|
|
23428
|
-
|
|
23653
|
+
const DAY_MS = 1440 * 60 * 1e3;
|
|
23654
|
+
/** Day number of the calendar date a parsed date names. Parsed dates are built from local-time fields. */
|
|
23655
|
+
function calendarDay(date) {
|
|
23656
|
+
return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / DAY_MS;
|
|
23657
|
+
}
|
|
23658
|
+
const knownTimeZones = /* @__PURE__ */ new Map();
|
|
23659
|
+
/** `timeZone` when this runtime recognises it; otherwise undefined, which means the process time zone. */
|
|
23660
|
+
function resolveTimeZone(timeZone) {
|
|
23661
|
+
if (!timeZone) return void 0;
|
|
23662
|
+
let known = knownTimeZones.get(timeZone);
|
|
23663
|
+
if (known === void 0) {
|
|
23664
|
+
try {
|
|
23665
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
23666
|
+
known = true;
|
|
23667
|
+
} catch {
|
|
23668
|
+
known = false;
|
|
23669
|
+
}
|
|
23670
|
+
knownTimeZones.set(timeZone, known);
|
|
23671
|
+
}
|
|
23672
|
+
return known ? timeZone : void 0;
|
|
23673
|
+
}
|
|
23674
|
+
/** Day number of the calendar date `instant` falls on in `timeZone` (the process time zone when unset). */
|
|
23675
|
+
function calendarDayIn(instant, timeZone) {
|
|
23676
|
+
const zone = resolveTimeZone(timeZone);
|
|
23677
|
+
if (!zone) return calendarDay(instant);
|
|
23678
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
23679
|
+
timeZone: zone,
|
|
23680
|
+
year: "numeric",
|
|
23681
|
+
month: "numeric",
|
|
23682
|
+
day: "numeric"
|
|
23683
|
+
}).formatToParts(instant);
|
|
23684
|
+
const part = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
23685
|
+
return Date.UTC(part("year"), part("month") - 1, part("day")) / DAY_MS;
|
|
23686
|
+
}
|
|
23687
|
+
function formatRelativeDays(diffDays) {
|
|
23429
23688
|
if (diffDays < 0) {
|
|
23430
23689
|
const futureDays = Math.abs(diffDays);
|
|
23431
23690
|
if (futureDays === 1) return "tomorrow";
|
|
@@ -23451,8 +23710,7 @@ function formatRelativeTime(date, currentDate) {
|
|
|
23451
23710
|
* Returns null for consecutive days (no gap marker needed).
|
|
23452
23711
|
*/
|
|
23453
23712
|
function formatGapBetweenDates(prevDate, currDate) {
|
|
23454
|
-
const
|
|
23455
|
-
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
23713
|
+
const diffDays = calendarDay(currDate) - calendarDay(prevDate);
|
|
23456
23714
|
if (diffDays <= 1) return null;
|
|
23457
23715
|
else if (diffDays < 7) return `[${diffDays} days later]`;
|
|
23458
23716
|
else if (diffDays < 14) return `[1 week later]`;
|
|
@@ -23460,45 +23718,156 @@ function formatGapBetweenDates(prevDate, currDate) {
|
|
|
23460
23718
|
else if (diffDays < 60) return `[1 month later]`;
|
|
23461
23719
|
else return `[${Math.floor(diffDays / 30)} months later]`;
|
|
23462
23720
|
}
|
|
23721
|
+
const MONTH_INDEX = {
|
|
23722
|
+
jan: 0,
|
|
23723
|
+
feb: 1,
|
|
23724
|
+
mar: 2,
|
|
23725
|
+
apr: 3,
|
|
23726
|
+
may: 4,
|
|
23727
|
+
jun: 5,
|
|
23728
|
+
jul: 6,
|
|
23729
|
+
aug: 7,
|
|
23730
|
+
sep: 8,
|
|
23731
|
+
oct: 9,
|
|
23732
|
+
nov: 10,
|
|
23733
|
+
dec: 11
|
|
23734
|
+
};
|
|
23735
|
+
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)?)\.?`;
|
|
23736
|
+
const DAY = String.raw`\d{1,2}(?:st|nd|rd|th)?`;
|
|
23737
|
+
const YEAR = String.raw`(?:19|20|21)\d{2}(?!\d)`;
|
|
23738
|
+
const MERIDIEM = String.raw`(?:[AaPp]\.[Mm]\.|[AaPp][Mm]\b)`;
|
|
23739
|
+
const TIME = String.raw`(?:,?\s+(?:at\s+)?\d{1,2}:\d{2}(?:\s*${MERIDIEM})?|\s+at\s+\d{1,2}\s*${MERIDIEM})`;
|
|
23740
|
+
const QUALIFIER = String.raw`(?:(?:early|mid|late)[- ](?:to[- ](?:early|mid|late)[- ])?)`;
|
|
23741
|
+
const RANGE_SEP = String.raw`(?:\s*[–—]\s*|\s*-\s*|\s+(?:to|through|until)\s+)`;
|
|
23742
|
+
/**
|
|
23743
|
+
* Dates written into observation text that carry their own year, longest forms first:
|
|
23744
|
+
* "Mar 1 - Mar 18, 2025", "May 27-28, 2023", "January 10, 2024 at 3:00 PM",
|
|
23745
|
+
* "June–July 2022", "Dec 2021 – June 2023", "late April 2023".
|
|
23746
|
+
* Bare years, dates without a year, and ISO dates (often part of model IDs, API versions,
|
|
23747
|
+
* paths and branch names) are deliberately not matched.
|
|
23748
|
+
*/
|
|
23749
|
+
const FREE_TEXT_DATE = new RegExp([
|
|
23750
|
+
String.raw`\b${MONTH}\s+${DAY}(?:,?\s+${YEAR})?${RANGE_SEP}${MONTH}\s+${DAY},?\s+${YEAR}${TIME}?`,
|
|
23751
|
+
String.raw`\b${MONTH}\s+${DAY}${RANGE_SEP}${DAY},?\s+${YEAR}`,
|
|
23752
|
+
String.raw`\b${MONTH}\s+${DAY},?\s+${YEAR}${TIME}?`,
|
|
23753
|
+
String.raw`\b${QUALIFIER}?${MONTH}(?:\s+${YEAR})?${RANGE_SEP}${QUALIFIER}?${MONTH}\s+${YEAR}`,
|
|
23754
|
+
String.raw`\b${QUALIFIER}?${MONTH}\s+${YEAR}`
|
|
23755
|
+
].join("|"), "g");
|
|
23756
|
+
const ENDPOINT = new RegExp(String.raw`^(?:(early|mid|late)\s*)?(?:(${MONTH})\s*)?(?:(\d{1,2})(?:st|nd|rd|th)?)?,?\s*(?:(${YEAR}))?$`, "i");
|
|
23757
|
+
function parseEndpoint(text) {
|
|
23758
|
+
const match = ENDPOINT.exec(text.trim());
|
|
23759
|
+
if (!match || !match[0]) return null;
|
|
23760
|
+
const [, qualifier, month, day, year] = match;
|
|
23761
|
+
return {
|
|
23762
|
+
...qualifier ? { qualifier: qualifier.toLowerCase() } : {},
|
|
23763
|
+
...month ? { month: MONTH_INDEX[month.slice(0, 3).toLowerCase()] } : {},
|
|
23764
|
+
...day ? { day: Number(day) } : {},
|
|
23765
|
+
...year ? { year: Number(year) } : {}
|
|
23766
|
+
};
|
|
23767
|
+
}
|
|
23768
|
+
const QUALIFIER_DAY = {
|
|
23769
|
+
early: 7,
|
|
23770
|
+
mid: 15,
|
|
23771
|
+
late: 23
|
|
23772
|
+
};
|
|
23773
|
+
const QUALIFIER_MONTH = {
|
|
23774
|
+
early: [1, 15],
|
|
23775
|
+
mid: [6, 1],
|
|
23776
|
+
late: [10, 15]
|
|
23777
|
+
};
|
|
23778
|
+
/** Earliest and latest local day an endpoint can mean; null when it has no year or is not a real date. */
|
|
23779
|
+
function endpointBounds(date) {
|
|
23780
|
+
const { qualifier, month, day, year } = date;
|
|
23781
|
+
if (year === void 0) return null;
|
|
23782
|
+
if (month === void 0) {
|
|
23783
|
+
if (day !== void 0) return null;
|
|
23784
|
+
if (qualifier) {
|
|
23785
|
+
const [m, d] = QUALIFIER_MONTH[qualifier];
|
|
23786
|
+
return [new Date(year, m, d), new Date(year, m, d)];
|
|
23787
|
+
}
|
|
23788
|
+
return [new Date(year, 0, 1), new Date(year, 11, 31)];
|
|
23789
|
+
}
|
|
23790
|
+
if (day !== void 0) {
|
|
23791
|
+
const point = new Date(year, month, day);
|
|
23792
|
+
if (point.getMonth() !== month) return null;
|
|
23793
|
+
return [point, point];
|
|
23794
|
+
}
|
|
23795
|
+
if (qualifier) {
|
|
23796
|
+
const point = new Date(year, month, QUALIFIER_DAY[qualifier]);
|
|
23797
|
+
return [point, point];
|
|
23798
|
+
}
|
|
23799
|
+
return [new Date(year, month, 1), new Date(year, month + 1, 0)];
|
|
23800
|
+
}
|
|
23801
|
+
/** Parses one alternative: a single date or a two-ended range, filling each end's missing parts from the other. */
|
|
23802
|
+
function parseSpanAlternative(text) {
|
|
23803
|
+
const ends = text.split(new RegExp(`${RANGE_SEP}(?=\\S)`)).filter(Boolean);
|
|
23804
|
+
if (ends.length === 0 || ends.length > 2) return null;
|
|
23805
|
+
const parsed = ends.map(parseEndpoint);
|
|
23806
|
+
if (parsed.some((end) => end === null)) return null;
|
|
23807
|
+
const [first, second = first] = parsed;
|
|
23808
|
+
const from = { ...first };
|
|
23809
|
+
const to = { ...second };
|
|
23810
|
+
const fromYearInferred = from.year === void 0;
|
|
23811
|
+
const toYearInferred = to.year === void 0;
|
|
23812
|
+
from.year ??= to.year;
|
|
23813
|
+
to.year ??= from.year;
|
|
23814
|
+
if (from.month === void 0 && (from.day !== void 0 || from.qualifier) && to.month !== void 0) from.month = to.month;
|
|
23815
|
+
if (to.month === void 0 && to.day !== void 0 && from.month !== void 0) to.month = from.month;
|
|
23816
|
+
if (from.month !== void 0 && to.month !== void 0 && from.month > to.month && from.year === to.year) {
|
|
23817
|
+
if (fromYearInferred) from.year -= 1;
|
|
23818
|
+
else if (toYearInferred) to.year += 1;
|
|
23819
|
+
}
|
|
23820
|
+
const fromBounds = endpointBounds(from);
|
|
23821
|
+
const toBounds = endpointBounds(to);
|
|
23822
|
+
if (!fromBounds || !toBounds) return null;
|
|
23823
|
+
const span = {
|
|
23824
|
+
start: fromBounds[0],
|
|
23825
|
+
end: toBounds[1]
|
|
23826
|
+
};
|
|
23827
|
+
return span.start <= span.end ? span : null;
|
|
23828
|
+
}
|
|
23829
|
+
function normalizeDateText(text) {
|
|
23830
|
+
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) => {
|
|
23831
|
+
const month = Object.keys(MONTH_INDEX)[Number(m) - 1];
|
|
23832
|
+
return month ? `${month} ${Number(d)}, ${y}` : whole;
|
|
23833
|
+
}).replace(new RegExp(TIME, "g"), "").replace(/\b(early|mid|late|to)-/gi, "$1 ").replace(/ {2,}/g, " ").trim();
|
|
23834
|
+
}
|
|
23463
23835
|
/**
|
|
23464
|
-
* Parses a date
|
|
23465
|
-
*
|
|
23836
|
+
* Parses the whole of `text` as a date or date range. Accepts "May 30, 2023",
|
|
23837
|
+
* "Mar 22, 2025 at 18:08", "May 27-28, 2023", "Dec 27–31, 2021", "Mar 1 - Mar 18, 2025",
|
|
23838
|
+
* "Aug 1, 2024 - Feb 28, 2025", "August 2024", "June–July 2022", "late April 2023",
|
|
23839
|
+
* "mid-to-late May 2023", "late 2023", "2035", "2021–2026", an "approx." prefix, and
|
|
23840
|
+
* "A or B" alternatives (their union). Returns null when no year is known.
|
|
23466
23841
|
*/
|
|
23467
|
-
function
|
|
23468
|
-
|
|
23469
|
-
|
|
23470
|
-
|
|
23471
|
-
|
|
23472
|
-
|
|
23473
|
-
|
|
23474
|
-
|
|
23475
|
-
|
|
23476
|
-
|
|
23477
|
-
|
|
23478
|
-
|
|
23479
|
-
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
|
|
23484
|
-
|
|
23485
|
-
|
|
23486
|
-
|
|
23487
|
-
|
|
23488
|
-
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23494
|
-
if (!
|
|
23495
|
-
|
|
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;
|
|
23842
|
+
function parseDateSpan(text) {
|
|
23843
|
+
const alternatives = normalizeDateText(text).split(/\s+or\s+/i).map(parseSpanAlternative);
|
|
23844
|
+
if (alternatives.length === 0 || alternatives.some((span) => span === null)) return null;
|
|
23845
|
+
const spans = alternatives;
|
|
23846
|
+
return {
|
|
23847
|
+
start: new Date(Math.min(...spans.map((span) => span.start.getTime()))),
|
|
23848
|
+
end: new Date(Math.max(...spans.map((span) => span.end.getTime())))
|
|
23849
|
+
};
|
|
23850
|
+
}
|
|
23851
|
+
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");
|
|
23852
|
+
/**
|
|
23853
|
+
* Like `parseDateSpan`, but falls back to the first dated expression inside longer text,
|
|
23854
|
+
* then to a year on its own ("by late 2027", "$500 in 2024"). Only for text known to be a date.
|
|
23855
|
+
*/
|
|
23856
|
+
function findDateSpan(text) {
|
|
23857
|
+
const whole = parseDateSpan(text);
|
|
23858
|
+
if (whole) return whole;
|
|
23859
|
+
for (const pattern of [FREE_TEXT_DATE, QUALIFIED_YEAR]) for (const match of text.matchAll(pattern)) {
|
|
23860
|
+
const span = parseDateSpan(match[0]);
|
|
23861
|
+
if (span) return span;
|
|
23862
|
+
}
|
|
23863
|
+
return null;
|
|
23864
|
+
}
|
|
23865
|
+
function relativeSpan(span, today) {
|
|
23866
|
+
const start = formatRelativeDays(today - calendarDay(span.start));
|
|
23867
|
+
const end = formatRelativeDays(today - calendarDay(span.end));
|
|
23868
|
+
if (start === end) return start;
|
|
23869
|
+
if (!end.startsWith("in ")) return `${start} to ${end}`;
|
|
23870
|
+
return start.startsWith("in ") ? `${start} to ${end.slice(3)}` : `${start} to ${end.slice(3)} from now`;
|
|
23502
23871
|
}
|
|
23503
23872
|
/**
|
|
23504
23873
|
* Detects if an observation line indicates future intent (will do, plans to, looking forward to, etc.)
|
|
@@ -23518,40 +23887,59 @@ function isFutureIntentObservation(line) {
|
|
|
23518
23887
|
}
|
|
23519
23888
|
/**
|
|
23520
23889
|
* 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 -
|
|
23890
|
+
* Matches patterns like "(estimated May 27-28, 2023)" or "(meaning May 30, 2023 at 3:00 PM)"
|
|
23891
|
+
* and expands them to "(meaning May 30, 2023 - 3 weeks ago)". Notes without a year are left alone.
|
|
23523
23892
|
*/
|
|
23524
|
-
function expandInlineEstimatedDates(observations, currentDate) {
|
|
23525
|
-
|
|
23526
|
-
|
|
23527
|
-
|
|
23528
|
-
|
|
23529
|
-
|
|
23530
|
-
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23535
|
-
}
|
|
23536
|
-
|
|
23893
|
+
function expandInlineEstimatedDates(observations, currentDate, timeZone) {
|
|
23894
|
+
const inlineDateRegex = /\((estimated|meaning)\s([^()]*)\)/gi;
|
|
23895
|
+
const today = calendarDayIn(currentDate, timeZone);
|
|
23896
|
+
return observations.replace(inlineDateRegex, (match, prefix, noteText, offset) => {
|
|
23897
|
+
const dateContent = noteText.trimStart();
|
|
23898
|
+
const span = findDateSpan(dateContent);
|
|
23899
|
+
if (!span) return match;
|
|
23900
|
+
const relative = relativeSpan(span, today);
|
|
23901
|
+
const lineStart = observations.lastIndexOf("\n", offset) + 1;
|
|
23902
|
+
const lineBeforeDate = observations.slice(lineStart, offset);
|
|
23903
|
+
if (calendarDay(span.end) < today && isFutureIntentObservation(lineBeforeDate)) return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
|
|
23904
|
+
return `(${prefix} ${dateContent} - ${relative})`;
|
|
23905
|
+
});
|
|
23906
|
+
}
|
|
23907
|
+
/**
|
|
23908
|
+
* Annotates dates written into observation text, e.g. "exam on January 10, 2024" becomes
|
|
23909
|
+
* "exam on January 10, 2024 (5 months ago)". Only dates that state their year are annotated;
|
|
23910
|
+
* "Date:" headers and "(meaning/estimated …)" notes are handled separately and skipped here.
|
|
23911
|
+
*/
|
|
23912
|
+
function annotateObservationTextDates(observations, currentDate, timeZone) {
|
|
23913
|
+
const regex = new RegExp(String.raw`^Date:.*$|\((?:[Ee]stimated|[Mm]eaning)\b[^()]*\)|${FREE_TEXT_DATE.source}`, "gm");
|
|
23914
|
+
const today = calendarDayIn(currentDate, timeZone);
|
|
23915
|
+
return observations.replace(regex, (match, offset) => {
|
|
23916
|
+
if (match.startsWith("Date:") || match.startsWith("(")) return match;
|
|
23917
|
+
const span = parseDateSpan(match);
|
|
23918
|
+
if (!span) return match;
|
|
23919
|
+
const relative = relativeSpan(span, today);
|
|
23920
|
+
return observations[offset + match.length] === ")" ? `${match} - ${relative}` : `${match} (${relative})`;
|
|
23537
23921
|
});
|
|
23538
23922
|
}
|
|
23539
23923
|
/**
|
|
23540
23924
|
* Add relative time annotations to observations.
|
|
23541
|
-
* Transforms "Date: May 15, 2023" headers to "Date: May 15, 2023 (5 days ago)"
|
|
23542
|
-
*
|
|
23925
|
+
* Transforms "Date: May 15, 2023" headers to "Date: May 15, 2023 (5 days ago)", range headers such as
|
|
23926
|
+
* "Date: Aug 1, 2024 - Feb 28, 2025" to "(7 months ago to 4 weeks ago)", and annotates inline dates.
|
|
23927
|
+
*
|
|
23928
|
+
* `timeZone` is the zone the Observer wrote its dates in (the record's `observedTimezone`). "Today" is
|
|
23929
|
+
* `currentDate`'s calendar date in that zone, so the result doesn't depend on the zone of the process rendering it.
|
|
23543
23930
|
*/
|
|
23544
|
-
function addRelativeTimeToObservations(observations, currentDate) {
|
|
23545
|
-
const
|
|
23546
|
-
const
|
|
23931
|
+
function addRelativeTimeToObservations(observations, currentDate, timeZone) {
|
|
23932
|
+
const today = calendarDayIn(currentDate, timeZone);
|
|
23933
|
+
const withInlineDates = annotateObservationTextDates(expandInlineEstimatedDates(observations, currentDate, timeZone), currentDate, timeZone);
|
|
23934
|
+
const dateHeaderRegex = /^(Date:[ \t]*)(.*)$/gm;
|
|
23547
23935
|
const dates = [];
|
|
23548
23936
|
let regexMatch;
|
|
23549
23937
|
while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
|
|
23550
|
-
const dateStr = regexMatch[2];
|
|
23551
|
-
const
|
|
23552
|
-
if (
|
|
23938
|
+
const dateStr = regexMatch[2].trimEnd();
|
|
23939
|
+
const span = parseDateSpan(dateStr);
|
|
23940
|
+
if (span) dates.push({
|
|
23553
23941
|
index: regexMatch.index,
|
|
23554
|
-
|
|
23942
|
+
span,
|
|
23555
23943
|
match: regexMatch[0],
|
|
23556
23944
|
prefix: regexMatch[1],
|
|
23557
23945
|
dateStr
|
|
@@ -23565,11 +23953,10 @@ function addRelativeTimeToObservations(observations, currentDate) {
|
|
|
23565
23953
|
const prev = i > 0 ? dates[i - 1] : null;
|
|
23566
23954
|
result += withInlineDates.slice(lastIndex, curr.index);
|
|
23567
23955
|
if (prev) {
|
|
23568
|
-
const gap = formatGapBetweenDates(prev.
|
|
23956
|
+
const gap = formatGapBetweenDates(prev.span.end, curr.span.start);
|
|
23569
23957
|
if (gap) result += `\n${gap}\n\n`;
|
|
23570
23958
|
}
|
|
23571
|
-
|
|
23572
|
-
result += `${curr.prefix}${curr.dateStr} (${relative})`;
|
|
23959
|
+
result += `${curr.prefix}${curr.dateStr} (${relativeSpan(curr.span, today)})`;
|
|
23573
23960
|
lastIndex = curr.index + curr.match.length;
|
|
23574
23961
|
}
|
|
23575
23962
|
result += withInlineDates.slice(lastIndex);
|
|
@@ -24574,11 +24961,20 @@ const OBSERVER_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
24574
24961
|
"heif",
|
|
24575
24962
|
"avif"
|
|
24576
24963
|
]);
|
|
24577
|
-
function formatObserverDate(createdAt) {
|
|
24578
|
-
|
|
24964
|
+
function formatObserverDate(createdAt, timeZone) {
|
|
24965
|
+
if (!createdAt) return "";
|
|
24966
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
24967
|
+
timeZone,
|
|
24968
|
+
year: "numeric",
|
|
24969
|
+
month: "short",
|
|
24970
|
+
day: "numeric"
|
|
24971
|
+
}).formatToParts(createdAt);
|
|
24972
|
+
const part = (type) => parts.find((p) => p.type === type)?.value;
|
|
24973
|
+
return `${part("month")} ${part("day")} ${part("year")}`;
|
|
24579
24974
|
}
|
|
24580
|
-
function formatObserverTime(createdAt) {
|
|
24975
|
+
function formatObserverTime(createdAt, timeZone) {
|
|
24581
24976
|
return createdAt ? createdAt.toLocaleTimeString("en-US", {
|
|
24977
|
+
timeZone,
|
|
24582
24978
|
hour: "numeric",
|
|
24583
24979
|
minute: "2-digit",
|
|
24584
24980
|
hour12: true
|
|
@@ -24668,11 +25064,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
|
|
|
24668
25064
|
const label = resolveObserverAttachmentLabel(part);
|
|
24669
25065
|
return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
|
|
24670
25066
|
}
|
|
24671
|
-
function isRecord$
|
|
25067
|
+
function isRecord$2(value) {
|
|
24672
25068
|
return !!value && typeof value === "object";
|
|
24673
25069
|
}
|
|
24674
25070
|
function mapToolResultBlockToAttachment(block) {
|
|
24675
|
-
if (!isRecord$
|
|
25071
|
+
if (!isRecord$2(block) || typeof block.type !== "string") return;
|
|
24676
25072
|
const mediaType = typeof block.mediaType === "string" ? block.mediaType : void 0;
|
|
24677
25073
|
const filename = typeof block.filename === "string" ? block.filename : void 0;
|
|
24678
25074
|
switch (block.type) {
|
|
@@ -24733,7 +25129,7 @@ function mapToolResultBlockToAttachment(block) {
|
|
|
24733
25129
|
}
|
|
24734
25130
|
}
|
|
24735
25131
|
function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
24736
|
-
if (!isRecord$
|
|
25132
|
+
if (!isRecord$2(result) || result.type !== "content" || !Array.isArray(result.value)) return {
|
|
24737
25133
|
resultWithoutAttachments: result,
|
|
24738
25134
|
attachments: []
|
|
24739
25135
|
};
|
|
@@ -24747,7 +25143,7 @@ function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
|
24747
25143
|
if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) attachments.push(toObserverInputAttachmentPart(attachment));
|
|
24748
25144
|
const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
|
|
24749
25145
|
return {
|
|
24750
|
-
type: isRecord$
|
|
25146
|
+
type: isRecord$2(block) ? block.type : void 0,
|
|
24751
25147
|
placeholder
|
|
24752
25148
|
};
|
|
24753
25149
|
});
|
|
@@ -24864,6 +25260,7 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
24864
25260
|
const maxLen = options?.maxPartLength;
|
|
24865
25261
|
const maxToolResultTokens = options?.maxToolResultTokens ?? 5e3;
|
|
24866
25262
|
const attachmentFilter = options?.attachmentFilter;
|
|
25263
|
+
const timeZone = resolveTimeZone(options?.timeZone);
|
|
24867
25264
|
const role = getObserverMessageLabel(msg);
|
|
24868
25265
|
const attachments = [];
|
|
24869
25266
|
const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
|
|
@@ -24873,8 +25270,8 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
24873
25270
|
if (!body) return;
|
|
24874
25271
|
const normalizedCreatedAt = normalizeObserverCreatedAt(createdAt) ?? messageCreatedAt;
|
|
24875
25272
|
lines.push({
|
|
24876
|
-
date: formatObserverDate(normalizedCreatedAt),
|
|
24877
|
-
time: formatObserverTime(normalizedCreatedAt),
|
|
25273
|
+
date: formatObserverDate(normalizedCreatedAt, timeZone),
|
|
25274
|
+
time: formatObserverTime(normalizedCreatedAt, timeZone),
|
|
24878
25275
|
title,
|
|
24879
25276
|
body
|
|
24880
25277
|
});
|
|
@@ -25427,169 +25824,6 @@ function optimizeObservationsForContext(observations) {
|
|
|
25427
25824
|
optimized = optimized.replace(/\n{3,}/g, "\n\n");
|
|
25428
25825
|
return optimized.trim();
|
|
25429
25826
|
}
|
|
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
25827
|
function createTemporaryOmMemoryContext(prefix) {
|
|
25594
25828
|
const options = {
|
|
25595
25829
|
thread: `${prefix}-${(0, crypto$1.randomUUID)()}`,
|
|
@@ -25724,11 +25958,27 @@ var ObserverRunner = class {
|
|
|
25724
25958
|
this.mastra = mastra;
|
|
25725
25959
|
}
|
|
25726
25960
|
createAgent(model, isMultiThread = false, memory, extractors = this.observationConfig.extractors ?? []) {
|
|
25961
|
+
let agentModel = model;
|
|
25962
|
+
if (Array.isArray(agentModel)) agentModel = agentModel.map((fallback) => ({
|
|
25963
|
+
...fallback,
|
|
25964
|
+
maxRetries: 0
|
|
25965
|
+
}));
|
|
25966
|
+
else if (typeof agentModel === "function") {
|
|
25967
|
+
const resolveDynamicModel = agentModel;
|
|
25968
|
+
agentModel = (async (args) => {
|
|
25969
|
+
const resolvedModel = await resolveDynamicModel(args);
|
|
25970
|
+
return Array.isArray(resolvedModel) ? resolvedModel.map((fallback) => ({
|
|
25971
|
+
...fallback,
|
|
25972
|
+
maxRetries: 0
|
|
25973
|
+
})) : resolvedModel;
|
|
25974
|
+
});
|
|
25975
|
+
}
|
|
25727
25976
|
return new _mastra_core_agent.Agent({
|
|
25728
25977
|
id: isMultiThread ? "multi-thread-observer" : "observational-memory-observer",
|
|
25729
25978
|
name: isMultiThread ? "multi-thread-observer" : "Observer",
|
|
25979
|
+
maxRetries: 0,
|
|
25730
25980
|
instructions: buildObserverSystemPrompt(isMultiThread, this.observationConfig.instruction, this.observationConfig.threadTitle, extractors),
|
|
25731
|
-
model,
|
|
25981
|
+
model: agentModel,
|
|
25732
25982
|
...memory ? { memory } : {},
|
|
25733
25983
|
...this.mastra ? { mastra: this.mastra } : {}
|
|
25734
25984
|
});
|
|
@@ -25820,7 +26070,10 @@ var ObserverRunner = class {
|
|
|
25820
26070
|
...options,
|
|
25821
26071
|
includeThreadTitle: this.observationConfig.threadTitle,
|
|
25822
26072
|
extractors: activeExtractors
|
|
25823
|
-
}, {
|
|
26073
|
+
}, {
|
|
26074
|
+
attachmentFilter,
|
|
26075
|
+
timeZone: options?.timeZone
|
|
26076
|
+
})];
|
|
25824
26077
|
const doGenerate = async () => {
|
|
25825
26078
|
return withRetry(() => withOmTracingSpan({
|
|
25826
26079
|
phase: "observer",
|
|
@@ -25858,12 +26111,14 @@ var ObserverRunner = class {
|
|
|
25858
26111
|
hasRequestContext: Boolean(internalRequestContext),
|
|
25859
26112
|
aborted: abortSignal?.aborted ?? false
|
|
25860
26113
|
});
|
|
25861
|
-
throw error;
|
|
26114
|
+
if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
|
|
26115
|
+
throw new OmModelExecutionError("observer-model", error);
|
|
25862
26116
|
}
|
|
25863
26117
|
}, abortSignal)
|
|
25864
26118
|
}), {
|
|
25865
26119
|
label: "observer",
|
|
25866
|
-
abortSignal
|
|
26120
|
+
abortSignal,
|
|
26121
|
+
maxRetries: this.observationConfig.maxRetries
|
|
25867
26122
|
});
|
|
25868
26123
|
};
|
|
25869
26124
|
let result = await doGenerate();
|
|
@@ -25931,7 +26186,7 @@ var ObserverRunner = class {
|
|
|
25931
26186
|
/**
|
|
25932
26187
|
* Call the Observer agent for multiple threads in a single batched request.
|
|
25933
26188
|
*/
|
|
25934
|
-
async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext) {
|
|
26189
|
+
async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext, timeZone) {
|
|
25935
26190
|
const contextFor = (threadId) => ({
|
|
25936
26191
|
threadId,
|
|
25937
26192
|
...hookContext
|
|
@@ -25942,7 +26197,7 @@ var ObserverRunner = class {
|
|
|
25942
26197
|
if (filtered.length > 0) messagesByThread.set(threadId, filtered);
|
|
25943
26198
|
}
|
|
25944
26199
|
const threadOrder = allThreadOrder.filter((threadId) => messagesByThread.has(threadId));
|
|
25945
|
-
const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model);
|
|
26200
|
+
const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone);
|
|
25946
26201
|
for (const threadId of allThreadOrder) {
|
|
25947
26202
|
const threadResult = output.results.get(threadId);
|
|
25948
26203
|
if (!threadResult) {
|
|
@@ -25953,7 +26208,7 @@ var ObserverRunner = class {
|
|
|
25953
26208
|
}
|
|
25954
26209
|
return output;
|
|
25955
26210
|
}
|
|
25956
|
-
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model) {
|
|
26211
|
+
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone) {
|
|
25957
26212
|
if (threadOrder.length === 0) return { results: /* @__PURE__ */ new Map() };
|
|
25958
26213
|
const inputTokens = Array.from(messagesByThread.values()).reduce((total, messages) => total + this.tokenCounter.countMessages(messages), 0);
|
|
25959
26214
|
const resolvedModel = (() => {
|
|
@@ -25995,7 +26250,8 @@ var ObserverRunner = class {
|
|
|
25995
26250
|
priorSuggestedResponse: priorMetadataByThread?.get(threadId)?.suggestedResponse,
|
|
25996
26251
|
priorThreadTitle: priorMetadataByThread?.get(threadId)?.threadTitle,
|
|
25997
26252
|
priorExtractedValues: priorMetadataByThread?.get(threadId)?.extracted,
|
|
25998
|
-
model: resolvedModel.model
|
|
26253
|
+
model: resolvedModel.model,
|
|
26254
|
+
timeZone
|
|
25999
26255
|
});
|
|
26000
26256
|
results.set(threadId, {
|
|
26001
26257
|
observations: threadResult.observations,
|
|
@@ -26012,6 +26268,7 @@ var ObserverRunner = class {
|
|
|
26012
26268
|
totalUsage.totalTokens += threadResult.usage.totalTokens ?? 0;
|
|
26013
26269
|
}
|
|
26014
26270
|
}
|
|
26271
|
+
for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
|
|
26015
26272
|
return {
|
|
26016
26273
|
results,
|
|
26017
26274
|
usage: totalUsage
|
|
@@ -26020,8 +26277,10 @@ var ObserverRunner = class {
|
|
|
26020
26277
|
const agent = this.createAgent(resolvedModel.model, true, void 0, activeExtractors);
|
|
26021
26278
|
const internalRequestContext = withOmInternalThreadId(requestContext, agent.id);
|
|
26022
26279
|
const multiThreadAttachmentFilter = this.resolveAttachmentFilter(resolvedModel.model, requestContext);
|
|
26023
|
-
const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, void 0, this.observationConfig.threadTitle, activeExtractors, {
|
|
26024
|
-
|
|
26280
|
+
const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, void 0, this.observationConfig.threadTitle, activeExtractors, {
|
|
26281
|
+
attachmentFilter: multiThreadAttachmentFilter,
|
|
26282
|
+
timeZone
|
|
26283
|
+
})];
|
|
26025
26284
|
const doGenerate = async () => {
|
|
26026
26285
|
return withRetry(() => withOmTracingSpan({
|
|
26027
26286
|
phase: "observer-multi-thread",
|
|
@@ -26059,12 +26318,14 @@ var ObserverRunner = class {
|
|
|
26059
26318
|
hasRequestContext: Boolean(internalRequestContext),
|
|
26060
26319
|
aborted: abortSignal?.aborted ?? false
|
|
26061
26320
|
});
|
|
26062
|
-
throw error;
|
|
26321
|
+
if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
|
|
26322
|
+
throw new OmModelExecutionError("observer-model", error);
|
|
26063
26323
|
}
|
|
26064
26324
|
}, abortSignal)
|
|
26065
26325
|
}), {
|
|
26066
26326
|
label: "observer-multi-thread",
|
|
26067
|
-
abortSignal
|
|
26327
|
+
abortSignal,
|
|
26328
|
+
maxRetries: this.observationConfig.maxRetries
|
|
26068
26329
|
});
|
|
26069
26330
|
};
|
|
26070
26331
|
let result = await doGenerate();
|
|
@@ -26120,6 +26381,7 @@ var ObserverRunner = class {
|
|
|
26120
26381
|
});
|
|
26121
26382
|
}
|
|
26122
26383
|
for (const threadId of threadOrder) if (!results.has(threadId)) results.set(threadId, { observations: "" });
|
|
26384
|
+
for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
|
|
26123
26385
|
const usage = result.totalUsage ?? result.usage;
|
|
26124
26386
|
return {
|
|
26125
26387
|
results,
|
|
@@ -27607,6 +27869,8 @@ async function summarizeConversation(opts) {
|
|
|
27607
27869
|
const runner = new ObserverRunner({
|
|
27608
27870
|
observationConfig: {
|
|
27609
27871
|
model: opts.model,
|
|
27872
|
+
maxRetries: 8,
|
|
27873
|
+
failurePolicy: "abort",
|
|
27610
27874
|
messageTokens: OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens,
|
|
27611
27875
|
shareTokenBudget: false,
|
|
27612
27876
|
modelSettings: { ...OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings },
|
|
@@ -29417,6 +29681,7 @@ function createObservationFailedMarker(params) {
|
|
|
29417
29681
|
durationMs,
|
|
29418
29682
|
tokensAttempted: params.tokensAttempted,
|
|
29419
29683
|
error: formatOmError(params.error),
|
|
29684
|
+
...getOmFailureMetadata(params.error, params.failurePolicy ?? "abort"),
|
|
29420
29685
|
recordId: params.recordId,
|
|
29421
29686
|
threadId: params.threadId
|
|
29422
29687
|
}
|
|
@@ -29478,6 +29743,7 @@ function createBufferingFailedMarker(params) {
|
|
|
29478
29743
|
durationMs,
|
|
29479
29744
|
tokensAttempted: params.tokensAttempted,
|
|
29480
29745
|
error: formatOmError(params.error),
|
|
29746
|
+
...getOmFailureMetadata(params.error, params.failurePolicy ?? "abort"),
|
|
29481
29747
|
recordId: params.recordId,
|
|
29482
29748
|
threadId: params.threadId
|
|
29483
29749
|
}
|
|
@@ -29532,7 +29798,10 @@ function createThreadUpdateMarker(params) {
|
|
|
29532
29798
|
function findLastCompletedObservationBoundary(message) {
|
|
29533
29799
|
const parts = message.content?.parts;
|
|
29534
29800
|
if (!parts || !Array.isArray(parts)) return -1;
|
|
29535
|
-
for (let i = parts.length - 1; i >= 0; i--)
|
|
29801
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
29802
|
+
const part = parts[i];
|
|
29803
|
+
if (part?.type === "data-om-observation-end" && part.data?.operationType !== "reflection") return i;
|
|
29804
|
+
}
|
|
29536
29805
|
return -1;
|
|
29537
29806
|
}
|
|
29538
29807
|
/**
|
|
@@ -29782,6 +30051,7 @@ function scanObservationGroupTags(observations) {
|
|
|
29782
30051
|
const incompleteOpenings = [];
|
|
29783
30052
|
let cursor = 0;
|
|
29784
30053
|
let closeFloor = 0;
|
|
30054
|
+
let nestedFloor = -1;
|
|
29785
30055
|
while (cursor < observations.length) {
|
|
29786
30056
|
const start = observations.indexOf(OBSERVATION_GROUP_OPEN, cursor);
|
|
29787
30057
|
if (start === -1) break;
|
|
@@ -29802,8 +30072,12 @@ function scanObservationGroupTags(observations) {
|
|
|
29802
30072
|
cursor = openEnd + 1;
|
|
29803
30073
|
continue;
|
|
29804
30074
|
}
|
|
29805
|
-
|
|
29806
|
-
|
|
30075
|
+
if (nestedFloor <= openEnd) {
|
|
30076
|
+
const lineOpen = observations.indexOf(`\n${OBSERVATION_GROUP_OPEN}`, openEnd);
|
|
30077
|
+
nestedFloor = lineOpen === -1 ? observations.length : lineOpen + 1;
|
|
30078
|
+
}
|
|
30079
|
+
const nestedStart = nestedFloor;
|
|
30080
|
+
if (nestedStart < closeStart) {
|
|
29807
30081
|
incompleteOpenings.push({
|
|
29808
30082
|
start,
|
|
29809
30083
|
end: openEnd + 1
|
|
@@ -30186,6 +30460,7 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
30186
30460
|
observationTokens: processed.observationTokens,
|
|
30187
30461
|
threadId,
|
|
30188
30462
|
writer,
|
|
30463
|
+
messageList: this.opts.messageList,
|
|
30189
30464
|
abortSignal,
|
|
30190
30465
|
mainAgent: this.opts.agent,
|
|
30191
30466
|
sendSignal: this.opts.sendSignal,
|
|
@@ -30210,6 +30485,7 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
30210
30485
|
operationType: "observation",
|
|
30211
30486
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30212
30487
|
error: formatOmError(error),
|
|
30488
|
+
...getOmFailureMetadata(error, this.observationConfig.failurePolicy),
|
|
30213
30489
|
recordId: record.id,
|
|
30214
30490
|
threadId
|
|
30215
30491
|
}
|
|
@@ -30223,6 +30499,10 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
30223
30499
|
};
|
|
30224
30500
|
}
|
|
30225
30501
|
omError("[OM] Observation failed", error);
|
|
30502
|
+
if (this.observationConfig.failurePolicy === "continue" && isOmModelExecutionError(error) && error.failureKind === "observer-model") return {
|
|
30503
|
+
observed: false,
|
|
30504
|
+
error
|
|
30505
|
+
};
|
|
30226
30506
|
throw error;
|
|
30227
30507
|
}
|
|
30228
30508
|
}
|
|
@@ -30488,7 +30768,8 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
30488
30768
|
threadId: this.opts.threadId,
|
|
30489
30769
|
resourceId: this.opts.resourceId,
|
|
30490
30770
|
trigger: this.opts.trigger,
|
|
30491
|
-
mainAgent: this.opts.agent
|
|
30771
|
+
mainAgent: this.opts.agent,
|
|
30772
|
+
timeZone: this.opts.record.observedTimezone
|
|
30492
30773
|
});
|
|
30493
30774
|
const hookedValues = await applyExtractorHooks({
|
|
30494
30775
|
source: "observer",
|
|
@@ -30497,7 +30778,10 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
30497
30778
|
failures: result.extractionFailures,
|
|
30498
30779
|
previousValues: this.priorExtractedValues,
|
|
30499
30780
|
rawObservations: result.observations,
|
|
30500
|
-
recentMessages: formatMessagesForObserver(this.opts.messages, {
|
|
30781
|
+
recentMessages: formatMessagesForObserver(this.opts.messages, {
|
|
30782
|
+
maxPartLength: 500,
|
|
30783
|
+
timeZone: this.opts.record.observedTimezone
|
|
30784
|
+
}),
|
|
30501
30785
|
threadId: this.opts.threadId,
|
|
30502
30786
|
resourceId: this.opts.resourceId,
|
|
30503
30787
|
mainAgent: this.opts.agent,
|
|
@@ -30628,6 +30912,7 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
30628
30912
|
startedAt: this.startedAt,
|
|
30629
30913
|
tokensAttempted: this.tokensToObserve,
|
|
30630
30914
|
error,
|
|
30915
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
30631
30916
|
recordId: this.opts.record.id,
|
|
30632
30917
|
threadId: this.opts.threadId
|
|
30633
30918
|
});
|
|
@@ -30677,7 +30962,8 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
30677
30962
|
threadId: this.opts.threadId,
|
|
30678
30963
|
resourceId: this.opts.resourceId,
|
|
30679
30964
|
trigger: this.opts.trigger,
|
|
30680
|
-
mainAgent: this.opts.agent
|
|
30965
|
+
mainAgent: this.opts.agent,
|
|
30966
|
+
timeZone: this.opts.record.observedTimezone
|
|
30681
30967
|
});
|
|
30682
30968
|
const hookedValues = await applyExtractorHooks({
|
|
30683
30969
|
source: "observer",
|
|
@@ -30686,7 +30972,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
30686
30972
|
failures: result.extractionFailures,
|
|
30687
30973
|
previousValues: this.priorExtractedValues,
|
|
30688
30974
|
rawObservations: result.observations,
|
|
30689
|
-
recentMessages: formatMessagesForObserver(messages, {
|
|
30975
|
+
recentMessages: formatMessagesForObserver(messages, {
|
|
30976
|
+
maxPartLength: 500,
|
|
30977
|
+
timeZone: this.opts.record.observedTimezone
|
|
30978
|
+
}),
|
|
30690
30979
|
threadId: this.opts.threadId,
|
|
30691
30980
|
resourceId: this.opts.resourceId,
|
|
30692
30981
|
mainAgent: this.opts.agent,
|
|
@@ -30832,6 +31121,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
30832
31121
|
startedAt: this.startedAt,
|
|
30833
31122
|
tokensAttempted,
|
|
30834
31123
|
error,
|
|
31124
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
30835
31125
|
recordId: record.id,
|
|
30836
31126
|
threadId
|
|
30837
31127
|
});
|
|
@@ -31006,7 +31296,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
31006
31296
|
return this.deps.observer.callMultiThread(_existingObservations, batch.threadMap, batch.threadIds, this.opts.abortSignal, this.opts.requestContext, this.priorMetadataByThread, this.opts.observabilityContext, void 0, {
|
|
31007
31297
|
resourceId: this.opts.resourceId,
|
|
31008
31298
|
trigger: this.opts.trigger
|
|
31009
|
-
});
|
|
31299
|
+
}, this.opts.record.observedTimezone);
|
|
31010
31300
|
}));
|
|
31011
31301
|
for (const batchResult of batchResults) {
|
|
31012
31302
|
for (const [threadId, result] of batchResult.results) this.multiThreadResults.set(threadId, result);
|
|
@@ -31039,7 +31329,10 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
31039
31329
|
failures: result.extractionFailures,
|
|
31040
31330
|
previousValues,
|
|
31041
31331
|
rawObservations: result.observations,
|
|
31042
|
-
recentMessages: formatMessagesForObserver(threadMessages, {
|
|
31332
|
+
recentMessages: formatMessagesForObserver(threadMessages, {
|
|
31333
|
+
maxPartLength: 500,
|
|
31334
|
+
timeZone: this.opts.record.observedTimezone
|
|
31335
|
+
}),
|
|
31043
31336
|
threadId,
|
|
31044
31337
|
resourceId: this.resourceId,
|
|
31045
31338
|
mainAgent: this.opts.agent,
|
|
@@ -31198,6 +31491,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
31198
31491
|
startedAt: this.startedAt,
|
|
31199
31492
|
tokensAttempted,
|
|
31200
31493
|
error,
|
|
31494
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
31201
31495
|
recordId: this.opts.record.id,
|
|
31202
31496
|
threadId
|
|
31203
31497
|
});
|
|
@@ -31604,19 +31898,28 @@ var ObservationStep = class {
|
|
|
31604
31898
|
};
|
|
31605
31899
|
}
|
|
31606
31900
|
}
|
|
31607
|
-
|
|
31608
|
-
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
31612
|
-
|
|
31613
|
-
|
|
31614
|
-
|
|
31615
|
-
|
|
31616
|
-
|
|
31617
|
-
|
|
31618
|
-
|
|
31619
|
-
|
|
31901
|
+
let obsResult;
|
|
31902
|
+
try {
|
|
31903
|
+
obsResult = await om.observe({
|
|
31904
|
+
threadId,
|
|
31905
|
+
resourceId,
|
|
31906
|
+
messages: observableMessages,
|
|
31907
|
+
messageList,
|
|
31908
|
+
trigger: "turn-sync",
|
|
31909
|
+
agent: this.turn.agent,
|
|
31910
|
+
sendSignal: this.turn.sendSignal,
|
|
31911
|
+
sendStateSignal: this.turn.sendStateSignal,
|
|
31912
|
+
requestContext: this.turn.requestContext,
|
|
31913
|
+
writer: this.turn.writer,
|
|
31914
|
+
observabilityContext: this.turn.observabilityContext
|
|
31915
|
+
});
|
|
31916
|
+
} catch (error) {
|
|
31917
|
+
if (om.config.observation.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "observer-model") throw error;
|
|
31918
|
+
return {
|
|
31919
|
+
succeeded: false,
|
|
31920
|
+
record: freshStatus.record
|
|
31921
|
+
};
|
|
31922
|
+
}
|
|
31620
31923
|
if (obsResult.observed) {
|
|
31621
31924
|
const observedMessageIds = new Set(obsResult.record.observedMessageIds ?? []);
|
|
31622
31925
|
const liveMessages = getObservableMessages(messageList);
|
|
@@ -32268,11 +32571,27 @@ var ReflectorRunner = class {
|
|
|
32268
32571
|
this.mastra = mastra;
|
|
32269
32572
|
}
|
|
32270
32573
|
createAgent(model, memory, extractors = this.reflectionConfig.extractors) {
|
|
32574
|
+
let agentModel = model;
|
|
32575
|
+
if (Array.isArray(agentModel)) agentModel = agentModel.map((fallback) => ({
|
|
32576
|
+
...fallback,
|
|
32577
|
+
maxRetries: 0
|
|
32578
|
+
}));
|
|
32579
|
+
else if (typeof agentModel === "function") {
|
|
32580
|
+
const resolveDynamicModel = agentModel;
|
|
32581
|
+
agentModel = (async (args) => {
|
|
32582
|
+
const resolvedModel = await resolveDynamicModel(args);
|
|
32583
|
+
return Array.isArray(resolvedModel) ? resolvedModel.map((fallback) => ({
|
|
32584
|
+
...fallback,
|
|
32585
|
+
maxRetries: 0
|
|
32586
|
+
})) : resolvedModel;
|
|
32587
|
+
});
|
|
32588
|
+
}
|
|
32271
32589
|
return new _mastra_core_agent.Agent({
|
|
32272
32590
|
id: "observational-memory-reflector",
|
|
32273
32591
|
name: "Reflector",
|
|
32274
32592
|
instructions: buildReflectorSystemPrompt(this.reflectionConfig.instruction, extractors),
|
|
32275
|
-
model,
|
|
32593
|
+
model: agentModel,
|
|
32594
|
+
maxRetries: 0,
|
|
32276
32595
|
...memory ? { memory } : {},
|
|
32277
32596
|
...this.mastra ? { mastra: this.mastra } : {}
|
|
32278
32597
|
});
|
|
@@ -32354,36 +32673,42 @@ var ReflectorRunner = class {
|
|
|
32354
32673
|
},
|
|
32355
32674
|
callback: (childObservabilityContext) => withAbortCheck(async () => {
|
|
32356
32675
|
chunkCount = 0;
|
|
32357
|
-
|
|
32358
|
-
|
|
32359
|
-
|
|
32360
|
-
|
|
32361
|
-
|
|
32362
|
-
|
|
32363
|
-
|
|
32364
|
-
|
|
32365
|
-
|
|
32366
|
-
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
32676
|
+
try {
|
|
32677
|
+
return await (await agent.stream(prompt, {
|
|
32678
|
+
modelSettings: { ...this.reflectionConfig.modelSettings },
|
|
32679
|
+
providerOptions: this.reflectionConfig.providerOptions,
|
|
32680
|
+
...temporaryMemory ? { memory: temporaryMemory.options } : {},
|
|
32681
|
+
...abortSignal ? { abortSignal } : {},
|
|
32682
|
+
...internalRequestContext ? { requestContext: internalRequestContext } : {},
|
|
32683
|
+
...childObservabilityContext,
|
|
32684
|
+
...attemptNumber === 1 ? {
|
|
32685
|
+
onChunk(chunk) {
|
|
32686
|
+
chunkCount++;
|
|
32687
|
+
if (chunkCount === 1 || chunkCount % 50 === 0) {
|
|
32688
|
+
const preview = chunk.type === "text-delta" ? ` text="${chunk.textDelta?.slice(0, 80)}..."` : chunk.type === "tool-call" ? ` tool=${chunk.toolName}` : "";
|
|
32689
|
+
omDebug(`[OM:callReflector] chunk#${chunkCount}: type=${chunk.type}${preview}`);
|
|
32690
|
+
}
|
|
32691
|
+
},
|
|
32692
|
+
onFinish(event) {
|
|
32693
|
+
omDebug(`[OM:callReflector] onFinish: chunks=${chunkCount}, finishReason=${event.finishReason}, inputTokens=${event.usage?.inputTokens}, outputTokens=${event.usage?.outputTokens}, textLen=${event.text?.length}`);
|
|
32694
|
+
},
|
|
32695
|
+
onAbort(event) {
|
|
32696
|
+
omDebug(`[OM:callReflector] onAbort: chunks=${chunkCount}, reason=${event?.reason ?? "unknown"}`);
|
|
32697
|
+
},
|
|
32698
|
+
onError({ error }) {
|
|
32699
|
+
omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
|
|
32370
32700
|
}
|
|
32371
|
-
}
|
|
32372
|
-
|
|
32373
|
-
|
|
32374
|
-
|
|
32375
|
-
|
|
32376
|
-
|
|
32377
|
-
},
|
|
32378
|
-
onError({ error }) {
|
|
32379
|
-
omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
|
|
32380
|
-
}
|
|
32381
|
-
} : {}
|
|
32382
|
-
})).getFullOutput();
|
|
32701
|
+
} : {}
|
|
32702
|
+
})).getFullOutput();
|
|
32703
|
+
} catch (error) {
|
|
32704
|
+
if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
|
|
32705
|
+
throw new OmModelExecutionError("reflector-model", error);
|
|
32706
|
+
}
|
|
32383
32707
|
}, abortSignal)
|
|
32384
32708
|
}), {
|
|
32385
32709
|
label: "reflector",
|
|
32386
|
-
abortSignal
|
|
32710
|
+
abortSignal,
|
|
32711
|
+
maxRetries: this.reflectionConfig.maxRetries
|
|
32387
32712
|
});
|
|
32388
32713
|
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
32714
|
const usage = result.totalUsage ?? result.usage;
|
|
@@ -32507,6 +32832,7 @@ var ReflectorRunner = class {
|
|
|
32507
32832
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32508
32833
|
tokensAttempted: observationTokens,
|
|
32509
32834
|
error,
|
|
32835
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
32510
32836
|
recordId: record.id,
|
|
32511
32837
|
threadId: record.threadId ?? ""
|
|
32512
32838
|
});
|
|
@@ -32919,24 +33245,43 @@ var ReflectorRunner = class {
|
|
|
32919
33245
|
usage: reflectResult.usage
|
|
32920
33246
|
});
|
|
32921
33247
|
} catch (error) {
|
|
32922
|
-
|
|
32923
|
-
|
|
32924
|
-
|
|
32925
|
-
|
|
32926
|
-
|
|
32927
|
-
|
|
32928
|
-
|
|
32929
|
-
|
|
32930
|
-
|
|
32931
|
-
|
|
32932
|
-
|
|
32933
|
-
|
|
32934
|
-
|
|
32935
|
-
|
|
33248
|
+
reflectionError = error instanceof Error ? error : new Error(String(error));
|
|
33249
|
+
const failedMarker = createObservationFailedMarker({
|
|
33250
|
+
cycleId: streamContext?.cycleId ?? cycleId,
|
|
33251
|
+
operationType: "reflection",
|
|
33252
|
+
startedAt: streamContext?.startedAt ?? startedAt,
|
|
33253
|
+
tokensAttempted: observationTokens,
|
|
33254
|
+
error,
|
|
33255
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
33256
|
+
recordId: record.id,
|
|
33257
|
+
threadId
|
|
33258
|
+
});
|
|
33259
|
+
if (writer) await writer.custom({
|
|
33260
|
+
...failedMarker,
|
|
33261
|
+
transient: true
|
|
33262
|
+
}).catch(() => {});
|
|
33263
|
+
let persistedToList = false;
|
|
33264
|
+
try {
|
|
33265
|
+
persistedToList = await this.persistMarkerToMessage(failedMarker, messageList, threadId, record.resourceId ?? void 0);
|
|
33266
|
+
} catch {
|
|
33267
|
+
persistedToList = false;
|
|
33268
|
+
}
|
|
33269
|
+
if (!persistedToList) try {
|
|
32936
33270
|
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
|
|
33271
|
+
} catch (markerError) {
|
|
33272
|
+
omError("[OM] Failed to persist reflection-failed marker", markerError);
|
|
32937
33273
|
}
|
|
32938
|
-
|
|
32939
|
-
|
|
33274
|
+
this.emitDebugEvent({
|
|
33275
|
+
type: "reflection_failed",
|
|
33276
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
33277
|
+
threadId,
|
|
33278
|
+
resourceId: record.resourceId ?? "",
|
|
33279
|
+
inputTokens: observationTokens,
|
|
33280
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
33281
|
+
failureKind: failedMarker.data.failureKind,
|
|
33282
|
+
error: reflectionError.message
|
|
33283
|
+
});
|
|
33284
|
+
if (lifecycleError !== void 0 || abortSignal?.aborted || this.reflectionConfig.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "reflector-model") throw error;
|
|
32940
33285
|
this.syncReflectionSuppression.set(lockKey, observationTokens);
|
|
32941
33286
|
omError("[OM] Reflection failed", error);
|
|
32942
33287
|
} finally {
|
|
@@ -33570,6 +33915,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33570
33915
|
const observationActivateAfterIdlePath = config.observation?.activateAfterIdle !== void 0 ? "observation.activateAfterIdle" : "activateAfterIdle";
|
|
33571
33916
|
this.observationConfig = {
|
|
33572
33917
|
model: observationModel,
|
|
33918
|
+
maxRetries: config.observation?.maxRetries ?? RETRY_CONFIG.maxRetries,
|
|
33919
|
+
failurePolicy: config.observation?.failurePolicy ?? "abort",
|
|
33573
33920
|
messageTokens: isSharedBudget ? {
|
|
33574
33921
|
min: messageTokens,
|
|
33575
33922
|
max: totalBudget
|
|
@@ -33599,6 +33946,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33599
33946
|
};
|
|
33600
33947
|
this.reflectionConfig = {
|
|
33601
33948
|
model: reflectionModel,
|
|
33949
|
+
maxRetries: config.reflection?.maxRetries ?? RETRY_CONFIG.maxRetries,
|
|
33950
|
+
failurePolicy: config.reflection?.failurePolicy ?? "abort",
|
|
33602
33951
|
observationTokens,
|
|
33603
33952
|
shareTokenBudget: isSharedBudget,
|
|
33604
33953
|
modelSettings: {
|
|
@@ -33672,9 +34021,15 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33672
34021
|
retrieval: this.retrieval,
|
|
33673
34022
|
observation: {
|
|
33674
34023
|
messageTokens: this.observationConfig.messageTokens,
|
|
33675
|
-
previousObserverTokens: this.observationConfig.previousObserverTokens
|
|
34024
|
+
previousObserverTokens: this.observationConfig.previousObserverTokens,
|
|
34025
|
+
maxRetries: this.observationConfig.maxRetries,
|
|
34026
|
+
failurePolicy: this.observationConfig.failurePolicy
|
|
33676
34027
|
},
|
|
33677
|
-
reflection: {
|
|
34028
|
+
reflection: {
|
|
34029
|
+
observationTokens: this.reflectionConfig.observationTokens,
|
|
34030
|
+
maxRetries: this.reflectionConfig.maxRetries,
|
|
34031
|
+
failurePolicy: this.reflectionConfig.failurePolicy
|
|
34032
|
+
}
|
|
33678
34033
|
};
|
|
33679
34034
|
}
|
|
33680
34035
|
/**
|
|
@@ -33778,11 +34133,15 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33778
34133
|
messageTokens: this.observationConfig.messageTokens,
|
|
33779
34134
|
model: observationResolved.model,
|
|
33780
34135
|
previousObserverTokens: this.observationConfig.previousObserverTokens,
|
|
34136
|
+
maxRetries: this.observationConfig.maxRetries,
|
|
34137
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
33781
34138
|
routing: observationResolved.routing
|
|
33782
34139
|
},
|
|
33783
34140
|
reflection: {
|
|
33784
34141
|
observationTokens: this.reflectionConfig.observationTokens,
|
|
33785
34142
|
model: reflectionResolved.model,
|
|
34143
|
+
maxRetries: this.reflectionConfig.maxRetries,
|
|
34144
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
33786
34145
|
routing: reflectionResolved.routing
|
|
33787
34146
|
}
|
|
33788
34147
|
};
|
|
@@ -33799,6 +34158,7 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33799
34158
|
* Ensures bufferTokens is less than the threshold and bufferActivation is valid.
|
|
33800
34159
|
*/
|
|
33801
34160
|
validateBufferConfig() {
|
|
34161
|
+
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
34162
|
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
34163
|
const observationThreshold = getMaxThreshold(this.observationConfig.messageTokens);
|
|
33804
34164
|
if (this.observationConfig.bufferTokens !== void 0) {
|
|
@@ -33926,18 +34286,19 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33926
34286
|
};
|
|
33927
34287
|
}
|
|
33928
34288
|
/**
|
|
33929
|
-
* Persist a data-om-* marker part on the
|
|
33930
|
-
*
|
|
33931
|
-
*
|
|
34289
|
+
* Persist a data-om-* marker part on the message that owns the operation in
|
|
34290
|
+
* messageList and save it to the DB. Observation markers belong to the latest
|
|
34291
|
+
* assistant message; reflection markers belong to the current user input.
|
|
33932
34292
|
* @internal Used by ReflectorRunner. Do not call directly.
|
|
33933
34293
|
*/
|
|
33934
34294
|
async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
|
|
33935
|
-
if (!messageList) return;
|
|
33936
|
-
const allMsgs =
|
|
34295
|
+
if (!messageList) return false;
|
|
34296
|
+
const allMsgs = messageList.get.all.db();
|
|
34297
|
+
const markerData = marker.data;
|
|
34298
|
+
const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
|
|
33937
34299
|
for (let i = allMsgs.length - 1; i >= 0; i--) {
|
|
33938
34300
|
const msg = allMsgs[i];
|
|
33939
|
-
if (msg?.role ===
|
|
33940
|
-
const markerData = marker.data;
|
|
34301
|
+
if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
|
|
33941
34302
|
if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
|
|
33942
34303
|
try {
|
|
33943
34304
|
await this.messageHistory.persistMessages({
|
|
@@ -33947,16 +34308,19 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33947
34308
|
});
|
|
33948
34309
|
} catch (e) {
|
|
33949
34310
|
omDebug(`[OM:persistMarker] failed to save marker to DB: ${e}`);
|
|
34311
|
+
return false;
|
|
33950
34312
|
}
|
|
33951
|
-
return;
|
|
34313
|
+
return true;
|
|
33952
34314
|
}
|
|
33953
34315
|
}
|
|
34316
|
+
return false;
|
|
33954
34317
|
}
|
|
33955
34318
|
/**
|
|
33956
|
-
* Persist a marker to the
|
|
33957
|
-
*
|
|
33958
|
-
*
|
|
33959
|
-
*
|
|
34319
|
+
* Persist a marker to the message that owns the operation in storage.
|
|
34320
|
+
* Observation markers belong to the latest assistant message. Reflection runs
|
|
34321
|
+
* before the current assistant reply exists, so its failure marker belongs to
|
|
34322
|
+
* the latest user input that triggered it.
|
|
34323
|
+
* @internal Used by observation strategies and the reflector. Do not call directly.
|
|
33960
34324
|
*/
|
|
33961
34325
|
async persistMarkerToStorage(marker, threadId, resourceId) {
|
|
33962
34326
|
try {
|
|
@@ -33968,8 +34332,9 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
33968
34332
|
direction: "DESC"
|
|
33969
34333
|
}
|
|
33970
34334
|
}))?.messages ?? [];
|
|
33971
|
-
|
|
33972
|
-
|
|
34335
|
+
const markerData = marker.data;
|
|
34336
|
+
const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
|
|
34337
|
+
for (const msg of messages) if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
|
|
33973
34338
|
if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
|
|
33974
34339
|
await this.messageHistory.persistMessages({
|
|
33975
34340
|
messages: [msg],
|
|
@@ -34211,9 +34576,9 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
34211
34576
|
* In resource scope mode, filters continuity messages to only show
|
|
34212
34577
|
* the message for the current thread.
|
|
34213
34578
|
*/
|
|
34214
|
-
formatObservationsForContext(observations, currentTask, suggestedResponse, extractedValues, unobservedContextBlocks, currentDate, retrieval = false) {
|
|
34579
|
+
formatObservationsForContext(observations, currentTask, suggestedResponse, extractedValues, unobservedContextBlocks, currentDate, retrieval = false, timeZone) {
|
|
34215
34580
|
let optimized = retrieval ? renderObservationGroupsForReflection(observations) ?? optimizeObservationsForContext(observations) : optimizeObservationsForContext(observations);
|
|
34216
|
-
if (currentDate) optimized = addRelativeTimeToObservations(optimized, currentDate);
|
|
34581
|
+
if (currentDate) optimized = addRelativeTimeToObservations(optimized, currentDate, timeZone);
|
|
34217
34582
|
const messages = [`${getObservationContextPrompt(this.scope)}\n\n${OBSERVATION_CONTEXT_INSTRUCTIONS}${retrieval ? `\n\n${getRetrievalInstructions(this.retrievalScope, this.retrievalInstructions, this.retrievalSearch)}` : ""}`];
|
|
34218
34583
|
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
34584
|
const observationChunks = this.splitObservationContextChunks(optimized);
|
|
@@ -34313,12 +34678,15 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
34313
34678
|
* These are injected into the Actor's context so it has awareness of activity
|
|
34314
34679
|
* in other threads for the same resource.
|
|
34315
34680
|
*/
|
|
34316
|
-
async formatUnobservedContextBlocks(messagesByThread, currentThreadId) {
|
|
34681
|
+
async formatUnobservedContextBlocks(messagesByThread, currentThreadId, timeZone) {
|
|
34317
34682
|
const blocks = [];
|
|
34318
34683
|
for (const [threadId, messages] of messagesByThread) {
|
|
34319
34684
|
if (threadId === currentThreadId) continue;
|
|
34320
34685
|
if (messages.length === 0) continue;
|
|
34321
|
-
const formattedMessages = formatMessagesForObserver(messages, {
|
|
34686
|
+
const formattedMessages = formatMessagesForObserver(messages, {
|
|
34687
|
+
maxPartLength: 500,
|
|
34688
|
+
timeZone
|
|
34689
|
+
});
|
|
34322
34690
|
if (formattedMessages) {
|
|
34323
34691
|
const obscuredId = await this.representThreadIDInContext(threadId);
|
|
34324
34692
|
blocks.push(`<other-conversation id="${obscuredId}">
|
|
@@ -34495,7 +34863,7 @@ ${formattedMessages}
|
|
|
34495
34863
|
transient: true
|
|
34496
34864
|
}).catch(() => {});
|
|
34497
34865
|
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({
|
|
34866
|
+
if ((await this.runBufferedObservationCycle({
|
|
34499
34867
|
threadId,
|
|
34500
34868
|
resourceId: freshRecord.resourceId ?? void 0,
|
|
34501
34869
|
trigger: "async-buffer"
|
|
@@ -34510,10 +34878,11 @@ ${formattedMessages}
|
|
|
34510
34878
|
requestContext,
|
|
34511
34879
|
observabilityContext,
|
|
34512
34880
|
trigger: "async-buffer"
|
|
34513
|
-
}).run())
|
|
34514
|
-
|
|
34515
|
-
|
|
34516
|
-
|
|
34881
|
+
}).run()))?.observed) {
|
|
34882
|
+
const maxTs = this.getMaxMessageTimestamp(messagesToBuffer);
|
|
34883
|
+
const cursor = new Date(maxTs.getTime() + 1);
|
|
34884
|
+
BufferingCoordinator.lastBufferedAtTime.set(bufferKey, cursor);
|
|
34885
|
+
}
|
|
34517
34886
|
}
|
|
34518
34887
|
/**
|
|
34519
34888
|
* Trigger async buffered observation if the token count has crossed a new interval.
|
|
@@ -34741,7 +35110,7 @@ ${formattedMessages}
|
|
|
34741
35110
|
const currentTask = activeExtractors.some((extractor) => extractor.slug === "current-task") ? omMetadata?.currentTask : void 0;
|
|
34742
35111
|
const suggestedResponse = activeExtractors.some((extractor) => extractor.slug === "suggested-response") ? omMetadata?.suggestedResponse : void 0;
|
|
34743
35112
|
const currentDate = opts.currentDate ?? /* @__PURE__ */ new Date();
|
|
34744
|
-
return this.formatObservationsForContext(record.activeObservations, currentTask, suggestedResponse, omMetadata?.extracted, unobservedContextBlocks, currentDate, this.retrieval);
|
|
35113
|
+
return this.formatObservationsForContext(record.activeObservations, currentTask, suggestedResponse, omMetadata?.extracted, unobservedContextBlocks, currentDate, this.retrieval, record.observedTimezone);
|
|
34745
35114
|
}
|
|
34746
35115
|
/**
|
|
34747
35116
|
* Get unobserved messages from other threads for resource-scoped observation.
|
|
@@ -34753,7 +35122,8 @@ ${formattedMessages}
|
|
|
34753
35122
|
async getOtherThreadsContext(resourceId, currentThreadId) {
|
|
34754
35123
|
const { threads: allThreads } = await this.storage.listThreads({ filter: { resourceId } });
|
|
34755
35124
|
const messagesByThread = /* @__PURE__ */ new Map();
|
|
34756
|
-
const
|
|
35125
|
+
const record = await this.getRecord(currentThreadId, resourceId);
|
|
35126
|
+
const recordLastObservedAt = record?.lastObservedAt;
|
|
34757
35127
|
for (const thread of allThreads) {
|
|
34758
35128
|
if (thread.id === currentThreadId) continue;
|
|
34759
35129
|
const threadLastObservedAt = (0, _mastra_core_memory.getThreadOMMetadata)(thread.metadata)?.lastObservedAt ?? recordLastObservedAt;
|
|
@@ -34770,7 +35140,7 @@ ${formattedMessages}
|
|
|
34770
35140
|
if (filtered.length > 0) messagesByThread.set(thread.id, filtered);
|
|
34771
35141
|
}
|
|
34772
35142
|
if (messagesByThread.size === 0) return void 0;
|
|
34773
|
-
return await this.formatUnobservedContextBlocks(messagesByThread, currentThreadId) || void 0;
|
|
35143
|
+
return await this.formatUnobservedContextBlocks(messagesByThread, currentThreadId, record?.observedTimezone) || void 0;
|
|
34774
35144
|
}
|
|
34775
35145
|
/**
|
|
34776
35146
|
* Emit debug event and stream progress for UI feedback.
|
|
@@ -35113,7 +35483,7 @@ ${formattedMessages}
|
|
|
35113
35483
|
...startMarker,
|
|
35114
35484
|
transient: true
|
|
35115
35485
|
}).catch(() => {});
|
|
35116
|
-
await this.runBufferedObservationCycle({
|
|
35486
|
+
if (!(await this.runBufferedObservationCycle({
|
|
35117
35487
|
threadId,
|
|
35118
35488
|
resourceId: record.resourceId ?? resourceId,
|
|
35119
35489
|
trigger: "async-buffer"
|
|
@@ -35132,7 +35502,10 @@ ${formattedMessages}
|
|
|
35132
35502
|
currentModel: opts.currentModel,
|
|
35133
35503
|
observabilityContext,
|
|
35134
35504
|
trigger: "async-buffer"
|
|
35135
|
-
}).run())
|
|
35505
|
+
}).run()))?.observed) return {
|
|
35506
|
+
buffered: false,
|
|
35507
|
+
record
|
|
35508
|
+
};
|
|
35136
35509
|
if (isOmReproCaptureEnabled()) writeObserverExchangeReproCapture({
|
|
35137
35510
|
threadId,
|
|
35138
35511
|
resourceId: record.resourceId ?? void 0,
|
|
@@ -35492,6 +35865,7 @@ ${formattedMessages}
|
|
|
35492
35865
|
observed = result.observed;
|
|
35493
35866
|
observationUsage = result.usage;
|
|
35494
35867
|
observationProviderMetadata = result.providerMetadata;
|
|
35868
|
+
observationError = result.error;
|
|
35495
35869
|
});
|
|
35496
35870
|
} catch (error) {
|
|
35497
35871
|
lifecycleError = error;
|
|
@@ -37402,6 +37776,7 @@ ${workingMemory}`;
|
|
|
37402
37776
|
model: omConfig.model,
|
|
37403
37777
|
mastra: this._mastraInstance,
|
|
37404
37778
|
onIndexObservations,
|
|
37779
|
+
onDebugEvent: omConfig.onDebugEvent,
|
|
37405
37780
|
hooks: omConfig.hooks,
|
|
37406
37781
|
observation: omConfig.observation ? {
|
|
37407
37782
|
model: omConfig.observation.model,
|
|
@@ -37418,10 +37793,14 @@ ${workingMemory}`;
|
|
|
37418
37793
|
threadTitle: omConfig.observation.threadTitle,
|
|
37419
37794
|
observeAttachments: omConfig.observation.observeAttachments,
|
|
37420
37795
|
continuationHints: omConfig.observation.continuationHints,
|
|
37796
|
+
maxRetries: omConfig.observation.maxRetries,
|
|
37797
|
+
failurePolicy: omConfig.observation.failurePolicy,
|
|
37421
37798
|
extract: omConfig.observation.extract
|
|
37422
37799
|
} : void 0,
|
|
37423
37800
|
reflection: omConfig.reflection ? {
|
|
37424
37801
|
model: omConfig.reflection.model,
|
|
37802
|
+
maxRetries: omConfig.reflection.maxRetries,
|
|
37803
|
+
failurePolicy: omConfig.reflection.failurePolicy,
|
|
37425
37804
|
observationTokens: omConfig.reflection.observationTokens,
|
|
37426
37805
|
modelSettings: omConfig.reflection.modelSettings,
|
|
37427
37806
|
providerOptions: omConfig.reflection.providerOptions,
|
|
@@ -49274,4 +49653,4 @@ const agentBuilderWorkflows = {
|
|
|
49274
49653
|
//#endregion
|
|
49275
49654
|
exports.agentBuilderWorkflows = agentBuilderWorkflows;
|
|
49276
49655
|
|
|
49277
|
-
//# sourceMappingURL=dist-
|
|
49656
|
+
//# sourceMappingURL=dist-_n0Mp6PP.cjs.map
|