@mastra/memory 1.32.2-alpha.0 → 1.33.0-alpha.2
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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-evals-evals-with-memory.md +3 -1
- package/dist/docs/references/docs-memory-observational-memory.md +34 -0
- package/dist/docs/references/reference-memory-observational-memory.md +8 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/processors/index.cjs +1 -1
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/date-utils.d.ts +36 -15
- package/dist/processors/observational-memory/date-utils.d.ts.map +1 -1
- package/dist/processors/observational-memory/error.d.ts +12 -0
- package/dist/processors/observational-memory/error.d.ts.map +1 -1
- package/dist/processors/observational-memory/extraction-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/markers.d.ts +3 -0
- package/dist/processors/observational-memory/markers.d.ts.map +1 -1
- package/dist/processors/observational-memory/message-utils.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-groups.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/base.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +17 -8
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts +2 -0
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-runner.d.ts +3 -1
- package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/processor.d.ts +8 -0
- package/dist/processors/observational-memory/processor.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-runner.d.ts +1 -1
- package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/retry.d.ts +10 -0
- package/dist/processors/observational-memory/retry.d.ts.map +1 -1
- package/dist/processors/observational-memory/summarize.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +25 -1
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/dist/{src-B-_pkR2t.js → src-BikfoIdS.js} +747 -368
- package/dist/{src-B-_pkR2t.js.map → src-BikfoIdS.js.map} +1 -1
- package/dist/{src-CBxKXo2w.cjs → src-CeFdO1YZ.cjs} +747 -368
- package/dist/{src-CBxKXo2w.cjs.map → src-CeFdO1YZ.cjs.map} +1 -1
- package/package.json +5 -5
|
@@ -18696,10 +18696,227 @@ async function applyExtractorHooks(opts) {
|
|
|
18696
18696
|
};
|
|
18697
18697
|
}
|
|
18698
18698
|
//#endregion
|
|
18699
|
-
//#region src/processors/observational-memory/
|
|
18699
|
+
//#region src/processors/observational-memory/retry.ts
|
|
18700
|
+
/**
|
|
18701
|
+
* Retry knobs for the internal OM transport-error retry wrapper.
|
|
18702
|
+
* Exported as a mutable object so tests can shrink the backoff schedule
|
|
18703
|
+
* without changing public API.
|
|
18704
|
+
*
|
|
18705
|
+
* With the defaults the per-retry pre-jitter backoff schedule is:
|
|
18706
|
+
* 1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s (cap)
|
|
18707
|
+
* giving 8 retries / 9 total attempts and ~247s (~4 minutes) of waiting
|
|
18708
|
+
* before the final attempt fails. Designed to ride out short provider /
|
|
18709
|
+
* network blips without holding the actor turn for much longer than that.
|
|
18710
|
+
*
|
|
18711
|
+
* @internal
|
|
18712
|
+
*/
|
|
18713
|
+
const RETRY_CONFIG = {
|
|
18714
|
+
/** Maximum number of retry *attempts* (total tries = maxRetries + 1). */
|
|
18715
|
+
maxRetries: 8,
|
|
18716
|
+
/** Initial backoff delay in milliseconds. */
|
|
18717
|
+
initialDelayMs: 1e3,
|
|
18718
|
+
/** Multiplier applied to the delay after each failed attempt. */
|
|
18719
|
+
backoffFactor: 2,
|
|
18720
|
+
/** Cap on per-attempt delay (ms). */
|
|
18721
|
+
maxDelayMs: 12e4,
|
|
18722
|
+
/** Random jitter as a fraction of the computed delay (e.g. 0.2 = ±20%). */
|
|
18723
|
+
jitter: .2
|
|
18724
|
+
};
|
|
18725
|
+
const TRANSIENT_MESSAGE_SUBSTRINGS = [
|
|
18726
|
+
"terminated",
|
|
18727
|
+
"fetch failed",
|
|
18728
|
+
"econnreset",
|
|
18729
|
+
"econnrefused",
|
|
18730
|
+
"enotfound",
|
|
18731
|
+
"eai_again",
|
|
18732
|
+
"socket hang up",
|
|
18733
|
+
"network error",
|
|
18734
|
+
"request timed out",
|
|
18735
|
+
"request timeout",
|
|
18736
|
+
"connection reset",
|
|
18737
|
+
"connection closed"
|
|
18738
|
+
];
|
|
18700
18739
|
function isRecord$5(value) {
|
|
18701
18740
|
return typeof value === "object" && value !== null;
|
|
18702
18741
|
}
|
|
18742
|
+
function isAbortError$1(error) {
|
|
18743
|
+
if (!isRecord$5(error)) return false;
|
|
18744
|
+
if (error.name === "AbortError") return true;
|
|
18745
|
+
if (typeof error.code === "string" && error.code === "ABORT_ERR") return true;
|
|
18746
|
+
return false;
|
|
18747
|
+
}
|
|
18748
|
+
function hasTransientMessage(value) {
|
|
18749
|
+
if (!isRecord$5(value)) return false;
|
|
18750
|
+
const message = typeof value.message === "string" ? value.message.toLowerCase() : "";
|
|
18751
|
+
if (message && TRANSIENT_MESSAGE_SUBSTRINGS.some((sub) => message.includes(sub))) return true;
|
|
18752
|
+
if (typeof value.code === "string" && value.code.toUpperCase().startsWith("UND_ERR_")) return true;
|
|
18753
|
+
return false;
|
|
18754
|
+
}
|
|
18755
|
+
function isRetryableHttpStatus(status) {
|
|
18756
|
+
if (status === 408 || status === 425 || status === 429) return true;
|
|
18757
|
+
return status >= 500 && status <= 599;
|
|
18758
|
+
}
|
|
18759
|
+
function hasRetryableHttpStatus(value) {
|
|
18760
|
+
if (!isRecord$5(value)) return false;
|
|
18761
|
+
if (typeof value.statusCode === "number" && isRetryableHttpStatus(value.statusCode)) return true;
|
|
18762
|
+
if (typeof value.code === "number" && isRetryableHttpStatus(value.code)) return true;
|
|
18763
|
+
return false;
|
|
18764
|
+
}
|
|
18765
|
+
function hasIsRetryableFlag(value) {
|
|
18766
|
+
if (!isRecord$5(value)) return false;
|
|
18767
|
+
return value.isRetryable === true;
|
|
18768
|
+
}
|
|
18769
|
+
/**
|
|
18770
|
+
* Returns true when a user-initiated cancellation appears anywhere in the
|
|
18771
|
+
* error's `cause`/`error` wrapper chain, so a wrapped abort can never be
|
|
18772
|
+
* mistaken for a retryable or survivable provider failure.
|
|
18773
|
+
*
|
|
18774
|
+
* @internal
|
|
18775
|
+
*/
|
|
18776
|
+
function hasAbortInChain(error) {
|
|
18777
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18778
|
+
function visit(candidate) {
|
|
18779
|
+
if (isAbortError$1(candidate)) return true;
|
|
18780
|
+
if (!isRecord$5(candidate)) return false;
|
|
18781
|
+
if (seen.has(candidate)) return false;
|
|
18782
|
+
seen.add(candidate);
|
|
18783
|
+
return visit(candidate.cause) || visit(candidate.error);
|
|
18784
|
+
}
|
|
18785
|
+
return visit(error);
|
|
18786
|
+
}
|
|
18787
|
+
/**
|
|
18788
|
+
* Returns true when the given error looks like a transient transport-class
|
|
18789
|
+
* failure that's worth retrying — undici `terminated`, `fetch failed`,
|
|
18790
|
+
* `UND_ERR_*` codes, AI SDK `APICallError` with `isRetryable: true`, and
|
|
18791
|
+
* common HTTP 408/425/429/5xx statuses. Walks the `error.cause` chain so
|
|
18792
|
+
* wrapper errors don't hide the real failure.
|
|
18793
|
+
*
|
|
18794
|
+
* Never retries on user-initiated aborts.
|
|
18795
|
+
*
|
|
18796
|
+
* @internal
|
|
18797
|
+
*/
|
|
18798
|
+
function isTransientLLMError(error) {
|
|
18799
|
+
if (hasAbortInChain(error)) return false;
|
|
18800
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
18801
|
+
function visit(candidate) {
|
|
18802
|
+
if (isRecord$5(candidate)) {
|
|
18803
|
+
if (visited.has(candidate)) return false;
|
|
18804
|
+
visited.add(candidate);
|
|
18805
|
+
}
|
|
18806
|
+
if (hasTransientMessage(candidate)) return true;
|
|
18807
|
+
if (hasRetryableHttpStatus(candidate)) return true;
|
|
18808
|
+
if (hasIsRetryableFlag(candidate)) return true;
|
|
18809
|
+
if (isRecord$5(candidate)) {
|
|
18810
|
+
if (visit(candidate.cause)) return true;
|
|
18811
|
+
if (visit(candidate.error)) return true;
|
|
18812
|
+
}
|
|
18813
|
+
return false;
|
|
18814
|
+
}
|
|
18815
|
+
return visit(error);
|
|
18816
|
+
}
|
|
18817
|
+
/**
|
|
18818
|
+
* Compute the backoff delay (ms) for the Nth retry (0-indexed).
|
|
18819
|
+
*
|
|
18820
|
+
* Exponential growth (`initialDelayMs * backoffFactor^attempt`) capped at
|
|
18821
|
+
* `maxDelayMs`, then nudged by ±`jitter` (fractional). Exported for unit
|
|
18822
|
+
* tests that lock the schedule against drift.
|
|
18823
|
+
*
|
|
18824
|
+
* @internal
|
|
18825
|
+
*/
|
|
18826
|
+
function computeDelay(attempt) {
|
|
18827
|
+
const base = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, attempt);
|
|
18828
|
+
const capped = Math.min(base, RETRY_CONFIG.maxDelayMs);
|
|
18829
|
+
if (RETRY_CONFIG.jitter <= 0) return capped;
|
|
18830
|
+
const jitterRange = capped * RETRY_CONFIG.jitter;
|
|
18831
|
+
const offset = (Math.random() * 2 - 1) * jitterRange;
|
|
18832
|
+
return Math.max(0, Math.round(capped + offset));
|
|
18833
|
+
}
|
|
18834
|
+
function sleep(ms, abortSignal) {
|
|
18835
|
+
if (ms <= 0) return Promise.resolve();
|
|
18836
|
+
return new Promise((resolve, reject) => {
|
|
18837
|
+
if (abortSignal?.aborted) {
|
|
18838
|
+
reject(/* @__PURE__ */ new Error("The operation was aborted."));
|
|
18839
|
+
return;
|
|
18840
|
+
}
|
|
18841
|
+
const timer = setTimeout(() => {
|
|
18842
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
18843
|
+
resolve();
|
|
18844
|
+
}, ms);
|
|
18845
|
+
const onAbort = () => {
|
|
18846
|
+
clearTimeout(timer);
|
|
18847
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
18848
|
+
reject(/* @__PURE__ */ new Error("The operation was aborted."));
|
|
18849
|
+
};
|
|
18850
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
18851
|
+
});
|
|
18852
|
+
}
|
|
18853
|
+
/**
|
|
18854
|
+
* Run `fn` with retries on transient transport-class errors.
|
|
18855
|
+
*
|
|
18856
|
+
* Non-transient errors (auth, validation, schema, etc.) are rethrown
|
|
18857
|
+
* immediately. User-initiated aborts are rethrown without delay.
|
|
18858
|
+
*
|
|
18859
|
+
* @internal
|
|
18860
|
+
*/
|
|
18861
|
+
async function withRetry(fn, opts) {
|
|
18862
|
+
const { label, abortSignal, maxRetries = RETRY_CONFIG.maxRetries } = opts;
|
|
18863
|
+
let attempt = 0;
|
|
18864
|
+
while (true) {
|
|
18865
|
+
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
18866
|
+
try {
|
|
18867
|
+
return await fn();
|
|
18868
|
+
} catch (error) {
|
|
18869
|
+
if (hasAbortInChain(error) || abortSignal?.aborted) throw error;
|
|
18870
|
+
if (attempt >= maxRetries || !isTransientLLMError(error)) {
|
|
18871
|
+
if (attempt > 0) omDebug(`[OM:retry:${label}] giving up after ${attempt} retry/retries: ${error instanceof Error ? error.message : String(error)}`);
|
|
18872
|
+
throw error;
|
|
18873
|
+
}
|
|
18874
|
+
const delay = computeDelay(attempt);
|
|
18875
|
+
attempt++;
|
|
18876
|
+
omDebug(`[OM:retry:${label}] transient error on attempt ${attempt}, retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
18877
|
+
await sleep(delay, abortSignal);
|
|
18878
|
+
}
|
|
18879
|
+
}
|
|
18880
|
+
}
|
|
18881
|
+
//#endregion
|
|
18882
|
+
//#region src/processors/observational-memory/error.ts
|
|
18883
|
+
const AI_API_CALL_ERROR_MARKER = Symbol.for("vercel.ai.error.AI_APICallError");
|
|
18884
|
+
function isRecord$4(value) {
|
|
18885
|
+
return typeof value === "object" && value !== null;
|
|
18886
|
+
}
|
|
18887
|
+
function isOmModelExecutionFailure(error) {
|
|
18888
|
+
if (hasAbortInChain(error)) return false;
|
|
18889
|
+
if (isTransientLLMError(error)) return true;
|
|
18890
|
+
const visited = /* @__PURE__ */ new Set();
|
|
18891
|
+
let current = error;
|
|
18892
|
+
while (isRecord$4(current) && !visited.has(current)) {
|
|
18893
|
+
visited.add(current);
|
|
18894
|
+
if (Object.prototype.hasOwnProperty.call(current, AI_API_CALL_ERROR_MARKER)) return Reflect.get(current, AI_API_CALL_ERROR_MARKER) === true;
|
|
18895
|
+
current = current.cause ?? current.error;
|
|
18896
|
+
}
|
|
18897
|
+
return false;
|
|
18898
|
+
}
|
|
18899
|
+
var OmModelExecutionError = class extends Error {
|
|
18900
|
+
failureKind;
|
|
18901
|
+
constructor(failureKind, cause) {
|
|
18902
|
+
super(formatOmError(cause), { cause });
|
|
18903
|
+
this.failureKind = failureKind;
|
|
18904
|
+
this.name = "OmModelExecutionError";
|
|
18905
|
+
}
|
|
18906
|
+
};
|
|
18907
|
+
function isOmModelExecutionError(error) {
|
|
18908
|
+
try {
|
|
18909
|
+
return error instanceof OmModelExecutionError;
|
|
18910
|
+
} catch {
|
|
18911
|
+
return false;
|
|
18912
|
+
}
|
|
18913
|
+
}
|
|
18914
|
+
function getOmFailureMetadata(error, failurePolicy) {
|
|
18915
|
+
return {
|
|
18916
|
+
failurePolicy,
|
|
18917
|
+
...isOmModelExecutionError(error) ? { failureKind: error.failureKind } : {}
|
|
18918
|
+
};
|
|
18919
|
+
}
|
|
18703
18920
|
/** Keep provider diagnostics in streamed/persisted markers, not whole API request/response objects. */
|
|
18704
18921
|
function formatOmError(error) {
|
|
18705
18922
|
const details = /* @__PURE__ */ new Set();
|
|
@@ -18709,7 +18926,7 @@ function formatOmError(error) {
|
|
|
18709
18926
|
};
|
|
18710
18927
|
function collectErrorDetails(value, depth) {
|
|
18711
18928
|
if (depth > 5) return;
|
|
18712
|
-
if (!isRecord$
|
|
18929
|
+
if (!isRecord$4(value)) {
|
|
18713
18930
|
if (value !== void 0) add(String(value));
|
|
18714
18931
|
return;
|
|
18715
18932
|
}
|
|
@@ -18719,10 +18936,10 @@ function formatOmError(error) {
|
|
|
18719
18936
|
if (typeof value.statusCode === "number") add(`HTTP ${value.statusCode}`);
|
|
18720
18937
|
if (typeof value.responseBody === "string") try {
|
|
18721
18938
|
const body = JSON.parse(value.responseBody);
|
|
18722
|
-
if (isRecord$
|
|
18939
|
+
if (isRecord$4(body)) {
|
|
18723
18940
|
add(body.message);
|
|
18724
18941
|
add(body.detail);
|
|
18725
|
-
if (isRecord$
|
|
18942
|
+
if (isRecord$4(body.error)) add(body.error.message);
|
|
18726
18943
|
else add(body.error);
|
|
18727
18944
|
}
|
|
18728
18945
|
} catch {}
|
|
@@ -18737,8 +18954,8 @@ function formatOmError(error) {
|
|
|
18737
18954
|
}
|
|
18738
18955
|
//#endregion
|
|
18739
18956
|
//#region src/processors/observational-memory/extraction-runner.ts
|
|
18740
|
-
function isAbortError
|
|
18741
|
-
return abortSignal?.aborted === true || error
|
|
18957
|
+
function isAbortError(error, abortSignal) {
|
|
18958
|
+
return abortSignal?.aborted === true || hasAbortInChain(error);
|
|
18742
18959
|
}
|
|
18743
18960
|
function shouldRetryEmptyStructuredObject(object, extractors) {
|
|
18744
18961
|
return Object.keys(object).length === 0 && extractors.some((extractor) => extractor.retryStructuredExtractionOnEmptyObject);
|
|
@@ -18764,7 +18981,12 @@ If a prior value is still applicable and carry-forward is enabled, return that p
|
|
|
18764
18981
|
${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values\n\n${priorLines.join("\n\n")}` : ""}`;
|
|
18765
18982
|
const values = {};
|
|
18766
18983
|
const failures = [];
|
|
18767
|
-
const streamWithStructuredOutput = async (jsonPromptInjection) => {
|
|
18984
|
+
const streamWithStructuredOutput = async (jsonPromptInjection) => withRetry(() => streamOnce(jsonPromptInjection), {
|
|
18985
|
+
label: `om-${opts.source}-structured-extraction`,
|
|
18986
|
+
abortSignal: opts.abortSignal,
|
|
18987
|
+
maxRetries: 1
|
|
18988
|
+
});
|
|
18989
|
+
const streamOnce = async (jsonPromptInjection) => {
|
|
18768
18990
|
const object = await (await opts.agent.stream(prompt, {
|
|
18769
18991
|
structuredOutput: {
|
|
18770
18992
|
schema,
|
|
@@ -18784,11 +19006,21 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
18784
19006
|
object = await streamWithStructuredOutput();
|
|
18785
19007
|
retryEmptyObject = shouldRetryEmptyStructuredObject(object, structuredExtractors);
|
|
18786
19008
|
} catch (error) {
|
|
18787
|
-
if (isAbortError
|
|
19009
|
+
if (isAbortError(error, opts.abortSignal)) throw error;
|
|
19010
|
+
if (isTransientLLMError(error)) {
|
|
19011
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19012
|
+
return {
|
|
19013
|
+
values,
|
|
19014
|
+
failures: structuredExtractors.map((extractor) => ({
|
|
19015
|
+
slug: extractor.slug,
|
|
19016
|
+
error: message
|
|
19017
|
+
}))
|
|
19018
|
+
};
|
|
19019
|
+
}
|
|
18788
19020
|
try {
|
|
18789
19021
|
object = await streamWithStructuredOutput(coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
|
|
18790
19022
|
} catch (fallbackError) {
|
|
18791
|
-
if (isAbortError
|
|
19023
|
+
if (isAbortError(fallbackError, opts.abortSignal)) throw fallbackError;
|
|
18792
19024
|
const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
18793
19025
|
return {
|
|
18794
19026
|
values,
|
|
@@ -18802,7 +19034,7 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
18802
19034
|
if (retryEmptyObject) try {
|
|
18803
19035
|
object = await streamWithStructuredOutput(coreFeatures.has("json-prompt-injection:inline") ? "inline" : true);
|
|
18804
19036
|
} catch (fallbackError) {
|
|
18805
|
-
if (isAbortError
|
|
19037
|
+
if (isAbortError(fallbackError, opts.abortSignal)) throw fallbackError;
|
|
18806
19038
|
const message = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
18807
19039
|
return {
|
|
18808
19040
|
values,
|
|
@@ -18830,15 +19062,15 @@ ${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values
|
|
|
18830
19062
|
//#endregion
|
|
18831
19063
|
//#region src/system-reminders.ts
|
|
18832
19064
|
const LEGACY_SYSTEM_REMINDER_METADATA_KEY = "dynamicAgentsMdReminder";
|
|
18833
|
-
function isRecord$
|
|
19065
|
+
function isRecord$3(value) {
|
|
18834
19066
|
return typeof value === "object" && value !== null;
|
|
18835
19067
|
}
|
|
18836
19068
|
function isSystemReminderMessage(message) {
|
|
18837
|
-
if (!isRecord$
|
|
19069
|
+
if (!isRecord$3(message.content)) return false;
|
|
18838
19070
|
const metadata = message.content.metadata;
|
|
18839
|
-
if (message.role === "signal") return isRecord$
|
|
19071
|
+
if (message.role === "signal") return isRecord$3(metadata) && isRecord$3(metadata.signal) && (metadata.signal.type === "system-reminder" || metadata.signal.type === "reactive");
|
|
18840
19072
|
if (message.role !== "user") return false;
|
|
18841
|
-
if (isRecord$
|
|
19073
|
+
if (isRecord$3(metadata) && (isRecord$3(metadata.systemReminder) || LEGACY_SYSTEM_REMINDER_METADATA_KEY in metadata)) return true;
|
|
18842
19074
|
const firstTextPart = message.content.parts.find((part) => part.type === "text");
|
|
18843
19075
|
return typeof firstTextPart?.text === "string" && firstTextPart.text.startsWith("<system-reminder");
|
|
18844
19076
|
}
|
|
@@ -18901,12 +19133,41 @@ function stripEphemeralAnchorIds(observations) {
|
|
|
18901
19133
|
* Date/time utility functions for Observational Memory.
|
|
18902
19134
|
* Pure functions for formatting relative timestamps and annotating observations.
|
|
18903
19135
|
*/
|
|
18904
|
-
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
19136
|
+
const DAY_MS = 1440 * 60 * 1e3;
|
|
19137
|
+
/** Day number of the calendar date a parsed date names. Parsed dates are built from local-time fields. */
|
|
19138
|
+
function calendarDay(date) {
|
|
19139
|
+
return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / DAY_MS;
|
|
19140
|
+
}
|
|
19141
|
+
const knownTimeZones = /* @__PURE__ */ new Map();
|
|
19142
|
+
/** `timeZone` when this runtime recognises it; otherwise undefined, which means the process time zone. */
|
|
19143
|
+
function resolveTimeZone(timeZone) {
|
|
19144
|
+
if (!timeZone) return void 0;
|
|
19145
|
+
let known = knownTimeZones.get(timeZone);
|
|
19146
|
+
if (known === void 0) {
|
|
19147
|
+
try {
|
|
19148
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
19149
|
+
known = true;
|
|
19150
|
+
} catch {
|
|
19151
|
+
known = false;
|
|
19152
|
+
}
|
|
19153
|
+
knownTimeZones.set(timeZone, known);
|
|
19154
|
+
}
|
|
19155
|
+
return known ? timeZone : void 0;
|
|
19156
|
+
}
|
|
19157
|
+
/** Day number of the calendar date `instant` falls on in `timeZone` (the process time zone when unset). */
|
|
19158
|
+
function calendarDayIn(instant, timeZone) {
|
|
19159
|
+
const zone = resolveTimeZone(timeZone);
|
|
19160
|
+
if (!zone) return calendarDay(instant);
|
|
19161
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
19162
|
+
timeZone: zone,
|
|
19163
|
+
year: "numeric",
|
|
19164
|
+
month: "numeric",
|
|
19165
|
+
day: "numeric"
|
|
19166
|
+
}).formatToParts(instant);
|
|
19167
|
+
const part = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
19168
|
+
return Date.UTC(part("year"), part("month") - 1, part("day")) / DAY_MS;
|
|
19169
|
+
}
|
|
19170
|
+
function formatRelativeDays(diffDays) {
|
|
18910
19171
|
if (diffDays < 0) {
|
|
18911
19172
|
const futureDays = Math.abs(diffDays);
|
|
18912
19173
|
if (futureDays === 1) return "tomorrow";
|
|
@@ -18932,8 +19193,7 @@ function formatRelativeTime(date, currentDate) {
|
|
|
18932
19193
|
* Returns null for consecutive days (no gap marker needed).
|
|
18933
19194
|
*/
|
|
18934
19195
|
function formatGapBetweenDates(prevDate, currDate) {
|
|
18935
|
-
const
|
|
18936
|
-
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
19196
|
+
const diffDays = calendarDay(currDate) - calendarDay(prevDate);
|
|
18937
19197
|
if (diffDays <= 1) return null;
|
|
18938
19198
|
else if (diffDays < 7) return `[${diffDays} days later]`;
|
|
18939
19199
|
else if (diffDays < 14) return `[1 week later]`;
|
|
@@ -18941,45 +19201,156 @@ function formatGapBetweenDates(prevDate, currDate) {
|
|
|
18941
19201
|
else if (diffDays < 60) return `[1 month later]`;
|
|
18942
19202
|
else return `[${Math.floor(diffDays / 30)} months later]`;
|
|
18943
19203
|
}
|
|
19204
|
+
const MONTH_INDEX = {
|
|
19205
|
+
jan: 0,
|
|
19206
|
+
feb: 1,
|
|
19207
|
+
mar: 2,
|
|
19208
|
+
apr: 3,
|
|
19209
|
+
may: 4,
|
|
19210
|
+
jun: 5,
|
|
19211
|
+
jul: 6,
|
|
19212
|
+
aug: 7,
|
|
19213
|
+
sep: 8,
|
|
19214
|
+
oct: 9,
|
|
19215
|
+
nov: 10,
|
|
19216
|
+
dec: 11
|
|
19217
|
+
};
|
|
19218
|
+
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)?)\.?`;
|
|
19219
|
+
const DAY = String.raw`\d{1,2}(?:st|nd|rd|th)?`;
|
|
19220
|
+
const YEAR = String.raw`(?:19|20|21)\d{2}(?!\d)`;
|
|
19221
|
+
const MERIDIEM = String.raw`(?:[AaPp]\.[Mm]\.|[AaPp][Mm]\b)`;
|
|
19222
|
+
const TIME = String.raw`(?:,?\s+(?:at\s+)?\d{1,2}:\d{2}(?:\s*${MERIDIEM})?|\s+at\s+\d{1,2}\s*${MERIDIEM})`;
|
|
19223
|
+
const QUALIFIER = String.raw`(?:(?:early|mid|late)[- ](?:to[- ](?:early|mid|late)[- ])?)`;
|
|
19224
|
+
const RANGE_SEP = String.raw`(?:\s*[–—]\s*|\s*-\s*|\s+(?:to|through|until)\s+)`;
|
|
19225
|
+
/**
|
|
19226
|
+
* Dates written into observation text that carry their own year, longest forms first:
|
|
19227
|
+
* "Mar 1 - Mar 18, 2025", "May 27-28, 2023", "January 10, 2024 at 3:00 PM",
|
|
19228
|
+
* "June–July 2022", "Dec 2021 – June 2023", "late April 2023".
|
|
19229
|
+
* Bare years, dates without a year, and ISO dates (often part of model IDs, API versions,
|
|
19230
|
+
* paths and branch names) are deliberately not matched.
|
|
19231
|
+
*/
|
|
19232
|
+
const FREE_TEXT_DATE = new RegExp([
|
|
19233
|
+
String.raw`\b${MONTH}\s+${DAY}(?:,?\s+${YEAR})?${RANGE_SEP}${MONTH}\s+${DAY},?\s+${YEAR}${TIME}?`,
|
|
19234
|
+
String.raw`\b${MONTH}\s+${DAY}${RANGE_SEP}${DAY},?\s+${YEAR}`,
|
|
19235
|
+
String.raw`\b${MONTH}\s+${DAY},?\s+${YEAR}${TIME}?`,
|
|
19236
|
+
String.raw`\b${QUALIFIER}?${MONTH}(?:\s+${YEAR})?${RANGE_SEP}${QUALIFIER}?${MONTH}\s+${YEAR}`,
|
|
19237
|
+
String.raw`\b${QUALIFIER}?${MONTH}\s+${YEAR}`
|
|
19238
|
+
].join("|"), "g");
|
|
19239
|
+
const ENDPOINT = new RegExp(String.raw`^(?:(early|mid|late)\s*)?(?:(${MONTH})\s*)?(?:(\d{1,2})(?:st|nd|rd|th)?)?,?\s*(?:(${YEAR}))?$`, "i");
|
|
19240
|
+
function parseEndpoint(text) {
|
|
19241
|
+
const match = ENDPOINT.exec(text.trim());
|
|
19242
|
+
if (!match || !match[0]) return null;
|
|
19243
|
+
const [, qualifier, month, day, year] = match;
|
|
19244
|
+
return {
|
|
19245
|
+
...qualifier ? { qualifier: qualifier.toLowerCase() } : {},
|
|
19246
|
+
...month ? { month: MONTH_INDEX[month.slice(0, 3).toLowerCase()] } : {},
|
|
19247
|
+
...day ? { day: Number(day) } : {},
|
|
19248
|
+
...year ? { year: Number(year) } : {}
|
|
19249
|
+
};
|
|
19250
|
+
}
|
|
19251
|
+
const QUALIFIER_DAY = {
|
|
19252
|
+
early: 7,
|
|
19253
|
+
mid: 15,
|
|
19254
|
+
late: 23
|
|
19255
|
+
};
|
|
19256
|
+
const QUALIFIER_MONTH = {
|
|
19257
|
+
early: [1, 15],
|
|
19258
|
+
mid: [6, 1],
|
|
19259
|
+
late: [10, 15]
|
|
19260
|
+
};
|
|
19261
|
+
/** Earliest and latest local day an endpoint can mean; null when it has no year or is not a real date. */
|
|
19262
|
+
function endpointBounds(date) {
|
|
19263
|
+
const { qualifier, month, day, year } = date;
|
|
19264
|
+
if (year === void 0) return null;
|
|
19265
|
+
if (month === void 0) {
|
|
19266
|
+
if (day !== void 0) return null;
|
|
19267
|
+
if (qualifier) {
|
|
19268
|
+
const [m, d] = QUALIFIER_MONTH[qualifier];
|
|
19269
|
+
return [new Date(year, m, d), new Date(year, m, d)];
|
|
19270
|
+
}
|
|
19271
|
+
return [new Date(year, 0, 1), new Date(year, 11, 31)];
|
|
19272
|
+
}
|
|
19273
|
+
if (day !== void 0) {
|
|
19274
|
+
const point = new Date(year, month, day);
|
|
19275
|
+
if (point.getMonth() !== month) return null;
|
|
19276
|
+
return [point, point];
|
|
19277
|
+
}
|
|
19278
|
+
if (qualifier) {
|
|
19279
|
+
const point = new Date(year, month, QUALIFIER_DAY[qualifier]);
|
|
19280
|
+
return [point, point];
|
|
19281
|
+
}
|
|
19282
|
+
return [new Date(year, month, 1), new Date(year, month + 1, 0)];
|
|
19283
|
+
}
|
|
19284
|
+
/** Parses one alternative: a single date or a two-ended range, filling each end's missing parts from the other. */
|
|
19285
|
+
function parseSpanAlternative(text) {
|
|
19286
|
+
const ends = text.split(new RegExp(`${RANGE_SEP}(?=\\S)`)).filter(Boolean);
|
|
19287
|
+
if (ends.length === 0 || ends.length > 2) return null;
|
|
19288
|
+
const parsed = ends.map(parseEndpoint);
|
|
19289
|
+
if (parsed.some((end) => end === null)) return null;
|
|
19290
|
+
const [first, second = first] = parsed;
|
|
19291
|
+
const from = { ...first };
|
|
19292
|
+
const to = { ...second };
|
|
19293
|
+
const fromYearInferred = from.year === void 0;
|
|
19294
|
+
const toYearInferred = to.year === void 0;
|
|
19295
|
+
from.year ??= to.year;
|
|
19296
|
+
to.year ??= from.year;
|
|
19297
|
+
if (from.month === void 0 && (from.day !== void 0 || from.qualifier) && to.month !== void 0) from.month = to.month;
|
|
19298
|
+
if (to.month === void 0 && to.day !== void 0 && from.month !== void 0) to.month = from.month;
|
|
19299
|
+
if (from.month !== void 0 && to.month !== void 0 && from.month > to.month && from.year === to.year) {
|
|
19300
|
+
if (fromYearInferred) from.year -= 1;
|
|
19301
|
+
else if (toYearInferred) to.year += 1;
|
|
19302
|
+
}
|
|
19303
|
+
const fromBounds = endpointBounds(from);
|
|
19304
|
+
const toBounds = endpointBounds(to);
|
|
19305
|
+
if (!fromBounds || !toBounds) return null;
|
|
19306
|
+
const span = {
|
|
19307
|
+
start: fromBounds[0],
|
|
19308
|
+
end: toBounds[1]
|
|
19309
|
+
};
|
|
19310
|
+
return span.start <= span.end ? span : null;
|
|
19311
|
+
}
|
|
19312
|
+
function normalizeDateText(text) {
|
|
19313
|
+
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) => {
|
|
19314
|
+
const month = Object.keys(MONTH_INDEX)[Number(m) - 1];
|
|
19315
|
+
return month ? `${month} ${Number(d)}, ${y}` : whole;
|
|
19316
|
+
}).replace(new RegExp(TIME, "g"), "").replace(/\b(early|mid|late|to)-/gi, "$1 ").replace(/ {2,}/g, " ").trim();
|
|
19317
|
+
}
|
|
19318
|
+
/**
|
|
19319
|
+
* Parses the whole of `text` as a date or date range. Accepts "May 30, 2023",
|
|
19320
|
+
* "Mar 22, 2025 at 18:08", "May 27-28, 2023", "Dec 27–31, 2021", "Mar 1 - Mar 18, 2025",
|
|
19321
|
+
* "Aug 1, 2024 - Feb 28, 2025", "August 2024", "June–July 2022", "late April 2023",
|
|
19322
|
+
* "mid-to-late May 2023", "late 2023", "2035", "2021–2026", an "approx." prefix, and
|
|
19323
|
+
* "A or B" alternatives (their union). Returns null when no year is known.
|
|
19324
|
+
*/
|
|
19325
|
+
function parseDateSpan(text) {
|
|
19326
|
+
const alternatives = normalizeDateText(text).split(/\s+or\s+/i).map(parseSpanAlternative);
|
|
19327
|
+
if (alternatives.length === 0 || alternatives.some((span) => span === null)) return null;
|
|
19328
|
+
const spans = alternatives;
|
|
19329
|
+
return {
|
|
19330
|
+
start: new Date(Math.min(...spans.map((span) => span.start.getTime()))),
|
|
19331
|
+
end: new Date(Math.max(...spans.map((span) => span.end.getTime())))
|
|
19332
|
+
};
|
|
19333
|
+
}
|
|
19334
|
+
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");
|
|
18944
19335
|
/**
|
|
18945
|
-
*
|
|
18946
|
-
*
|
|
19336
|
+
* Like `parseDateSpan`, but falls back to the first dated expression inside longer text,
|
|
19337
|
+
* then to a year on its own ("by late 2027", "$500 in 2024"). Only for text known to be a date.
|
|
18947
19338
|
*/
|
|
18948
|
-
function
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
18952
|
-
const
|
|
18953
|
-
if (
|
|
18954
|
-
}
|
|
18955
|
-
|
|
18956
|
-
|
|
18957
|
-
|
|
18958
|
-
|
|
18959
|
-
|
|
18960
|
-
|
|
18961
|
-
}
|
|
18962
|
-
|
|
18963
|
-
const vagueMatch = dateContent.match(/(late|early|mid)[- ]?(?:to[- ]?(?:late|early|mid)[- ]?)?([A-Z][a-z]+)\s+(\d{4})/i);
|
|
18964
|
-
if (vagueMatch) {
|
|
18965
|
-
const month = vagueMatch[2];
|
|
18966
|
-
const year = vagueMatch[3];
|
|
18967
|
-
const modifier = vagueMatch[1].toLowerCase();
|
|
18968
|
-
let day = 15;
|
|
18969
|
-
if (modifier === "early") day = 7;
|
|
18970
|
-
if (modifier === "late") day = 23;
|
|
18971
|
-
const parsed = /* @__PURE__ */ new Date(`${month} ${day}, ${year}`);
|
|
18972
|
-
if (!isNaN(parsed.getTime())) targetDate = parsed;
|
|
18973
|
-
}
|
|
18974
|
-
}
|
|
18975
|
-
if (!targetDate) {
|
|
18976
|
-
const crossMonthMatch = dateContent.match(/([A-Z][a-z]+)\s+to\s+(?:early\s+)?([A-Z][a-z]+)\s+(\d{4})/i);
|
|
18977
|
-
if (crossMonthMatch) {
|
|
18978
|
-
const parsed = /* @__PURE__ */ new Date(`${crossMonthMatch[2]} 1, ${crossMonthMatch[3]}`);
|
|
18979
|
-
if (!isNaN(parsed.getTime())) targetDate = parsed;
|
|
18980
|
-
}
|
|
18981
|
-
}
|
|
18982
|
-
return targetDate;
|
|
19339
|
+
function findDateSpan(text) {
|
|
19340
|
+
const whole = parseDateSpan(text);
|
|
19341
|
+
if (whole) return whole;
|
|
19342
|
+
for (const pattern of [FREE_TEXT_DATE, QUALIFIED_YEAR]) for (const match of text.matchAll(pattern)) {
|
|
19343
|
+
const span = parseDateSpan(match[0]);
|
|
19344
|
+
if (span) return span;
|
|
19345
|
+
}
|
|
19346
|
+
return null;
|
|
19347
|
+
}
|
|
19348
|
+
function relativeSpan(span, today) {
|
|
19349
|
+
const start = formatRelativeDays(today - calendarDay(span.start));
|
|
19350
|
+
const end = formatRelativeDays(today - calendarDay(span.end));
|
|
19351
|
+
if (start === end) return start;
|
|
19352
|
+
if (!end.startsWith("in ")) return `${start} to ${end}`;
|
|
19353
|
+
return start.startsWith("in ") ? `${start} to ${end.slice(3)}` : `${start} to ${end.slice(3)} from now`;
|
|
18983
19354
|
}
|
|
18984
19355
|
/**
|
|
18985
19356
|
* Detects if an observation line indicates future intent (will do, plans to, looking forward to, etc.)
|
|
@@ -18999,40 +19370,59 @@ function isFutureIntentObservation(line) {
|
|
|
18999
19370
|
}
|
|
19000
19371
|
/**
|
|
19001
19372
|
* Expand inline estimated dates with relative time.
|
|
19002
|
-
* Matches patterns like "(estimated May 27-28, 2023)" or "(meaning May 30, 2023)"
|
|
19003
|
-
* and expands them to "(meaning May 30, 2023 -
|
|
19373
|
+
* Matches patterns like "(estimated May 27-28, 2023)" or "(meaning May 30, 2023 at 3:00 PM)"
|
|
19374
|
+
* and expands them to "(meaning May 30, 2023 - 3 weeks ago)". Notes without a year are left alone.
|
|
19004
19375
|
*/
|
|
19005
|
-
function expandInlineEstimatedDates(observations, currentDate) {
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
}
|
|
19017
|
-
|
|
19376
|
+
function expandInlineEstimatedDates(observations, currentDate, timeZone) {
|
|
19377
|
+
const inlineDateRegex = /\((estimated|meaning)\s([^()]*)\)/gi;
|
|
19378
|
+
const today = calendarDayIn(currentDate, timeZone);
|
|
19379
|
+
return observations.replace(inlineDateRegex, (match, prefix, noteText, offset) => {
|
|
19380
|
+
const dateContent = noteText.trimStart();
|
|
19381
|
+
const span = findDateSpan(dateContent);
|
|
19382
|
+
if (!span) return match;
|
|
19383
|
+
const relative = relativeSpan(span, today);
|
|
19384
|
+
const lineStart = observations.lastIndexOf("\n", offset) + 1;
|
|
19385
|
+
const lineBeforeDate = observations.slice(lineStart, offset);
|
|
19386
|
+
if (calendarDay(span.end) < today && isFutureIntentObservation(lineBeforeDate)) return `(${prefix} ${dateContent} - ${relative}, likely already happened)`;
|
|
19387
|
+
return `(${prefix} ${dateContent} - ${relative})`;
|
|
19388
|
+
});
|
|
19389
|
+
}
|
|
19390
|
+
/**
|
|
19391
|
+
* Annotates dates written into observation text, e.g. "exam on January 10, 2024" becomes
|
|
19392
|
+
* "exam on January 10, 2024 (5 months ago)". Only dates that state their year are annotated;
|
|
19393
|
+
* "Date:" headers and "(meaning/estimated …)" notes are handled separately and skipped here.
|
|
19394
|
+
*/
|
|
19395
|
+
function annotateObservationTextDates(observations, currentDate, timeZone) {
|
|
19396
|
+
const regex = new RegExp(String.raw`^Date:.*$|\((?:[Ee]stimated|[Mm]eaning)\b[^()]*\)|${FREE_TEXT_DATE.source}`, "gm");
|
|
19397
|
+
const today = calendarDayIn(currentDate, timeZone);
|
|
19398
|
+
return observations.replace(regex, (match, offset) => {
|
|
19399
|
+
if (match.startsWith("Date:") || match.startsWith("(")) return match;
|
|
19400
|
+
const span = parseDateSpan(match);
|
|
19401
|
+
if (!span) return match;
|
|
19402
|
+
const relative = relativeSpan(span, today);
|
|
19403
|
+
return observations[offset + match.length] === ")" ? `${match} - ${relative}` : `${match} (${relative})`;
|
|
19018
19404
|
});
|
|
19019
19405
|
}
|
|
19020
19406
|
/**
|
|
19021
19407
|
* Add relative time annotations to observations.
|
|
19022
|
-
* Transforms "Date: May 15, 2023" headers to "Date: May 15, 2023 (5 days ago)"
|
|
19023
|
-
*
|
|
19408
|
+
* Transforms "Date: May 15, 2023" headers to "Date: May 15, 2023 (5 days ago)", range headers such as
|
|
19409
|
+
* "Date: Aug 1, 2024 - Feb 28, 2025" to "(7 months ago to 4 weeks ago)", and annotates inline dates.
|
|
19410
|
+
*
|
|
19411
|
+
* `timeZone` is the zone the Observer wrote its dates in (the record's `observedTimezone`). "Today" is
|
|
19412
|
+
* `currentDate`'s calendar date in that zone, so the result doesn't depend on the zone of the process rendering it.
|
|
19024
19413
|
*/
|
|
19025
|
-
function addRelativeTimeToObservations(observations, currentDate) {
|
|
19026
|
-
const
|
|
19027
|
-
const
|
|
19414
|
+
function addRelativeTimeToObservations(observations, currentDate, timeZone) {
|
|
19415
|
+
const today = calendarDayIn(currentDate, timeZone);
|
|
19416
|
+
const withInlineDates = annotateObservationTextDates(expandInlineEstimatedDates(observations, currentDate, timeZone), currentDate, timeZone);
|
|
19417
|
+
const dateHeaderRegex = /^(Date:[ \t]*)(.*)$/gm;
|
|
19028
19418
|
const dates = [];
|
|
19029
19419
|
let regexMatch;
|
|
19030
19420
|
while ((regexMatch = dateHeaderRegex.exec(withInlineDates)) !== null) {
|
|
19031
|
-
const dateStr = regexMatch[2];
|
|
19032
|
-
const
|
|
19033
|
-
if (
|
|
19421
|
+
const dateStr = regexMatch[2].trimEnd();
|
|
19422
|
+
const span = parseDateSpan(dateStr);
|
|
19423
|
+
if (span) dates.push({
|
|
19034
19424
|
index: regexMatch.index,
|
|
19035
|
-
|
|
19425
|
+
span,
|
|
19036
19426
|
match: regexMatch[0],
|
|
19037
19427
|
prefix: regexMatch[1],
|
|
19038
19428
|
dateStr
|
|
@@ -19046,11 +19436,10 @@ function addRelativeTimeToObservations(observations, currentDate) {
|
|
|
19046
19436
|
const prev = i > 0 ? dates[i - 1] : null;
|
|
19047
19437
|
result += withInlineDates.slice(lastIndex, curr.index);
|
|
19048
19438
|
if (prev) {
|
|
19049
|
-
const gap = formatGapBetweenDates(prev.
|
|
19439
|
+
const gap = formatGapBetweenDates(prev.span.end, curr.span.start);
|
|
19050
19440
|
if (gap) result += `\n${gap}\n\n`;
|
|
19051
19441
|
}
|
|
19052
|
-
|
|
19053
|
-
result += `${curr.prefix}${curr.dateStr} (${relative})`;
|
|
19442
|
+
result += `${curr.prefix}${curr.dateStr} (${relativeSpan(curr.span, today)})`;
|
|
19054
19443
|
lastIndex = curr.index + curr.match.length;
|
|
19055
19444
|
}
|
|
19056
19445
|
result += withInlineDates.slice(lastIndex);
|
|
@@ -20065,11 +20454,20 @@ const OBSERVER_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
20065
20454
|
"heif",
|
|
20066
20455
|
"avif"
|
|
20067
20456
|
]);
|
|
20068
|
-
function formatObserverDate(createdAt) {
|
|
20069
|
-
|
|
20457
|
+
function formatObserverDate(createdAt, timeZone) {
|
|
20458
|
+
if (!createdAt) return "";
|
|
20459
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
20460
|
+
timeZone,
|
|
20461
|
+
year: "numeric",
|
|
20462
|
+
month: "short",
|
|
20463
|
+
day: "numeric"
|
|
20464
|
+
}).formatToParts(createdAt);
|
|
20465
|
+
const part = (type) => parts.find((p) => p.type === type)?.value;
|
|
20466
|
+
return `${part("month")} ${part("day")} ${part("year")}`;
|
|
20070
20467
|
}
|
|
20071
|
-
function formatObserverTime(createdAt) {
|
|
20468
|
+
function formatObserverTime(createdAt, timeZone) {
|
|
20072
20469
|
return createdAt ? createdAt.toLocaleTimeString("en-US", {
|
|
20470
|
+
timeZone,
|
|
20073
20471
|
hour: "numeric",
|
|
20074
20472
|
minute: "2-digit",
|
|
20075
20473
|
hour12: true
|
|
@@ -20159,11 +20557,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
|
|
|
20159
20557
|
const label = resolveObserverAttachmentLabel(part);
|
|
20160
20558
|
return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
|
|
20161
20559
|
}
|
|
20162
|
-
function isRecord$
|
|
20560
|
+
function isRecord$2(value) {
|
|
20163
20561
|
return !!value && typeof value === "object";
|
|
20164
20562
|
}
|
|
20165
20563
|
function mapToolResultBlockToAttachment(block) {
|
|
20166
|
-
if (!isRecord$
|
|
20564
|
+
if (!isRecord$2(block) || typeof block.type !== "string") return;
|
|
20167
20565
|
const mediaType = typeof block.mediaType === "string" ? block.mediaType : void 0;
|
|
20168
20566
|
const filename = typeof block.filename === "string" ? block.filename : void 0;
|
|
20169
20567
|
switch (block.type) {
|
|
@@ -20224,7 +20622,7 @@ function mapToolResultBlockToAttachment(block) {
|
|
|
20224
20622
|
}
|
|
20225
20623
|
}
|
|
20226
20624
|
function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
20227
|
-
if (!isRecord$
|
|
20625
|
+
if (!isRecord$2(result) || result.type !== "content" || !Array.isArray(result.value)) return {
|
|
20228
20626
|
resultWithoutAttachments: result,
|
|
20229
20627
|
attachments: []
|
|
20230
20628
|
};
|
|
@@ -20238,7 +20636,7 @@ function extractToolResultAttachments(result, counter, attachmentFilter) {
|
|
|
20238
20636
|
if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) attachments.push(toObserverInputAttachmentPart(attachment));
|
|
20239
20637
|
const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
|
|
20240
20638
|
return {
|
|
20241
|
-
type: isRecord$
|
|
20639
|
+
type: isRecord$2(block) ? block.type : void 0,
|
|
20242
20640
|
placeholder
|
|
20243
20641
|
};
|
|
20244
20642
|
});
|
|
@@ -20355,6 +20753,7 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
20355
20753
|
const maxLen = options?.maxPartLength;
|
|
20356
20754
|
const maxToolResultTokens = options?.maxToolResultTokens ?? 5e3;
|
|
20357
20755
|
const attachmentFilter = options?.attachmentFilter;
|
|
20756
|
+
const timeZone = resolveTimeZone(options?.timeZone);
|
|
20358
20757
|
const role = getObserverMessageLabel(msg);
|
|
20359
20758
|
const attachments = [];
|
|
20360
20759
|
const messageCreatedAt = normalizeObserverCreatedAt(msg.createdAt);
|
|
@@ -20364,8 +20763,8 @@ function formatObserverMessage(msg, counter, toolFormatting, options) {
|
|
|
20364
20763
|
if (!body) return;
|
|
20365
20764
|
const normalizedCreatedAt = normalizeObserverCreatedAt(createdAt) ?? messageCreatedAt;
|
|
20366
20765
|
lines.push({
|
|
20367
|
-
date: formatObserverDate(normalizedCreatedAt),
|
|
20368
|
-
time: formatObserverTime(normalizedCreatedAt),
|
|
20766
|
+
date: formatObserverDate(normalizedCreatedAt, timeZone),
|
|
20767
|
+
time: formatObserverTime(normalizedCreatedAt, timeZone),
|
|
20369
20768
|
title,
|
|
20370
20769
|
body
|
|
20371
20770
|
});
|
|
@@ -20919,171 +21318,6 @@ function optimizeObservationsForContext(observations) {
|
|
|
20919
21318
|
return optimized.trim();
|
|
20920
21319
|
}
|
|
20921
21320
|
//#endregion
|
|
20922
|
-
//#region src/processors/observational-memory/retry.ts
|
|
20923
|
-
/**
|
|
20924
|
-
* Retry knobs for the internal OM transport-error retry wrapper.
|
|
20925
|
-
* Exported as a mutable object so tests can shrink the backoff schedule
|
|
20926
|
-
* without changing public API.
|
|
20927
|
-
*
|
|
20928
|
-
* With the defaults the per-retry pre-jitter backoff schedule is:
|
|
20929
|
-
* 1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s (cap)
|
|
20930
|
-
* giving 8 retries / 9 total attempts and ~247s (~4 minutes) of waiting
|
|
20931
|
-
* before the final attempt fails. Designed to ride out short provider /
|
|
20932
|
-
* network blips without holding the actor turn for much longer than that.
|
|
20933
|
-
*
|
|
20934
|
-
* @internal
|
|
20935
|
-
*/
|
|
20936
|
-
const RETRY_CONFIG = {
|
|
20937
|
-
/** Maximum number of retry *attempts* (total tries = maxRetries + 1). */
|
|
20938
|
-
maxRetries: 8,
|
|
20939
|
-
/** Initial backoff delay in milliseconds. */
|
|
20940
|
-
initialDelayMs: 1e3,
|
|
20941
|
-
/** Multiplier applied to the delay after each failed attempt. */
|
|
20942
|
-
backoffFactor: 2,
|
|
20943
|
-
/** Cap on per-attempt delay (ms). */
|
|
20944
|
-
maxDelayMs: 12e4,
|
|
20945
|
-
/** Random jitter as a fraction of the computed delay (e.g. 0.2 = ±20%). */
|
|
20946
|
-
jitter: .2
|
|
20947
|
-
};
|
|
20948
|
-
const TRANSIENT_MESSAGE_SUBSTRINGS = [
|
|
20949
|
-
"terminated",
|
|
20950
|
-
"fetch failed",
|
|
20951
|
-
"econnreset",
|
|
20952
|
-
"econnrefused",
|
|
20953
|
-
"enotfound",
|
|
20954
|
-
"eai_again",
|
|
20955
|
-
"socket hang up",
|
|
20956
|
-
"network error",
|
|
20957
|
-
"request timed out",
|
|
20958
|
-
"request timeout",
|
|
20959
|
-
"connection reset",
|
|
20960
|
-
"connection closed"
|
|
20961
|
-
];
|
|
20962
|
-
function isRecord$2(value) {
|
|
20963
|
-
return typeof value === "object" && value !== null;
|
|
20964
|
-
}
|
|
20965
|
-
function isAbortError(error) {
|
|
20966
|
-
if (!isRecord$2(error)) return false;
|
|
20967
|
-
if (error.name === "AbortError") return true;
|
|
20968
|
-
if (typeof error.code === "string" && error.code === "ABORT_ERR") return true;
|
|
20969
|
-
return false;
|
|
20970
|
-
}
|
|
20971
|
-
function hasTransientMessage(value) {
|
|
20972
|
-
if (!isRecord$2(value)) return false;
|
|
20973
|
-
const message = typeof value.message === "string" ? value.message.toLowerCase() : "";
|
|
20974
|
-
if (message && TRANSIENT_MESSAGE_SUBSTRINGS.some((sub) => message.includes(sub))) return true;
|
|
20975
|
-
if (typeof value.code === "string" && value.code.toUpperCase().startsWith("UND_ERR_")) return true;
|
|
20976
|
-
return false;
|
|
20977
|
-
}
|
|
20978
|
-
function isRetryableHttpStatus(status) {
|
|
20979
|
-
if (status === 408 || status === 425 || status === 429) return true;
|
|
20980
|
-
return status >= 500 && status <= 599;
|
|
20981
|
-
}
|
|
20982
|
-
function hasRetryableHttpStatus(value) {
|
|
20983
|
-
if (!isRecord$2(value)) return false;
|
|
20984
|
-
if (typeof value.statusCode === "number" && isRetryableHttpStatus(value.statusCode)) return true;
|
|
20985
|
-
if (typeof value.code === "number" && isRetryableHttpStatus(value.code)) return true;
|
|
20986
|
-
return false;
|
|
20987
|
-
}
|
|
20988
|
-
function hasIsRetryableFlag(value) {
|
|
20989
|
-
if (!isRecord$2(value)) return false;
|
|
20990
|
-
return value.isRetryable === true;
|
|
20991
|
-
}
|
|
20992
|
-
/**
|
|
20993
|
-
* Returns true when the given error looks like a transient transport-class
|
|
20994
|
-
* failure that's worth retrying — undici `terminated`, `fetch failed`,
|
|
20995
|
-
* `UND_ERR_*` codes, AI SDK `APICallError` with `isRetryable: true`, and
|
|
20996
|
-
* common HTTP 408/425/429/5xx statuses. Walks the `error.cause` chain so
|
|
20997
|
-
* wrapper errors don't hide the real failure.
|
|
20998
|
-
*
|
|
20999
|
-
* Never retries on user-initiated aborts.
|
|
21000
|
-
*
|
|
21001
|
-
* @internal
|
|
21002
|
-
*/
|
|
21003
|
-
function isTransientLLMError(error) {
|
|
21004
|
-
if (isAbortError(error)) return false;
|
|
21005
|
-
const visited = /* @__PURE__ */ new WeakSet();
|
|
21006
|
-
function visit(candidate) {
|
|
21007
|
-
if (isRecord$2(candidate)) {
|
|
21008
|
-
if (visited.has(candidate)) return false;
|
|
21009
|
-
visited.add(candidate);
|
|
21010
|
-
}
|
|
21011
|
-
if (hasTransientMessage(candidate)) return true;
|
|
21012
|
-
if (hasRetryableHttpStatus(candidate)) return true;
|
|
21013
|
-
if (hasIsRetryableFlag(candidate)) return true;
|
|
21014
|
-
if (isRecord$2(candidate)) {
|
|
21015
|
-
if (visit(candidate.cause)) return true;
|
|
21016
|
-
if (visit(candidate.error)) return true;
|
|
21017
|
-
}
|
|
21018
|
-
return false;
|
|
21019
|
-
}
|
|
21020
|
-
return visit(error);
|
|
21021
|
-
}
|
|
21022
|
-
/**
|
|
21023
|
-
* Compute the backoff delay (ms) for the Nth retry (0-indexed).
|
|
21024
|
-
*
|
|
21025
|
-
* Exponential growth (`initialDelayMs * backoffFactor^attempt`) capped at
|
|
21026
|
-
* `maxDelayMs`, then nudged by ±`jitter` (fractional). Exported for unit
|
|
21027
|
-
* tests that lock the schedule against drift.
|
|
21028
|
-
*
|
|
21029
|
-
* @internal
|
|
21030
|
-
*/
|
|
21031
|
-
function computeDelay(attempt) {
|
|
21032
|
-
const base = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, attempt);
|
|
21033
|
-
const capped = Math.min(base, RETRY_CONFIG.maxDelayMs);
|
|
21034
|
-
if (RETRY_CONFIG.jitter <= 0) return capped;
|
|
21035
|
-
const jitterRange = capped * RETRY_CONFIG.jitter;
|
|
21036
|
-
const offset = (Math.random() * 2 - 1) * jitterRange;
|
|
21037
|
-
return Math.max(0, Math.round(capped + offset));
|
|
21038
|
-
}
|
|
21039
|
-
function sleep(ms, abortSignal) {
|
|
21040
|
-
if (ms <= 0) return Promise.resolve();
|
|
21041
|
-
return new Promise((resolve, reject) => {
|
|
21042
|
-
if (abortSignal?.aborted) {
|
|
21043
|
-
reject(/* @__PURE__ */ new Error("The operation was aborted."));
|
|
21044
|
-
return;
|
|
21045
|
-
}
|
|
21046
|
-
const timer = setTimeout(() => {
|
|
21047
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
21048
|
-
resolve();
|
|
21049
|
-
}, ms);
|
|
21050
|
-
const onAbort = () => {
|
|
21051
|
-
clearTimeout(timer);
|
|
21052
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
21053
|
-
reject(/* @__PURE__ */ new Error("The operation was aborted."));
|
|
21054
|
-
};
|
|
21055
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
21056
|
-
});
|
|
21057
|
-
}
|
|
21058
|
-
/**
|
|
21059
|
-
* Run `fn` with retries on transient transport-class errors.
|
|
21060
|
-
*
|
|
21061
|
-
* Non-transient errors (auth, validation, schema, etc.) are rethrown
|
|
21062
|
-
* immediately. User-initiated aborts are rethrown without delay.
|
|
21063
|
-
*
|
|
21064
|
-
* @internal
|
|
21065
|
-
*/
|
|
21066
|
-
async function withRetry(fn, opts) {
|
|
21067
|
-
const { label, abortSignal } = opts;
|
|
21068
|
-
let attempt = 0;
|
|
21069
|
-
while (true) {
|
|
21070
|
-
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
21071
|
-
try {
|
|
21072
|
-
return await fn();
|
|
21073
|
-
} catch (error) {
|
|
21074
|
-
if (isAbortError(error) || abortSignal?.aborted) throw error;
|
|
21075
|
-
if (attempt >= RETRY_CONFIG.maxRetries || !isTransientLLMError(error)) {
|
|
21076
|
-
if (attempt > 0) omDebug(`[OM:retry:${label}] giving up after ${attempt} retry/retries: ${error instanceof Error ? error.message : String(error)}`);
|
|
21077
|
-
throw error;
|
|
21078
|
-
}
|
|
21079
|
-
const delay = computeDelay(attempt);
|
|
21080
|
-
attempt++;
|
|
21081
|
-
omDebug(`[OM:retry:${label}] transient error on attempt ${attempt}, retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
21082
|
-
await sleep(delay, abortSignal);
|
|
21083
|
-
}
|
|
21084
|
-
}
|
|
21085
|
-
}
|
|
21086
|
-
//#endregion
|
|
21087
21321
|
//#region src/processors/observational-memory/temporary-memory.ts
|
|
21088
21322
|
function createTemporaryOmMemoryContext(prefix) {
|
|
21089
21323
|
const options = {
|
|
@@ -21225,11 +21459,27 @@ var ObserverRunner = class {
|
|
|
21225
21459
|
this.mastra = mastra;
|
|
21226
21460
|
}
|
|
21227
21461
|
createAgent(model, isMultiThread = false, memory, extractors = this.observationConfig.extractors ?? []) {
|
|
21462
|
+
let agentModel = model;
|
|
21463
|
+
if (Array.isArray(agentModel)) agentModel = agentModel.map((fallback) => ({
|
|
21464
|
+
...fallback,
|
|
21465
|
+
maxRetries: 0
|
|
21466
|
+
}));
|
|
21467
|
+
else if (typeof agentModel === "function") {
|
|
21468
|
+
const resolveDynamicModel = agentModel;
|
|
21469
|
+
agentModel = (async (args) => {
|
|
21470
|
+
const resolvedModel = await resolveDynamicModel(args);
|
|
21471
|
+
return Array.isArray(resolvedModel) ? resolvedModel.map((fallback) => ({
|
|
21472
|
+
...fallback,
|
|
21473
|
+
maxRetries: 0
|
|
21474
|
+
})) : resolvedModel;
|
|
21475
|
+
});
|
|
21476
|
+
}
|
|
21228
21477
|
return new Agent({
|
|
21229
21478
|
id: isMultiThread ? "multi-thread-observer" : "observational-memory-observer",
|
|
21230
21479
|
name: isMultiThread ? "multi-thread-observer" : "Observer",
|
|
21480
|
+
maxRetries: 0,
|
|
21231
21481
|
instructions: buildObserverSystemPrompt(isMultiThread, this.observationConfig.instruction, this.observationConfig.threadTitle, extractors),
|
|
21232
|
-
model,
|
|
21482
|
+
model: agentModel,
|
|
21233
21483
|
...memory ? { memory } : {},
|
|
21234
21484
|
...this.mastra ? { mastra: this.mastra } : {}
|
|
21235
21485
|
});
|
|
@@ -21321,7 +21571,10 @@ var ObserverRunner = class {
|
|
|
21321
21571
|
...options,
|
|
21322
21572
|
includeThreadTitle: this.observationConfig.threadTitle,
|
|
21323
21573
|
extractors: activeExtractors
|
|
21324
|
-
}, {
|
|
21574
|
+
}, {
|
|
21575
|
+
attachmentFilter,
|
|
21576
|
+
timeZone: options?.timeZone
|
|
21577
|
+
})];
|
|
21325
21578
|
const doGenerate = async () => {
|
|
21326
21579
|
return withRetry(() => withOmTracingSpan({
|
|
21327
21580
|
phase: "observer",
|
|
@@ -21359,12 +21612,14 @@ var ObserverRunner = class {
|
|
|
21359
21612
|
hasRequestContext: Boolean(internalRequestContext),
|
|
21360
21613
|
aborted: abortSignal?.aborted ?? false
|
|
21361
21614
|
});
|
|
21362
|
-
throw error;
|
|
21615
|
+
if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
|
|
21616
|
+
throw new OmModelExecutionError("observer-model", error);
|
|
21363
21617
|
}
|
|
21364
21618
|
}, abortSignal)
|
|
21365
21619
|
}), {
|
|
21366
21620
|
label: "observer",
|
|
21367
|
-
abortSignal
|
|
21621
|
+
abortSignal,
|
|
21622
|
+
maxRetries: this.observationConfig.maxRetries
|
|
21368
21623
|
});
|
|
21369
21624
|
};
|
|
21370
21625
|
let result = await doGenerate();
|
|
@@ -21432,7 +21687,7 @@ var ObserverRunner = class {
|
|
|
21432
21687
|
/**
|
|
21433
21688
|
* Call the Observer agent for multiple threads in a single batched request.
|
|
21434
21689
|
*/
|
|
21435
|
-
async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext) {
|
|
21690
|
+
async callMultiThread(existingObservations, allMessagesByThread, allThreadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, hookContext, timeZone) {
|
|
21436
21691
|
const contextFor = (threadId) => ({
|
|
21437
21692
|
threadId,
|
|
21438
21693
|
...hookContext
|
|
@@ -21443,7 +21698,7 @@ var ObserverRunner = class {
|
|
|
21443
21698
|
if (filtered.length > 0) messagesByThread.set(threadId, filtered);
|
|
21444
21699
|
}
|
|
21445
21700
|
const threadOrder = allThreadOrder.filter((threadId) => messagesByThread.has(threadId));
|
|
21446
|
-
const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model);
|
|
21701
|
+
const output = await this.callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone);
|
|
21447
21702
|
for (const threadId of allThreadOrder) {
|
|
21448
21703
|
const threadResult = output.results.get(threadId);
|
|
21449
21704
|
if (!threadResult) {
|
|
@@ -21454,7 +21709,7 @@ var ObserverRunner = class {
|
|
|
21454
21709
|
}
|
|
21455
21710
|
return output;
|
|
21456
21711
|
}
|
|
21457
|
-
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model) {
|
|
21712
|
+
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext, priorMetadataByThread, observabilityContext, model, timeZone) {
|
|
21458
21713
|
if (threadOrder.length === 0) return { results: /* @__PURE__ */ new Map() };
|
|
21459
21714
|
const inputTokens = Array.from(messagesByThread.values()).reduce((total, messages) => total + this.tokenCounter.countMessages(messages), 0);
|
|
21460
21715
|
const resolvedModel = (() => {
|
|
@@ -21496,7 +21751,8 @@ var ObserverRunner = class {
|
|
|
21496
21751
|
priorSuggestedResponse: priorMetadataByThread?.get(threadId)?.suggestedResponse,
|
|
21497
21752
|
priorThreadTitle: priorMetadataByThread?.get(threadId)?.threadTitle,
|
|
21498
21753
|
priorExtractedValues: priorMetadataByThread?.get(threadId)?.extracted,
|
|
21499
|
-
model: resolvedModel.model
|
|
21754
|
+
model: resolvedModel.model,
|
|
21755
|
+
timeZone
|
|
21500
21756
|
});
|
|
21501
21757
|
results.set(threadId, {
|
|
21502
21758
|
observations: threadResult.observations,
|
|
@@ -21513,6 +21769,7 @@ var ObserverRunner = class {
|
|
|
21513
21769
|
totalUsage.totalTokens += threadResult.usage.totalTokens ?? 0;
|
|
21514
21770
|
}
|
|
21515
21771
|
}
|
|
21772
|
+
for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
|
|
21516
21773
|
return {
|
|
21517
21774
|
results,
|
|
21518
21775
|
usage: totalUsage
|
|
@@ -21521,8 +21778,10 @@ var ObserverRunner = class {
|
|
|
21521
21778
|
const agent = this.createAgent(resolvedModel.model, true, void 0, activeExtractors);
|
|
21522
21779
|
const internalRequestContext = withOmInternalThreadId(requestContext, agent.id);
|
|
21523
21780
|
const multiThreadAttachmentFilter = this.resolveAttachmentFilter(resolvedModel.model, requestContext);
|
|
21524
|
-
const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, void 0, this.observationConfig.threadTitle, activeExtractors, {
|
|
21525
|
-
|
|
21781
|
+
const observerMessages = [buildMultiThreadObserverRequestMessage(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, void 0, this.observationConfig.threadTitle, activeExtractors, {
|
|
21782
|
+
attachmentFilter: multiThreadAttachmentFilter,
|
|
21783
|
+
timeZone
|
|
21784
|
+
})];
|
|
21526
21785
|
const doGenerate = async () => {
|
|
21527
21786
|
return withRetry(() => withOmTracingSpan({
|
|
21528
21787
|
phase: "observer-multi-thread",
|
|
@@ -21560,12 +21819,14 @@ var ObserverRunner = class {
|
|
|
21560
21819
|
hasRequestContext: Boolean(internalRequestContext),
|
|
21561
21820
|
aborted: abortSignal?.aborted ?? false
|
|
21562
21821
|
});
|
|
21563
|
-
throw error;
|
|
21822
|
+
if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
|
|
21823
|
+
throw new OmModelExecutionError("observer-model", error);
|
|
21564
21824
|
}
|
|
21565
21825
|
}, abortSignal)
|
|
21566
21826
|
}), {
|
|
21567
21827
|
label: "observer-multi-thread",
|
|
21568
|
-
abortSignal
|
|
21828
|
+
abortSignal,
|
|
21829
|
+
maxRetries: this.observationConfig.maxRetries
|
|
21569
21830
|
});
|
|
21570
21831
|
};
|
|
21571
21832
|
let result = await doGenerate();
|
|
@@ -21621,6 +21882,7 @@ var ObserverRunner = class {
|
|
|
21621
21882
|
});
|
|
21622
21883
|
}
|
|
21623
21884
|
for (const threadId of threadOrder) if (!results.has(threadId)) results.set(threadId, { observations: "" });
|
|
21885
|
+
for (const msgs of messagesByThread.values()) for (const msg of msgs) this.observedMessageIds.add(msg.id);
|
|
21624
21886
|
const usage = result.totalUsage ?? result.usage;
|
|
21625
21887
|
return {
|
|
21626
21888
|
results,
|
|
@@ -23114,6 +23376,8 @@ async function summarizeConversation(opts) {
|
|
|
23114
23376
|
const runner = new ObserverRunner({
|
|
23115
23377
|
observationConfig: {
|
|
23116
23378
|
model: opts.model,
|
|
23379
|
+
maxRetries: 8,
|
|
23380
|
+
failurePolicy: "abort",
|
|
23117
23381
|
messageTokens: OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens,
|
|
23118
23382
|
shareTokenBudget: false,
|
|
23119
23383
|
modelSettings: { ...OBSERVATIONAL_MEMORY_DEFAULTS.observation.modelSettings },
|
|
@@ -24944,6 +25208,7 @@ function createObservationFailedMarker(params) {
|
|
|
24944
25208
|
durationMs,
|
|
24945
25209
|
tokensAttempted: params.tokensAttempted,
|
|
24946
25210
|
error: formatOmError(params.error),
|
|
25211
|
+
...getOmFailureMetadata(params.error, params.failurePolicy ?? "abort"),
|
|
24947
25212
|
recordId: params.recordId,
|
|
24948
25213
|
threadId: params.threadId
|
|
24949
25214
|
}
|
|
@@ -25005,6 +25270,7 @@ function createBufferingFailedMarker(params) {
|
|
|
25005
25270
|
durationMs,
|
|
25006
25271
|
tokensAttempted: params.tokensAttempted,
|
|
25007
25272
|
error: formatOmError(params.error),
|
|
25273
|
+
...getOmFailureMetadata(params.error, params.failurePolicy ?? "abort"),
|
|
25008
25274
|
recordId: params.recordId,
|
|
25009
25275
|
threadId: params.threadId
|
|
25010
25276
|
}
|
|
@@ -25061,7 +25327,10 @@ function createThreadUpdateMarker(params) {
|
|
|
25061
25327
|
function findLastCompletedObservationBoundary(message) {
|
|
25062
25328
|
const parts = message.content?.parts;
|
|
25063
25329
|
if (!parts || !Array.isArray(parts)) return -1;
|
|
25064
|
-
for (let i = parts.length - 1; i >= 0; i--)
|
|
25330
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
25331
|
+
const part = parts[i];
|
|
25332
|
+
if (part?.type === "data-om-observation-end" && part.data?.operationType !== "reflection") return i;
|
|
25333
|
+
}
|
|
25065
25334
|
return -1;
|
|
25066
25335
|
}
|
|
25067
25336
|
/**
|
|
@@ -25313,6 +25582,7 @@ function scanObservationGroupTags(observations) {
|
|
|
25313
25582
|
const incompleteOpenings = [];
|
|
25314
25583
|
let cursor = 0;
|
|
25315
25584
|
let closeFloor = 0;
|
|
25585
|
+
let nestedFloor = -1;
|
|
25316
25586
|
while (cursor < observations.length) {
|
|
25317
25587
|
const start = observations.indexOf(OBSERVATION_GROUP_OPEN, cursor);
|
|
25318
25588
|
if (start === -1) break;
|
|
@@ -25333,8 +25603,12 @@ function scanObservationGroupTags(observations) {
|
|
|
25333
25603
|
cursor = openEnd + 1;
|
|
25334
25604
|
continue;
|
|
25335
25605
|
}
|
|
25336
|
-
|
|
25337
|
-
|
|
25606
|
+
if (nestedFloor <= openEnd) {
|
|
25607
|
+
const lineOpen = observations.indexOf(`\n${OBSERVATION_GROUP_OPEN}`, openEnd);
|
|
25608
|
+
nestedFloor = lineOpen === -1 ? observations.length : lineOpen + 1;
|
|
25609
|
+
}
|
|
25610
|
+
const nestedStart = nestedFloor;
|
|
25611
|
+
if (nestedStart < closeStart) {
|
|
25338
25612
|
incompleteOpenings.push({
|
|
25339
25613
|
start,
|
|
25340
25614
|
end: openEnd + 1
|
|
@@ -25721,6 +25995,7 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
25721
25995
|
observationTokens: processed.observationTokens,
|
|
25722
25996
|
threadId,
|
|
25723
25997
|
writer,
|
|
25998
|
+
messageList: this.opts.messageList,
|
|
25724
25999
|
abortSignal,
|
|
25725
26000
|
mainAgent: this.opts.agent,
|
|
25726
26001
|
sendSignal: this.opts.sendSignal,
|
|
@@ -25745,6 +26020,7 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
25745
26020
|
operationType: "observation",
|
|
25746
26021
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25747
26022
|
error: formatOmError(error),
|
|
26023
|
+
...getOmFailureMetadata(error, this.observationConfig.failurePolicy),
|
|
25748
26024
|
recordId: record.id,
|
|
25749
26025
|
threadId
|
|
25750
26026
|
}
|
|
@@ -25758,6 +26034,10 @@ var ObservationStrategy = class ObservationStrategy {
|
|
|
25758
26034
|
};
|
|
25759
26035
|
}
|
|
25760
26036
|
omError("[OM] Observation failed", error);
|
|
26037
|
+
if (this.observationConfig.failurePolicy === "continue" && isOmModelExecutionError(error) && error.failureKind === "observer-model") return {
|
|
26038
|
+
observed: false,
|
|
26039
|
+
error
|
|
26040
|
+
};
|
|
25761
26041
|
throw error;
|
|
25762
26042
|
}
|
|
25763
26043
|
}
|
|
@@ -26027,7 +26307,8 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
26027
26307
|
threadId: this.opts.threadId,
|
|
26028
26308
|
resourceId: this.opts.resourceId,
|
|
26029
26309
|
trigger: this.opts.trigger,
|
|
26030
|
-
mainAgent: this.opts.agent
|
|
26310
|
+
mainAgent: this.opts.agent,
|
|
26311
|
+
timeZone: this.opts.record.observedTimezone
|
|
26031
26312
|
});
|
|
26032
26313
|
const hookedValues = await applyExtractorHooks({
|
|
26033
26314
|
source: "observer",
|
|
@@ -26036,7 +26317,10 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
26036
26317
|
failures: result.extractionFailures,
|
|
26037
26318
|
previousValues: this.priorExtractedValues,
|
|
26038
26319
|
rawObservations: result.observations,
|
|
26039
|
-
recentMessages: formatMessagesForObserver(this.opts.messages, {
|
|
26320
|
+
recentMessages: formatMessagesForObserver(this.opts.messages, {
|
|
26321
|
+
maxPartLength: 500,
|
|
26322
|
+
timeZone: this.opts.record.observedTimezone
|
|
26323
|
+
}),
|
|
26040
26324
|
threadId: this.opts.threadId,
|
|
26041
26325
|
resourceId: this.opts.resourceId,
|
|
26042
26326
|
mainAgent: this.opts.agent,
|
|
@@ -26167,6 +26451,7 @@ var SyncObservationStrategy = class extends ObservationStrategy {
|
|
|
26167
26451
|
startedAt: this.startedAt,
|
|
26168
26452
|
tokensAttempted: this.tokensToObserve,
|
|
26169
26453
|
error,
|
|
26454
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
26170
26455
|
recordId: this.opts.record.id,
|
|
26171
26456
|
threadId: this.opts.threadId
|
|
26172
26457
|
});
|
|
@@ -26218,7 +26503,8 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
26218
26503
|
threadId: this.opts.threadId,
|
|
26219
26504
|
resourceId: this.opts.resourceId,
|
|
26220
26505
|
trigger: this.opts.trigger,
|
|
26221
|
-
mainAgent: this.opts.agent
|
|
26506
|
+
mainAgent: this.opts.agent,
|
|
26507
|
+
timeZone: this.opts.record.observedTimezone
|
|
26222
26508
|
});
|
|
26223
26509
|
const hookedValues = await applyExtractorHooks({
|
|
26224
26510
|
source: "observer",
|
|
@@ -26227,7 +26513,10 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
26227
26513
|
failures: result.extractionFailures,
|
|
26228
26514
|
previousValues: this.priorExtractedValues,
|
|
26229
26515
|
rawObservations: result.observations,
|
|
26230
|
-
recentMessages: formatMessagesForObserver(messages, {
|
|
26516
|
+
recentMessages: formatMessagesForObserver(messages, {
|
|
26517
|
+
maxPartLength: 500,
|
|
26518
|
+
timeZone: this.opts.record.observedTimezone
|
|
26519
|
+
}),
|
|
26231
26520
|
threadId: this.opts.threadId,
|
|
26232
26521
|
resourceId: this.opts.resourceId,
|
|
26233
26522
|
mainAgent: this.opts.agent,
|
|
@@ -26373,6 +26662,7 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
|
|
|
26373
26662
|
startedAt: this.startedAt,
|
|
26374
26663
|
tokensAttempted,
|
|
26375
26664
|
error,
|
|
26665
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
26376
26666
|
recordId: record.id,
|
|
26377
26667
|
threadId
|
|
26378
26668
|
});
|
|
@@ -26549,7 +26839,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
26549
26839
|
return this.deps.observer.callMultiThread(_existingObservations, batch.threadMap, batch.threadIds, this.opts.abortSignal, this.opts.requestContext, this.priorMetadataByThread, this.opts.observabilityContext, void 0, {
|
|
26550
26840
|
resourceId: this.opts.resourceId,
|
|
26551
26841
|
trigger: this.opts.trigger
|
|
26552
|
-
});
|
|
26842
|
+
}, this.opts.record.observedTimezone);
|
|
26553
26843
|
}));
|
|
26554
26844
|
for (const batchResult of batchResults) {
|
|
26555
26845
|
for (const [threadId, result] of batchResult.results) this.multiThreadResults.set(threadId, result);
|
|
@@ -26582,7 +26872,10 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
26582
26872
|
failures: result.extractionFailures,
|
|
26583
26873
|
previousValues,
|
|
26584
26874
|
rawObservations: result.observations,
|
|
26585
|
-
recentMessages: formatMessagesForObserver(threadMessages, {
|
|
26875
|
+
recentMessages: formatMessagesForObserver(threadMessages, {
|
|
26876
|
+
maxPartLength: 500,
|
|
26877
|
+
timeZone: this.opts.record.observedTimezone
|
|
26878
|
+
}),
|
|
26586
26879
|
threadId,
|
|
26587
26880
|
resourceId: this.resourceId,
|
|
26588
26881
|
mainAgent: this.opts.agent,
|
|
@@ -26741,6 +27034,7 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
|
|
|
26741
27034
|
startedAt: this.startedAt,
|
|
26742
27035
|
tokensAttempted,
|
|
26743
27036
|
error,
|
|
27037
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
26744
27038
|
recordId: this.opts.record.id,
|
|
26745
27039
|
threadId
|
|
26746
27040
|
});
|
|
@@ -27155,19 +27449,28 @@ var ObservationStep = class {
|
|
|
27155
27449
|
};
|
|
27156
27450
|
}
|
|
27157
27451
|
}
|
|
27158
|
-
|
|
27159
|
-
|
|
27160
|
-
|
|
27161
|
-
|
|
27162
|
-
|
|
27163
|
-
|
|
27164
|
-
|
|
27165
|
-
|
|
27166
|
-
|
|
27167
|
-
|
|
27168
|
-
|
|
27169
|
-
|
|
27170
|
-
|
|
27452
|
+
let obsResult;
|
|
27453
|
+
try {
|
|
27454
|
+
obsResult = await om.observe({
|
|
27455
|
+
threadId,
|
|
27456
|
+
resourceId,
|
|
27457
|
+
messages: observableMessages,
|
|
27458
|
+
messageList,
|
|
27459
|
+
trigger: "turn-sync",
|
|
27460
|
+
agent: this.turn.agent,
|
|
27461
|
+
sendSignal: this.turn.sendSignal,
|
|
27462
|
+
sendStateSignal: this.turn.sendStateSignal,
|
|
27463
|
+
requestContext: this.turn.requestContext,
|
|
27464
|
+
writer: this.turn.writer,
|
|
27465
|
+
observabilityContext: this.turn.observabilityContext
|
|
27466
|
+
});
|
|
27467
|
+
} catch (error) {
|
|
27468
|
+
if (om.config.observation.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "observer-model") throw error;
|
|
27469
|
+
return {
|
|
27470
|
+
succeeded: false,
|
|
27471
|
+
record: freshStatus.record
|
|
27472
|
+
};
|
|
27473
|
+
}
|
|
27171
27474
|
if (obsResult.observed) {
|
|
27172
27475
|
const observedMessageIds = new Set(obsResult.record.observedMessageIds ?? []);
|
|
27173
27476
|
const liveMessages = getObservableMessages(messageList);
|
|
@@ -27825,11 +28128,27 @@ var ReflectorRunner = class {
|
|
|
27825
28128
|
this.mastra = mastra;
|
|
27826
28129
|
}
|
|
27827
28130
|
createAgent(model, memory, extractors = this.reflectionConfig.extractors) {
|
|
28131
|
+
let agentModel = model;
|
|
28132
|
+
if (Array.isArray(agentModel)) agentModel = agentModel.map((fallback) => ({
|
|
28133
|
+
...fallback,
|
|
28134
|
+
maxRetries: 0
|
|
28135
|
+
}));
|
|
28136
|
+
else if (typeof agentModel === "function") {
|
|
28137
|
+
const resolveDynamicModel = agentModel;
|
|
28138
|
+
agentModel = (async (args) => {
|
|
28139
|
+
const resolvedModel = await resolveDynamicModel(args);
|
|
28140
|
+
return Array.isArray(resolvedModel) ? resolvedModel.map((fallback) => ({
|
|
28141
|
+
...fallback,
|
|
28142
|
+
maxRetries: 0
|
|
28143
|
+
})) : resolvedModel;
|
|
28144
|
+
});
|
|
28145
|
+
}
|
|
27828
28146
|
return new Agent({
|
|
27829
28147
|
id: "observational-memory-reflector",
|
|
27830
28148
|
name: "Reflector",
|
|
27831
28149
|
instructions: buildReflectorSystemPrompt(this.reflectionConfig.instruction, extractors),
|
|
27832
|
-
model,
|
|
28150
|
+
model: agentModel,
|
|
28151
|
+
maxRetries: 0,
|
|
27833
28152
|
...memory ? { memory } : {},
|
|
27834
28153
|
...this.mastra ? { mastra: this.mastra } : {}
|
|
27835
28154
|
});
|
|
@@ -27911,36 +28230,42 @@ var ReflectorRunner = class {
|
|
|
27911
28230
|
},
|
|
27912
28231
|
callback: (childObservabilityContext) => withAbortCheck(async () => {
|
|
27913
28232
|
chunkCount = 0;
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27925
|
-
|
|
27926
|
-
|
|
28233
|
+
try {
|
|
28234
|
+
return await (await agent.stream(prompt, {
|
|
28235
|
+
modelSettings: { ...this.reflectionConfig.modelSettings },
|
|
28236
|
+
providerOptions: this.reflectionConfig.providerOptions,
|
|
28237
|
+
...temporaryMemory ? { memory: temporaryMemory.options } : {},
|
|
28238
|
+
...abortSignal ? { abortSignal } : {},
|
|
28239
|
+
...internalRequestContext ? { requestContext: internalRequestContext } : {},
|
|
28240
|
+
...childObservabilityContext,
|
|
28241
|
+
...attemptNumber === 1 ? {
|
|
28242
|
+
onChunk(chunk) {
|
|
28243
|
+
chunkCount++;
|
|
28244
|
+
if (chunkCount === 1 || chunkCount % 50 === 0) {
|
|
28245
|
+
const preview = chunk.type === "text-delta" ? ` text="${chunk.textDelta?.slice(0, 80)}..."` : chunk.type === "tool-call" ? ` tool=${chunk.toolName}` : "";
|
|
28246
|
+
omDebug(`[OM:callReflector] chunk#${chunkCount}: type=${chunk.type}${preview}`);
|
|
28247
|
+
}
|
|
28248
|
+
},
|
|
28249
|
+
onFinish(event) {
|
|
28250
|
+
omDebug(`[OM:callReflector] onFinish: chunks=${chunkCount}, finishReason=${event.finishReason}, inputTokens=${event.usage?.inputTokens}, outputTokens=${event.usage?.outputTokens}, textLen=${event.text?.length}`);
|
|
28251
|
+
},
|
|
28252
|
+
onAbort(event) {
|
|
28253
|
+
omDebug(`[OM:callReflector] onAbort: chunks=${chunkCount}, reason=${event?.reason ?? "unknown"}`);
|
|
28254
|
+
},
|
|
28255
|
+
onError({ error }) {
|
|
28256
|
+
omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
|
|
27927
28257
|
}
|
|
27928
|
-
}
|
|
27929
|
-
|
|
27930
|
-
|
|
27931
|
-
|
|
27932
|
-
|
|
27933
|
-
|
|
27934
|
-
},
|
|
27935
|
-
onError({ error }) {
|
|
27936
|
-
omError(`[OM:callReflector] onError after ${chunkCount} chunks`, error);
|
|
27937
|
-
}
|
|
27938
|
-
} : {}
|
|
27939
|
-
})).getFullOutput();
|
|
28258
|
+
} : {}
|
|
28259
|
+
})).getFullOutput();
|
|
28260
|
+
} catch (error) {
|
|
28261
|
+
if (abortSignal?.aborted || !isOmModelExecutionFailure(error)) throw error;
|
|
28262
|
+
throw new OmModelExecutionError("reflector-model", error);
|
|
28263
|
+
}
|
|
27940
28264
|
}, abortSignal)
|
|
27941
28265
|
}), {
|
|
27942
28266
|
label: "reflector",
|
|
27943
|
-
abortSignal
|
|
28267
|
+
abortSignal,
|
|
28268
|
+
maxRetries: this.reflectionConfig.maxRetries
|
|
27944
28269
|
});
|
|
27945
28270
|
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}`);
|
|
27946
28271
|
const usage = result.totalUsage ?? result.usage;
|
|
@@ -28064,6 +28389,7 @@ var ReflectorRunner = class {
|
|
|
28064
28389
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28065
28390
|
tokensAttempted: observationTokens,
|
|
28066
28391
|
error,
|
|
28392
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
28067
28393
|
recordId: record.id,
|
|
28068
28394
|
threadId: record.threadId ?? ""
|
|
28069
28395
|
});
|
|
@@ -28476,24 +28802,43 @@ var ReflectorRunner = class {
|
|
|
28476
28802
|
usage: reflectResult.usage
|
|
28477
28803
|
});
|
|
28478
28804
|
} catch (error) {
|
|
28479
|
-
|
|
28480
|
-
|
|
28481
|
-
|
|
28482
|
-
|
|
28483
|
-
|
|
28484
|
-
|
|
28485
|
-
|
|
28486
|
-
|
|
28487
|
-
|
|
28488
|
-
|
|
28489
|
-
|
|
28490
|
-
|
|
28491
|
-
|
|
28492
|
-
|
|
28805
|
+
reflectionError = error instanceof Error ? error : new Error(String(error));
|
|
28806
|
+
const failedMarker = createObservationFailedMarker({
|
|
28807
|
+
cycleId: streamContext?.cycleId ?? cycleId,
|
|
28808
|
+
operationType: "reflection",
|
|
28809
|
+
startedAt: streamContext?.startedAt ?? startedAt,
|
|
28810
|
+
tokensAttempted: observationTokens,
|
|
28811
|
+
error,
|
|
28812
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
28813
|
+
recordId: record.id,
|
|
28814
|
+
threadId
|
|
28815
|
+
});
|
|
28816
|
+
if (writer) await writer.custom({
|
|
28817
|
+
...failedMarker,
|
|
28818
|
+
transient: true
|
|
28819
|
+
}).catch(() => {});
|
|
28820
|
+
let persistedToList = false;
|
|
28821
|
+
try {
|
|
28822
|
+
persistedToList = await this.persistMarkerToMessage(failedMarker, messageList, threadId, record.resourceId ?? void 0);
|
|
28823
|
+
} catch {
|
|
28824
|
+
persistedToList = false;
|
|
28825
|
+
}
|
|
28826
|
+
if (!persistedToList) try {
|
|
28493
28827
|
await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
|
|
28828
|
+
} catch (markerError) {
|
|
28829
|
+
omError("[OM] Failed to persist reflection-failed marker", markerError);
|
|
28494
28830
|
}
|
|
28495
|
-
|
|
28496
|
-
|
|
28831
|
+
this.emitDebugEvent({
|
|
28832
|
+
type: "reflection_failed",
|
|
28833
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
28834
|
+
threadId,
|
|
28835
|
+
resourceId: record.resourceId ?? "",
|
|
28836
|
+
inputTokens: observationTokens,
|
|
28837
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
28838
|
+
failureKind: failedMarker.data.failureKind,
|
|
28839
|
+
error: reflectionError.message
|
|
28840
|
+
});
|
|
28841
|
+
if (lifecycleError !== void 0 || abortSignal?.aborted || this.reflectionConfig.failurePolicy !== "continue" || !isOmModelExecutionError(error) || error.failureKind !== "reflector-model") throw error;
|
|
28497
28842
|
this.syncReflectionSuppression.set(lockKey, observationTokens);
|
|
28498
28843
|
omError("[OM] Reflection failed", error);
|
|
28499
28844
|
} finally {
|
|
@@ -29131,6 +29476,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29131
29476
|
const observationActivateAfterIdlePath = config.observation?.activateAfterIdle !== void 0 ? "observation.activateAfterIdle" : "activateAfterIdle";
|
|
29132
29477
|
this.observationConfig = {
|
|
29133
29478
|
model: observationModel,
|
|
29479
|
+
maxRetries: config.observation?.maxRetries ?? RETRY_CONFIG.maxRetries,
|
|
29480
|
+
failurePolicy: config.observation?.failurePolicy ?? "abort",
|
|
29134
29481
|
messageTokens: isSharedBudget ? {
|
|
29135
29482
|
min: messageTokens,
|
|
29136
29483
|
max: totalBudget
|
|
@@ -29160,6 +29507,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29160
29507
|
};
|
|
29161
29508
|
this.reflectionConfig = {
|
|
29162
29509
|
model: reflectionModel,
|
|
29510
|
+
maxRetries: config.reflection?.maxRetries ?? RETRY_CONFIG.maxRetries,
|
|
29511
|
+
failurePolicy: config.reflection?.failurePolicy ?? "abort",
|
|
29163
29512
|
observationTokens,
|
|
29164
29513
|
shareTokenBudget: isSharedBudget,
|
|
29165
29514
|
modelSettings: {
|
|
@@ -29233,9 +29582,15 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29233
29582
|
retrieval: this.retrieval,
|
|
29234
29583
|
observation: {
|
|
29235
29584
|
messageTokens: this.observationConfig.messageTokens,
|
|
29236
|
-
previousObserverTokens: this.observationConfig.previousObserverTokens
|
|
29585
|
+
previousObserverTokens: this.observationConfig.previousObserverTokens,
|
|
29586
|
+
maxRetries: this.observationConfig.maxRetries,
|
|
29587
|
+
failurePolicy: this.observationConfig.failurePolicy
|
|
29237
29588
|
},
|
|
29238
|
-
reflection: {
|
|
29589
|
+
reflection: {
|
|
29590
|
+
observationTokens: this.reflectionConfig.observationTokens,
|
|
29591
|
+
maxRetries: this.reflectionConfig.maxRetries,
|
|
29592
|
+
failurePolicy: this.reflectionConfig.failurePolicy
|
|
29593
|
+
}
|
|
29239
29594
|
};
|
|
29240
29595
|
}
|
|
29241
29596
|
/**
|
|
@@ -29339,11 +29694,15 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29339
29694
|
messageTokens: this.observationConfig.messageTokens,
|
|
29340
29695
|
model: observationResolved.model,
|
|
29341
29696
|
previousObserverTokens: this.observationConfig.previousObserverTokens,
|
|
29697
|
+
maxRetries: this.observationConfig.maxRetries,
|
|
29698
|
+
failurePolicy: this.observationConfig.failurePolicy,
|
|
29342
29699
|
routing: observationResolved.routing
|
|
29343
29700
|
},
|
|
29344
29701
|
reflection: {
|
|
29345
29702
|
observationTokens: this.reflectionConfig.observationTokens,
|
|
29346
29703
|
model: reflectionResolved.model,
|
|
29704
|
+
maxRetries: this.reflectionConfig.maxRetries,
|
|
29705
|
+
failurePolicy: this.reflectionConfig.failurePolicy,
|
|
29347
29706
|
routing: reflectionResolved.routing
|
|
29348
29707
|
}
|
|
29349
29708
|
};
|
|
@@ -29360,6 +29719,7 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29360
29719
|
* Ensures bufferTokens is less than the threshold and bufferActivation is valid.
|
|
29361
29720
|
*/
|
|
29362
29721
|
validateBufferConfig() {
|
|
29722
|
+
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}`);
|
|
29363
29723
|
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.");
|
|
29364
29724
|
const observationThreshold = getMaxThreshold(this.observationConfig.messageTokens);
|
|
29365
29725
|
if (this.observationConfig.bufferTokens !== void 0) {
|
|
@@ -29487,18 +29847,19 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29487
29847
|
};
|
|
29488
29848
|
}
|
|
29489
29849
|
/**
|
|
29490
|
-
* Persist a data-om-* marker part on the
|
|
29491
|
-
*
|
|
29492
|
-
*
|
|
29850
|
+
* Persist a data-om-* marker part on the message that owns the operation in
|
|
29851
|
+
* messageList and save it to the DB. Observation markers belong to the latest
|
|
29852
|
+
* assistant message; reflection markers belong to the current user input.
|
|
29493
29853
|
* @internal Used by ReflectorRunner. Do not call directly.
|
|
29494
29854
|
*/
|
|
29495
29855
|
async persistMarkerToMessage(marker, messageList, threadId, resourceId) {
|
|
29496
|
-
if (!messageList) return;
|
|
29497
|
-
const allMsgs =
|
|
29856
|
+
if (!messageList) return false;
|
|
29857
|
+
const allMsgs = messageList.get.all.db();
|
|
29858
|
+
const markerData = marker.data;
|
|
29859
|
+
const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
|
|
29498
29860
|
for (let i = allMsgs.length - 1; i >= 0; i--) {
|
|
29499
29861
|
const msg = allMsgs[i];
|
|
29500
|
-
if (msg?.role ===
|
|
29501
|
-
const markerData = marker.data;
|
|
29862
|
+
if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
|
|
29502
29863
|
if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
|
|
29503
29864
|
try {
|
|
29504
29865
|
await this.messageHistory.persistMessages({
|
|
@@ -29508,16 +29869,19 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29508
29869
|
});
|
|
29509
29870
|
} catch (e) {
|
|
29510
29871
|
omDebug(`[OM:persistMarker] failed to save marker to DB: ${e}`);
|
|
29872
|
+
return false;
|
|
29511
29873
|
}
|
|
29512
|
-
return;
|
|
29874
|
+
return true;
|
|
29513
29875
|
}
|
|
29514
29876
|
}
|
|
29877
|
+
return false;
|
|
29515
29878
|
}
|
|
29516
29879
|
/**
|
|
29517
|
-
* Persist a marker to the
|
|
29518
|
-
*
|
|
29519
|
-
*
|
|
29520
|
-
*
|
|
29880
|
+
* Persist a marker to the message that owns the operation in storage.
|
|
29881
|
+
* Observation markers belong to the latest assistant message. Reflection runs
|
|
29882
|
+
* before the current assistant reply exists, so its failure marker belongs to
|
|
29883
|
+
* the latest user input that triggered it.
|
|
29884
|
+
* @internal Used by observation strategies and the reflector. Do not call directly.
|
|
29521
29885
|
*/
|
|
29522
29886
|
async persistMarkerToStorage(marker, threadId, resourceId) {
|
|
29523
29887
|
try {
|
|
@@ -29529,8 +29893,9 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29529
29893
|
direction: "DESC"
|
|
29530
29894
|
}
|
|
29531
29895
|
}))?.messages ?? [];
|
|
29532
|
-
|
|
29533
|
-
|
|
29896
|
+
const markerData = marker.data;
|
|
29897
|
+
const targetRole = markerData?.operationType === "reflection" ? "user" : "assistant";
|
|
29898
|
+
for (const msg of messages) if (msg?.role === targetRole && msg.content?.parts && Array.isArray(msg.content.parts)) {
|
|
29534
29899
|
if (!(markerData?.cycleId && msg.content.parts.some((p) => p?.type === marker.type && p?.data?.cycleId === markerData.cycleId))) msg.content.parts.push(marker);
|
|
29535
29900
|
await this.messageHistory.persistMessages({
|
|
29536
29901
|
messages: [msg],
|
|
@@ -29772,9 +30137,9 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29772
30137
|
* In resource scope mode, filters continuity messages to only show
|
|
29773
30138
|
* the message for the current thread.
|
|
29774
30139
|
*/
|
|
29775
|
-
formatObservationsForContext(observations, currentTask, suggestedResponse, extractedValues, unobservedContextBlocks, currentDate, retrieval = false) {
|
|
30140
|
+
formatObservationsForContext(observations, currentTask, suggestedResponse, extractedValues, unobservedContextBlocks, currentDate, retrieval = false, timeZone) {
|
|
29776
30141
|
let optimized = retrieval ? renderObservationGroupsForReflection(observations) ?? optimizeObservationsForContext(observations) : optimizeObservationsForContext(observations);
|
|
29777
|
-
if (currentDate) optimized = addRelativeTimeToObservations(optimized, currentDate);
|
|
30142
|
+
if (currentDate) optimized = addRelativeTimeToObservations(optimized, currentDate, timeZone);
|
|
29778
30143
|
const messages = [`${getObservationContextPrompt(this.scope)}\n\n${OBSERVATION_CONTEXT_INSTRUCTIONS}${retrieval ? `\n\n${getRetrievalInstructions(this.retrievalScope, this.retrievalInstructions, this.retrievalSearch)}` : ""}`];
|
|
29779
30144
|
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`);
|
|
29780
30145
|
const observationChunks = this.splitObservationContextChunks(optimized);
|
|
@@ -29874,12 +30239,15 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
29874
30239
|
* These are injected into the Actor's context so it has awareness of activity
|
|
29875
30240
|
* in other threads for the same resource.
|
|
29876
30241
|
*/
|
|
29877
|
-
async formatUnobservedContextBlocks(messagesByThread, currentThreadId) {
|
|
30242
|
+
async formatUnobservedContextBlocks(messagesByThread, currentThreadId, timeZone) {
|
|
29878
30243
|
const blocks = [];
|
|
29879
30244
|
for (const [threadId, messages] of messagesByThread) {
|
|
29880
30245
|
if (threadId === currentThreadId) continue;
|
|
29881
30246
|
if (messages.length === 0) continue;
|
|
29882
|
-
const formattedMessages = formatMessagesForObserver(messages, {
|
|
30247
|
+
const formattedMessages = formatMessagesForObserver(messages, {
|
|
30248
|
+
maxPartLength: 500,
|
|
30249
|
+
timeZone
|
|
30250
|
+
});
|
|
29883
30251
|
if (formattedMessages) {
|
|
29884
30252
|
const obscuredId = await this.representThreadIDInContext(threadId);
|
|
29885
30253
|
blocks.push(`<other-conversation id="${obscuredId}">
|
|
@@ -30056,7 +30424,7 @@ ${formattedMessages}
|
|
|
30056
30424
|
transient: true
|
|
30057
30425
|
}).catch(() => {});
|
|
30058
30426
|
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(",")}`);
|
|
30059
|
-
await this.runBufferedObservationCycle({
|
|
30427
|
+
if ((await this.runBufferedObservationCycle({
|
|
30060
30428
|
threadId,
|
|
30061
30429
|
resourceId: freshRecord.resourceId ?? void 0,
|
|
30062
30430
|
trigger: "async-buffer"
|
|
@@ -30071,10 +30439,11 @@ ${formattedMessages}
|
|
|
30071
30439
|
requestContext,
|
|
30072
30440
|
observabilityContext,
|
|
30073
30441
|
trigger: "async-buffer"
|
|
30074
|
-
}).run())
|
|
30075
|
-
|
|
30076
|
-
|
|
30077
|
-
|
|
30442
|
+
}).run()))?.observed) {
|
|
30443
|
+
const maxTs = this.getMaxMessageTimestamp(messagesToBuffer);
|
|
30444
|
+
const cursor = new Date(maxTs.getTime() + 1);
|
|
30445
|
+
BufferingCoordinator.lastBufferedAtTime.set(bufferKey, cursor);
|
|
30446
|
+
}
|
|
30078
30447
|
}
|
|
30079
30448
|
/**
|
|
30080
30449
|
* Trigger async buffered observation if the token count has crossed a new interval.
|
|
@@ -30302,7 +30671,7 @@ ${formattedMessages}
|
|
|
30302
30671
|
const currentTask = activeExtractors.some((extractor) => extractor.slug === "current-task") ? omMetadata?.currentTask : void 0;
|
|
30303
30672
|
const suggestedResponse = activeExtractors.some((extractor) => extractor.slug === "suggested-response") ? omMetadata?.suggestedResponse : void 0;
|
|
30304
30673
|
const currentDate = opts.currentDate ?? /* @__PURE__ */ new Date();
|
|
30305
|
-
return this.formatObservationsForContext(record.activeObservations, currentTask, suggestedResponse, omMetadata?.extracted, unobservedContextBlocks, currentDate, this.retrieval);
|
|
30674
|
+
return this.formatObservationsForContext(record.activeObservations, currentTask, suggestedResponse, omMetadata?.extracted, unobservedContextBlocks, currentDate, this.retrieval, record.observedTimezone);
|
|
30306
30675
|
}
|
|
30307
30676
|
/**
|
|
30308
30677
|
* Get unobserved messages from other threads for resource-scoped observation.
|
|
@@ -30314,7 +30683,8 @@ ${formattedMessages}
|
|
|
30314
30683
|
async getOtherThreadsContext(resourceId, currentThreadId) {
|
|
30315
30684
|
const { threads: allThreads } = await this.storage.listThreads({ filter: { resourceId } });
|
|
30316
30685
|
const messagesByThread = /* @__PURE__ */ new Map();
|
|
30317
|
-
const
|
|
30686
|
+
const record = await this.getRecord(currentThreadId, resourceId);
|
|
30687
|
+
const recordLastObservedAt = record?.lastObservedAt;
|
|
30318
30688
|
for (const thread of allThreads) {
|
|
30319
30689
|
if (thread.id === currentThreadId) continue;
|
|
30320
30690
|
const threadLastObservedAt = getThreadOMMetadata(thread.metadata)?.lastObservedAt ?? recordLastObservedAt;
|
|
@@ -30331,7 +30701,7 @@ ${formattedMessages}
|
|
|
30331
30701
|
if (filtered.length > 0) messagesByThread.set(thread.id, filtered);
|
|
30332
30702
|
}
|
|
30333
30703
|
if (messagesByThread.size === 0) return void 0;
|
|
30334
|
-
return await this.formatUnobservedContextBlocks(messagesByThread, currentThreadId) || void 0;
|
|
30704
|
+
return await this.formatUnobservedContextBlocks(messagesByThread, currentThreadId, record?.observedTimezone) || void 0;
|
|
30335
30705
|
}
|
|
30336
30706
|
/**
|
|
30337
30707
|
* Emit debug event and stream progress for UI feedback.
|
|
@@ -30674,7 +31044,7 @@ ${formattedMessages}
|
|
|
30674
31044
|
...startMarker,
|
|
30675
31045
|
transient: true
|
|
30676
31046
|
}).catch(() => {});
|
|
30677
|
-
await this.runBufferedObservationCycle({
|
|
31047
|
+
if (!(await this.runBufferedObservationCycle({
|
|
30678
31048
|
threadId,
|
|
30679
31049
|
resourceId: record.resourceId ?? resourceId,
|
|
30680
31050
|
trigger: "async-buffer"
|
|
@@ -30693,7 +31063,10 @@ ${formattedMessages}
|
|
|
30693
31063
|
currentModel: opts.currentModel,
|
|
30694
31064
|
observabilityContext,
|
|
30695
31065
|
trigger: "async-buffer"
|
|
30696
|
-
}).run())
|
|
31066
|
+
}).run()))?.observed) return {
|
|
31067
|
+
buffered: false,
|
|
31068
|
+
record
|
|
31069
|
+
};
|
|
30697
31070
|
if (isOmReproCaptureEnabled()) writeObserverExchangeReproCapture({
|
|
30698
31071
|
threadId,
|
|
30699
31072
|
resourceId: record.resourceId ?? void 0,
|
|
@@ -31053,6 +31426,7 @@ ${formattedMessages}
|
|
|
31053
31426
|
observed = result.observed;
|
|
31054
31427
|
observationUsage = result.usage;
|
|
31055
31428
|
observationProviderMetadata = result.providerMetadata;
|
|
31429
|
+
observationError = result.error;
|
|
31056
31430
|
});
|
|
31057
31431
|
} catch (error) {
|
|
31058
31432
|
lifecycleError = error;
|
|
@@ -32988,6 +33362,7 @@ ${workingMemory}`;
|
|
|
32988
33362
|
model: omConfig.model,
|
|
32989
33363
|
mastra: this._mastraInstance,
|
|
32990
33364
|
onIndexObservations,
|
|
33365
|
+
onDebugEvent: omConfig.onDebugEvent,
|
|
32991
33366
|
hooks: omConfig.hooks,
|
|
32992
33367
|
observation: omConfig.observation ? {
|
|
32993
33368
|
model: omConfig.observation.model,
|
|
@@ -33004,10 +33379,14 @@ ${workingMemory}`;
|
|
|
33004
33379
|
threadTitle: omConfig.observation.threadTitle,
|
|
33005
33380
|
observeAttachments: omConfig.observation.observeAttachments,
|
|
33006
33381
|
continuationHints: omConfig.observation.continuationHints,
|
|
33382
|
+
maxRetries: omConfig.observation.maxRetries,
|
|
33383
|
+
failurePolicy: omConfig.observation.failurePolicy,
|
|
33007
33384
|
extract: omConfig.observation.extract
|
|
33008
33385
|
} : void 0,
|
|
33009
33386
|
reflection: omConfig.reflection ? {
|
|
33010
33387
|
model: omConfig.reflection.model,
|
|
33388
|
+
maxRetries: omConfig.reflection.maxRetries,
|
|
33389
|
+
failurePolicy: omConfig.reflection.failurePolicy,
|
|
33011
33390
|
observationTokens: omConfig.reflection.observationTokens,
|
|
33012
33391
|
modelSettings: omConfig.reflection.modelSettings,
|
|
33013
33392
|
providerOptions: omConfig.reflection.providerOptions,
|
|
@@ -34079,4 +34458,4 @@ Notes:
|
|
|
34079
34458
|
//#endregion
|
|
34080
34459
|
export { formatMessagesForObserver as A, OBSERVATION_CONTEXT_PROMPT_THREAD as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, StaleKnowledgeSemanticIndexError as G, getObservationContextPrompt as H, stripEphemeralAnchorIds as I, SUBCONSCIOUS_ACTIVITY_STATE_ID as J, SubconsciousRemindExtractor as K, OBSERVATIONAL_MEMORY_DEFAULTS as L, optimizeObservationsForContext as M, parseObserverOutput as N, buildObserverSystemPrompt as O, injectAnchorIds as P, Extractor as Q, OBSERVATION_CONTEXT_INSTRUCTIONS as R, deepMergeWorkingMemory as S, TokenCounter as T, Subconscious as U, OBSERVATION_CONTINUATION_HINT as V, KnowledgeSemanticIndexCoordinator as W, publishSubconsciousActivity as X, buildSubconsciousActivitySnapshot as Y, renderSubconsciousActivity as Z, wrapInObservationGroup as _, extractWorkingMemoryContent as a, WorkingMemoryStateProcessor as b, getObservationsAsOf as c, combineObservationGroupRanges as d, deriveObservationGroupProvenance as f, stripObservationGroups as g, renderObservationGroupsForReflection as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, ObservationalMemoryProcessor as l, reconcileObservationGroupsFromReflection as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, parseObservationGroups as p, ModelByInputTokens as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, ObservationalMemory as u, WORKING_MEMORY_STATE_ID as v, summarizeConversation as w, WorkingMemoryExtractor as x, WORKING_MEMORY_STATE_PROCESSOR_ID as y, OBSERVATION_CONTEXT_PROMPT as z };
|
|
34081
34460
|
|
|
34082
|
-
//# sourceMappingURL=src-
|
|
34461
|
+
//# sourceMappingURL=src-BikfoIdS.js.map
|