@mastra/memory 1.7.0 → 1.8.0-alpha.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 (29) hide show
  1. package/CHANGELOG.md +26 -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/reference-memory-observational-memory.md +2 -0
  10. package/dist/index.cjs +64 -368
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +64 -368
  14. package/dist/index.js.map +1 -1
  15. package/dist/{observational-memory-AU6MIH4Q.cjs → observational-memory-3HFM7PY2.cjs} +17 -17
  16. package/dist/{observational-memory-AU6MIH4Q.cjs.map → observational-memory-3HFM7PY2.cjs.map} +1 -1
  17. package/dist/{observational-memory-YRWU6CY3.js → observational-memory-XXD6E2SO.js} +3 -3
  18. package/dist/{observational-memory-YRWU6CY3.js.map → observational-memory-XXD6E2SO.js.map} +1 -1
  19. package/dist/processors/index.cjs +15 -15
  20. package/dist/processors/index.js +1 -1
  21. package/dist/processors/observational-memory/observational-memory.d.ts +21 -0
  22. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  23. package/dist/processors/observational-memory/observer-agent.d.ts +14 -2
  24. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/types.d.ts +7 -0
  26. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  27. package/package.json +6 -6
  28. package/dist/chunk-M7RAJAZ6.js.map +0 -1
  29. package/dist/chunk-SHID74TI.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.8.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added observer context optimization for Observational Memory. The `observation.previousObserverTokens` field reduces Observer input token costs for long-running conversations: ([#13568](https://github.com/mastra-ai/mastra/pull/13568))
8
+ - **previousObserverTokens** (default: `2000`): Truncates the 'Previous Observations' section to a token budget, keeping the most recent observations and automatically replacing already-reflected lines with the buffered reflection summary. Set to `0` to omit previous observations entirely, or `false` to disable truncation and keep the full observation history.
9
+
10
+ ```typescript
11
+ const memory = new Memory({
12
+ options: {
13
+ observationalMemory: {
14
+ model: 'google/gemini-2.5-flash',
15
+ observation: {
16
+ previousObserverTokens: 10_000,
17
+ },
18
+ },
19
+ },
20
+ });
21
+ ```
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [[`ea86967`](https://github.com/mastra-ai/mastra/commit/ea86967449426e0a3673253bd1c2c052a99d970d), [`db21c21`](https://github.com/mastra-ai/mastra/commit/db21c21a6ae5f33539262cc535342fa8757eb359), [`a1d6b9c`](https://github.com/mastra-ai/mastra/commit/a1d6b9c907c909f259632a7ea26e9e3c221fb691), [`11f5dbe`](https://github.com/mastra-ai/mastra/commit/11f5dbe9a1e7ad8ef3b1ea34fb4a9fa3631d1587), [`c562ec2`](https://github.com/mastra-ai/mastra/commit/c562ec228f1af63693e2984ffa9712aa6db8fea8), [`6751354`](https://github.com/mastra-ai/mastra/commit/67513544d1a64be891d9de7624d40aadc895d56e), [`c958cd3`](https://github.com/mastra-ai/mastra/commit/c958cd36627c1eea122ec241b2b15492977a263a), [`86f2426`](https://github.com/mastra-ai/mastra/commit/86f242631d252a172d2f9f9a2ea0feb8647a76b0), [`950eb07`](https://github.com/mastra-ai/mastra/commit/950eb07b7e7354629630e218d49550fdd299c452)]:
26
+ - @mastra/core@1.13.0-alpha.0
27
+ - @mastra/schema-compat@1.2.2-alpha.0
28
+
3
29
  ## 1.7.0
4
30
 
5
31
  ### Minor Changes
@@ -796,6 +796,11 @@ ${maybeTruncate(resultStr, maxLen)}`;
796
796
  ${maybeTruncate(argsStr, maxLen)}`;
797
797
  }
798
798
  const partType = part.type;
799
+ if (partType === "reasoning") {
800
+ const reasoning = part.reasoning;
801
+ if (reasoning) return maybeTruncate(reasoning, maxLen);
802
+ return "";
803
+ }
799
804
  if (partType === "image" || partType === "file") {
800
805
  const attachment = part;
801
806
  const inputAttachment = toObserverInputAttachmentPart(attachment);
@@ -810,6 +815,9 @@ ${maybeTruncate(argsStr, maxLen)}`;
810
815
  } else if (msg.content?.content) {
811
816
  content = maybeTruncate(msg.content.content, maxLen);
812
817
  }
818
+ if (!content && attachments.length === 0) {
819
+ return { text: "", attachments };
820
+ }
813
821
  return {
814
822
  text: `**${role}${timestampStr}:**
815
823
  ${content}`,
@@ -818,18 +826,21 @@ ${content}`,
818
826
  }
819
827
  function formatMessagesForObserver(messages, options) {
820
828
  const counter = { nextImageId: 1, nextFileId: 1 };
821
- return messages.map((msg) => formatObserverMessage(msg, counter, options).text).join("\n\n---\n\n");
829
+ return messages.map((msg) => formatObserverMessage(msg, counter, options).text).filter(Boolean).join("\n\n---\n\n");
822
830
  }
823
831
  function buildObserverHistoryMessage(messages) {
824
832
  const counter = { nextImageId: 1, nextFileId: 1 };
825
833
  const content = [{ type: "text", text: "## New Message History to Observe\n\n" }];
826
- messages.forEach((message, index) => {
834
+ let visibleCount = 0;
835
+ messages.forEach((message) => {
827
836
  const formatted = formatObserverMessage(message, counter);
828
- content.push({ type: "text", text: formatted.text });
829
- content.push(...formatted.attachments);
830
- if (index < messages.length - 1) {
837
+ if (!formatted.text && formatted.attachments.length === 0) return;
838
+ if (visibleCount > 0) {
831
839
  content.push({ type: "text", text: "\n\n---\n\n" });
832
840
  }
841
+ content.push({ type: "text", text: formatted.text });
842
+ content.push(...formatted.attachments);
843
+ visibleCount++;
833
844
  });
834
845
  return {
835
846
  role: "user",
@@ -858,16 +869,22 @@ The following messages are from ${threadOrder.length} different conversation thr
858
869
  threadOrder.forEach((threadId, threadIndex) => {
859
870
  const messages = messagesByThread.get(threadId);
860
871
  if (!messages || messages.length === 0) return;
861
- content.push({ type: "text", text: `<thread id="${threadId}">
862
- ` });
863
- messages.forEach((message, messageIndex) => {
872
+ const threadContent = [];
873
+ let visibleCount = 0;
874
+ messages.forEach((message) => {
864
875
  const formatted = formatObserverMessage(message, counter);
865
- content.push({ type: "text", text: formatted.text });
866
- content.push(...formatted.attachments);
867
- if (messageIndex < messages.length - 1) {
868
- content.push({ type: "text", text: "\n\n---\n\n" });
876
+ if (!formatted.text && formatted.attachments.length === 0) return;
877
+ if (visibleCount > 0) {
878
+ threadContent.push({ type: "text", text: "\n\n---\n\n" });
869
879
  }
880
+ threadContent.push({ type: "text", text: formatted.text });
881
+ threadContent.push(...formatted.attachments);
882
+ visibleCount++;
870
883
  });
884
+ if (visibleCount === 0) return;
885
+ content.push({ type: "text", text: `<thread id="${threadId}">
886
+ ` });
887
+ content.push(...threadContent);
871
888
  content.push({ type: "text", text: "\n</thread>" });
872
889
  if (threadIndex < threadOrder.length - 1) {
873
890
  content.push({ type: "text", text: "\n\n" });
@@ -878,7 +895,7 @@ The following messages are from ${threadOrder.length} different conversation thr
878
895
  content
879
896
  };
880
897
  }
881
- function buildMultiThreadObserverTaskPrompt(existingObservations) {
898
+ function buildMultiThreadObserverTaskPrompt(existingObservations, threadOrder, priorMetadataByThread, wasTruncated) {
882
899
  let prompt = "";
883
900
  if (existingObservations) {
884
901
  prompt += `## Previous Observations
@@ -890,6 +907,39 @@ ${existingObservations}
890
907
  `;
891
908
  prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
892
909
  }
910
+ const hasTruncatedObservations = wasTruncated ?? false;
911
+ const threadMetadataLines = threadOrder?.map((threadId) => {
912
+ const metadata = priorMetadataByThread?.get(threadId);
913
+ if (!metadata?.currentTask && !metadata?.suggestedResponse) {
914
+ return "";
915
+ }
916
+ const lines = [`- thread ${threadId}`];
917
+ if (metadata.currentTask) {
918
+ lines.push(` - prior current-task: ${metadata.currentTask}`);
919
+ }
920
+ if (metadata.suggestedResponse) {
921
+ lines.push(` - prior suggested-response: ${metadata.suggestedResponse}`);
922
+ }
923
+ return lines.join("\n");
924
+ }).filter(Boolean).join("\n");
925
+ if (threadMetadataLines) {
926
+ prompt += `## Prior Thread Metadata
927
+
928
+ ${threadMetadataLines}
929
+
930
+ `;
931
+ if (hasTruncatedObservations) {
932
+ prompt += `Previous observations were truncated for context budget reasons.
933
+ `;
934
+ prompt += `The main agent still has full memory context outside this observer window.
935
+ `;
936
+ }
937
+ prompt += `Use each thread's prior current-task and suggested-response as continuity hints, then update them based on that thread's new messages.
938
+
939
+ ---
940
+
941
+ `;
942
+ }
893
943
  prompt += `## Your Task
894
944
 
895
945
  `;
@@ -978,6 +1028,32 @@ ${existingObservations}
978
1028
  `;
979
1029
  prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
980
1030
  }
1031
+ const hasTruncatedObservations = options?.wasTruncated ?? false;
1032
+ const priorMetadataLines = [];
1033
+ if (options?.priorCurrentTask) {
1034
+ priorMetadataLines.push(`- prior current-task: ${options.priorCurrentTask}`);
1035
+ }
1036
+ if (options?.priorSuggestedResponse) {
1037
+ priorMetadataLines.push(`- prior suggested-response: ${options.priorSuggestedResponse}`);
1038
+ }
1039
+ if (priorMetadataLines.length > 0) {
1040
+ prompt += `## Prior Thread Metadata
1041
+
1042
+ ${priorMetadataLines.join("\n")}
1043
+
1044
+ `;
1045
+ if (hasTruncatedObservations) {
1046
+ prompt += `Previous observations were truncated for context budget reasons.
1047
+ `;
1048
+ prompt += `The main agent still has full memory context outside this observer window.
1049
+ `;
1050
+ }
1051
+ prompt += `Use the prior current-task and suggested-response as continuity hints, then update them based on the new messages.
1052
+
1053
+ ---
1054
+
1055
+ `;
1056
+ }
981
1057
  prompt += `## Your Task
982
1058
 
983
1059
  `;
@@ -3271,6 +3347,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3271
3347
  config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
3272
3348
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
3273
3349
  ),
3350
+ previousObserverTokens: config.observation?.previousObserverTokens ?? 2e3,
3274
3351
  instruction: config.observation?.instruction
3275
3352
  };
3276
3353
  this.reflectionConfig = {
@@ -3307,7 +3384,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3307
3384
  return {
3308
3385
  scope: this.scope,
3309
3386
  observation: {
3310
- messageTokens: this.observationConfig.messageTokens
3387
+ messageTokens: this.observationConfig.messageTokens,
3388
+ previousObserverTokens: this.observationConfig.previousObserverTokens
3311
3389
  },
3312
3390
  reflection: {
3313
3391
  observationTokens: this.reflectionConfig.observationTokens
@@ -3387,7 +3465,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3387
3465
  scope: this.scope,
3388
3466
  observation: {
3389
3467
  messageTokens: this.observationConfig.messageTokens,
3390
- model: observationModelName
3468
+ model: observationModelName,
3469
+ previousObserverTokens: this.observationConfig.previousObserverTokens
3391
3470
  },
3392
3471
  reflection: {
3393
3472
  observationTokens: this.reflectionConfig.observationTokens,
@@ -3452,6 +3531,13 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3452
3531
  );
3453
3532
  }
3454
3533
  }
3534
+ if (this.observationConfig.previousObserverTokens !== void 0 && this.observationConfig.previousObserverTokens !== false) {
3535
+ if (!Number.isFinite(this.observationConfig.previousObserverTokens) || this.observationConfig.previousObserverTokens < 0) {
3536
+ throw new Error(
3537
+ `observation.previousObserverTokens must be false or a finite number >= 0, got ${this.observationConfig.previousObserverTokens}`
3538
+ );
3539
+ }
3540
+ }
3455
3541
  if (this.reflectionConfig.bufferActivation !== void 0) {
3456
3542
  if (this.reflectionConfig.bufferActivation <= 0 || this.reflectionConfig.bufferActivation > 1) {
3457
3543
  throw new Error(
@@ -3849,6 +3935,141 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3849
3935
  }
3850
3936
  return result;
3851
3937
  }
3938
+ /**
3939
+ * Prepare optimized observer context by applying truncation and buffered-reflection inclusion.
3940
+ *
3941
+ * Returns the (possibly optimized) observations string to pass as "Previous Observations"
3942
+ * to the observer prompt. When no optimization options are set, returns the input unchanged.
3943
+ */
3944
+ prepareObserverContext(existingObservations, record) {
3945
+ const { previousObserverTokens } = this.observationConfig;
3946
+ const tokenBudget = previousObserverTokens === void 0 || previousObserverTokens === false ? void 0 : previousObserverTokens;
3947
+ if (tokenBudget === void 0) {
3948
+ return { context: existingObservations, wasTruncated: false };
3949
+ }
3950
+ const bufferedReflection = record?.bufferedReflection && record?.reflectedObservationLineCount ? record.bufferedReflection : void 0;
3951
+ if (!existingObservations) {
3952
+ return { context: bufferedReflection, wasTruncated: false };
3953
+ }
3954
+ let observations = existingObservations;
3955
+ if (bufferedReflection && record?.reflectedObservationLineCount) {
3956
+ const allLines = observations.split("\n");
3957
+ const unreflectedLines = allLines.slice(record.reflectedObservationLineCount);
3958
+ const unreflectedContent = unreflectedLines.join("\n").trim();
3959
+ observations = unreflectedContent ? `${bufferedReflection}
3960
+
3961
+ ${unreflectedContent}` : bufferedReflection;
3962
+ }
3963
+ let wasTruncated = false;
3964
+ if (tokenBudget !== void 0) {
3965
+ if (tokenBudget === 0) {
3966
+ return { context: "", wasTruncated: true };
3967
+ }
3968
+ const currentTokens = this.tokenCounter.countObservations(observations);
3969
+ if (currentTokens > tokenBudget) {
3970
+ observations = this.truncateObservationsToTokenBudget(observations, tokenBudget);
3971
+ wasTruncated = true;
3972
+ }
3973
+ }
3974
+ return { context: observations, wasTruncated };
3975
+ }
3976
+ /**
3977
+ * Truncate observations to fit within a token budget.
3978
+ *
3979
+ * Strategy:
3980
+ * 1. Keep a raw tail of recent observations (end of block).
3981
+ * 2. Add a truncation marker: [X observations truncated here], placed at the hidden gap.
3982
+ * 3. Try to preserve important observations (🔴) from older context, newest-first.
3983
+ * 4. Enforce that at least 50% of kept observations remain raw tail observations.
3984
+ */
3985
+ truncateObservationsToTokenBudget(observations, budget) {
3986
+ if (budget === 0) {
3987
+ return "";
3988
+ }
3989
+ const totalTokens = this.tokenCounter.countObservations(observations);
3990
+ if (totalTokens <= budget) {
3991
+ return observations;
3992
+ }
3993
+ const lines = observations.split("\n");
3994
+ const totalCount = lines.length;
3995
+ const lineTokens = new Array(totalCount);
3996
+ const isImportant = new Array(totalCount);
3997
+ for (let i = 0; i < totalCount; i++) {
3998
+ lineTokens[i] = this.tokenCounter.countString(lines[i]);
3999
+ isImportant[i] = lines[i].includes("\u{1F534}");
4000
+ }
4001
+ const suffixTokens = new Array(totalCount + 1);
4002
+ suffixTokens[totalCount] = 0;
4003
+ for (let i = totalCount - 1; i >= 0; i--) {
4004
+ suffixTokens[i] = suffixTokens[i + 1] + lineTokens[i];
4005
+ }
4006
+ const headImportantIndexes = [];
4007
+ const buildCandidateString = (tailStart, selectedImportantIndexes) => {
4008
+ const keptIndexes = [
4009
+ ...selectedImportantIndexes,
4010
+ ...Array.from({ length: totalCount - tailStart }, (_, i) => tailStart + i)
4011
+ ].sort((a, b) => a - b);
4012
+ if (keptIndexes.length === 0) {
4013
+ return `[${totalCount} observations truncated here]`;
4014
+ }
4015
+ const outputLines = [];
4016
+ let previousKeptIndex = -1;
4017
+ for (const keptIndex of keptIndexes) {
4018
+ const hiddenCount = keptIndex - previousKeptIndex - 1;
4019
+ if (hiddenCount === 1) {
4020
+ outputLines.push(lines[previousKeptIndex + 1]);
4021
+ } else if (hiddenCount > 1) {
4022
+ outputLines.push(`[${hiddenCount} observations truncated here]`);
4023
+ }
4024
+ outputLines.push(lines[keptIndex]);
4025
+ previousKeptIndex = keptIndex;
4026
+ }
4027
+ const trailingHiddenCount = totalCount - previousKeptIndex - 1;
4028
+ if (trailingHiddenCount === 1) {
4029
+ outputLines.push(lines[totalCount - 1]);
4030
+ } else if (trailingHiddenCount > 1) {
4031
+ outputLines.push(`[${trailingHiddenCount} observations truncated here]`);
4032
+ }
4033
+ return outputLines.join("\n");
4034
+ };
4035
+ const estimateKeptContentCost = (tailStart, selectedImportantIndexes) => {
4036
+ let cost = suffixTokens[tailStart];
4037
+ for (const idx of selectedImportantIndexes) {
4038
+ cost += lineTokens[idx];
4039
+ }
4040
+ return cost;
4041
+ };
4042
+ let bestCandidate;
4043
+ let bestImportantCount = -1;
4044
+ let bestRawTailLength = -1;
4045
+ for (let tailStart = 1; tailStart < totalCount; tailStart++) {
4046
+ if (isImportant[tailStart - 1]) {
4047
+ headImportantIndexes.push(tailStart - 1);
4048
+ }
4049
+ const rawTailLength = totalCount - tailStart;
4050
+ const maxImportantByRatio = rawTailLength;
4051
+ let importantToKeep = Math.min(headImportantIndexes.length, maxImportantByRatio);
4052
+ const getSelectedImportant = (count) => count > 0 ? headImportantIndexes.slice(Math.max(0, headImportantIndexes.length - count)) : [];
4053
+ while (importantToKeep > 0 && estimateKeptContentCost(tailStart, getSelectedImportant(importantToKeep)) > budget) {
4054
+ importantToKeep -= 1;
4055
+ }
4056
+ if (estimateKeptContentCost(tailStart, getSelectedImportant(importantToKeep)) > budget) {
4057
+ continue;
4058
+ }
4059
+ if (importantToKeep > bestImportantCount || importantToKeep === bestImportantCount && rawTailLength > bestRawTailLength) {
4060
+ const candidate = buildCandidateString(tailStart, getSelectedImportant(importantToKeep));
4061
+ if (this.tokenCounter.countObservations(candidate) <= budget) {
4062
+ bestCandidate = candidate;
4063
+ bestImportantCount = importantToKeep;
4064
+ bestRawTailLength = rawTailLength;
4065
+ }
4066
+ }
4067
+ }
4068
+ if (!bestCandidate) {
4069
+ return `[${totalCount} observations truncated here]`;
4070
+ }
4071
+ return bestCandidate;
4072
+ }
3852
4073
  /**
3853
4074
  * Call the Observer agent to extract observations.
3854
4075
  */
@@ -3857,12 +4078,17 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3857
4078
  const observerMessages = [
3858
4079
  {
3859
4080
  role: "user",
3860
- content: buildObserverTaskPrompt(existingObservations, options)
4081
+ content: buildObserverTaskPrompt(existingObservations, {
4082
+ skipContinuationHints: options?.skipContinuationHints,
4083
+ priorCurrentTask: options?.priorCurrentTask,
4084
+ priorSuggestedResponse: options?.priorSuggestedResponse,
4085
+ wasTruncated: options?.wasTruncated
4086
+ })
3861
4087
  },
3862
4088
  buildObserverHistoryMessage(messagesToObserve)
3863
4089
  ];
3864
4090
  const doGenerate = async () => {
3865
- return this.withAbortCheck(async () => {
4091
+ const result2 = await this.withAbortCheck(async () => {
3866
4092
  const streamResult = await agent.stream(observerMessages, {
3867
4093
  modelSettings: {
3868
4094
  ...this.observationConfig.modelSettings
@@ -3873,6 +4099,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3873
4099
  });
3874
4100
  return streamResult.getFullOutput();
3875
4101
  }, abortSignal);
4102
+ return result2;
3876
4103
  };
3877
4104
  let result = await doGenerate();
3878
4105
  let parsed = parseObserverOutput(result.text);
@@ -3903,17 +4130,23 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3903
4130
  * Returns per-thread results with observations, currentTask, and suggestedContinuation,
3904
4131
  * plus the total usage for the batch.
3905
4132
  */
3906
- async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext) {
4133
+ async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, abortSignal, requestContext, wasTruncated) {
4134
+ const systemPrompt = buildObserverSystemPrompt(true, this.observationConfig.instruction);
3907
4135
  const agent = new Agent({
3908
4136
  id: "multi-thread-observer",
3909
4137
  name: "multi-thread-observer",
3910
4138
  model: this.observationConfig.model,
3911
- instructions: buildObserverSystemPrompt(true, this.observationConfig.instruction)
4139
+ instructions: systemPrompt
3912
4140
  });
3913
4141
  const observerMessages = [
3914
4142
  {
3915
4143
  role: "user",
3916
- content: buildMultiThreadObserverTaskPrompt(existingObservations)
4144
+ content: buildMultiThreadObserverTaskPrompt(
4145
+ existingObservations,
4146
+ threadOrder,
4147
+ priorMetadataByThread,
4148
+ wasTruncated
4149
+ )
3917
4150
  },
3918
4151
  buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder)
3919
4152
  ];
@@ -3925,7 +4158,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3925
4158
  this.observedMessageIds.add(msg.id);
3926
4159
  }
3927
4160
  const doGenerate = async () => {
3928
- return this.withAbortCheck(async () => {
4161
+ const result2 = await this.withAbortCheck(async () => {
3929
4162
  const streamResult = await agent.stream(observerMessages, {
3930
4163
  modelSettings: {
3931
4164
  ...this.observationConfig.modelSettings
@@ -3936,6 +4169,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3936
4169
  });
3937
4170
  return streamResult.getFullOutput();
3938
4171
  }, abortSignal);
4172
+ return result2;
3939
4173
  };
3940
4174
  let result = await doGenerate();
3941
4175
  let parsed = parseMultiThreadObserverOutput(result.text);
@@ -5369,12 +5603,18 @@ ${newThreadSection}`;
5369
5603
  );
5370
5604
  }
5371
5605
  }
5372
- const result = await this.callObserver(
5606
+ const { context: observerContext, wasTruncated } = this.prepareObserverContext(
5373
5607
  freshRecord?.activeObservations ?? record.activeObservations,
5374
- messagesToObserve,
5375
- abortSignal,
5376
- { requestContext }
5608
+ freshRecord ?? record
5377
5609
  );
5610
+ const thread = await this.storage.getThreadById({ threadId });
5611
+ const threadOMMetadata = getThreadOMMetadata(thread?.metadata);
5612
+ const result = await this.callObserver(observerContext, messagesToObserve, abortSignal, {
5613
+ requestContext,
5614
+ priorCurrentTask: threadOMMetadata?.currentTask,
5615
+ priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
5616
+ wasTruncated
5617
+ });
5378
5618
  const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
5379
5619
  let newObservations;
5380
5620
  if (this.scope === "resource") {
@@ -5391,16 +5631,16 @@ ${result.observations}` : result.observations;
5391
5631
  const newMessageIds = messagesToObserve.map((m) => m.id);
5392
5632
  const existingIds = freshRecord?.observedMessageIds ?? record.observedMessageIds ?? [];
5393
5633
  const allObservedIds = [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])];
5394
- const thread = await this.storage.getThreadById({ threadId });
5395
- if (thread) {
5396
- const newMetadata = setThreadOMMetadata(thread.metadata, {
5634
+ const threadForMetadata = await this.storage.getThreadById({ threadId });
5635
+ if (threadForMetadata) {
5636
+ const newMetadata = setThreadOMMetadata(threadForMetadata.metadata, {
5397
5637
  suggestedResponse: result.suggestedContinuation,
5398
5638
  currentTask: result.currentTask,
5399
5639
  lastObservedMessageCursor: this.getLastObservedMessageCursor(messagesToObserve)
5400
5640
  });
5401
5641
  await this.storage.updateThread({
5402
5642
  id: threadId,
5403
- title: thread.title ?? "",
5643
+ title: threadForMetadata.title ?? "",
5404
5644
  metadata: newMetadata
5405
5645
  });
5406
5646
  }
@@ -5634,12 +5874,21 @@ ${result.observations}` : result.observations;
5634
5874
  const bufferedChunks = this.getBufferedChunks(record);
5635
5875
  const bufferedChunksText = bufferedChunks.map((c) => c.observations).join("\n\n");
5636
5876
  const combinedObservations = this.combineObservationsForBuffering(record.activeObservations, bufferedChunksText);
5877
+ const { context: observerContext, wasTruncated } = this.prepareObserverContext(combinedObservations, record);
5878
+ const thread = await this.storage.getThreadById({ threadId });
5879
+ const threadOMMetadata = getThreadOMMetadata(thread?.metadata);
5637
5880
  const result = await this.callObserver(
5638
- combinedObservations,
5881
+ observerContext,
5639
5882
  messagesToBuffer,
5640
5883
  void 0,
5641
5884
  // No abort signal for background ops
5642
- { skipContinuationHints: true, requestContext }
5885
+ {
5886
+ skipContinuationHints: true,
5887
+ requestContext,
5888
+ priorCurrentTask: threadOMMetadata?.currentTask,
5889
+ priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
5890
+ wasTruncated
5891
+ }
5643
5892
  );
5644
5893
  if (!result.observations) {
5645
5894
  omDebug(`[OM:doAsyncBufferedObservation] empty observations returned, skipping buffer storage`);
@@ -5706,8 +5955,6 @@ ${result.observations}` : result.observations;
5706
5955
  }
5707
5956
  return `${activeObservations}
5708
5957
 
5709
- --- BUFFERED (pending activation) ---
5710
-
5711
5958
  ${bufferedObservations}`;
5712
5959
  }
5713
5960
  /**
@@ -6067,7 +6314,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6067
6314
  const threadMetadataMap = /* @__PURE__ */ new Map();
6068
6315
  for (const thread of allThreads) {
6069
6316
  const omMetadata = getThreadOMMetadata(thread.metadata);
6070
- threadMetadataMap.set(thread.id, { lastObservedAt: omMetadata?.lastObservedAt });
6317
+ threadMetadataMap.set(thread.id, {
6318
+ lastObservedAt: omMetadata?.lastObservedAt,
6319
+ currentTask: omMetadata?.currentTask,
6320
+ suggestedResponse: omMetadata?.suggestedResponse
6321
+ });
6071
6322
  }
6072
6323
  const messagesByThread = /* @__PURE__ */ new Map();
6073
6324
  for (const thread of allThreads) {
@@ -6147,7 +6398,12 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6147
6398
  return;
6148
6399
  }
6149
6400
  }
6150
- const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
6401
+ const rawExistingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
6402
+ const { context: optimizedObservations, wasTruncated } = this.prepareObserverContext(
6403
+ rawExistingObservations,
6404
+ freshRecord ?? record
6405
+ );
6406
+ const existingObservations = optimizedObservations ?? rawExistingObservations;
6151
6407
  for (const threadId of threadOrder) {
6152
6408
  const msgs = messagesByThread.get(threadId);
6153
6409
  if (msgs && msgs.length > 0) {
@@ -6211,12 +6467,24 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6211
6467
  batches.push(currentBatch);
6212
6468
  }
6213
6469
  const batchPromises = batches.map(async (batch) => {
6470
+ const batchPriorMetadata = /* @__PURE__ */ new Map();
6471
+ for (const threadId of batch.threadIds) {
6472
+ const metadata = threadMetadataMap.get(threadId);
6473
+ if (metadata?.currentTask || metadata?.suggestedResponse) {
6474
+ batchPriorMetadata.set(threadId, {
6475
+ currentTask: metadata.currentTask,
6476
+ suggestedResponse: metadata.suggestedResponse
6477
+ });
6478
+ }
6479
+ }
6214
6480
  const batchResult = await this.callMultiThreadObserver(
6215
6481
  existingObservations,
6216
6482
  batch.threadMap,
6217
6483
  batch.threadIds,
6484
+ batchPriorMetadata,
6218
6485
  abortSignal,
6219
- requestContext
6486
+ requestContext,
6487
+ wasTruncated
6220
6488
  );
6221
6489
  return batchResult;
6222
6490
  });
@@ -6247,7 +6515,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6247
6515
  result
6248
6516
  });
6249
6517
  }
6250
- let currentObservations = existingObservations;
6518
+ let currentObservations = rawExistingObservations;
6251
6519
  let cycleObservationTokens = 0;
6252
6520
  for (const obsResult of observationResults) {
6253
6521
  if (!obsResult) continue;
@@ -6701,5 +6969,5 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6701
6969
  };
6702
6970
 
6703
6971
  export { OBSERVATIONAL_MEMORY_DEFAULTS, OBSERVATION_CONTEXT_INSTRUCTIONS, OBSERVATION_CONTEXT_PROMPT, OBSERVATION_CONTINUATION_HINT, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, extractCurrentTask, formatMessagesForObserver, hasCurrentTaskSection, optimizeObservationsForContext, parseObserverOutput };
6704
- //# sourceMappingURL=chunk-M7RAJAZ6.js.map
6705
- //# sourceMappingURL=chunk-M7RAJAZ6.js.map
6972
+ //# sourceMappingURL=chunk-SUU4IAZJ.js.map
6973
+ //# sourceMappingURL=chunk-SUU4IAZJ.js.map