@mastra/memory 1.27.0-alpha.3 → 1.28.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/dist/docs/SKILL.md +4 -2
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-guides-context-engineering.md +297 -0
  5. package/dist/docs/references/docs-storage.md +1 -0
  6. package/dist/docs/references/integrations-databases-mongodb.md +1 -1
  7. package/dist/docs/references/integrations-databases-postgresql.md +2 -0
  8. package/dist/docs/references/integrations-databases-valkey.md +99 -0
  9. package/dist/docs/references/reference-vectors-mongodb.md +11 -11
  10. package/dist/docs/references/reference-vectors-pg.md +2 -0
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.d.ts +6 -3
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/processors/index.cjs +1 -1
  16. package/dist/processors/index.js +1 -1
  17. package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
  18. package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -1
  19. package/dist/processors/observational-memory/observation-turn/load-memory-context.d.ts +3 -1
  20. package/dist/processors/observational-memory/observation-turn/load-memory-context.d.ts.map +1 -1
  21. package/dist/processors/observational-memory/observation-turn/turn.d.ts +2 -1
  22. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  23. package/dist/processors/observational-memory/observational-memory.d.ts +6 -12
  24. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/observer-agent.d.ts +13 -0
  26. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
  28. package/dist/processors/observational-memory/processor.d.ts +2 -0
  29. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  30. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  31. package/dist/processors/observational-memory/tracing.d.ts.map +1 -1
  32. package/dist/processors/observational-memory/types.d.ts +23 -13
  33. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  34. package/dist/{src-Ik0ksTOt.cjs → src-CIBCjg2A.cjs} +324 -164
  35. package/dist/{src-Ik0ksTOt.cjs.map → src-CIBCjg2A.cjs.map} +1 -1
  36. package/dist/{src-t_4yoTX9.js → src-kLYHhPqh.js} +324 -164
  37. package/dist/{src-t_4yoTX9.js.map → src-kLYHhPqh.js.map} +1 -1
  38. package/package.json +7 -7
