@mastra/memory 1.7.0 → 1.8.0

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 (30) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/{chunk-M7RAJAZ6.js → chunk-SUU4IAZJ.js} +307 -39
  3. package/dist/chunk-SUU4IAZJ.js.map +1 -0
  4. package/dist/{chunk-SHID74TI.cjs → chunk-YPFNHFT6.cjs} +307 -39
  5. package/dist/chunk-YPFNHFT6.cjs.map +1 -0
  6. package/dist/docs/SKILL.md +1 -1
  7. package/dist/docs/assets/SOURCE_MAP.json +24 -24
  8. package/dist/docs/references/docs-memory-observational-memory.md +23 -0
  9. package/dist/docs/references/docs-memory-overview.md +3 -3
  10. package/dist/docs/references/reference-memory-observational-memory.md +2 -0
  11. package/dist/index.cjs +64 -368
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +64 -368
  15. package/dist/index.js.map +1 -1
  16. package/dist/{observational-memory-AU6MIH4Q.cjs → observational-memory-3HFM7PY2.cjs} +17 -17
  17. package/dist/{observational-memory-AU6MIH4Q.cjs.map → observational-memory-3HFM7PY2.cjs.map} +1 -1
  18. package/dist/{observational-memory-YRWU6CY3.js → observational-memory-XXD6E2SO.js} +3 -3
  19. package/dist/{observational-memory-YRWU6CY3.js.map → observational-memory-XXD6E2SO.js.map} +1 -1
  20. package/dist/processors/index.cjs +15 -15
  21. package/dist/processors/index.js +1 -1
  22. package/dist/processors/observational-memory/observational-memory.d.ts +21 -0
  23. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observer-agent.d.ts +14 -2
  25. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  26. package/dist/processors/observational-memory/types.d.ts +7 -0
  27. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  28. package/package.json +8 -8
  29. package/dist/chunk-M7RAJAZ6.js.map +0 -1
  30. package/dist/chunk-SHID74TI.cjs.map +0 -1
@@ -803,6 +803,11 @@ ${maybeTruncate(resultStr, maxLen)}`;
803
803
  ${maybeTruncate(argsStr, maxLen)}`;
804
804
  }
805
805
  const partType = part.type;
806
+ if (partType === "reasoning") {
807
+ const reasoning = part.reasoning;
808
+ if (reasoning) return maybeTruncate(reasoning, maxLen);
809
+ return "";
810
+ }
806
811
  if (partType === "image" || partType === "file") {
807
812
  const attachment = part;
808
813
  const inputAttachment = toObserverInputAttachmentPart(attachment);
@@ -817,6 +822,9 @@ ${maybeTruncate(argsStr, maxLen)}`;
817
822
  } else if (msg.content?.content) {
818
823
  content = maybeTruncate(msg.content.content, maxLen);
819
824
  }
825
+ if (!content && attachments.length === 0) {
826
+ return { text: "", attachments };
827
+ }
820
828
  return {
821
829
  text: `**${role}${timestampStr}:**
822
830
  ${content}`,
@@ -825,18 +833,21 @@ ${content}`,
825
833
  }
826
834
  function formatMessagesForObserver(messages, options) {
827
835
  const counter = { nextImageId: 1, nextFileId: 1 };
828
- return messages.map((msg) => formatObserverMessage(msg, counter, options).text).join("\n\n---\n\n");
836
+ return messages.map((msg) => formatObserverMessage(msg, counter, options).text).filter(Boolean).join("\n\n---\n\n");
829
837
  }
