@mastra/memory 1.39.0-alpha.2 → 1.39.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-memory-settled.md +13 -5
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +4 -1
- 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/extraction-runner.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/sync.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/step.d.ts +2 -0
- package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-turn/turn.d.ts +13 -0
- package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +17 -3
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/processor.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-runner.d.ts +3 -0
- package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
- package/dist/processors/observational-memory/retry.d.ts +28 -1
- package/dist/processors/observational-memory/retry.d.ts.map +1 -1
- package/dist/{src-BDhP0_OK.js → src--zkaCof1.js} +224 -44
- package/dist/{src-BDhP0_OK.js.map → src--zkaCof1.js.map} +1 -1
- package/dist/{src-CJiR-m2F.cjs → src-BGgY5kgl.cjs} +224 -44
- package/dist/{src-CJiR-m2F.cjs.map → src-BGgY5kgl.cjs.map} +1 -1
- package/package.json +4 -3
|
@@ -18685,7 +18685,15 @@ const RETRY_CONFIG = {
|
|
|
18685
18685
|
/** Cap on per-attempt delay (ms). */
|
|
18686
18686
|
maxDelayMs: 12e4,
|
|
18687
18687
|
/** Random jitter as a fraction of the computed delay (e.g. 0.2 = ±20%). */
|
|
18688
|
-
jitter: .2
|
|
18688
|
+
jitter: .2,
|
|
18689
|
+
/**
|
|
18690
|
+
* How long one observer, reflector, or extraction model call may go without streaming any
|
|
18691
|
+
* output before it is aborted and retried. Without it a provider that accepts the request but
|
|
18692
|
+
* never answers stalls the call forever, and the retry schedule above never gets a chance to run.
|
|
18693
|
+
* It measures inactivity, not total duration, so a long reply that keeps streaming is never cut
|
|
18694
|
+
* off. It is generous because reasoning models can think silently before their first token.
|
|
18695
|
+
*/
|
|
18696
|
+
modelCallIdleTimeoutMs: 3e5
|
|
18689
18697
|
};
|
|
18690
18698
|
const TRANSIENT_MESSAGE_SUBSTRINGS = [
|
|
18691
18699
|
"terminated",
|
|
@@ -18704,6 +18712,19 @@ const TRANSIENT_MESSAGE_SUBSTRINGS = [
|
|
|
18704
18712
|
];
|
|
18705
18713
|
const INCOMPLETE_FINISH_REASONS = /* @__PURE__ */ new Set(["other", "unknown"]);
|
|
18706
18714
|
/**
|
|
18715
|
+
* A model call that streamed nothing for `RETRY_CONFIG.modelCallIdleTimeoutMs`. Retryable, and
|
|
18716
|
+
* deliberately not an AbortError so `withRetry` doesn't treat it as cancellation.
|
|
18717
|
+
*
|
|
18718
|
+
* @internal
|
|
18719
|
+
*/
|
|
18720
|
+
var OmModelCallTimeoutError = class extends Error {
|
|
18721
|
+
isRetryable = true;
|
|
18722
|
+
constructor(label, idleTimeoutMs) {
|
|
18723
|
+
super(`${label} call produced no output for ${idleTimeoutMs}ms`);
|
|
18724
|
+
this.name = "OmModelCallTimeoutError";
|
|
18725
|
+
}
|
|
18726
|
+
};
|
|
18727
|
+
/**
|
|
18707
18728
|
* A model reply that ended before the model finished. Carries the AI SDK's
|
|
18708
18729
|
* `isRetryable` flag, which `isTransientLLMError` and core's error processors
|
|
18709
18730
|
* already recognize.
|
|
@@ -18846,6 +18867,51 @@ function sleep(ms, abortSignal) {
|
|
|
18846
18867
|
});
|
|
18847
18868
|
}
|
|
18848
18869
|
/**
|
|
18870
|
+
* Run one attempt, failing with a retryable `OmModelCallTimeoutError` if it
|
|
18871
|
+
* goes `idleTimeoutMs` without calling `onActivity`. `fn` gets a signal that
|
|
18872
|
+
* aborts on timeout or on the caller's signal, so the underlying request is
|
|
18873
|
+
* cancelled too.
|
|
18874
|
+
*/
|
|
18875
|
+
async function attemptWithIdleTimeout(fn, label, idleTimeoutMs, abortSignal) {
|
|
18876
|
+
const controller = new AbortController();
|
|
18877
|
+
let rejectOnAbort;
|
|
18878
|
+
const callerAborted = new Promise((_, reject) => {
|
|
18879
|
+
rejectOnAbort = reject;
|
|
18880
|
+
});
|
|
18881
|
+
const onAbort = () => {
|
|
18882
|
+
controller.abort(abortSignal?.reason);
|
|
18883
|
+
rejectOnAbort(abortSignal?.reason ?? /* @__PURE__ */ new Error("The operation was aborted."));
|
|
18884
|
+
};
|
|
18885
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
18886
|
+
let timer;
|
|
18887
|
+
let rejectOnIdle;
|
|
18888
|
+
const idle = new Promise((_, reject) => {
|
|
18889
|
+
rejectOnIdle = reject;
|
|
18890
|
+
});
|
|
18891
|
+
const armIdleTimer = () => {
|
|
18892
|
+
clearTimeout(timer);
|
|
18893
|
+
timer = setTimeout(() => {
|
|
18894
|
+
const error = new OmModelCallTimeoutError(label, idleTimeoutMs);
|
|
18895
|
+
rejectOnIdle(error);
|
|
18896
|
+
controller.abort(error);
|
|
18897
|
+
}, idleTimeoutMs);
|
|
18898
|
+
};
|
|
18899
|
+
const onActivity = () => {
|
|
18900
|
+
if (!controller.signal.aborted) armIdleTimer();
|
|
18901
|
+
};
|
|
18902
|
+
armIdleTimer();
|
|
18903
|
+
try {
|
|
18904
|
+
return await Promise.race([
|
|
18905
|
+
fn(controller.signal, onActivity),
|
|
18906
|
+
idle,
|
|
18907
|
+
callerAborted
|
|
18908
|
+
]);
|
|
18909
|
+
} finally {
|
|
18910
|
+
clearTimeout(timer);
|
|
18911
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
18912
|
+
}
|
|
18913
|
+
}
|
|
18914
|
+
/**
|
|
18849
18915
|
* Run `fn` with retries on transient transport-class errors.
|
|
18850
18916
|
*
|
|
18851
18917
|
* Non-transient errors (auth, validation, schema, etc.) are rethrown
|
|
@@ -18854,14 +18920,16 @@ function sleep(ms, abortSignal) {
|
|
|
18854
18920
|
* @internal
|
|
18855
18921
|
*/
|
|
18856
18922
|
async function withRetry(fn, opts) {
|
|
18857
|
-
const { label, abortSignal, maxRetries = RETRY_CONFIG.maxRetries } = opts;
|
|
18923
|
+
const { label, abortSignal, maxRetries = RETRY_CONFIG.maxRetries, idleTimeoutMs } = opts;
|
|
18858
18924
|
let attempt = 0;
|
|
18859
18925
|
while (true) {
|
|
18860
18926
|
if (abortSignal?.aborted) throw new Error("The operation was aborted.");
|
|
18861
18927
|
try {
|
|
18862
|
-
return await fn();
|
|
18928
|
+
if (idleTimeoutMs === void 0) return await fn(abortSignal ?? new AbortController().signal, () => {});
|
|
18929
|
+
return await attemptWithIdleTimeout(fn, label, idleTimeoutMs, abortSignal);
|
|
18863
18930
|
} catch (error) {
|
|
18864
|
-
if (
|
|
18931
|
+
if (abortSignal?.aborted) throw error;
|
|
18932
|
+
if (!(error instanceof OmModelCallTimeoutError) && hasAbortInChain(error)) throw error;
|
|
18865
18933
|
if (attempt >= maxRetries || !isTransientLLMError(error)) {
|
|
18866
18934
|
if (attempt > 0) omDebug(`[OM:retry:${label}] giving up after ${attempt} retry/retries: ${error instanceof Error ? error.message : String(error)}`);
|
|
18867
18935
|
throw error;
|
|
@@ -18981,19 +19049,21 @@ If a prior value is still applicable and carry-forward is enabled, return that p
|
|
|
18981
19049
|
${extractorInstructions}${priorLines.length > 0 ? `\n\n## Prior Extracted Values\n\n${priorLines.join("\n\n")}` : ""}`;
|
|
18982
19050
|
const values = {};
|
|
18983
19051
|
const failures = [];
|
|
18984
|
-
const streamWithStructuredOutput = async (jsonPromptInjection) => withRetry(() => streamOnce(jsonPromptInjection), {
|
|
19052
|
+
const streamWithStructuredOutput = async (jsonPromptInjection) => withRetry((attemptSignal, onActivity) => streamOnce(jsonPromptInjection, attemptSignal, onActivity), {
|
|
18985
19053
|
label: `om-${opts.source}-structured-extraction`,
|
|
18986
19054
|
abortSignal: opts.abortSignal,
|
|
18987
|
-
maxRetries: 1
|
|
19055
|
+
maxRetries: 1,
|
|
19056
|
+
idleTimeoutMs: RETRY_CONFIG.modelCallIdleTimeoutMs
|
|
18988
19057
|
});
|
|
18989
|
-
const streamOnce = async (jsonPromptInjection) => {
|
|
19058
|
+
const streamOnce = async (jsonPromptInjection, abortSignal, onActivity) => {
|
|
18990
19059
|
const object = await (await opts.agent.stream(prompt, {
|
|
18991
19060
|
structuredOutput: {
|
|
18992
19061
|
schema,
|
|
18993
19062
|
...jsonPromptInjection ? { jsonPromptInjection } : {}
|
|
18994
19063
|
},
|
|
18995
19064
|
...opts.memory ? { memory: opts.memory } : {},
|
|
18996
|
-
|
|
19065
|
+
abortSignal,
|
|
19066
|
+
onChunk: onActivity,
|
|
18997
19067
|
...opts.requestContext ? { requestContext: opts.requestContext } : {},
|
|
18998
19068
|
...opts.observabilityContext
|
|
18999
19069
|
})).object;
|
|
@@ -21761,7 +21831,7 @@ var ObserverRunner = class {
|
|
|
21761
21831
|
timeZone: options?.timeZone
|
|
21762
21832
|
})];
|
|
21763
21833
|
const doGenerate = async () => {
|
|
21764
|
-
return withRetry(() => withOmTracingSpan({
|
|
21834
|
+
return withRetry((attemptSignal, onActivity) => withOmTracingSpan({
|
|
21765
21835
|
phase: "observer",
|
|
21766
21836
|
model: resolvedModel.model,
|
|
21767
21837
|
inputTokens,
|
|
@@ -21784,7 +21854,8 @@ var ObserverRunner = class {
|
|
|
21784
21854
|
modelSettings: { ...this.observationConfig.modelSettings },
|
|
21785
21855
|
providerOptions: this.observationConfig.providerOptions,
|
|
21786
21856
|
...attemptMemory ? { memory: attemptMemory } : {},
|
|
21787
|
-
|
|
21857
|
+
abortSignal: attemptSignal,
|
|
21858
|
+
onChunk: onActivity,
|
|
21788
21859
|
...internalRequestContext ? { requestContext: internalRequestContext } : {},
|
|
21789
21860
|
...childObservabilityContext
|
|
21790
21861
|
})).getFullOutput(), "OM observer");
|
|
@@ -21806,6 +21877,7 @@ var ObserverRunner = class {
|
|
|
21806
21877
|
}), {
|
|
21807
21878
|
label: "observer",
|
|
21808
21879
|
abortSignal,
|
|
21880
|
+
idleTimeoutMs: RETRY_CONFIG.modelCallIdleTimeoutMs,
|
|
21809
21881
|
maxRetries: this.observationConfig.maxRetries
|
|
21810
21882
|
});
|
|
21811
21883
|
};
|
|
@@ -21974,7 +22046,7 @@ var ObserverRunner = class {
|
|
|
21974
22046
|
timeZone
|
|
21975
22047
|
})];
|
|
21976
22048
|
const doGenerate = async () => {
|
|
21977
|
-
return withRetry(() => withOmTracingSpan({
|
|
22049
|
+
return withRetry((attemptSignal, onActivity) => withOmTracingSpan({
|
|
21978
22050
|
phase: "observer-multi-thread",
|
|
21979
22051
|
model: resolvedModel.model,
|
|
21980
22052
|
inputTokens,
|
|
@@ -21995,7 +22067,8 @@ var ObserverRunner = class {
|
|
|
21995
22067
|
maxSteps: 1,
|
|
21996
22068
|
modelSettings: { ...this.observationConfig.modelSettings },
|
|
21997
22069
|
providerOptions: this.observationConfig.providerOptions,
|
|
21998
|
-
|
|
22070
|
+
abortSignal: attemptSignal,
|
|
22071
|
+
onChunk: onActivity,
|
|
21999
22072
|
...internalRequestContext ? { requestContext: internalRequestContext } : {},
|
|
22000
22073
|
...childObservabilityContext
|
|
22001
22074
|
})).getFullOutput(), "OM multi-thread observer");
|
|
@@ -22019,6 +22092,7 @@ var ObserverRunner = class {
|
|
|
22019
22092
|
}), {
|
|
22020
22093
|
label: "observer-multi-thread",
|
|
22021
22094
|
abortSignal,
|
|
22095
|
+
idleTimeoutMs: RETRY_CONFIG.modelCallIdleTimeoutMs,
|
|
22022
22096
|
maxRetries: this.observationConfig.maxRetries
|
|
22023
22097
|
});
|
|
22024
22098
|
};
|
|
@@ -28598,6 +28672,7 @@ var ObservationStep = class {
|
|
|
28598
28672
|
let observerExchange;
|
|
28599
28673
|
const step0Messages = this.stepNumber === 0 ? getObservableMessages(messageList) : [];
|
|
28600
28674
|
if (this.stepNumber === 0) {
|
|
28675
|
+
this.turn.inputMessageIds = messageList.get.input.db().map((msg) => msg.id);
|
|
28601
28676
|
const activation = await om.activate({
|
|
28602
28677
|
threadId,
|
|
28603
28678
|
resourceId,
|
|
@@ -28699,19 +28774,36 @@ var ObservationStep = class {
|
|
|
28699
28774
|
observabilityContext: this.turn.observabilityContext
|
|
28700
28775
|
}).catch((err) => {
|
|
28701
28776
|
omDebug(`[OM:buffer] fire-and-forget buffer failed: ${err?.message}`);
|
|
28702
|
-
})
|
|
28777
|
+
}), {
|
|
28778
|
+
threadId,
|
|
28779
|
+
resourceId
|
|
28780
|
+
});
|
|
28703
28781
|
buffered = true;
|
|
28704
28782
|
}
|
|
28705
28783
|
}
|
|
28706
28784
|
const bandDefersObservation = statusSnapshot.inAsyncObservationBand && !statusSnapshot.canActivate;
|
|
28707
28785
|
const willObserveNow = statusSnapshot.shouldObserve && !hasIncompleteToolCalls && !bandDefersObservation;
|
|
28708
|
-
/**
|
|
28709
|
-
|
|
28786
|
+
/**
|
|
28787
|
+
* In-flight message ids cleanup must never remove from live context: the turn's own
|
|
28788
|
+
* request, plus whatever the model has not read yet (the pending messages at step 0, the
|
|
28789
|
+
* previous step's output — e.g. a tool result — at step > 0).
|
|
28790
|
+
*/
|
|
28791
|
+
let inFlightIds;
|
|
28710
28792
|
if (this.stepNumber > 0 || willObserveNow) {
|
|
28711
28793
|
if (this.stepNumber > 0) {
|
|
28712
28794
|
const newInput = messageList.clear.input.db();
|
|
28713
28795
|
const newOutput = messageList.clear.response.db();
|
|
28714
28796
|
const messagesToSave = [...newInput, ...newOutput];
|
|
28797
|
+
const drainedIds = new Set(messagesToSave.map((msg) => msg.id));
|
|
28798
|
+
const updatedEarlier = messageList.get.all.db().filter((msg) => !drainedIds.has(msg.id) && this.turn.drainedPartKeys.has(msg.id));
|
|
28799
|
+
const drainedKeys = /* @__PURE__ */ new Map();
|
|
28800
|
+
const unreadIds = [...messagesToSave, ...updatedEarlier].filter((msg) => {
|
|
28801
|
+
const keys = getPartKeys(msg);
|
|
28802
|
+
const seen = this.turn.drainedPartKeys.get(msg.id);
|
|
28803
|
+
drainedKeys.set(msg.id, /* @__PURE__ */ new Set([...seen ?? [], ...keys]));
|
|
28804
|
+
return !seen || keys.some((key) => !seen.has(key));
|
|
28805
|
+
}).map((msg) => msg.id);
|
|
28806
|
+
inFlightIds = [.../* @__PURE__ */ new Set([...this.turn.inputMessageIds, ...unreadIds])];
|
|
28715
28807
|
if (messagesToSave.length > 0) {
|
|
28716
28808
|
for (const msg of messagesToSave) messageList.add(msg, "memory");
|
|
28717
28809
|
try {
|
|
@@ -28722,10 +28814,11 @@ var ObservationStep = class {
|
|
|
28722
28814
|
throw error;
|
|
28723
28815
|
}
|
|
28724
28816
|
}
|
|
28817
|
+
for (const [id, keys] of drainedKeys) this.turn.drainedPartKeys.set(id, keys);
|
|
28725
28818
|
} else {
|
|
28726
28819
|
const pending = [...messageList.get.input.db(), ...messageList.get.response.db()];
|
|
28727
28820
|
if (pending.length > 0) await om.persistMessages(pending, threadId, resourceId);
|
|
28728
|
-
|
|
28821
|
+
inFlightIds = pending.map((msg) => msg.id);
|
|
28729
28822
|
}
|
|
28730
28823
|
if (this.stepNumber === 0 && willObserveNow && this.turn.responseMessageId) {
|
|
28731
28824
|
const seed = {
|
|
@@ -28759,7 +28852,7 @@ var ObservationStep = class {
|
|
|
28759
28852
|
messages: messageList,
|
|
28760
28853
|
observedMessageIds: observedIds,
|
|
28761
28854
|
retentionFloor: minRemaining,
|
|
28762
|
-
preserveMessageIds:
|
|
28855
|
+
preserveMessageIds: [...inFlightIds ?? [], ...this.changedSinceDrain(messageList)]
|
|
28763
28856
|
});
|
|
28764
28857
|
if (statusSnapshot.asyncObservationEnabled) await om.resetBufferingState({
|
|
28765
28858
|
threadId,
|
|
@@ -28787,7 +28880,7 @@ var ObservationStep = class {
|
|
|
28787
28880
|
});
|
|
28788
28881
|
if (!didThresholdCleanup) {
|
|
28789
28882
|
const fallbackCursor = this.turn.record.threadId ? (0, _mastra_core_memory.getThreadOMMetadata)((await om.getStorage().getThreadById({ threadId: this.turn.record.threadId }))?.metadata)?.lastObservedMessageCursor : void 0;
|
|
28790
|
-
const pendingMessageIds = new Set([...messageList.get.input.db(), ...messageList.get.response.db()].map((msg) => msg.id).filter(Boolean));
|
|
28883
|
+
const pendingMessageIds = new Set([...messageList.get.input.db(), ...messageList.get.response.db()].map((msg) => msg.id).concat(inFlightIds ?? [], this.turn.inputMessageIds, this.changedSinceDrain(messageList)).filter(Boolean));
|
|
28791
28884
|
filterObservedMessages({
|
|
28792
28885
|
messageList,
|
|
28793
28886
|
record: this.turn.record,
|
|
@@ -28816,6 +28909,13 @@ var ObservationStep = class {
|
|
|
28816
28909
|
this._prepared = true;
|
|
28817
28910
|
return this._context;
|
|
28818
28911
|
}
|
|
28912
|
+
/** Ids of already-drained messages that now hold parts the model has not been sent. */
|
|
28913
|
+
changedSinceDrain(messageList) {
|
|
28914
|
+
return messageList.get.all.db().filter((msg) => {
|
|
28915
|
+
const seen = this.turn.drainedPartKeys.get(msg.id);
|
|
28916
|
+
return seen !== void 0 && getPartKeys(msg).some((key) => !seen.has(key));
|
|
28917
|
+
}).map((msg) => msg.id);
|
|
28918
|
+
}
|
|
28819
28919
|
/**
|
|
28820
28920
|
* Run the full threshold observation pipeline:
|
|
28821
28921
|
* waitForBuffering → re-check → activate buffered chunks → reflect → observe
|
|
@@ -28988,6 +29088,49 @@ function restoreDrainedMessages(messageList, drained, source) {
|
|
|
28988
29088
|
const ids = drained.map((msg) => msg.id).filter((id) => !stillQueued.has(id));
|
|
28989
29089
|
for (const msg of messageList.removeByIds(ids)) messageList.add(msg, source, { merge: false });
|
|
28990
29090
|
}
|
|
29091
|
+
/**
|
|
29092
|
+
* Identity keys of a message's non-marker parts. Tool parts are keyed by call id, state and result,
|
|
29093
|
+
* so a result merged in place into an existing tool-invocation part yields a new key. Bookkeeping
|
|
29094
|
+
* fields that change when a part is re-stamped are not part of the identity.
|
|
29095
|
+
*/
|
|
29096
|
+
function getPartKeys(msg) {
|
|
29097
|
+
const seen = /* @__PURE__ */ new Map();
|
|
29098
|
+
return (msg.content.parts ?? []).flatMap((part) => {
|
|
29099
|
+
if (part.type.startsWith("data-om-")) return [];
|
|
29100
|
+
let identity;
|
|
29101
|
+
if (part.type === "tool-invocation") identity = toolPartIdentity(part.toolInvocation);
|
|
29102
|
+
else {
|
|
29103
|
+
const { providerMetadata: _providerMetadata, createdAt: _createdAt, updatedAt: _updatedAt, ...rest } = part;
|
|
29104
|
+
identity = JSON.stringify(rest);
|
|
29105
|
+
}
|
|
29106
|
+
const ordinal = seen.get(identity) ?? 0;
|
|
29107
|
+
seen.set(identity, ordinal + 1);
|
|
29108
|
+
return [`${identity}#${ordinal}`];
|
|
29109
|
+
});
|
|
29110
|
+
}
|
|
29111
|
+
/**
|
|
29112
|
+
* Serializing a tool result on every step is quadratic in turn length (each step rescans every drained
|
|
29113
|
+
* message), so the identity is cached per invocation object and reused while its state and result
|
|
29114
|
+
* reference are unchanged. A result merged in place replaces that reference and is re-serialized.
|
|
29115
|
+
*/
|
|
29116
|
+
const toolPartIdentityCache = /* @__PURE__ */ new WeakMap();
|
|
29117
|
+
function toolPartIdentity(invocation) {
|
|
29118
|
+
const result = invocation.state === "result" ? invocation.result : null;
|
|
29119
|
+
const cached = toolPartIdentityCache.get(invocation);
|
|
29120
|
+
if (cached && cached.state === invocation.state && cached.result === result) return cached.identity;
|
|
29121
|
+
const identity = JSON.stringify([
|
|
29122
|
+
"tool-invocation",
|
|
29123
|
+
invocation.toolCallId,
|
|
29124
|
+
invocation.state,
|
|
29125
|
+
result
|
|
29126
|
+
]);
|
|
29127
|
+
toolPartIdentityCache.set(invocation, {
|
|
29128
|
+
state: invocation.state,
|
|
29129
|
+
result,
|
|
29130
|
+
identity
|
|
29131
|
+
});
|
|
29132
|
+
return identity;
|
|
29133
|
+
}
|
|
28991
29134
|
//#endregion
|
|
28992
29135
|
//#region src/processors/observational-memory/observation-turn/turn.ts
|
|
28993
29136
|
/**
|
|
@@ -29021,6 +29164,19 @@ var ObservationTurn = class {
|
|
|
29021
29164
|
_ended = false;
|
|
29022
29165
|
/** Generation count at turn start — used to detect if reflection happened during the turn. */
|
|
29023
29166
|
_generationCountAtStart = -1;
|
|
29167
|
+
/**
|
|
29168
|
+
* Ids of the messages this turn was started with (the user's request). Mid-turn
|
|
29169
|
+
* observation may record them, but they stay in the live prompt until the turn ends.
|
|
29170
|
+
* @internal Set by step 0.
|
|
29171
|
+
*/
|
|
29172
|
+
inputMessageIds = [];
|
|
29173
|
+
/**
|
|
29174
|
+
* Fingerprints of the non-marker parts each message had when a step last drained it. A message
|
|
29175
|
+
* is unread step output only if it gained a part the model has not seen — including a tool result
|
|
29176
|
+
* merged in place into an existing tool-invocation part — not when it only gained OM markers.
|
|
29177
|
+
* @internal
|
|
29178
|
+
*/
|
|
29179
|
+
drainedPartKeys = /* @__PURE__ */ new Map();
|
|
29024
29180
|
/** Memory context provider — set via start(). Used by steps for beforeBuffer persistence. */
|
|
29025
29181
|
memory;
|
|
29026
29182
|
/** Optional stream writer for emitting markers. */
|
|
@@ -29174,7 +29330,10 @@ var ObservationTurn = class {
|
|
|
29174
29330
|
skipMinimumTokenCheck: true
|
|
29175
29331
|
}).catch((err) => {
|
|
29176
29332
|
omDebug(`[OM:turn.end] idle buffer failed: ${err?.message}`);
|
|
29177
|
-
})
|
|
29333
|
+
}), {
|
|
29334
|
+
threadId: this.threadId,
|
|
29335
|
+
resourceId: this.resourceId
|
|
29336
|
+
});
|
|
29178
29337
|
}
|
|
29179
29338
|
return { record: this._record };
|
|
29180
29339
|
}
|
|
@@ -29618,6 +29777,7 @@ var ReflectorRunner = class {
|
|
|
29618
29777
|
* number per active thread/resource key and live for the runner's lifetime.
|
|
29619
29778
|
*/
|
|
29620
29779
|
syncReflectionSuppression = /* @__PURE__ */ new Map();
|
|
29780
|
+
trackBackgroundWork;
|
|
29621
29781
|
constructor(opts) {
|
|
29622
29782
|
this.reflectionConfig = opts.reflectionConfig;
|
|
29623
29783
|
this.observationConfig = opts.observationConfig;
|
|
@@ -29634,6 +29794,7 @@ var ReflectorRunner = class {
|
|
|
29634
29794
|
this.memory = opts.memory;
|
|
29635
29795
|
this.onReflectionCommitted = opts.onReflectionCommitted;
|
|
29636
29796
|
this.hooks = opts.hooks;
|
|
29797
|
+
this.trackBackgroundWork = opts.trackBackgroundWork;
|
|
29637
29798
|
}
|
|
29638
29799
|
__registerMastra(mastra) {
|
|
29639
29800
|
this.mastra = mastra;
|
|
@@ -29730,7 +29891,7 @@ var ReflectorRunner = class {
|
|
|
29730
29891
|
const prompt = buildReflectorPrompt(observations, manualPrompt, currentLevel, skipContinuationHints, activeExtractors, priorExtractedValues);
|
|
29731
29892
|
omDebug(`[OM:callReflector] ${isRetry ? `retry #${attemptNumber - 1}` : "first attempt"}: level=${currentLevel}, originalTokens=${originalTokens}, targetThreshold=${targetThreshold}, promptLen=${prompt.length}, skipContinuationHints=${skipContinuationHints}`);
|
|
29732
29893
|
let chunkCount = 0;
|
|
29733
|
-
const result = await withRetry(() => withOmTracingSpan({
|
|
29894
|
+
const result = await withRetry((attemptSignal, onActivity) => withOmTracingSpan({
|
|
29734
29895
|
phase: "reflector",
|
|
29735
29896
|
model: resolvedModel.model,
|
|
29736
29897
|
inputTokens: originalTokens,
|
|
@@ -29754,17 +29915,19 @@ var ReflectorRunner = class {
|
|
|
29754
29915
|
modelSettings: { ...this.reflectionConfig.modelSettings },
|
|
29755
29916
|
providerOptions: this.reflectionConfig.providerOptions,
|
|
29756
29917
|
...attemptMemory ? { memory: attemptMemory } : {},
|
|
29757
|
-
|
|
29918
|
+
abortSignal: attemptSignal,
|
|
29758
29919
|
...internalRequestContext ? { requestContext: internalRequestContext } : {},
|
|
29759
29920
|
...childObservabilityContext,
|
|
29921
|
+
onChunk(chunk) {
|
|
29922
|
+
onActivity();
|
|
29923
|
+
if (attemptNumber !== 1) return;
|
|
29924
|
+
chunkCount++;
|
|
29925
|
+
if (chunkCount === 1 || chunkCount % 50 === 0) {
|
|
29926
|
+
const preview = chunk.type === "text-delta" ? ` text="${chunk.textDelta?.slice(0, 80)}..."` : chunk.type === "tool-call" ? ` tool=${chunk.toolName}` : "";
|
|
29927
|
+
omDebug(`[OM:callReflector] chunk#${chunkCount}: type=${chunk.type}${preview}`);
|
|
29928
|
+
}
|
|
29929
|
+
},
|
|
29760
29930
|
...attemptNumber === 1 ? {
|
|
29761
|
-
onChunk(chunk) {
|
|
29762
|
-
chunkCount++;
|
|
29763
|
-
if (chunkCount === 1 || chunkCount % 50 === 0) {
|
|
29764
|
-
const preview = chunk.type === "text-delta" ? ` text="${chunk.textDelta?.slice(0, 80)}..."` : chunk.type === "tool-call" ? ` tool=${chunk.toolName}` : "";
|
|
29765
|
-
omDebug(`[OM:callReflector] chunk#${chunkCount}: type=${chunk.type}${preview}`);
|
|
29766
|
-
}
|
|
29767
|
-
},
|
|
29768
29931
|
onFinish(event) {
|
|
29769
29932
|
omDebug(`[OM:callReflector] onFinish: chunks=${chunkCount}, finishReason=${event.finishReason}, inputTokens=${event.usage?.inputTokens}, outputTokens=${event.usage?.outputTokens}, textLen=${event.text?.length}`);
|
|
29770
29933
|
},
|
|
@@ -29784,6 +29947,7 @@ var ReflectorRunner = class {
|
|
|
29784
29947
|
}), {
|
|
29785
29948
|
label: "reflector",
|
|
29786
29949
|
abortSignal,
|
|
29950
|
+
idleTimeoutMs: RETRY_CONFIG.modelCallIdleTimeoutMs,
|
|
29787
29951
|
maxRetries: this.reflectionConfig.maxRetries
|
|
29788
29952
|
});
|
|
29789
29953
|
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}`);
|
|
@@ -29891,10 +30055,11 @@ var ReflectorRunner = class {
|
|
|
29891
30055
|
if (this.buffering.isAsyncBufferingInProgress(bufferKey)) return;
|
|
29892
30056
|
BufferingCoordinator.lastBufferedBoundary.set(bufferKey, observationTokens);
|
|
29893
30057
|
registerOp(record.id, "bufferingReflection");
|
|
29894
|
-
this.storage.setBufferingReflectionFlag(record.id, true).catch((err) => {
|
|
30058
|
+
const flagSet = this.storage.setBufferingReflectionFlag(record.id, true).catch((err) => {
|
|
29895
30059
|
omError("[OM] Failed to set buffering reflection flag", err);
|
|
29896
30060
|
});
|
|
29897
30061
|
const asyncOp = (async () => {
|
|
30062
|
+
await flagSet;
|
|
29898
30063
|
let outcome;
|
|
29899
30064
|
let reflectionError;
|
|
29900
30065
|
try {
|
|
@@ -29935,12 +30100,13 @@ var ReflectorRunner = class {
|
|
|
29935
30100
|
}
|
|
29936
30101
|
BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
29937
30102
|
unregisterOp(record.id, "bufferingReflection");
|
|
29938
|
-
this.storage.setBufferingReflectionFlag(record.id, false).catch((err) => {
|
|
30103
|
+
await this.storage.setBufferingReflectionFlag(record.id, false).catch((err) => {
|
|
29939
30104
|
omError("[OM] Failed to clear buffering reflection flag", err);
|
|
29940
30105
|
});
|
|
29941
30106
|
}
|
|
29942
30107
|
})();
|
|
29943
30108
|
BufferingCoordinator.asyncBufferingOps.set(bufferKey, asyncOp);
|
|
30109
|
+
this.trackBackgroundWork?.(asyncOp, lockKey);
|
|
29944
30110
|
}
|
|
29945
30111
|
/**
|
|
29946
30112
|
* Perform async buffered reflection — reflects observations and stores to bufferedReflection.
|
|
@@ -30968,32 +31134,44 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
30968
31134
|
* Tracked so callers can join them via `settled()` before closing a storage
|
|
30969
31135
|
* connection they own — otherwise a background cycle's tail statements race the close.
|
|
30970
31136
|
*/
|
|
30971
|
-
pendingBackgroundWork = /* @__PURE__ */ new
|
|
31137
|
+
pendingBackgroundWork = /* @__PURE__ */ new Map();
|
|
30972
31138
|
/**
|
|
30973
31139
|
* Register fire-and-forget background work so `settled()` can join it.
|
|
31140
|
+
* `scope` is the conversation the work belongs to (thread/resource ids, or a lock key from
|
|
31141
|
+
* `buffering.getLockKey`),
|
|
31142
|
+
* so a scoped `settled()` can wait for one conversation without waiting for every other.
|
|
30974
31143
|
* Returns the original promise so callers keep their own rejection handling.
|
|
30975
31144
|
*/
|
|
30976
|
-
trackBackgroundWork(work) {
|
|
31145
|
+
trackBackgroundWork(work, scope) {
|
|
31146
|
+
const lockKey = typeof scope === "string" ? scope : this.buffering.getLockKey(scope.threadId, scope.resourceId);
|
|
30977
31147
|
const tracked = work.catch(() => {}).finally(() => {
|
|
30978
31148
|
this.pendingBackgroundWork.delete(tracked);
|
|
30979
31149
|
});
|
|
30980
|
-
this.pendingBackgroundWork.
|
|
31150
|
+
this.pendingBackgroundWork.set(tracked, lockKey);
|
|
30981
31151
|
return work;
|
|
30982
31152
|
}
|
|
30983
31153
|
/**
|
|
30984
|
-
* Resolve once
|
|
31154
|
+
* Resolve once background observational-memory work started so far has finished.
|
|
31155
|
+
*
|
|
31156
|
+
* Pass `threadId`/`resourceId` to wait only for that conversation's work. An engine shared by
|
|
31157
|
+
* many busy threads may never be fully idle, so an unscoped wait can last indefinitely there.
|
|
31158
|
+
* A scope without the id this engine keys work on (`resourceId` for resource scope, `threadId`
|
|
31159
|
+
* for thread scope) can't be matched to its work, so it falls back to waiting for all work.
|
|
30985
31160
|
*
|
|
30986
31161
|
* Background cycles enqueue further background work (a buffered observation can
|
|
30987
31162
|
* trigger a reflection, which runs nested agent streams), so this drains repeatedly
|
|
30988
31163
|
* until nothing is left rather than joining a single snapshot.
|
|
30989
31164
|
*/
|
|
30990
|
-
async settled() {
|
|
31165
|
+
async settled(scope) {
|
|
31166
|
+
const lockKey = (this.scope === "resource" ? scope?.resourceId : scope?.threadId) ? this.buffering.getLockKey(scope.threadId, scope.resourceId) : void 0;
|
|
31167
|
+
const pending = () => [...this.pendingBackgroundWork].filter(([, key]) => lockKey === void 0 || key === lockKey).map(([w]) => w);
|
|
30991
31168
|
const maxDrainRounds = 100;
|
|
30992
31169
|
for (let round = 0; round < maxDrainRounds; round++) {
|
|
30993
|
-
|
|
30994
|
-
|
|
31170
|
+
const work = pending();
|
|
31171
|
+
if (work.length === 0) return;
|
|
31172
|
+
await Promise.allSettled(work);
|
|
30995
31173
|
}
|
|
30996
|
-
if (
|
|
31174
|
+
if (pending().length === 0) return;
|
|
30997
31175
|
omError(`[OM:settled] background work still pending after ${maxDrainRounds} drain rounds; giving up waiting`);
|
|
30998
31176
|
}
|
|
30999
31177
|
/**
|
|
@@ -31149,7 +31327,8 @@ var ObservationalMemory = class ObservationalMemory {
|
|
|
31149
31327
|
mastra: config.mastra,
|
|
31150
31328
|
memory: this.memory,
|
|
31151
31329
|
onReflectionCommitted: config.onReflectionCommitted,
|
|
31152
|
-
hooks: this.hooks
|
|
31330
|
+
hooks: this.hooks,
|
|
31331
|
+
trackBackgroundWork: (work, lockKey) => this.trackBackgroundWork(work, lockKey)
|
|
31153
31332
|
});
|
|
31154
31333
|
this.validateBufferConfig();
|
|
31155
31334
|
if (this.scope === "resource" && !hasWarnedResourceScopeDeprecation) {
|
|
@@ -32005,11 +32184,12 @@ ${formattedMessages}
|
|
|
32005
32184
|
}).finally(() => {
|
|
32006
32185
|
if (BufferingCoordinator.asyncBufferingOps.get(bufferKey) === asyncOp) BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
|
|
32007
32186
|
unregisterOp(record.id, "bufferingObservation");
|
|
32008
|
-
runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false)).catch((err) => {
|
|
32187
|
+
return runOMCommit(lockKey, () => this.storage.setBufferingObservationFlag(record.id, false)).catch((err) => {
|
|
32009
32188
|
omError("[OM] Failed to clear buffering observation flag", err);
|
|
32010
32189
|
});
|
|
32011
32190
|
});
|
|
32012
32191
|
BufferingCoordinator.asyncBufferingOps.set(bufferKey, asyncOp);
|
|
32192
|
+
return { op: asyncOp };
|
|
32013
32193
|
}
|
|
32014
32194
|
/**
|
|
32015
32195
|
* Internal method that waits for existing buffering operation and then runs new buffering.
|
|
@@ -32109,7 +32289,7 @@ ${formattedMessages}
|
|
|
32109
32289
|
if (!this.buffering.isAsyncObservationEnabled()) return false;
|
|
32110
32290
|
const lockKey = this.buffering.getLockKey(opts.threadId, opts.resourceId);
|
|
32111
32291
|
const shouldTrigger = this.buffering.shouldTriggerAsyncObservation(opts.pendingTokens, lockKey, opts.record, this.storage, opts.threshold);
|
|
32112
|
-
if (shouldTrigger) this.trackBackgroundWork(this.startAsyncBufferedObservation(opts.record, opts.threadId, opts.unobservedMessages, lockKey, opts.writer, opts.unbufferedPendingTokens, opts.requestContext, opts.observabilityContext));
|
|
32292
|
+
if (shouldTrigger) this.trackBackgroundWork(this.startAsyncBufferedObservation(opts.record, opts.threadId, opts.unobservedMessages, lockKey, opts.writer, opts.unbufferedPendingTokens, opts.requestContext, opts.observabilityContext).then(({ op }) => op), lockKey);
|
|
32113
32293
|
return shouldTrigger;
|
|
32114
32294
|
}
|
|
32115
32295
|
isMessageList(value) {
|
|
@@ -34161,9 +34341,9 @@ var Memory = class Memory extends _mastra_core_memory.MastraMemory {
|
|
|
34161
34341
|
* await store.close();
|
|
34162
34342
|
* ```
|
|
34163
34343
|
*/
|
|
34164
|
-
async settled() {
|
|
34344
|
+
async settled(scope) {
|
|
34165
34345
|
await this.pendingVectorCleanup;
|
|
34166
|
-
await (this._omEngine ? await this._omEngine : this._omEngineInstance)?.settled();
|
|
34346
|
+
await (this._omEngine ? await this._omEngine : this._omEngineInstance)?.settled(scope);
|
|
34167
34347
|
await this.pendingVectorCleanup;
|
|
34168
34348
|
}
|
|
34169
34349
|
/** The shared ObservationalMemory engine. Lazily created on first access. */
|
|
@@ -36757,4 +36937,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
|
|
|
36757
36937
|
}
|
|
36758
36938
|
});
|
|
36759
36939
|
|
|
36760
|
-
//# sourceMappingURL=src-
|
|
36940
|
+
//# sourceMappingURL=src-BGgY5kgl.cjs.map
|