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