830
838
  function buildObserverHistoryMessage(messages) {
831
839
  const counter = { nextImageId: 1, nextFileId: 1 };
832
840
  const content = [{ type: "text", text: "## New Message History to Observe\n\n" }];
833
- messages.forEach((message, index) => {
841
+ let visibleCount = 0;
842
+ messages.forEach((message) => {
834
843
  const formatted = formatObserverMessage(message, counter);
835
- content.push({ type: "text", text: formatted.text });
836
- content.push(...formatted.attachments);
837
- if (index < messages.length - 1) {
844
+ if (!formatted.text && formatted.attachments.length === 0) return;
845
+ if (visibleCount > 0) {
838
846
  content.push({ type: "text", text: "\n\n---\n\n" });
839
847
  }
848
+ content.push({ type: "text", text: formatted.text });
849
+ content.push(...formatted.attachments);
850
+ visibleCount++;
840
851
  });
841
852
  return {
842
853
  role: "user",
@@ -865,16 +876,22 @@ The following messages are from ${threadOrder.length} different conversation thr
865
876
  threadOrder.forEach((threadId, threadIndex) => {
866
877
  const messages = messagesByThread.get(threadId);
867
878
  if (!messages || messages.length === 0) return;
868
- content.push({ type: "text", text: `<thread id="${threadId}">
869
- ` });
870
- messages.forEach((message, messageIndex) => {
879
+ const threadContent = [];
880
+ let visibleCount = 0;
881
+ messages.forEach((message) => {
871
882
  const formatted = formatObserverMessage(message, counter);
872
- content.push({ type: "text", text: formatted.text });
873
- content.push(...formatted.attachments);
874
- if (messageIndex < messages.length - 1) {
875
- content.push({ type: "text", text: "\n\n---\n\n" });
883
+ if (!formatted.text && formatted.attachments.length === 0) return;
884
+ if (visibleCount > 0) {
885
+ threadContent.push({ type: "text", text: "\n\n---\n\n" });
876
886
  }
887
+ threadContent.push({ type: "text", text: formatted.text });
888
+ threadContent.push(...formatted.attachments);
889
+ visibleCount++;
877
890
  });
891
+ if (visibleCount === 0) return;
892
+ content.push({ type: "text", text: `<thread id="${threadId}">
893
+ ` });
894
+ content.push(...threadContent);
878
895
  content.push({ type: "text", text: "\n</thread>" });
879
896
  if (threadIndex < threadOrder.length - 1) {
880
897
  content.push({ type: "text", text: "\n\n" });
@@ -885,7 +902,7 @@ The following messages are from ${threadOrder.length} different conversation thr
885
902
  content
886
903
  };
887
904
  }
888
- function buildMultiThreadObserverTaskPrompt(existingObservations) {
905
+ function buildMultiThreadObserverTaskPrompt(existingObservations, threadOrder, priorMetadataByThread, wasTruncated) {
889
906
  let prompt = "";
890
907
  if (existingObservations) {
891
908
  prompt += `## Previous Observations
@@ -897,6 +914,39 @@ ${existingObservations}
897
914
  `;
898
915
  prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
899
916
  }
917
+ const hasTruncatedObservations = wasTruncated ?? false;
918
+ const threadMetadataLines = threadOrder?.map((threadId) => {
919
+ const metadata = priorMetadataByThread?.get(threadId);
920
+ if (!metadata?.currentTask && !metadata?.suggestedResponse) {
921
+ return "";
922
+ }
923
+ const lines = [`- thread ${threadId}`];
924
+ if (metadata.currentTask) {
925
+ lines.push(` - prior current-task: ${metadata.currentTask}`);
926
+ }
927
+ if (metadata.suggestedResponse) {
928
+ lines.push(` - prior suggested-response: ${metadata.suggestedResponse}`);
929
+ }
930
+ return lines.join("\n");
931
+ }).filter(Boolean).join("\n");
932
+ if (threadMetadataLines) {
933
+ prompt += `## Prior Thread Metadata
934
+
935
+ ${threadMetadataLines}
936
+
937
+ `;
938
+ if (hasTruncatedObservations) {
939
+ prompt += `Previous observations were truncated for context budget reasons.
940
+ `;
941
+ prompt += `The main agent still has full memory context outside this observer window.
942
+ `;
943
+ }
944
+ prompt += `Use each thread's prior current-task and suggested-response as continuity hints, then update them based on that thread's new messages.
945
+
946
+ ---
947
+
948
+ `;
949
+ }
900
950
  prompt += `## Your Task
901
951
 
902
952
  `;
@@ -985,6 +1035,32 @@ ${existingObservations}
985
1035
  `;
986
1036
  prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
987
1037
  }
1038
+ const hasTruncatedObservations = options?.wasTruncated ?? false;
1039
+ const priorMetadataLines = [];
1040
+ if (options?.priorCurrentTask) {
1041
+ priorMetadataLines.push(`- prior current-task: ${options.priorCurrentTask}`);
1042
+ }
1043
+ if (options?.priorSuggestedResponse) {
1044
+ priorMetadataLines.push(`- prior suggested-response: ${options.priorSuggestedResponse}`);
1045
+ }
1046
+ if (priorMetadataLines.length > 0) {
1047
+ prompt += `## Prior Thread Metadata
1048
+
1049
+ ${priorMetadataLines.join("\n")}
1050
+
1051
+ `;
1052
+ if (hasTruncatedObservations) {
1053
+ prompt += `Previous observations were truncated for context budget reasons.
1054
+ `;
1055
+ prompt += `The main agent still has full memory context outside this observer window.
1056
+ `;
1057
+ }
1058
+ prompt += `Use the prior current-task and suggested-response as continuity hints, then update them based on the new messages.
1059
+
1060
+ ---
1061
+
1062
+ `;
1063
+ }
988
1064
  prompt += `## Your Task
989
1065
 
990
1066
  `;
@@ -3278,6 +3354,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3278
3354
  config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
3279
3355
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
3280
3356
  ),
3357
+ previousObserverTokens: config.observation?.previousObserverTokens ?? 2e3,
3281
3358
  instruction: config.observation?.instruction
3282
3359
  };
3283
3360
  this.reflectionConfig = {
@@ -3314,7 +3391,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3314
3391
  return {
3315
3392
  scope: this.scope,
3316
3393
  observation: {
3317
- messageTokens: this.observationConfig.messageTokens
3394
+ messageTokens: this.observationConfig.messageTokens,
3395
+ previousObserverTokens: this.observationConfig.previousObserverTokens
3318
3396
  },
3319
3397
  reflection: {
3320
3398
  observationTokens: this.reflectionConfig.observationTokens
@@ -3394,7 +3472,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3394
3472
  scope: this.scope,
3395
3473
  observation: {
3396
3474
  messageTokens: this.observationConfig.messageTokens,
3397
- model: observationModelName
3475
+ model: observationModelName,
3476
+ previousObserverTokens: this.observationConfig.previousObserverTokens
3398
3477
  },
3399
3478
  reflection: {
3400
3479
  observationTokens: this.reflectionConfig.observationTokens,
@@ -3459,6 +3538,13 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3459
3538
  );
3460
3539
  }
3461
3540
  }
3541
+ if (this.observationConfig.previousObserverTokens !== void 0 && this.observationConfig.previousObserverTokens !== false) {
3542
+ if (!Number.isFinite(this.observationConfig.previousObserverTokens) || this.observationConfig.previousObserverTokens < 0) {
3543
+ throw new Error(
3544
+ `observation.previousObserverTokens must be false or a finite number >= 0, got ${this.observationConfig.previousObserverTokens}`
3545
+ );
3546
+ }
3547
+ }
3462
3548
  if (this.reflectionConfig.bufferActivation !== void 0) {
3463
3549
  if (this.reflectionConfig.bufferActivation <= 0 || this.reflectionConfig.bufferActivation > 1) {
3464
3550
  throw new Error(
@@ -3856,6 +3942,141 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3856
3942
  }
3857
3943
  return result;
3858
3944
  }
3945
+ /**
3946
+ * Prepare optimized observer context by applying truncation and buffered-reflection inclusion.
3947
+ *
3948
+ * Returns the (possibly optimized) observations string to pass as "Previous Observations"
3949
+ * to the observer prompt. When no optimization options are set, returns the input unchanged.
3950
+ */
3951
+ prepareObserverContext(existingObservations, record) {
3952
+ const { previousObserverTokens } = this.observationConfig;
3953
+ const tokenBudget = previousObserverTokens === void 0 || previousObserverTokens === false ? void 0 : previousObserverTokens;
3954
+ if (tokenBudget === void 0) {
3955
+ return { context: existingObservations, wasTruncated: false };
3956
+ }
3957
+ const bufferedReflection = record?.bufferedReflection && record?.reflectedObservationLineCount ? record.bufferedReflection : void 0;
3958
+ if (!existingObservations) {
3959
+ return { context: bufferedReflection, wasTruncated: false };
3960
+ }
3961
+ let observations = existingObservations;
3962
+ if (bufferedReflection && record?.reflectedObservationLineCount) {
3963
+ const allLines = observations.split("\n");
3964
+ const unreflectedLines = allLines.slice(record.reflectedObservationLineCount);
3965
+ const unreflectedContent = unreflectedLines.join("\n").trim();
3966
+ observations = unreflectedContent ? `${bufferedReflection}
3967
+
3968
+ ${unreflectedContent}` : bufferedReflection;
3969
+ }
3970
+ let wasTruncated = false;
3971
+ if (tokenBudget !== void 0) {
3972
+ if (tokenBudget === 0) {
3973
+ return { context: "", wasTruncated: true };
3974
+ }
3975
+ const currentTokens = this.tokenCounter.countObservations(observations);
3976
+ if (currentTokens > tokenBudget) {
3977
+ observations = this.truncateObservationsToTokenBudget(observations, tokenBudget);
3978
+ wasTruncated = true;
3979
+ }
3980
+ }
3981
+ return { context: observations, wasTruncated };
3982
+ }
3983
+ /**
3984
+ * Truncate observations to fit within a token budget.
3985
+ *
3986
+ * Strategy:
3987
+ * 1. Keep a raw tail of recent observations (end of block).
3988
+ * 2. Add a truncation marker: [X observations truncated here], placed at the hidden gap.
3989
+ * 3. Try to preserve important observations (🔴) from older context, newest-first.
3990
+ * 4. Enforce that at least 50% of kept observations remain raw tail observations.
3991
+ */
3992
+ truncateObservationsToTokenBudget(observations, budget) {
3993
+ if (budget === 0) {
3994
+ return "";
3995
+ }
3996
+ const totalTokens = this.tokenCounter.countObservations(observations);
3997
+ if (totalTokens <= budget) {
3998
+ return observations;
3999
+ }
4000
+ const lines = observations.split("\n");
4001
+ const totalCount = lines.length;
4002
+ const lineTokens = new Array(totalCount);
4003
+ const isImportant = new Array(totalCount);
4004
+ for (let i = 0; i < totalCount; i++) {
4005
+ lineTokens[i] = this.tokenCounter.countString(lines[i]);
4006
+ isImportant[i] = lines[i].includes("\u{1F534}");
4007
+ }
4008
+ const suffixTokens = new Array(totalCount + 1);
4009
+ suffixTokens[totalCount] = 0;
4010
+ for (let i = totalCount - 1; i >= 0; i--) {
4011
+ suffixTokens[i] = suffixTokens[i + 1] + lineTokens[i];
4012
+ }
4013
+ const headImportantIndexes = [];
4014
+ const buildCandidateString = (tailStart, selectedImportantIndexes) => {
4015
+ const keptIndexes = [
4016
+ ...selectedImportantIndexes,
4017
+ ...Array.from({ length: totalCount - tailStart }, (_, i) => tailStart + i)
4018
+ ].sort((a, b) => a - b);
4019
+ if (keptIndexes.length === 0) {
4020
+ return `[${totalCount} observations truncated here]`;
4021
+ }
4022
+ const outputLines = [];
4023
+ let previousKeptIndex = -1;
4024
+ for (const keptIndex of keptIndexes) {
4025
+ const hiddenCount = keptIndex - previousKeptIndex - 1;
4026
+ if (hiddenCount === 1) {
4027
+ outputLines.push(lines[previousKeptIndex + 1]);
4028
+ } else if (hiddenCount > 1) {
4029
+ outputLines.push(`[${hiddenCount} observations truncated here]`);
4030
+ }
4031
+ outputLines.push(lines[keptIndex]);
4032
+ previousKeptIndex = keptIndex;
4033
+ }
4034
+ const trailingHiddenCount = totalCount - previousKeptIndex - 1;
4035
+ if (trailingHiddenCount === 1) {
4036
+ outputLines.push(lines[totalCount - 1]);
4037
+ } else if (trailingHiddenCount > 1) {
4038
+ outputLines.push(`[${trailingHiddenCount} observations truncated here]`);
4039
+ }
4040
+ return outputLines.join("\n");
4041
+ };
4042
+ const estimateKeptContentCost = (tailStart, selectedImportantIndexes) => {
4043
+ let cost = suffixTokens[tailStart];
4044
+ for (const idx of selectedImportantIndexes) {
4045
+ cost += lineTokens[idx];
4046
+ }
4047
+ return cost;
4048
+ };
4049
+ let bestCandidate;
4050
+ let bestImportantCount = -1;
4051
+ let bestRawTailLength = -1;
4052
+ for (let tailStart = 1; tailStart < totalCount; tailStart++) {
4053
+ if (isImportant[tailStart - 1]) {
4054
+ headImportantIndexes.push(tailStart - 1);
4055
+ }
4056
+ const rawTailLength = totalCount - tailStart;
4057
+ const maxImportantByRatio = rawTailLength;
4058
+ let importantToKeep = Math.min(headImportantIndexes.length, maxImportantByRatio);
4059
+ const getSelectedImportant = (count) => count > 0 ? headImportantIndexes.slice(Math.max(0, headImportantIndexes.length - count)) : [];
4060
+ while (importantToKeep > 0 && estimateKeptContentCost(tailStart, getSelectedImportant(importantToKeep)) > budget) {
4061
+ importantToKeep -= 1;
4062
+ }
4063
+ if (estimateKeptContentCost(tailStart, getSelectedImportant(importantToKeep)) > budget) {
4064
+ continue;
4065
+ }
4066
+ if (importantToKeep > bestImportantCount || importantToKeep === bestImportantCount && rawTailLength > bestRawTailLength) {
4067
+ const candidate = buildCandidateString(tailStart, getSelectedImportant(importantToKeep));
4068
+ if (this.tokenCounter.countObservations(candidate) <= budget) {
4069
+ bestCandidate = candidate;
4070
+ bestImportantCount = importantToKeep;
4071
+ bestRawTailLength = rawTailLength;
4072
+ }
4073
+ }
4074
+ }
4075
+ if (!bestCandidate) {
4076
+ return `[${totalCount} observations truncated here]`;
4077
+ }
4078
+ return bestCandidate;
4079
+ }
3859
4080
  /**
3860
4081
  * Call the Observer agent to extract observations.
3861
4082
  */
@@ -3864,12 +4085,17 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3864
4085
  const observerMessages = [
3865
4086
  {
3866
4087
  role: "user",
3867
- content: buildObserverTaskPrompt(existingObservations, options)
4088
+ content: buildObserverTaskPrompt(existingObservations, {
4089
+ skipContinuationHints: options?.skipContinuationHints,
4090
+ priorCurrentTask: options?.priorCurrentTask,
4091
+ priorSuggestedResponse: options?.priorSuggestedResponse,
4092
+ wasTruncated: options?.wasTruncated
4093
+ })
3868
4094
  },
3869
4095
  buildObserverHistoryMessage(messagesToObserve)
3870
4096
  ];
3871
4097
  const doGenerate = async () => {
3872
- return this.withAbortCheck(async () => {
4098
+ const result2 = await this.withAbortCheck(async () => {
3873
4099
  const streamResult = await agent.stream(observerMessages, {
3874
4100
  modelSettings: {
3875
4101
  ...this.observationConfig.modelSettings
@@ -3880,6 +4106,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3880
4106
  });
3881
4107
  return streamResult.getFullOutput();
3882
4108
  }, abortSignal);
4109
+ return result2;
3883
4110
  };
3884
4111
  let result = await doGenerate();
3885
4112
  let parsed = parseObserverOutput(result.text);
@@ -3910,17 +4137,23 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3910
4137
  * Returns per-thread results with observations, currentTask, and suggestedContinuation,
3911
4138
  * plus the total usage for the batch.
3912
4139
  */
3913
- async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext) {
4140
+ async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, abortSignal, requestContext, wasTruncated) {
4141
+ const systemPrompt = buildObserverSystemPrompt(true, this.observationConfig.instruction);
3914
4142
  const agent$1 = new agent.Agent({
3915
4143
  id: "multi-thread-observer",
3916
4144
  name: "multi-thread-observer",
3917
4145
  model: this.observationConfig.model,
3918
- instructions: buildObserverSystemPrompt(true, this.observationConfig.instruction)
4146
+ instructions: systemPrompt
3919
4147
  });
3920
4148
  const observerMessages = [
3921
4149
  {
3922
4150
  role: "user",
3923
- content: buildMultiThreadObserverTaskPrompt(existingObservations)
4151
+ content: buildMultiThreadObserverTaskPrompt(
4152
+ existingObservations,
4153
+ threadOrder,
4154
+ priorMetadataByThread,
4155
+ wasTruncated
4156
+ )
3924
4157
  },
3925
4158
  buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder)
3926
4159
  ];
@@ -3932,7 +4165,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3932
4165
  this.observedMessageIds.add(msg.id);
3933
4166
  }
3934
4167
  const doGenerate = async () => {
3935
- return this.withAbortCheck(async () => {
4168
+ const result2 = await this.withAbortCheck(async () => {
3936
4169
  const streamResult = await agent$1.stream(observerMessages, {
3937
4170
  modelSettings: {
3938
4171
  ...this.observationConfig.modelSettings
@@ -3943,6 +4176,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3943
4176
  });
3944
4177
  return streamResult.getFullOutput();
3945
4178
  }, abortSignal);
4179
+ return result2;
3946
4180
  };
3947
4181
  let result = await doGenerate();
3948
4182
  let parsed = parseMultiThreadObserverOutput(result.text);
@@ -5376,12 +5610,18 @@ ${newThreadSection}`;
5376
5610
  );
5377
5611
  }
5378
5612
  }
5379
- const result = await this.callObserver(
5613
+ const { context: observerContext, wasTruncated } = this.prepareObserverContext(
5380
5614
  freshRecord?.activeObservations ?? record.activeObservations,
5381
- messagesToObserve,
5382
- abortSignal,
5383
- { requestContext }
5615
+ freshRecord ?? record
5384
5616
  );
5617
+ const thread = await this.storage.getThreadById({ threadId });
5618
+ const threadOMMetadata = memory.getThreadOMMetadata(thread?.metadata);
5619
+ const result = await this.callObserver(observerContext, messagesToObserve, abortSignal, {
5620
+ requestContext,
5621
+ priorCurrentTask: threadOMMetadata?.currentTask,
5622
+ priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
5623
+ wasTruncated
5624
+ });
5385
5625
  const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
5386
5626
  let newObservations;
5387
5627
  if (this.scope === "resource") {
@@ -5398,16 +5638,16 @@ ${result.observations}` : result.observations;
5398
5638
  const newMessageIds = messagesToObserve.map((m) => m.id);
5399
5639
  const existingIds = freshRecord?.observedMessageIds ?? record.observedMessageIds ?? [];
5400
5640
  const allObservedIds = [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])];
5401
- const thread = await this.storage.getThreadById({ threadId });
5402
- if (thread) {
5403
- const newMetadata = memory.setThreadOMMetadata(thread.metadata, {
5641
+ const threadForMetadata = await this.storage.getThreadById({ threadId });
5642
+ if (threadForMetadata) {
5643
+ const newMetadata = memory.setThreadOMMetadata(threadForMetadata.metadata, {
5404
5644
  suggestedResponse: result.suggestedContinuation,
5405
5645
  currentTask: result.currentTask,
5406
5646
  lastObservedMessageCursor: this.getLastObservedMessageCursor(messagesToObserve)
5407
5647
  });
5408
5648
  await this.storage.updateThread({
5409
5649
  id: threadId,
5410
- title: thread.title ?? "",
5650
+ title: threadForMetadata.title ?? "",
5411
5651
  metadata: newMetadata
5412
5652
  });
5413
5653
  }
@@ -5641,12 +5881,21 @@ ${result.observations}` : result.observations;
5641
5881
  const bufferedChunks = this.getBufferedChunks(record);
5642
5882
  const bufferedChunksText = bufferedChunks.map((c) => c.observations).join("\n\n");
5643
5883
  const combinedObservations = this.combineObservationsForBuffering(record.activeObservations, bufferedChunksText);
5884
+ const { context: observerContext, wasTruncated } = this.prepareObserverContext(combinedObservations, record);
5885
+ const thread = await this.storage.getThreadById({ threadId });
5886
+ const threadOMMetadata = memory.getThreadOMMetadata(thread?.metadata);
5644
5887
  const result = await this.callObserver(
5645
- combinedObservations,
5888
+ observerContext,
5646
5889
  messagesToBuffer,
5647
5890
  void 0,
5648
5891
  // No abort signal for background ops
5649
- { skipContinuationHints: true, requestContext }
5892
+ {
5893
+ skipContinuationHints: true,
5894
+ requestContext,
5895
+ priorCurrentTask: threadOMMetadata?.currentTask,
5896
+ priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
5897
+ wasTruncated
5898
+ }
5650
5899
  );
5651
5900
  if (!result.observations) {
5652
5901
  omDebug(`[OM:doAsyncBufferedObservation] empty observations returned, skipping buffer storage`);
@@ -5713,8 +5962,6 @@ ${result.observations}` : result.observations;
5713
5962
  }
5714
5963
  return `${activeObservations}
5715
5964
 
5716
- --- BUFFERED (pending activation) ---
5717
-
5718
5965
  ${bufferedObservations}`;
5719
5966
  }
5720
5967
  /**
@@ -6074,7 +6321,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6074
6321
  const threadMetadataMap = /* @__PURE__ */ new Map();
6075
6322
  for (const thread of allThreads) {
6076
6323
  const omMetadata = memory.getThreadOMMetadata(thread.metadata);
6077
- threadMetadataMap.set(thread.id, { lastObservedAt: omMetadata?.lastObservedAt });
6324
+ threadMetadataMap.set(thread.id, {
6325
+ lastObservedAt: omMetadata?.lastObservedAt,
6326
+ currentTask: omMetadata?.currentTask,
6327
+ suggestedResponse: omMetadata?.suggestedResponse
6328
+ });
6078
6329
  }
6079
6330
  const messagesByThread = /* @__PURE__ */ new Map();
6080
6331
  for (const thread of allThreads) {
@@ -6154,7 +6405,12 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6154
6405
  return;
6155
6406
  }
6156
6407
  }
6157
- const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
6408
+ const rawExistingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
6409
+ const { context: optimizedObservations, wasTruncated } = this.prepareObserverContext(
6410
+ rawExistingObservations,
6411
+ freshRecord ?? record
6412
+ );
6413
+ const existingObservations = optimizedObservations ?? rawExistingObservations;
6158
6414
  for (const threadId of threadOrder) {
6159
6415
  const msgs = messagesByThread.get(threadId);
6160
6416
  if (msgs && msgs.length > 0) {
@@ -6218,12 +6474,24 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6218
6474
  batches.push(currentBatch);
6219
6475
  }
6220
6476
  const batchPromises = batches.map(async (batch) => {
6477
+ const batchPriorMetadata = /* @__PURE__ */ new Map();
6478
+ for (const threadId of batch.threadIds) {
6479
+ const metadata = threadMetadataMap.get(threadId);
6480
+ if (metadata?.currentTask || metadata?.suggestedResponse) {
6481
+ batchPriorMetadata.set(threadId, {
6482
+ currentTask: metadata.currentTask,
6483
+ suggestedResponse: metadata.suggestedResponse
6484
+ });
6485
+ }
6486
+ }
6221
6487
  const batchResult = await this.callMultiThreadObserver(
6222
6488
  existingObservations,
6223
6489
  batch.threadMap,
6224
6490
  batch.threadIds,
6491
+ batchPriorMetadata,
6225
6492
  abortSignal,
6226
- requestContext
6493
+ requestContext,
6494
+ wasTruncated
6227
6495
  );
6228
6496
  return batchResult;
6229
6497
  });
@@ -6254,7 +6522,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6254
6522
  result
6255
6523
  });
6256
6524
  }
6257
- let currentObservations = existingObservations;
6525
+ let currentObservations = rawExistingObservations;
6258
6526
  let cycleObservationTokens = 0;
6259
6527
  for (const obsResult of observationResults) {
6260
6528
  if (!obsResult) continue;
@@ -6721,5 +6989,5 @@ exports.formatMessagesForObserver = formatMessagesForObserver;
6721
6989
  exports.hasCurrentTaskSection = hasCurrentTaskSection;
6722
6990
  exports.optimizeObservationsForContext = optimizeObservationsForContext;
6723
6991
  exports.parseObserverOutput = parseObserverOutput;
6724
- //# sourceMappingURL=chunk-SHID74TI.cjs.map
6725
- //# sourceMappingURL=chunk-SHID74TI.cjs.map
6992
+ //# sourceMappingURL=chunk-YPFNHFT6.cjs.map
6993
+ //# sourceMappingURL=chunk-YPFNHFT6.cjs.map