@@ -18905,6 +18905,12 @@ function extractListItemsOnly(content) {
18905
18905
  */
18906
18906
  const MAX_OBSERVATION_LINE_CHARS = 1e4;
18907
18907
  /**
18908
+ * Minimum trimmed line length considered by the duplicate-line degenerate
18909
+ * check. Short lines (blank lines, separators, terse bullets) legitimately
18910
+ * repeat; long identical lines almost never do.
18911
+ */
18912
+ const MIN_DUPLICATE_LINE_CHARS = 24;
18913
+ /**
18908
18914
  * Truncate individual observation lines that exceed the maximum length.
18909
18915
  */
18910
18916
  function sanitizeObservationLines(observations) {
@@ -18942,9 +18948,81 @@ function detectDegenerateRepetition(text) {
18942
18948
  if (totalWindows > 5 && duplicateWindows / totalWindows > .4) return true;
18943
18949
  const lines = text.split("\n");
18944
18950
  for (const line of lines) if (line.length > 5e4) return true;
18951
+ const seenLines = /* @__PURE__ */ new Map();
18952
+ let duplicateLines = 0;
18953
+ let totalCountedLines = 0;
18954
+ for (const line of lines) {
18955
+ const trimmed = line.trim();
18956
+ if (trimmed.length < MIN_DUPLICATE_LINE_CHARS) continue;
18957
+ totalCountedLines++;
18958
+ const count = (seenLines.get(trimmed) ?? 0) + 1;
18959
+ seenLines.set(trimmed, count);
18960
+ if (count > 1) duplicateLines++;
18961
+ }
18962
+ if (totalCountedLines >= 20 && duplicateLines / totalCountedLines > .5) return true;
18945
18963
  return false;
18946
18964
  }
18947
18965
  /**
18966
+ * Build a single-line diagnostic description of output flagged by
18967
+ * {@link detectDegenerateRepetition}. Used when logging or surfacing a
18968
+ * degenerate-output failure so the discarded model output can be inspected —
18969
+ * without it there is no way to tell a real repetition loop apart from a
18970
+ * detector false-positive on legitimately repetitive content.
18971
+ *
18972
+ * Reuses the detector's sampling parameters (200-char windows, ~50 samples)
18973
+ * so the reported duplicate ratio and most-repeated window match what
18974
+ * triggered the detection. Snippets are JSON-escaped so the result stays on
18975
+ * one line.
18976
+ */
18977
+ function describeDegenerateOutput(text, snippetChars = 400) {
18978
+ const windowSize = 200;
18979
+ const step = Math.max(1, Math.floor(text.length / 50));
18980
+ const seen = /* @__PURE__ */ new Map();
18981
+ let duplicateWindows = 0;
18982
+ let totalWindows = 0;
18983
+ for (let i = 0; i + windowSize <= text.length; i += step) {
18984
+ const window = text.slice(i, i + windowSize);
18985
+ totalWindows++;
18986
+ const count = (seen.get(window) ?? 0) + 1;
18987
+ seen.set(window, count);
18988
+ if (count > 1) duplicateWindows++;
18989
+ }
18990
+ let topWindow = "";
18991
+ let topCount = 0;
18992
+ for (const [window, count] of seen) if (count > topCount) {
18993
+ topCount = count;
18994
+ topWindow = window;
18995
+ }
18996
+ let longestLine = 0;
18997
+ const seenLines = /* @__PURE__ */ new Map();
18998
+ let duplicateLines = 0;
18999
+ let totalCountedLines = 0;
19000
+ for (const line of text.split("\n")) {
19001
+ if (line.length > longestLine) longestLine = line.length;
19002
+ const trimmed = line.trim();
19003
+ if (trimmed.length < MIN_DUPLICATE_LINE_CHARS) continue;
19004
+ totalCountedLines++;
19005
+ const count = (seenLines.get(trimmed) ?? 0) + 1;
19006
+ seenLines.set(trimmed, count);
19007
+ if (count > 1) duplicateLines++;
19008
+ }
19009
+ const duplicateRatio = totalWindows > 0 ? (duplicateWindows / totalWindows).toFixed(2) : "n/a";
19010
+ const duplicateLineRatio = totalCountedLines > 0 ? (duplicateLines / totalCountedLines).toFixed(2) : "n/a";
19011
+ const parts = [
19012
+ `length=${text.length}`,
19013
+ `sampledWindows=${totalWindows}`,
19014
+ `duplicateRatio=${duplicateRatio}`,
19015
+ `duplicateLineRatio=${duplicateLineRatio}`,
19016
+ `countedLines=${totalCountedLines}`,
19017
+ `longestLine=${longestLine}`,
19018
+ `topWindowCount=${topCount}`
19019
+ ];
19020
+ if (topCount > 1) parts.push(`topWindow=${JSON.stringify(topWindow)}`);
19021
+ parts.push(`head=${JSON.stringify(text.slice(0, snippetChars))}`);
19022
+ if (text.length > snippetChars * 2) parts.push(`tail=${JSON.stringify(text.slice(-snippetChars))}`);
19023
+ return parts.join(" ");
19024
+ }
19025
+ /**
18948
19026
  * Check if observations contain a Current Task section.
18949
19027
  * Supports both XML format and legacy markdown format.
18950
19028
  */
@@ -19209,7 +19287,19 @@ async function withOmTracingSpan({ phase, model, inputTokens, requestContext, ob
19209
19287
  });
19210
19288
  const childObservabilityContext = createObservabilityContext({ currentSpan: span });
19211
19289
  if (!span) return callback(childObservabilityContext);
19212
- return span.executeInContext(() => callback(childObservabilityContext));
19290
+ return span.executeInContext(async () => {
19291
+ try {
19292
+ const result = await callback(childObservabilityContext);
19293
+ span.end();
19294
+ return result;
19295
+ } catch (error) {
19296
+ span.error({
19297
+ error,
19298
+ endSpan: true
19299
+ });
19300
+ throw error;
19301
+ }
19302
+ });
19213
19303
  }
19214
19304
  //#endregion
19215
19305
  //#region src/processors/observational-memory/observer-runner.ts
@@ -19347,13 +19437,13 @@ var ObserverRunner = class {
19347
19437
  let parsed = parseObserverOutput(result.text, activeExtractors);
19348
19438
  let retriedDueToDegenerate = false;
19349
19439
  if (parsed.degenerate) {
19350
- omDebug(`[OM:callObserver] degenerate repetition detected, retrying once`);
19440
+ omDebug(`[OM:callObserver] degenerate repetition detected, retrying once. ${describeDegenerateOutput(result.text, 2e3)}`);
19351
19441
  result = await doGenerate();
19352
19442
  parsed = parseObserverOutput(result.text, activeExtractors);
19353
19443
  retriedDueToDegenerate = true;
19354
19444
  if (parsed.degenerate) {
19355
- omDebug(`[OM:callObserver] degenerate repetition on retry, failing`);
19356
- throw new Error("Observer produced degenerate output after retry");
19445
+ omDebug(`[OM:callObserver] degenerate repetition on retry, failing. ${describeDegenerateOutput(result.text, 2e3)}`);
19446
+ throw new Error(`Observer produced degenerate output after retry. ${describeDegenerateOutput(result.text)}`);
19357
19447
  }
19358
19448
  }
19359
19449
  const structuredExtraction = await extractStructuredValues({
@@ -19497,13 +19587,13 @@ var ObserverRunner = class {
19497
19587
  let parsed = parseMultiThreadObserverOutput(result.text, activeExtractors);
19498
19588
  let retriedDueToDegenerate = false;
19499
19589
  if (parsed.degenerate) {
19500
- omDebug(`[OM:callMultiThreadObserver] degenerate repetition detected, retrying once`);
19590
+ omDebug(`[OM:callMultiThreadObserver] degenerate repetition detected, retrying once. ${describeDegenerateOutput(result.text, 2e3)}`);
19501
19591
  result = await doGenerate();
19502
19592
  parsed = parseMultiThreadObserverOutput(result.text, activeExtractors);
19503
19593
  retriedDueToDegenerate = true;
19504
19594
  if (parsed.degenerate) {
19505
- omDebug(`[OM:callMultiThreadObserver] degenerate repetition on retry, failing`);
19506
- throw new Error("Multi-thread observer produced degenerate output after retry");
19595
+ omDebug(`[OM:callMultiThreadObserver] degenerate repetition on retry, failing. ${describeDegenerateOutput(result.text, 2e3)}`);
19596
+ throw new Error(`Multi-thread observer produced degenerate output after retry. ${describeDegenerateOutput(result.text)}`);
19507
19597
  }
19508
19598
  }
19509
19599
  const structuredExtractionByThread = /* @__PURE__ */ new Map();
@@ -24346,10 +24436,11 @@ ObservationStrategy.create = ((om, opts) => {
24346
24436
  });
24347
24437
  //#endregion
24348
24438
  //#region src/processors/observational-memory/observation-turn/load-memory-context.ts
24349
- async function loadMemoryContextMessages({ memory, messageList, threadId, resourceId }) {
24439
+ async function loadMemoryContextMessages({ memory, messageList, threadId, resourceId, runState }) {
24350
24440
  const ctx = await memory.getContext({
24351
24441
  threadId,
24352
- resourceId
24442
+ resourceId,
24443
+ runState
24353
24444
  });
24354
24445
  for (const msg of ctx.messages) if (msg.role !== "system") messageList.add(msg, "memory");
24355
24446
  return ctx;
@@ -24807,10 +24898,11 @@ var ObservationTurn = class {
24807
24898
  * If a MemoryContextProvider is passed, loads historical messages and adds
24808
24899
  * them to the MessageList. Without a provider, only fetches/caches the record.
24809
24900
  */
24810
- async start(memory) {
24901
+ async start(memory, runState) {
24811
24902
  if (this._started) throw new Error("Turn already started");
24812
24903
  this._started = true;
24813
24904
  this._record = await this.om.getOrCreateRecord(this.threadId, this.resourceId);
24905
+ runState?.set(`observational-memory:record:${this.threadId}:${this.resourceId ?? ""}`, this._record);
24814
24906
  this._generationCountAtStart = this._record.generationCount;
24815
24907
  this.memory = memory;
24816
24908
  if (memory) {
@@ -24818,7 +24910,8 @@ var ObservationTurn = class {
24818
24910
  memory,
24819
24911
  messageList: this.messageList,
24820
24912
  threadId: this.threadId,
24821
- resourceId: this.resourceId
24913
+ resourceId: this.resourceId,
24914
+ runState
24822
24915
  });
24823
24916
  this._context = {
24824
24917
  messages: ctx.messages,
@@ -25380,8 +25473,6 @@ var ReflectorRunner = class {
25380
25473
  };
25381
25474
  let reflectedTokens = 0;
25382
25475
  let attemptNumber = 0;
25383
- /** Observations from the previous attempt, used to detect a no-progress ladder. */
25384
- let previousObservations;
25385
25476
  while (currentLevel <= maxLevel) {
25386
25477
  attemptNumber++;
25387
25478
  const isRetry = attemptNumber > 1;
@@ -25446,7 +25537,7 @@ var ReflectorRunner = class {
25446
25537
  finalProviderMetadata = result.providerMetadata ?? finalProviderMetadata;
25447
25538
  parsed = parseReflectorOutput(result.text, observations, activeExtractors);
25448
25539
  if (parsed.degenerate) {
25449
- omDebug(`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure`);
25540
+ omDebug(`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure. ${describeDegenerateOutput(result.text, 2e3)}`);
25450
25541
  reflectedTokens = originalTokens;
25451
25542
  } else reflectedTokens = this.tokenCounter.countObservations(parsed.observations);
25452
25543
  omDebug(`[OM:callReflector] attempt #${attemptNumber} parsed: reflectedTokens=${reflectedTokens}, targetThreshold=${targetThreshold}, compressionValid=${validateCompression(reflectedTokens, targetThreshold)}, parsedObsLen=${parsed.observations?.length}, degenerate=${parsed.degenerate ?? false}`);
@@ -25455,11 +25546,6 @@ var ReflectorRunner = class {
25455
25546
  omDebug(`[OM:callReflector] degenerate output persists at maxLevel=${maxLevel}, breaking`);
25456
25547
  break;
25457
25548
  }
25458
- if (!parsed.degenerate && previousObservations !== void 0 && parsed.observations === previousObservations) {
25459
- omDebug(`[OM:callReflector] attempt #${attemptNumber} returned output identical to the previous attempt; escalating cannot help, stopping the ladder`);
25460
- break;
25461
- }
25462
- previousObservations = parsed.observations;
25463
25549
  if (streamContext?.writer) {
25464
25550
  const failedMarker = createObservationFailedMarker({
25465
25551
  cycleId: streamContext.cycleId,
@@ -25543,52 +25629,51 @@ var ReflectorRunner = class {
25543
25629
  this.storage.setBufferingReflectionFlag(record.id, true).catch((err) => {
25544
25630
  omError("[OM] Failed to set buffering reflection flag", err);
25545
25631
  });
25546
- reflectionHooks?.onReflectionStart?.();
25547
- const asyncOp = this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext, priorExtractedValues, mainAgent, sendSignal).then((outcome) => {
25632
+ const asyncOp = (async () => {
25633
+ let outcome;
25634
+ let reflectionError;
25548
25635
  try {
25549
- reflectionHooks?.onReflectionEnd?.({
25550
- usage: outcome?.usage,
25551
- ...outcome?.providerMetadata ? { providerMetadata: outcome.providerMetadata } : {}
25552
- });
25553
- } catch (hookError) {
25554
- omError("[OM] onReflectionEnd hook failed after async buffered reflection", hookError);
25555
- }
25556
- }, async (error) => {
25557
- if (writer) try {
25558
- const failedMarker = createBufferingFailedMarker({
25559
- cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
25560
- operationType: "reflection",
25561
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
25562
- tokensAttempted: observationTokens,
25563
- error: error instanceof Error ? error.message : String(error),
25564
- recordId: record.id,
25565
- threadId: record.threadId ?? ""
25566
- });
25567
- writer.custom({
25568
- ...failedMarker,
25569
- transient: true
25570
- }).catch(() => {});
25571
- await this.persistMarkerToStorage(failedMarker, record.threadId ?? "", record.resourceId ?? void 0);
25572
- } catch (markerError) {
25573
- omError("[OM] Failed to persist buffering-failed marker after async buffered reflection failure", markerError);
25574
- }
25575
- omError("[OM] Async buffered reflection failed", error);
25576
- try {
25577
- reflectionHooks?.onReflectionEnd?.({
25578
- usage: void 0,
25579
- error: error instanceof Error ? error : new Error(String(error))
25636
+ await reflectionHooks?.onReflectionStart?.();
25637
+ outcome = await this.doAsyncBufferedReflection(record, bufferKey, writer, requestContext, observabilityContext, priorExtractedValues, mainAgent, sendSignal);
25638
+ } catch (error) {
25639
+ reflectionError = error instanceof Error ? error : new Error(String(error));
25640
+ if (writer) try {
25641
+ const failedMarker = createBufferingFailedMarker({
25642
+ cycleId: `reflect-buf-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
25643
+ operationType: "reflection",
25644
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
25645
+ tokensAttempted: observationTokens,
25646
+ error: reflectionError.message,
25647
+ recordId: record.id,
25648
+ threadId: record.threadId ?? ""
25649
+ });
25650
+ writer.custom({
25651
+ ...failedMarker,
25652
+ transient: true
25653
+ }).catch(() => {});
25654
+ await this.persistMarkerToStorage(failedMarker, record.threadId ?? "", record.resourceId ?? void 0);
25655
+ } catch (markerError) {
25656
+ omError("[OM] Failed to persist buffering-failed marker after async buffered reflection failure", markerError);
25657
+ }
25658
+ omError("[OM] Async buffered reflection failed", error);
25659
+ BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
25660
+ } finally {
25661
+ try {
25662
+ await reflectionHooks?.onReflectionEnd?.({
25663
+ usage: outcome?.usage,
25664
+ error: reflectionError,
25665
+ ...outcome?.providerMetadata ? { providerMetadata: outcome.providerMetadata } : {}
25666
+ });
25667
+ } catch (hookError) {
25668
+ omError("[OM] onReflectionEnd hook failed after async buffered reflection", hookError);
25669
+ }
25670
+ BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
25671
+ unregisterOp(record.id, "bufferingReflection");
25672
+ this.storage.setBufferingReflectionFlag(record.id, false).catch((err) => {
25673
+ omError("[OM] Failed to clear buffering reflection flag", err);
25580
25674
  });
25581
- } catch (hookError) {
25582
- omError("[OM] onReflectionEnd hook failed after async buffered reflection failure", hookError);
25583
25675
  }
25584
- BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
25585
- }).finally(() => {
25586
- BufferingCoordinator.asyncBufferingOps.delete(bufferKey);
25587
- unregisterOp(record.id, "bufferingReflection");
25588
- this.storage.setBufferingReflectionFlag(record.id, false).catch((err) => {
25589
- omError("[OM] Failed to clear buffering reflection flag", err);
25590
- });
25591
- });
25676
+ })();
25592
25677
  BufferingCoordinator.asyncBufferingOps.set(bufferKey, asyncOp);
25593
25678
  }
25594
25679
  /**
@@ -25889,8 +25974,15 @@ var ReflectorRunner = class {
25889
25974
  let reflectionUsage;
25890
25975
  let reflectionProviderMetadata;
25891
25976
  let reflectionError;
25892
- reflectionHooks?.onReflectionStart?.();
25977
+ let lifecycleError;
25893
25978
  try {
25979
+ try {
25980
+ await reflectionHooks?.onReflectionStart?.();
25981
+ } catch (error) {
25982
+ lifecycleError = error;
25983
+ reflectionError = error instanceof Error ? error : new Error(String(error));
25984
+ throw error;
25985
+ }
25894
25986
  const compressionStartLevel = await this.getCompressionStartLevel(requestContext);
25895
25987
  const reflectResult = await this.call(record.activeObservations, void 0, streamContext, reflectThreshold, abortSignal, void 0, compressionStartLevel, requestContext, priorExtractedValues, observabilityContext, void 0, mainAgent, sendSignal);
25896
25988
  reflectionUsage = reflectResult.usage;
@@ -25960,18 +26052,27 @@ var ReflectorRunner = class {
25960
26052
  await this.persistMarkerToStorage(failedMarker, threadId, record.resourceId ?? void 0);
25961
26053
  }
25962
26054
  reflectionError = error instanceof Error ? error : new Error(String(error));
25963
- if (abortSignal?.aborted) throw error;
26055
+ if (lifecycleError !== void 0 || abortSignal?.aborted) throw error;
25964
26056
  omError("[OM] Reflection failed", error);
25965
26057
  } finally {
25966
- await this.storage.setReflectingFlag(record.id, false);
25967
26058
  try {
25968
- reflectionHooks?.onReflectionEnd?.({
26059
+ await this.storage.setReflectingFlag(record.id, false);
26060
+ } finally {
26061
+ unregisterOp(record.id, "reflecting");
26062
+ }
26063
+ let endHookError;
26064
+ try {
26065
+ await reflectionHooks?.onReflectionEnd?.({
25969
26066
  usage: reflectionUsage,
25970
26067
  error: reflectionError,
25971
26068
  ...reflectionProviderMetadata ? { providerMetadata: reflectionProviderMetadata } : {}
25972
26069
  });
25973
- } finally {
25974
- unregisterOp(record.id, "reflecting");
26070
+ } catch (error) {
26071
+ endHookError = error;
26072
+ }
26073
+ if (endHookError !== void 0) {
26074
+ if (lifecycleError === void 0 && !abortSignal?.aborted) throw endHookError;
26075
+ omDebug(`[OM:hooks] onReflectionEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
25975
26076
  }
25976
26077
  }
25977
26078
  }
@@ -26432,6 +26533,8 @@ var ObservationalMemory = class ObservationalMemory {
26432
26533
  onIndexObservations;
26433
26534
  /** Config-level lifecycle hooks fired for every observation/reflection cycle. */
26434
26535
  hooks;
26536
+ /** Execution policy for config-level hooks on manual and turn-synchronous cycles. */
26537
+ hookExecution;
26435
26538
  /** Observer agent runner — handles LLM calls for extracting observations. */
26436
26539
  observer;
26437
26540
  /** Reflector agent runner — handles LLM calls for compressing observations. */
@@ -26538,6 +26641,7 @@ var ObservationalMemory = class ObservationalMemory {
26538
26641
  this.retrievalSearch = typeof config.retrieval === "object" && Boolean(config.retrieval.vector);
26539
26642
  this.onIndexObservations = config.onIndexObservations;
26540
26643
  this.hooks = config.hooks;
26644
+ this.hookExecution = config.hookExecution ?? "non-blocking";
26541
26645
  this.mastra = config.mastra;
26542
26646
  this.memory = config.memory;
26543
26647
  this.curationCadence = config.curationCadence;
@@ -28330,57 +28434,54 @@ ${formattedMessages}
28330
28434
  };
28331
28435
  }
28332
28436
  /**
28333
- * Compose the config-level hooks with optional per-call hooks into a single
28334
- * `ObserveHooks` object, binding call context onto the config-level
28335
- * callbacks. Config-level hooks are guarded so a throwing consumer hook can
28336
- * never fail an observation/reflection cycle; per-call hooks keep their
28337
- * existing payloads and propagation semantics. Returns undefined when
28338
- * neither is configured, so call sites stay zero-cost.
28339
- *
28340
- * Used internally by every pipeline path (manual observe/reflect,
28341
- * turn-engine sync observation, async buffering); public only so the
28342
- * observation turn engine can thread config-level reflection hooks into
28343
- * reflector calls.
28437
+ * Compose config-level hooks with optional per-call hooks and bind context to
28438
+ * config-level callbacks. The configured policy applies unless a background
28439
+ * path explicitly requests non-blocking invocation.
28344
28440
  *
28345
28441
  * @internal
28346
28442
  */
28347
- composeHooks(callHooks, context) {
28443
+ composeHooks(callHooks, context, execution = "configured") {
28348
28444
  const configHooks = this.hooks;
28349
28445
  if (!configHooks) return callHooks;
28350
28446
  if (!callHooks && !Object.keys(configHooks).length) return void 0;
28351
- const fireConfigHook = (name, arg) => {
28447
+ const shouldAwaitConfig = execution === "configured" && this.hookExecution === "await";
28448
+ const invokeConfigHook = async (name, arg) => {
28352
28449
  const hook = configHooks[name];
28353
28450
  if (!hook) return;
28451
+ if (shouldAwaitConfig) {
28452
+ await hook(arg);
28453
+ return;
28454
+ }
28354
28455
  const logHookFailure = (error) => omDebug(`[OM:hooks] config-level ${name} hook failed: ${error instanceof Error ? error.message : String(error)}`);
28355
28456
  try {
28356
- const out = hook(arg);
28357
- if (out && typeof out.then === "function") out.then(void 0, logHookFailure);
28457
+ const result = hook(arg);
28458
+ if (result && typeof result.then === "function") result.then(void 0, logHookFailure);
28358
28459
  } catch (error) {
28359
28460
  logHookFailure(error);
28360
28461
  }
28361
28462
  };
28362
28463
  return {
28363
- onObservationStart: () => {
28364
- fireConfigHook("onObservationStart", context);
28365
- callHooks?.onObservationStart?.();
28464
+ onObservationStart: async () => {
28465
+ await invokeConfigHook("onObservationStart", context);
28466
+ await callHooks?.onObservationStart?.();
28366
28467
  },
28367
- onObservationEnd: (result) => {
28368
- fireConfigHook("onObservationEnd", {
28468
+ onObservationEnd: async (result) => {
28469
+ await invokeConfigHook("onObservationEnd", {
28369
28470
  ...context,
28370
28471
  ...result
28371
28472
  });
28372
- callHooks?.onObservationEnd?.(result);
28473
+ await callHooks?.onObservationEnd?.(result);
28373
28474
  },
28374
- onReflectionStart: () => {
28375
- fireConfigHook("onReflectionStart", context);
28376
- callHooks?.onReflectionStart?.();
28475
+ onReflectionStart: async () => {
28476
+ await invokeConfigHook("onReflectionStart", context);
28477
+ await callHooks?.onReflectionStart?.();
28377
28478
  },
28378
- onReflectionEnd: (result) => {
28379
- fireConfigHook("onReflectionEnd", {
28479
+ onReflectionEnd: async (result) => {
28480
+ await invokeConfigHook("onReflectionEnd", {
28380
28481
  ...context,
28381
28482
  ...result
28382
28483
  });
28383
- callHooks?.onReflectionEnd?.(result);
28484
+ await callHooks?.onReflectionEnd?.(result);
28384
28485
  }
28385
28486
  };
28386
28487
  }
@@ -28393,22 +28494,29 @@ ${formattedMessages}
28393
28494
  * end hook reports `error` for failed cycles even though nothing throws.
28394
28495
  */
28395
28496
  async runBufferedObservationCycle(context, run) {
28396
- const hooks = this.composeHooks(void 0, context);
28397
- hooks?.onObservationStart?.();
28497
+ const hooks = this.composeHooks(void 0, context, "non-blocking");
28398
28498
  let runResult;
28399
28499
  let runError;
28500
+ let lifecycleError;
28400
28501
  try {
28502
+ await hooks?.onObservationStart?.();
28401
28503
  runResult = await run();
28402
28504
  return runResult;
28403
28505
  } catch (error) {
28506
+ lifecycleError = error;
28404
28507
  runError = error instanceof Error ? error : new Error(String(error));
28405
28508
  throw error;
28406
28509
  } finally {
28407
- hooks?.onObservationEnd?.({
28408
- usage: runResult?.usage,
28409
- error: runError ?? runResult?.error,
28410
- ...runResult?.providerMetadata ? { providerMetadata: runResult.providerMetadata } : {}
28411
- });
28510
+ try {
28511
+ await hooks?.onObservationEnd?.({
28512
+ usage: runResult?.usage,
28513
+ error: runError ?? runResult?.error,
28514
+ ...runResult?.providerMetadata ? { providerMetadata: runResult.providerMetadata } : {}
28515
+ });
28516
+ } catch (endHookError) {
28517
+ if (lifecycleError === void 0) throw endHookError;
28518
+ omDebug(`[OM:hooks] async-buffer onObservationEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
28519
+ }
28412
28520
  }
28413
28521
  }
28414
28522
  /**
@@ -28436,18 +28544,21 @@ ${formattedMessages}
28436
28544
  let observed = false;
28437
28545
  let observationUsage;
28438
28546
  let observationProviderMetadata;
28547
+ let observationError;
28548
+ let lifecycleError;
28549
+ let observationStarted = false;
28439
28550
  let generationBefore = -1;
28440
- await this.withLock(lockKey, async () => {
28441
- const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
28442
- generationBefore = freshRecord.generationCount;
28443
- const unobservedMessages = messages ? this.getUnobservedMessages(messages, freshRecord) : await this.loadMessagesFromStorage(threadId, resourceId, freshRecord.lastObservedAt ? new Date(freshRecord.lastObservedAt) : void 0);
28444
- if (!this.meetsObservationThreshold({
28445
- record: freshRecord,
28446
- unobservedTokens: await this.tokenCounter.countMessagesAsync(unobservedMessages)
28447
- })) return;
28448
- hooks?.onObservationStart?.();
28449
- let observationError;
28450
- try {
28551
+ try {
28552
+ await this.withLock(lockKey, async () => {
28553
+ const freshRecord = await this.getOrCreateRecord(threadId, resourceId);
28554
+ generationBefore = freshRecord.generationCount;
28555
+ const unobservedMessages = messages ? this.getUnobservedMessages(messages, freshRecord) : await this.loadMessagesFromStorage(threadId, resourceId, freshRecord.lastObservedAt ? new Date(freshRecord.lastObservedAt) : void 0);
28556
+ if (!this.meetsObservationThreshold({
28557
+ record: freshRecord,
28558
+ unobservedTokens: await this.tokenCounter.countMessagesAsync(unobservedMessages)
28559
+ })) return;
28560
+ observationStarted = true;
28561
+ await hooks?.onObservationStart?.();
28451
28562
  const result = await ObservationStrategy.create(this, {
28452
28563
  record: freshRecord,
28453
28564
  threadId,
@@ -28465,17 +28576,22 @@ ${formattedMessages}
28465
28576
  observed = result.observed;
28466
28577
  observationUsage = result.usage;
28467
28578
  observationProviderMetadata = result.providerMetadata;
28468
- } catch (error) {
28469
- observationError = error instanceof Error ? error : new Error(String(error));
28470
- throw error;
28471
- } finally {
28472
- hooks?.onObservationEnd?.({
28473
- usage: observationUsage,
28474
- error: observationError,
28475
- ...observationProviderMetadata ? { providerMetadata: observationProviderMetadata } : {}
28476
- });
28477
- }
28478
- });
28579
+ });
28580
+ } catch (error) {
28581
+ lifecycleError = error;
28582
+ observationError = error instanceof Error ? error : new Error(String(error));
28583
+ }
28584
+ if (observationStarted) try {
28585
+ await hooks?.onObservationEnd?.({
28586
+ usage: observationUsage,
28587
+ error: observationError,
28588
+ ...observationProviderMetadata ? { providerMetadata: observationProviderMetadata } : {}
28589
+ });
28590
+ } catch (endHookError) {
28591
+ if (lifecycleError === void 0) throw endHookError;
28592
+ omDebug(`[OM:hooks] onObservationEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
28593
+ }
28594
+ if (lifecycleError !== void 0) throw lifecycleError;
28479
28595
  const record = await this.getOrCreateRecord(threadId, resourceId);
28480
28596
  const reflected = record.generationCount > generationBefore && generationBefore >= 0;
28481
28597
  if (observed) this.maybeTriggerCadenceCuration(threadId, resourceId, record, requestContext).catch((error) => {
@@ -28549,11 +28665,18 @@ ${formattedMessages}
28549
28665
  resourceId,
28550
28666
  trigger: "manual"
28551
28667
  });
28552
- hooks?.onReflectionStart?.();
28553
28668
  let reflectionUsage;
28554
28669
  let reflectionProviderMetadata;
28555
28670
  let reflectionError;
28671
+ let lifecycleError;
28556
28672
  try {
28673
+ try {
28674
+ await hooks?.onReflectionStart?.();
28675
+ } catch (error) {
28676
+ lifecycleError = error;
28677
+ reflectionError = error instanceof Error ? error : new Error(String(error));
28678
+ throw error;
28679
+ }
28557
28680
  const thread = await this.storage.getThreadById({ threadId });
28558
28681
  const previousOmMetadata = getThreadOMMetadata(thread?.metadata);
28559
28682
  const priorExtractedValues = getPriorExtractedValues(previousOmMetadata, this.reflectionConfig.extractors);
@@ -28591,6 +28714,7 @@ ${formattedMessages}
28591
28714
  };
28592
28715
  } catch (error) {
28593
28716
  reflectionError = error instanceof Error ? error : new Error(String(error));
28717
+ if (lifecycleError !== void 0) throw error;
28594
28718
  omError("[OM] reflect() failed", error);
28595
28719
  return {
28596
28720
  reflected: false,
@@ -28598,13 +28722,25 @@ ${formattedMessages}
28598
28722
  usage: void 0
28599
28723
  };
28600
28724
  } finally {
28601
- hooks?.onReflectionEnd?.({
28602
- usage: reflectionUsage,
28603
- error: reflectionError,
28604
- ...reflectionProviderMetadata ? { providerMetadata: reflectionProviderMetadata } : {}
28605
- });
28606
- await this.storage.setReflectingFlag(record.id, false);
28607
- unregisterOp(record.id, "reflecting");
28725
+ try {
28726
+ await this.storage.setReflectingFlag(record.id, false);
28727
+ } finally {
28728
+ unregisterOp(record.id, "reflecting");
28729
+ }
28730
+ let endHookError;
28731
+ try {
28732
+ await hooks?.onReflectionEnd?.({
28733
+ usage: reflectionUsage,
28734
+ error: reflectionError,
28735
+ ...reflectionProviderMetadata ? { providerMetadata: reflectionProviderMetadata } : {}
28736
+ });
28737
+ } catch (error) {
28738
+ endHookError = error;
28739
+ }
28740
+ if (endHookError !== void 0) {
28741
+ if (lifecycleError === void 0) throw endHookError;
28742
+ omDebug(`[OM:hooks] onReflectionEnd hook failed after cycle failure: ${endHookError instanceof Error ? endHookError.message : String(endHookError)}`);
28743
+ }
28608
28744
  }
28609
28745
  }
28610
28746
  /**
@@ -28900,7 +29036,9 @@ var ObservationalMemoryProcessor = class {
28900
29036
  return messageList;
28901
29037
  }
28902
29038
  const { threadId, resourceId } = context;
28903
- const readOnly = parseMemoryRequestContext(requestContext)?.memoryConfig?.readOnly;
29039
+ const memoryContext = parseMemoryRequestContext(requestContext);
29040
+ const runState = memoryContext?.runState?.();
29041
+ const readOnly = memoryContext?.memoryConfig?.readOnly;
28904
29042
  const actorModelContext = model?.modelId ? {
28905
29043
  provider: model.provider,
28906
29044
  modelId: model.modelId,
@@ -28917,7 +29055,8 @@ var ObservationalMemoryProcessor = class {
28917
29055
  memory: this.memory,
28918
29056
  messageList,
28919
29057
  threadId,
28920
- resourceId
29058
+ resourceId,
29059
+ runState
28921
29060
  });
28922
29061
  const systemMessages = ctx.omRecord ? await this.engine.buildContextSystemMessages({
28923
29062
  threadId,
@@ -28958,7 +29097,7 @@ var ObservationalMemoryProcessor = class {
28958
29097
  this.turn.sendStateSignal = args.sendStateSignal;
28959
29098
  this.turn.agent = args.agent;
28960
29099
  this.turn.requestContext = requestContext;
28961
- await this.turn.start(this.memory);
29100
+ await this.turn.start(this.memory, runState);
28962
29101
  if (stepNumber === 0 && this.temporalMarkers) await insertTemporalGapMarkers({
28963
29102
  messageList,
28964
29103
  sendSignal: args.sendSignal
@@ -30128,14 +30267,18 @@ ${workingMemory}`;
30128
30267
  if (!this.threadConfig.workingMemory?.enabled) return null;
30129
30268
  return extractWorkingMemoryContent(text)?.trim() ?? null;
30130
30269
  }
30131
- async getWorkingMemory({ threadId, resourceId, memoryConfig }) {
30270
+ async getWorkingMemory({ threadId, resourceId, memoryConfig, runState }) {
30132
30271
  const config = this.getMergedThreadConfig(memoryConfig || {});
30133
30272
  if (!config.workingMemory?.enabled) return null;
30134
30273
  const scope = config.workingMemory.scope || "resource";
30135
30274
  let workingMemoryData = null;
30136
30275
  if (scope === "resource" && !resourceId) throw new Error("Memory error: Resource-scoped working memory is enabled but no resourceId was provided. Either provide a resourceId or explicitly set workingMemory.scope to 'thread'.");
30137
- if (scope === "resource" && resourceId) workingMemoryData = (await (await this.getMemoryStore()).getResourceById({ resourceId }))?.workingMemory || null;
30138
- else workingMemoryData = (await this.getThreadById({ threadId }))?.metadata?.workingMemory;
30276
+ if (scope === "resource" && resourceId) {
30277
+ const loadWorkingMemory = async () => {
30278
+ return (await (await this.getMemoryStore()).getResourceById({ resourceId }))?.workingMemory || null;
30279
+ };
30280
+ workingMemoryData = runState ? await runState.load(`working-memory:resource:${resourceId}`, loadWorkingMemory) : await loadWorkingMemory();
30281
+ } else workingMemoryData = (runState?.threadLoaded ? runState.thread : await this.getThreadById({ threadId }))?.metadata?.workingMemory;
30139
30282
  if (!workingMemoryData) return null;
30140
30283
  return workingMemoryData;
30141
30284
  }
@@ -30167,16 +30310,18 @@ ${workingMemory}`;
30167
30310
  content: (config.workingMemory.template || this.defaultWorkingMemoryTemplate).trim()
30168
30311
  };
30169
30312
  }
30170
- async getSystemMessage({ threadId, resourceId, memoryConfig }) {
30313
+ async getSystemMessage({ threadId, resourceId, memoryConfig, runState }) {
30171
30314
  const config = this.getMergedThreadConfig(memoryConfig);
30172
30315
  this.assertWorkingMemoryStateSignalsCompatibility(config);
30173
30316
  if (!config.workingMemory?.enabled) return null;
30174
30317
  if (config.workingMemory?.useStateSignals) return null;
30175
- const workingMemoryTemplate = await this.getWorkingMemoryTemplate({ memoryConfig });
30318
+ const loadTemplate = () => this.getWorkingMemoryTemplate({ memoryConfig });
30319
+ const workingMemoryTemplate = runState ? await runState.load("working-memory:template", loadTemplate) : await loadTemplate();
30176
30320
  const workingMemoryData = await this.getWorkingMemory({
30177
30321
  threadId,
30178
30322
  resourceId,
30179
- memoryConfig: config
30323
+ memoryConfig: config,
30324
+ runState
30180
30325
  });
30181
30326
  if (!workingMemoryTemplate) return null;
30182
30327
  const workingMemoryConfig = config.workingMemory;
@@ -30209,7 +30354,7 @@ ${workingMemory}`;
30209
30354
  * ```
30210
30355
  */
30211
30356
  async getContext(opts) {
30212
- const { threadId, resourceId, memoryConfig } = opts;
30357
+ const { threadId, resourceId, memoryConfig, runState } = opts;
30213
30358
  const config = this.getMergedThreadConfig(memoryConfig);
30214
30359
  const memoryStore = await this.getMemoryStore();
30215
30360
  const systemParts = [];
@@ -30219,10 +30364,14 @@ ${workingMemory}`;
30219
30364
  let otherThreadsContext;
30220
30365
  const omEngine = await this.omEngine;
30221
30366
  if (omEngine) {
30222
- omRecord = await omEngine.getRecord(threadId, resourceId);
30367
+ const loadOmRecord = () => omEngine.getRecord(threadId, resourceId);
30368
+ omRecord = runState ? await runState.load(`observational-memory:record:${threadId}:${resourceId ?? ""}`, loadOmRecord) : await loadOmRecord();
30223
30369
  if (omRecord?.activeObservations) {
30224
30370
  hasObservations = true;
30225
- if (omEngine.scope === "resource" && resourceId) otherThreadsContext = await omEngine.getOtherThreadsContext(resourceId, threadId);
30371
+ if (omEngine.scope === "resource" && resourceId) {
30372
+ const loadOtherThreadsContext = () => omEngine.getOtherThreadsContext(resourceId, threadId);
30373
+ otherThreadsContext = runState ? await runState.load(`observational-memory:other-threads:${resourceId}:${threadId}:${omRecord.lastObservedAt ?? ""}`, loadOtherThreadsContext) : await loadOtherThreadsContext();
30374
+ }
30226
30375
  const obsSystemMessage = await omEngine.buildContextSystemMessage({
30227
30376
  threadId,
30228
30377
  resourceId,
@@ -30250,30 +30399,41 @@ ${workingMemory}`;
30250
30399
  const workingMemoryMessage = await this.getSystemMessage({
30251
30400
  threadId,
30252
30401
  resourceId,
30253
- memoryConfig: config
30402
+ memoryConfig: config,
30403
+ runState
30254
30404
  });
30255
30405
  if (workingMemoryMessage) systemParts.push(workingMemoryMessage);
30256
30406
  let messages;
30257
30407
  if (omEngine && omRecord) {
30258
30408
  const dateFilter = omRecord.lastObservedAt ? { dateRange: { start: new Date(new Date(omRecord.lastObservedAt).getTime() + 1) } } : void 0;
30259
- if (omEngine.scope === "resource" && resourceId) messages = (await memoryStore.listMessagesByResourceId({
30260
- resourceId,
30261
- orderBy: {
30262
- field: "createdAt",
30263
- direction: "ASC"
30264
- },
30265
- perPage: false,
30266
- filter: dateFilter
30267
- })).messages;
30268
- else messages = (await memoryStore.listMessages({
30269
- threadId,
30270
- orderBy: {
30271
- field: "createdAt",
30272
- direction: "ASC"
30273
- },
30274
- perPage: false,
30275
- filter: dateFilter
30276
- })).messages;
30409
+ const boundary = omRecord.lastObservedAt ? new Date(omRecord.lastObservedAt).toISOString() : "";
30410
+ if (omEngine.scope === "resource" && resourceId) {
30411
+ const loadMessages = async () => {
30412
+ return (await memoryStore.listMessagesByResourceId({
30413
+ resourceId,
30414
+ orderBy: {
30415
+ field: "createdAt",
30416
+ direction: "ASC"
30417
+ },
30418
+ perPage: false,
30419
+ filter: dateFilter
30420
+ })).messages;
30421
+ };
30422
+ messages = runState ? await runState.load(`observational-memory:messages:resource:${resourceId}:${boundary}`, loadMessages) : await loadMessages();
30423
+ } else {
30424
+ const loadMessages = async () => {
30425
+ return (await memoryStore.listMessages({
30426
+ threadId,
30427
+ orderBy: {
30428
+ field: "createdAt",
30429
+ direction: "ASC"
30430
+ },
30431
+ perPage: false,
30432
+ filter: dateFilter
30433
+ })).messages;
30434
+ };
30435
+ messages = runState ? await runState.load(`observational-memory:messages:thread:${threadId}:${boundary}`, loadMessages) : await loadMessages();
30436
+ }
30277
30437
  } else {
30278
30438
  const lastMessages = config.lastMessages;
30279
30439
  if (lastMessages === false) messages = [];
@@ -31285,4 +31445,4 @@ Notes:
31285
31445
  //#endregion
31286
31446
  export { formatMessagesForObserver as A, OBSERVATION_CONTINUATION_HINT as B, SUMMARIZE_THREAD_DEFAULTS as C, buildObserverPrompt as D, OBSERVER_SYSTEM_PROMPT as E, parseAnchorId as F, ModelByInputTokens as G, KnowledgeSemanticIndexCoordinator as H, stripEphemeralAnchorIds as I, SUBCONSCIOUS_ACTIVITY_STATE_ID as J, SubconsciousCaptureExtractor 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, WorkingMemoryExtractor as S, TokenCounter as T, StaleKnowledgeSemanticIndexError as U, Subconscious as V, SubconsciousRemindExtractor as W, publishSubconsciousActivity as X, buildSubconsciousActivitySnapshot as Y, renderSubconsciousActivity as Z, reconcileObservationGroupsFromReflection as _, extractWorkingMemoryContent as a, wrapInObservationGroup as b, WORKING_MEMORY_STATE_ID as c, getObservationsAsOf as d, ObservationalMemoryProcessor as f, parseObservationGroups as g, deriveObservationGroupProvenance as h, WorkingMemory as i, hasCurrentTaskSection as j, extractCurrentTask as k, WORKING_MEMORY_STATE_PROCESSOR_ID as l, combineObservationGroupRanges as m, MessageHistory$1 as n, extractWorkingMemoryTags as o, ObservationalMemory as p, subconsciousCaptureSchema as q, SemanticRecall as r, removeWorkingMemoryTags as s, Memory as t, WorkingMemoryStateProcessor as u, renderObservationGroupsForReflection as v, summarizeConversation as w, deepMergeWorkingMemory as x, stripObservationGroups as y, OBSERVATION_CONTEXT_PROMPT as z };
31287
31447
 
31288
- //# sourceMappingURL=src-t_4yoTX9.js.map
31448
+ //# sourceMappingURL=src-kLYHhPqh.js.map